Skip to content
Snippets Groups Projects
Commit 57f3f69d authored by Robert Martin-Legene's avatar Robert Martin-Legene
Browse files

Mejorando libbfa.py

parent a386bbb6
No related branches found
No related tags found
No related merge requests found
......@@ -126,3 +126,10 @@ print(majorcontr.functions.councilLength.call())
# Fix
print(majorcontr.functions.councilLength().call())
```
<a name="argument-after-asterisk-must-be-an-iterable-not-nonetype"></a>
gas required exceeds allowance
```python
# Error text: ValueError: {'code': -32000, 'message': 'gas required exceeds allowance (8000000)'}
```
Tu transacción va a fallar.
......@@ -8,6 +8,7 @@ import web3.exceptions
import web3.middleware
import ecdsa
from Crypto.Hash import keccak
import eth_account;
class Bfa:
......@@ -19,9 +20,9 @@ class Bfa:
elif 'HOME' in os.environ and os.path.isdir(os.path.join(os.environ['HOME'], 'bfa')):
os.putenv('BFAHOME', os.path.join(os.environ['HOME'], 'bfa'))
if isinstance(provider, str):
if provider in ['prod', 'bfa2018' 'network']:
if provider in ['prod', 'bfa2018', 'network', '']:
provider = web3.HTTPProvider("http://public.bfa.ar:8545/")
elif provider in ['test2network', 'test2', 'test', '']:
elif provider in ['test2network', 'test2', 'test']:
provider = web3.HTTPProvider("http://public.test2.bfa.ar:8545/")
elif provider.startswith('http://') or provider.startswith('https://'):
provider = web3.HTTPProvider(provider)
......@@ -35,20 +36,64 @@ class Bfa:
class Account:
def __init__(self, accountname: str, passphrase: str = ''):
def __init__(self, *args):
if len(args) == 0:
self.new()
return
accountname = None
passphrase = ''
if len(args) >= 1:
accountname = args[0]
if len(args) >= 2:
passphrase = args[1]
self.keyfile = None
self.privatekey = None
self.key = None
self.unlock(accountname, passphrase)
self.nonce = 0
def __repr__(self) -> str:
return str(dict(keyfile=self.keyfile, privatekey=str(self.privatekey)))
return str(dict(keyfile=self.keyfile, key=str(self.key)))
def __str__(self) -> str:
return self.walletaddress()
return self.address
def new(self):
acct = eth_account.Account.create()
self.address = acct.address
self.key = acct.key
self.save()
# print(acct.address)
# print(acct.key.hex())
return acct
def save(self):
dir = None
if os.getenv('BFANODEDIR') and os.path.exists(os.path.join(os.environ['BFANODEDIR'], 'keystore')):
dir = os.path.join(os.environ['BFANODEDIR'], 'keystore')
elif os.getenv('BFANETWORKDIR') and os.path.exists(os.path.join(os.environ['BFANETWORKDIR'], 'node', 'keystore')):
dir = os.path.join(os.environ['BFANETWORKDIR'], 'node', 'keystore')
elif os.getenv('BFAHOME') and os.path.exists(os.path.join(os.environ['BFAHOME'], 'network', 'node', 'keystore')):
dir = os.path.join(os.environ['BFAHOME'], 'network', 'node', 'keystore')
elif os.getenv('HOME'):
dir = os.path.join(os.environ['HOME'], '.ethereum', 'keystore')
os.makedirs(dir, mode=0o700, exist_ok=True)
else:
raise OSError('I have no idea where to save the file.')
self.keyfile = os.path.join(dir, self.address)
encrypted = eth_account.Account.encrypt(self.key.hex(), '')
try:
with open(self.keyfile, 'w', encoding='utf=8') as outfile:
outfile.write(str(encrypted).replace("'", '"'))
except:
# os.remove(filename)
raise
pass
@staticmethod
def findkeyfilesindirectories(pattern: str):
def findkeyfilesindirectories(**kwargs):
pattern = ''
if 'pattern' in kwargs:
pattern = kwargs['pattern']
# Remove leading 0x if present
if pattern.startswith('0x'):
pattern = pattern[2:]
......@@ -61,7 +106,7 @@ class Account:
elif os.getenv('BFANETWORKDIR'):
wheretolook += [os.path.join(os.environ['BFANETWORKDIR'], 'node', 'keystore')]
elif os.getenv('BFAHOME'):
wheretolook += [os.path.join(os.environ['BFANETWORKDIR'], 'network', 'node', 'keystore')]
wheretolook += [os.path.join(os.environ['BFAHOME'], 'network', 'node', 'keystore')]
if os.getenv('HOME'):
wheretolook += [
os.path.join(os.environ['HOME'], '.ethereum', 'keystore'),
......@@ -95,35 +140,33 @@ class Account:
if os.path.isfile(accountname):
self.keyfile = accountname
else:
self.keyfile = self.findkeyfilesindirectories(accountname)
self.keyfile = self.findkeyfilesindirectories(pattern=accountname)
if self.keyfile is None:
raise FileNotFoundError('The account was not found.')
with open(self.keyfile) as fd:
encrypted_key = fd.read()
try:
self.privatekey = web3.Web3().eth.account.decrypt(encrypted_key, passphrase)
self.key = web3.Web3().eth.account.decrypt(encrypted_key, passphrase)
except ValueError as exc:
raise ValueError(
'The passphrase given for the account is incorrect, '
'or the input file is not a valid key file.'
'The passphrase given for the account in file {} is incorrect, '
'or the input file is not a valid key file.'.format(self.keyfile)
) from exc
def publickey(self) -> bytearray:
key = ecdsa.SigningKey.from_string(self.privatekey, curve=ecdsa.SECP256k1).verifying_key
# returns bytestring
return key.to_string()
def walletaddress(self) -> str:
# ADDRESS
publickey = ecdsa.SigningKey.from_string(self.key, curve=ecdsa.SECP256k1).verifying_key
pkbytestring = publickey.to_string() # returns bytestring
ourhash = keccak.new(digest_bits=256)
ourhash.update(self.publickey())
ourhash.update(pkbytestring)
digest = ourhash.hexdigest()
return web3.Web3().toChecksumAddress('0x' + digest[-40:])
self.address = web3.Web3().toChecksumAddress('0x' + digest[-40:])
def transact(self, *args, **kwargs):
w3 = kwargs.get('web3')
tx_details = self.calculate_tx_details(w3, *args, **kwargs)
afunction = kwargs.get('function')
tx_details = self.calculate_tx_details(w3, afunction, *args)
txobj = afunction(*args).buildTransaction(tx_details)
txobj = tx_details
if afunction:
txobj = afunction(*args).buildTransaction(tx_details)
receipt = self.txsignsendwait(w3, txobj)
return receipt
......@@ -135,38 +178,53 @@ class Account:
return receipt
def signtx(self, tx: dict):
signed = web3.Web3().eth.account.sign_transaction(tx, self.privatekey)
signed = web3.Web3().eth.account.sign_transaction(tx, self.key)
return signed
def calculate_tx_details(self, w3: web3.Web3, afunction, *args) -> dict:
# if kwargs has extragas=50000 then we add that number to the amount
# of gas for the transaction
def calculate_tx_details(self, w3: web3.Web3, *args, **kwargs) -> dict:
# Nonce may have increase on the network without us noticing
# or past transactions may not yet have been mined (and a flooded
# txpool).
# This is a resonable fix (try not to send too many transactions)
# If you use waitForTransactionReceipt() between each transaction
# you will not have problems because of this.
self.nonce = max(self.nonce, w3.eth.getTransactionCount(self.walletaddress()))
afunction = kwargs.get('function')
self.nonce = max(self.nonce, w3.eth.getTransactionCount(self.address))
# Set minimum gasPrice to 1 Gwei, but allow more if the network says so.
details = {
'chainId': w3.eth.chain_id(),
'gasPrice': min(w3.toWei('1', 'gwei'), w3.eth.gasPrice),
'nonce': self.nonce,
'from': self.walletaddress(),
'from': self.address,
}
for kw in [ 'to', 'value' ]:
val = kwargs.get(kw)
if val is not None:
details[kw] = val
# Ask for balance, so we can tell it, in case we have an exception
balance = w3.eth.getBalance(self.walletaddress())
balance = w3.eth.getBalance(self.address)
try:
# Ask a node how much gas it would cost to deploy
gas = afunction(*args).estimateGas(details)
if afunction:
# Ask a node how much gas it would cost to deploy
gas = afunction(*args).estimateGas(details)
else:
gas = w3.eth.estimateGas(details)
except web3.exceptions.SolidityError as exc:
raise web3.exceptions.SolidityError(
'The Ethereum Virtual Machine probably did not like that.'
) from exc
except ValueError as exc:
raise ValueError(
'Your account may not have enough balance to work on this '
'network. It currently has {} wei.'
'Your transaction will fail. Maybe you are calling your '
'contract wrong or are not allowed to call the funcion '
'by a function modifier or '
'your account may not have enough balance to work on this '
'network. Your account balance is currently {} wei.'
.format(balance)) from exc
if kwargs.get('extragas') is not None:
gas += kwargs.get('extragas')
details['gas'] = gas
return details
......@@ -258,9 +316,11 @@ class CompiledContract:
with open('{}.sol'.format(self.name), 'r') as infile:
with open('contract.sol', 'w') as outfile:
outfile.write(infile.read())
solc = subprocess.run(self.dockerargs(), stdout=subprocess.PIPE, check=True)
# Don't leave too much mess.
os.remove('contract.sol')
try:
solc = subprocess.run(self.dockerargs(), stdout=subprocess.PIPE, check=True)
finally:
# Don't leave too much mess.
os.remove('contract.sol')
txt = solc.stdout
output = txt.decode('utf-8')
self.json = json.loads(output)
......@@ -273,7 +333,7 @@ class CompiledContract:
except FileNotFoundError:
return
if len(output) < 2:
print("The JSON file is too little ({} bytes read from {}).".format(len(output), filename), file=sys.stderr)
print("The JSON file is too small ({} bytes read from {}).".format(len(output), filename), file=sys.stderr)
raise NameError
try:
self.json = json.loads(output)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment