registry.py (3577B)
1 # standard imports 2 import os 3 import logging 4 import json 5 import hashlib 6 7 # third-party imports 8 from chainlib.eth.contract import ( 9 ABIContractEncoder, 10 ABIContractType, 11 abi_decode_single, 12 ) 13 from chainlib.chain import ChainSpec 14 from chainlib.eth.constant import ( 15 ZERO_ADDRESS, 16 ) 17 from chainlib.jsonrpc import JSONRPCRequest 18 from hexathon import ( 19 even, 20 add_0x, 21 ) 22 from chainlib.eth.tx import TxFactory 23 24 # local imports 25 from .encoding import ( 26 to_identifier, 27 from_identifier_hex, 28 ) 29 from .interface import Registry 30 31 logg = logging.getLogger(__name__) 32 33 moddir = os.path.dirname(__file__) 34 datadir = os.path.join(moddir, 'data') 35 36 37 class ContractRegistry(Registry): 38 39 default_chain_spec = None 40 __chains_registry = {} 41 42 __abi = None 43 __bytecode = None 44 45 @staticmethod 46 def abi(): 47 if ContractRegistry.__abi == None: 48 f = open(os.path.join(datadir, 'Registry.json'), 'r') 49 ContractRegistry.__abi = json.load(f) 50 f.close() 51 return ContractRegistry.__abi 52 53 54 @staticmethod 55 def bytecode(): 56 if ContractRegistry.__bytecode == None: 57 f = open(os.path.join(datadir, 'Registry.bin')) 58 ContractRegistry.__bytecode = f.read() 59 f.close() 60 return ContractRegistry.__bytecode 61 62 63 @staticmethod 64 def gas(code=None): 65 return 1500000 66 67 68 def constructor(self, sender_address, identifier_strings=[]): 69 # TODO: handle arrays in chainlib encode instead 70 enc = ABIContractEncoder() 71 enc.uint256(32) 72 enc.uint256(len(identifier_strings)) 73 for s in identifier_strings: 74 enc.bytes32(to_identifier(s)) 75 data = enc.get_contents() 76 77 tx = self.template(sender_address, None, use_nonce=True) 78 tx = self.set_code(tx, ContractRegistry.bytecode() + data) 79 logg.debug('bytecode {}\ndata {}\ntx {}'.format(ContractRegistry.bytecode(), data, tx)) 80 return self.build(tx) 81 82 83 @staticmethod 84 def address(address=None): 85 if address != None: 86 ContractRegistry.__address = address 87 return Registry.__address 88 89 90 @staticmethod 91 def load_for(chain_spec): 92 chain_str = str(chain_spec) 93 raise NotImplementedError() 94 95 96 def set(self, contract_address, sender_address, identifier_string, address): 97 if len(identifier_string) > 32: 98 raise ValueError('String too long') 99 enc = ABIContractEncoder() 100 enc.method('set') 101 enc.typ(ABIContractType.BYTES32) 102 enc.typ(ABIContractType.ADDRESS) 103 identifier = to_identifier(identifier_string) 104 enc.bytes32(identifier) 105 enc.address(address) 106 data = enc.encode() 107 tx = self.template(sender_address, contract_address, use_nonce=True) 108 tx = self.set_code(tx, data) 109 return self.build(tx) 110 111 112 def identifier(self, contract_address, idx, sender_address=ZERO_ADDRESS, id_generator=None): 113 j = JSONRPCRequest(id_generator) 114 o = j.template() 115 o['method'] = 'eth_call' 116 enc = ABIContractEncoder() 117 enc.method('identifiers') 118 enc.typ(ABIContractType.UINT256) 119 enc.uint256(idx) 120 data = add_0x(enc.encode()) 121 tx = self.template(sender_address, contract_address) 122 tx = self.set_code(tx, data) 123 o['params'].append(self.normalize(tx)) 124 o = j.finalize(o) 125 return o 126 127 128 @classmethod 129 def parse_identifier(self, v): 130 return from_identifier_hex(v)