chainlib-eth

Ethereum implementation of the chainlib interface
Log | Files | Refs | README | LICENSE

commit 01f55363db5a8323c289ab82b996b66ebd0e9fff
parent 794ccc58807234ba3095079520a28c22b43e9247
Author: lash <dev@holbrook.no>
Date:   Sat, 10 Dec 2022 12:52:47 +0000

Add support for different uint sizes

Diffstat:
MCHANGELOG | 4++++
Mchainlib/eth/contract.py | 59++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mchainlib/eth/jsonrpc.py | 1-
Msetup.cfg | 2+-
Mtests/test_abi.py | 59++++++++++++++++++++++++++++++++++++++++++-----------------
5 files changed, 103 insertions(+), 22 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG @@ -1,3 +1,7 @@ +- 0.4.7 + * Add support for all power of two uint sizes +- 0.4.6 + * Correct license classifier for python package - 0.4.5 * Change license to AGPL3 and copyright waived to public domain - 0.4.4 diff --git a/chainlib/eth/contract.py b/chainlib/eth/contract.py @@ -27,6 +27,11 @@ class ABIContractType(enum.Enum): BYTES32 = 'bytes32' BYTES4 = 'bytes4' UINT256 = 'uint256' + UINT128 = 'uint128' + UINT64 = 'uint64' + UINT32 = 'uint32' + UINT16 = 'uint16' + UINT8 = 'uint8' ADDRESS = 'address' STRING = 'string' BOOLEAN = 'bool' @@ -138,6 +143,20 @@ class ABIContractDecoder(ABIContract): return int(v, 16) + def uintn(self, v, bitsize): + # all uints no matter what size are returned to 256 bit boundary + return self.uint256(v) + + l = len(v) * 8 * 2 + if bitsize % 8 > 0: + raise ValueError('must be 8 multiple') + elif bitsize > 256: + raise ValueError('max 256 bits') + elif l < bitsize: + raise ValueError('input value length {} shorter than bitsize {}'.format(l, bitsize)) + return int(v[:int(bitsize/8)], 16) + + def bytes32(self, v): """Parse value as bytes32. @@ -211,9 +230,21 @@ class ABIContractDecoder(ABIContract): r = [] logg.debug('contents {}'.format(self.contents)) for i in range(len(self.types)): - m = getattr(self, self.types[i]) - s = self.contents[i] - r.append(m(s)) + m = None + try: + m = getattr(self, self.types[i]) + logg.debug('executing module {}'.format(m)) + s = self.contents[i] + r.append(m(s)) + except AttributeError as e: + if len(self.types[i]) > 4 and self.types[i][:4] == 'uint': + m = getattr(self, 'uintn') + s = self.contents[i] + v = m(s, int(self.types[i][4:])) + r.append(v) + else: + raise e + return r @@ -306,6 +337,28 @@ class ABIContractEncoder(ABIMethodEncoder): self.__log_latest(v) + def uintn(self, v, bitsize): + """Encode value to uint256 and add to input value vector. + + :param v: Integer value + :type v: int + """ + if bitsize % 8 > 0: + raise ValueError('must be 8 multiple') + elif bitsize > 256: + raise ValueError('max 256 bits') + + # encodings of all uint types are padded to word boundary + return self.uint256(v) + + v = int(v) + b = v.to_bytes(int(bitsize / 8), 'big') + self.contents.append(b.hex()) + typ = getattr(ABIContractType, 'UINT' + str(bitsize)) + self.types.append(typ) + self.__log_latest(v) + + def bool(self, v): """Alias of chainlib.eth.contract.ABIContractEncoder.boolean. """ diff --git a/chainlib/eth/jsonrpc.py b/chainlib/eth/jsonrpc.py @@ -18,7 +18,6 @@ # external imports from hexathon import add_0x - def to_blockheight_param(height): """Translate blockheight specifier to Ethereum json-rpc blockheight argument. diff --git a/setup.cfg b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = chainlib-eth -version = 0.4.6 +version = 0.4.7 description = Ethereum implementation of the chainlib interface author = Louis Holbrook author_email = dev@holbrook.no diff --git a/tests/test_abi.py b/tests/test_abi.py @@ -1,29 +1,54 @@ +# standard imports +import unittest + +# local imports from chainlib.eth.contract import ( ABIContractEncoder, ABIContractType, ) -def test_abi_param(): +class TestContract(unittest.TestCase): + + def test_abi_param(self): + e = ABIContractEncoder() + e.uint256(42) + e.bytes32('0x666f6f') + e.address('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') + e.method('foo') + e.typ(ABIContractType.UINT256) + e.typ(ABIContractType.BYTES32) + e.typ(ABIContractType.ADDRESS) + + self.assertEqual(e.types[0], ABIContractType.UINT256) + self.assertEqual(e.types[1], ABIContractType.BYTES32) + self.assertEqual(e.types[2], ABIContractType.ADDRESS) + self.assertEqual(e.contents[0], '000000000000000000000000000000000000000000000000000000000000002a') + self.assertEqual(e.contents[1], '0000000000000000000000000000000000000000000000000000000000666f6f') + self.assertEqual(e.contents[2], '000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef') + + self.assertEqual(e.get(), 'a08f54bb000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000666f6f000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef') + - e = ABIContractEncoder() - e.uint256(42) - e.bytes32('0x666f6f') - e.address('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') - e.method('foo') - e.typ(ABIContractType.UINT256) - e.typ(ABIContractType.BYTES32) - e.typ(ABIContractType.ADDRESS) + def test_abi_uintn(self): + e = ABIContractEncoder() + e.uintn(42, 16) + e.uintn(13, 32) + e.uintn(666, 64) + e.uintn(1337, 128) + e.method('foo') + e.typ(ABIContractType.UINT16) + e.typ(ABIContractType.UINT32) + e.typ(ABIContractType.UINT64) + e.typ(ABIContractType.UINT128) - assert e.types[0] == ABIContractType.UINT256 - assert e.types[1] == ABIContractType.BYTES32 - assert e.types[2] == ABIContractType.ADDRESS - assert e.contents[0] == '000000000000000000000000000000000000000000000000000000000000002a' - assert e.contents[1] == '0000000000000000000000000000000000000000000000000000000000666f6f' - assert e.contents[2] == '000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' + self.assertEqual(e.contents[0], '000000000000000000000000000000000000000000000000000000000000002a') + self.assertEqual(e.contents[1], '000000000000000000000000000000000000000000000000000000000000000d') + self.assertEqual(e.contents[2], '000000000000000000000000000000000000000000000000000000000000029a') + self.assertEqual(e.contents[3], '0000000000000000000000000000000000000000000000000000000000000539') - assert e.get() == 'a08f54bb000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000666f6f000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' + self.assertEqual(e.get(), '5e260038000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000029a0000000000000000000000000000000000000000000000000000000000000539') if __name__ == '__main__': - test_abi_param() + unittest.main()