diff --git a/CHANGELOG b/CHANGELOG index 25f4354..3fa3e25 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ * 0.4.13-unreleased - Implement DictKeystore - Remove unused insert_key method in keystore interface - - Add transaction executor helper + - Add transaction executor helper, with adapter helper for eth * 0.4.12 - Enforce hex strings in signer backend for sign message * 0.4.11 diff --git a/crypto_dev_signer/eth/helper/__init__.py b/crypto_dev_signer/eth/helper/__init__.py new file mode 100644 index 0000000..e41e515 --- /dev/null +++ b/crypto_dev_signer/eth/helper/__init__.py @@ -0,0 +1 @@ +from .tx import EthTxExecutor diff --git a/crypto_dev_signer/eth/helper/tx.py b/crypto_dev_signer/eth/helper/tx.py new file mode 100644 index 0000000..6651a6c --- /dev/null +++ b/crypto_dev_signer/eth/helper/tx.py @@ -0,0 +1,49 @@ +# standard imports +import logging + +# local imports +from crypto_dev_signer.helper import TxExecutor + +logg = logging.getLogger() +logging.getLogger('web3').setLevel(logging.CRITICAL) +logging.getLogger('urllib3').setLevel(logging.CRITICAL) + + +class EthTxExecutor(TxExecutor): + + def __init__(self, w3, sender, signer, chain_id, verifier=None, block=False): + self.w3 = w3 + nonce = self.w3.eth.getTransactionCount(sender, 'pending') + super(EthTxExecutor, self).__init__(sender, signer, self.translator, self.dispatcher, self.reporter, nonce, chain_id, self.fee_helper, self.fee_price_helper, verifier, block) + + + def fee_helper(self, tx): + estimate = self.w3.eth.estimateGas(tx) + if estimate < 21000: + estimate = 21000 + logg.debug('estimate {} {}'.format(tx, estimate)) + return estimate + + + def fee_price_helper(self): + return self.w3.eth.gasPrice + + + def dispatcher(self, tx): + return self.w3.eth.sendRawTransaction(tx) + + + def reporter(self, tx): + return self.w3.eth.getTransactionReceipt(tx) + + + def translator(self, tx): + if tx.get('feePrice') != None: + tx['gasPrice'] = tx['feePrice'] + del tx['feePrice'] + + if tx.get('feeUnits') != None: + tx['gas'] = tx['feeUnits'] + del tx['feeUnits'] + + return tx diff --git a/crypto_dev_signer/helper/tx.py b/crypto_dev_signer/helper/tx.py index db98dcf..e2ebec7 100644 --- a/crypto_dev_signer/helper/tx.py +++ b/crypto_dev_signer/helper/tx.py @@ -13,8 +13,9 @@ logg = logging.getLogger() class TxExecutor: - def __init__(self, sender, signer, dispatcher, reporter, nonce, chain_id, fee_helper=None, fee_price_helper=None, verifier=None, block=False): + def __init__(self, sender, signer, translator, dispatcher, reporter, nonce, chain_id, fee_helper=None, fee_price_helper=None, verifier=None, block=False): self.sender = sender + self.translator = translator self.nonce = nonce self.signer = signer self.dispatcher = dispatcher @@ -33,7 +34,7 @@ class TxExecutor: self.verifier = verifier - def noop_fee_helper(self, sender, code, inputs): + def noop_fee_helper(self, tx): return 1 @@ -45,21 +46,29 @@ class TxExecutor: return rcpt + def noop_translator(self, tx): + return tx + + def sign_and_send(self, builder, force_wait=False): - fee_units = self.fee_helper(self.sender, None, None) + + fee_price = self.fee_price_helper() tx_tpl = { 'from': self.sender, 'chainId': self.chain_id, - 'feeUnits': fee_units, - 'feePrice': self.fee_price_helper(), + 'feeUnits': 0, #fee_units, + 'feePrice': fee_price, 'nonce': self.nonce, } - tx = tx_tpl + tx = self.translator(tx_tpl) for b in builder: tx = b(tx) + tx['feeUnits'] = self.fee_helper(tx) + tx = self.translator(tx) + logg.debug('from {} nonce {} tx {}'.format(self.sender, self.nonce, tx)) chain_tx = EIP155Transaction(tx, self.nonce, self.chain_id) diff --git a/setup.py b/setup.py index 468b846..4a01c3c 100644 --- a/setup.py +++ b/setup.py @@ -24,13 +24,14 @@ f.close() setup( name="crypto-dev-signer", - version="0.4.13b1", + version="0.4.13b2", description="A signer and keystore daemon and library for cryptocurrency software development", author="Louis Holbrook", author_email="dev@holbrook.no", packages=[ 'crypto_dev_signer.eth.signer', 'crypto_dev_signer.eth.web3ext', + 'crypto_dev_signer.eth.helper', 'crypto_dev_signer.eth', 'crypto_dev_signer.keystore', 'crypto_dev_signer.runnable', diff --git a/test/test_helper.py b/test/test_helper.py index 887a8ea..f83ed6e 100644 --- a/test/test_helper.py +++ b/test/test_helper.py @@ -3,10 +3,14 @@ import unittest import logging import os +# third-party imports +import web3 + # local imports from crypto_dev_signer.keystore import DictKeystore from crypto_dev_signer.eth.signer import ReferenceSigner from crypto_dev_signer.helper import TxExecutor +from crypto_dev_signer.eth.helper import EthTxExecutor logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() @@ -29,21 +33,23 @@ class MockEthTxBackend: def fee_price_helper(self): return 21 - def fee_helper(self, sender, code, inputs): - logg.debug('fee helper code {} inputs {}'.format(code, inputs)) + def fee_helper(self, tx): + logg.debug('fee helper tx {}'.format(tx)) return 2 def builder(self, tx): - return { - 'from': tx['from'], - 'to': '0x' + os.urandom(20).hex(), - 'data': '', - 'gasPrice': tx['feePrice'], - 'gas': tx['feeUnits'], - } + return tx def builder_two(self, tx): - tx['value'] = 1024 + tx['value'] = 10243 + tx['to'] = web3.Web3.toChecksumAddress('0x' + os.urandom(20).hex()) + tx['data'] = '' + if tx.get('feePrice') != None: + tx['gasPrice'] = tx['feePrice'] + del tx['feePrice'] + if tx.get('feeUnits') != None: + tx['gas'] = tx['feeUnits'] + del tx['feeUnits'] return tx @@ -66,10 +72,21 @@ class TestHelper(unittest.TestCase): def test_helper(self): backend = MockEthTxBackend() - executor = TxExecutor(self.address_hex, self.signer, backend.dispatcher, backend.reporter, 666, 13, backend.fee_helper, backend.fee_price_helper, backend.verifier) + executor = TxExecutor(self.address_hex, self.signer, backend.builder, backend.dispatcher, backend.reporter, 666, 13, backend.fee_helper, backend.fee_price_helper, backend.verifier) tx_ish = {'from': self.address_hex} - executor.sign_and_send([backend.builder, backend.builder_two]) + executor.sign_and_send([backend.builder_two]) + + + def test_eth_helper(self): + backend = MockEthTxBackend() + w3 = web3.Web3(web3.Web3.HTTPProvider('http://localhost:8545')) + executor = EthTxExecutor(w3, self.address_hex, self.signer, 8996) + + tx_ish = {'from': self.address_hex} + #executor.sign_and_send([backend.builder, backend.builder_two]) + with self.assertRaises(ValueError): + executor.sign_and_send([backend.builder_two]) if __name__ == '__main__':