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:
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()