Add EIP165 support
This commit is contained in:
parent
3241dfa9c5
commit
219c24b624
@ -24,6 +24,7 @@ re_method = r'^[a-zA-Z0-9_]+$'
|
|||||||
class ABIContractType(enum.Enum):
|
class ABIContractType(enum.Enum):
|
||||||
|
|
||||||
BYTES32 = 'bytes32'
|
BYTES32 = 'bytes32'
|
||||||
|
BYTES4 = 'bytes4'
|
||||||
UINT256 = 'uint256'
|
UINT256 = 'uint256'
|
||||||
ADDRESS = 'address'
|
ADDRESS = 'address'
|
||||||
STRING = 'string'
|
STRING = 'string'
|
||||||
@ -154,6 +155,12 @@ class ABIContractEncoder:
|
|||||||
self.__log_latest(v)
|
self.__log_latest(v)
|
||||||
|
|
||||||
|
|
||||||
|
def bytes4(self, v):
|
||||||
|
self.bytes_fixed(4, v)
|
||||||
|
self.types.append(ABIContractType.BYTES4)
|
||||||
|
self.__log_latest(v)
|
||||||
|
|
||||||
|
|
||||||
def string(self, v):
|
def string(self, v):
|
||||||
b = v.encode('utf-8')
|
b = v.encode('utf-8')
|
||||||
l = len(b)
|
l = len(b)
|
||||||
@ -186,8 +193,7 @@ class ABIContractEncoder:
|
|||||||
v = pad(b.hex(), mx)
|
v = pad(b.hex(), mx)
|
||||||
else:
|
else:
|
||||||
raise ValueError('invalid input {}'.format(typ))
|
raise ValueError('invalid input {}'.format(typ))
|
||||||
self.contents.append(v)
|
self.contents.append(v.ljust(64, '0'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_method(self):
|
def get_method(self):
|
||||||
|
38
chainlib/eth/eip165.py
Normal file
38
chainlib/eth/eip165.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# standard imports
|
||||||
|
from chainlib.eth.constant import ZERO_ADDRESS
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from chainlib.jsonrpc import (
|
||||||
|
jsonrpc_template,
|
||||||
|
)
|
||||||
|
from hexathon import (
|
||||||
|
add_0x,
|
||||||
|
)
|
||||||
|
from chainlib.eth.contract import (
|
||||||
|
ABIContractEncoder,
|
||||||
|
ABIContractDecoder,
|
||||||
|
ABIContractType,
|
||||||
|
abi_decode_single,
|
||||||
|
)
|
||||||
|
from chainlib.eth.tx import TxFactory
|
||||||
|
|
||||||
|
|
||||||
|
class EIP165(TxFactory):
|
||||||
|
|
||||||
|
def supports_interface(self, contract_address, interface_sum, sender_address=ZERO_ADDRESS):
|
||||||
|
o = jsonrpc_template()
|
||||||
|
o['method'] = 'eth_call'
|
||||||
|
enc = ABIContractEncoder()
|
||||||
|
enc.method('supportsInterface')
|
||||||
|
enc.typ(ABIContractType.BYTES4)
|
||||||
|
enc.bytes4(interface_sum)
|
||||||
|
data = add_0x(enc.get())
|
||||||
|
tx = self.template(sender_address, contract_address)
|
||||||
|
tx = self.set_code(tx, data)
|
||||||
|
o['params'].append(self.normalize(tx))
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse_supports_interface(self, v):
|
||||||
|
return abi_decode_single(ABIContractType.BOOLEAN, v)
|
@ -168,6 +168,7 @@ def transaction(hsh):
|
|||||||
o['params'].append(add_0x(hsh))
|
o['params'].append(add_0x(hsh))
|
||||||
return o
|
return o
|
||||||
|
|
||||||
|
|
||||||
def transaction_by_block(hsh, idx):
|
def transaction_by_block(hsh, idx):
|
||||||
o = jsonrpc_template()
|
o = jsonrpc_template()
|
||||||
o['method'] = 'eth_getTransactionByBlockHashAndIndex'
|
o['method'] = 'eth_getTransactionByBlockHashAndIndex'
|
||||||
|
@ -27,7 +27,7 @@ from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
|
|||||||
from crypto_dev_signer.encoding import private_key_to_address
|
from crypto_dev_signer.encoding import private_key_to_address
|
||||||
|
|
||||||
|
|
||||||
logg = logging.getLogger()
|
logg = logging.getLogger().getChild(__name__)
|
||||||
|
|
||||||
test_pk = bytes.fromhex('5087503f0a9cc35b38665955eb830c63f778453dd11b8fa5bd04bc41fd2cc6d6')
|
test_pk = bytes.fromhex('5087503f0a9cc35b38665955eb830c63f778453dd11b8fa5bd04bc41fd2cc6d6')
|
||||||
|
|
||||||
@ -99,8 +99,12 @@ class TestRPCConnection(RPCConnection):
|
|||||||
|
|
||||||
def eth_getTransactionByBlock(self, p):
|
def eth_getTransactionByBlock(self, p):
|
||||||
block = self.eth_getBlockByHash(p)
|
block = self.eth_getBlockByHash(p)
|
||||||
tx_hash = block['transactions'][p[1]]
|
try:
|
||||||
tx = self.eth_getTransaction([tx_hash])
|
tx_index = int(p[1], 16)
|
||||||
|
except TypeError:
|
||||||
|
tx_index = int(p[1])
|
||||||
|
tx_hash = block['transactions'][tx_index]
|
||||||
|
tx = self.eth_getTransactionByHash([tx_hash])
|
||||||
return tx
|
return tx
|
||||||
|
|
||||||
def eth_getBalance(self, p):
|
def eth_getBalance(self, p):
|
||||||
@ -120,6 +124,14 @@ class TestRPCConnection(RPCConnection):
|
|||||||
return tx
|
return tx
|
||||||
|
|
||||||
|
|
||||||
|
def eth_getTransactionByBlockHashAndIndex(self, p):
|
||||||
|
#logg.debug('p {}'.format(p))
|
||||||
|
#block = self.eth_getBlockByHash(p[0])
|
||||||
|
#tx = block.transactions[p[1]]
|
||||||
|
#return eth_getTransactionByHash(tx[0])
|
||||||
|
return self.eth_getTransactionByBlock(p)
|
||||||
|
|
||||||
|
|
||||||
def eth_getTransactionReceipt(self, p):
|
def eth_getTransactionReceipt(self, p):
|
||||||
rcpt = self.backend.get_transaction_receipt(p[0])
|
rcpt = self.backend.get_transaction_receipt(p[0])
|
||||||
if rcpt.get('block_number') == None:
|
if rcpt.get('block_number') == None:
|
||||||
|
@ -19,7 +19,10 @@ from .base import (
|
|||||||
EthTesterSigner,
|
EthTesterSigner,
|
||||||
TestRPCConnection,
|
TestRPCConnection,
|
||||||
)
|
)
|
||||||
from chainlib.connection import RPCConnection
|
from chainlib.connection import (
|
||||||
|
RPCConnection,
|
||||||
|
ConnType,
|
||||||
|
)
|
||||||
from chainlib.eth.address import to_checksum_address
|
from chainlib.eth.address import to_checksum_address
|
||||||
from chainlib.chain import ChainSpec
|
from chainlib.chain import ChainSpec
|
||||||
|
|
||||||
@ -66,8 +69,10 @@ class EthTesterCase(unittest.TestCase):
|
|||||||
def rpc_with_tester(chain_spec=self.chain_spec, url=None):
|
def rpc_with_tester(chain_spec=self.chain_spec, url=None):
|
||||||
return self.rpc
|
return self.rpc
|
||||||
|
|
||||||
RPCConnection.register_location('custom', self.chain_spec, tag='default', constructor=rpc_with_tester, exist_ok=True)
|
RPCConnection.register_constructor(ConnType.CUSTOM, rpc_with_tester, tag='default')
|
||||||
RPCConnection.register_location('custom', self.chain_spec, tag='signer', constructor=rpc_with_tester, exist_ok=True)
|
RPCConnection.register_constructor(ConnType.CUSTOM, rpc_with_tester, tag='signer')
|
||||||
|
RPCConnection.register_location('custom', self.chain_spec, tag='default', exist_ok=True)
|
||||||
|
RPCConnection.register_location('custom', self.chain_spec, tag='signer', exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = chainlib
|
name = chainlib
|
||||||
version = 0.0.2a19
|
version = 0.0.2b1
|
||||||
description = Generic blockchain access library and tooling
|
description = Generic blockchain access library and tooling
|
||||||
author = Louis Holbrook
|
author = Louis Holbrook
|
||||||
author_email = dev@holbrook.no
|
author_email = dev@holbrook.no
|
||||||
|
62
tests/test_eip165.py
Normal file
62
tests/test_eip165.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# standard imports
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from chainlib.eth.unittest.ethtester import EthTesterCase
|
||||||
|
from chainlib.eth.nonce import RPCNonceOracle
|
||||||
|
from chainlib.eth.gas import OverrideGasOracle
|
||||||
|
from chainlib.connection import RPCConnection
|
||||||
|
from chainlib.eth.tx import (
|
||||||
|
TxFactory,
|
||||||
|
receipt,
|
||||||
|
)
|
||||||
|
from chainlib.eth.eip165 import EIP165
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logg = logging.getLogger()
|
||||||
|
|
||||||
|
script_dir = os.path.realpath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
class TestSupports(EthTesterCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestSupports, self).setUp()
|
||||||
|
#nonce_oracle = TestNonceOracle(self.accounts[0])
|
||||||
|
self.conn = RPCConnection.connect(self.chain_spec, 'default')
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], self.conn)
|
||||||
|
|
||||||
|
f = open(os.path.join(script_dir, 'testdata', 'Supports.bin'))
|
||||||
|
code = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
txf = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
tx = txf.template(self.accounts[0], None, use_nonce=True)
|
||||||
|
tx = txf.set_code(tx, code)
|
||||||
|
(tx_hash_hex, o) = txf.build(tx)
|
||||||
|
|
||||||
|
r = self.conn.do(o)
|
||||||
|
logg.debug('deployed with hash {}'.format(r))
|
||||||
|
|
||||||
|
o = receipt(tx_hash_hex)
|
||||||
|
r = self.conn.do(o)
|
||||||
|
self.address = r['contract_address']
|
||||||
|
|
||||||
|
|
||||||
|
def test_supports(self):
|
||||||
|
gas_oracle = OverrideGasOracle(limit=100000, conn=self.conn)
|
||||||
|
c = EIP165(self.chain_spec, gas_oracle=gas_oracle)
|
||||||
|
o = c.supports_interface(self.address, '0xdeadbeef', sender_address=self.accounts[0])
|
||||||
|
r = self.conn.do(o)
|
||||||
|
v = c.parse_supports_interface(r)
|
||||||
|
self.assertEqual(v, 1)
|
||||||
|
|
||||||
|
o = c.supports_interface(self.address, '0xbeeffeed', sender_address=self.accounts[0])
|
||||||
|
r = self.conn.do(o)
|
||||||
|
v = c.parse_supports_interface(r)
|
||||||
|
self.assertEqual(v, 0)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
1
tests/testdata/Supports.bin
vendored
Normal file
1
tests/testdata/Supports.bin
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
608060405234801561001057600080fd5b506101c9806100206000396000f3fe608060405234801561001057600080fd5b5060043610610048576000357c01000000000000000000000000000000000000000000000000000000009004806301ffc9a71461004d575b600080fd5b610067600480360381019061006291906100f1565b61007d565b6040516100749190610129565b60405180910390f35b600063deadbeef7c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156100d257600190506100d7565b600090505b919050565b6000813590506100eb8161017c565b92915050565b60006020828403121561010357600080fd5b6000610111848285016100dc565b91505092915050565b61012381610144565b82525050565b600060208201905061013e600083018461011a565b92915050565b60008115159050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61018581610150565b811461019057600080fd5b5056fea264697066735822122073144ec34d71a86680325f1aee9a3b9041e22c422e7b1b82d409b303d52192de64736f6c63430008030033
|
10
tests/testdata/Supports.sol
vendored
Normal file
10
tests/testdata/Supports.sol
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
contract Supports {
|
||||||
|
function supportsInterface(bytes4 _sum) public pure returns (bool) {
|
||||||
|
if (_sum == 0xdeadbeef) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user