def signTransaction(self, transaction_dict, private_key): ''' @param private_key in bytes, str, or int. ''' assert isinstance(transaction_dict, Mapping) account = self.privateKeyToAccount(private_key) # sign transaction ( v, r, s, rlp_encoded, ) = sign_transaction_dict(account._key_obj, transaction_dict) transaction_hash = keccak(rlp_encoded) return AttributeDict({ 'rawTransaction': HexBytes(rlp_encoded), 'hash': HexBytes(transaction_hash), 'r': r, 's': s, 'v': v, })
def signTransaction(self, transaction_dict, private_key): ''' @param private_key in bytes, str, or int. In Python 2, a bytes, unicode or str object will be interpreted as hexstr In Python 3, only a str object will be interpreted as hexstr ''' assert isinstance(transaction_dict, Mapping) account = self.privateKeyToAccount(private_key) # sign transaction ( v, r, s, rlp_encoded, ) = sign_transaction_dict(account._key_obj, transaction_dict) transaction_hash = keccak(rlp_encoded) return AttributeDict({ 'rawTransaction': HexBytes(rlp_encoded), 'hash': HexBytes(transaction_hash), 'r': HexBytes(r), 's': HexBytes(s), 'v': v, })
def signTransaction(self, transaction_dict, private_key): ''' @param private_key in bytes, str, or int. In Python 2, a bytes, unicode or str object will be interpreted as hexstr In Python 3, only a str object will be interpreted as hexstr ''' assert isinstance(transaction_dict, Mapping) account = self.privateKeyToAccount(private_key) # sign transaction ( v, r, s, transaction_hash, rlp_encoded, ) = sign_transaction_dict(account._key_obj, transaction_dict) # format most returned elements as hex signature_info = { key: to_hex_with_size(val, 256) # minimum size is 32 bytes for key, val in ( ('rawTransaction', rlp_encoded), ('hash', transaction_hash), ('r', r), ('s', s), ) } signature_info['v'] = v return AttributeDict(signature_info)
def sign(self, message=None, private_key=None, message_hexstr=None, message_text=None): ''' @param private_key in bytes, str, or int. In Python 2, a bytes, unicode or str object will be interpreted as hexstr In Python 3, only a str object will be interpreted as hexstr ''' msg_bytes = to_bytes(message, hexstr=message_hexstr, text=message_text) msg_hash = self.hashMessage(msg_bytes) key_bytes = hexstr_if_str(to_bytes, private_key) key = self._keys.PrivateKey(key_bytes) (v, r, s, eth_signature_bytes) = sign_message_hash(key, msg_hash) (r_hex, s_hex, eth_signature_hex) = map(to_hex, (r, s, eth_signature_bytes)) return AttributeDict({ 'message': msg_bytes, 'messageHash': msg_hash, 'r': r_hex, 's': s_hex, 'v': v, 'signature': eth_signature_hex, })
def _update_channel_data_from_blockchain(self, channel): channel_blockchain_data = self.mpe_contract.functions.channels( channel["channelId"]).call() channel = dict(channel) channel["nonce"] = channel_blockchain_data[0] channel["amount"] = channel_blockchain_data[5] channel["expiration"] = channel_blockchain_data[6] return AttributeDict(channel)
def middleware(method, params): response = make_request(method, params) if 'result' in response: result = response['result'] if is_dict(result) and not isinstance(result, AttributeDict): return assoc(response, 'result', AttributeDict.recursive(result)) else: return response else: return response
def test_attributedict_dict_in_list_in_dict(): data = {'instructions': [ 0, 1, 'neither shalt thou count, excepting that thou then proceedeth to three', {'if_naughty': 'snuff it'}, 'shalt thou not count', 'right out', ]} attrdict = AttributeDict.recursive(data) assert attrdict.instructions[3].if_naughty == 'snuff it'
def sign(self, message=None, private_key=None, message_hexstr=None, message_text=None): ''' @param private_key in bytes, str, or int. ''' msg_bytes = to_bytes(message, hexstr=message_hexstr, text=message_text) msg_hash = self.hashMessage(msg_bytes) key_bytes = HexBytes(private_key) key = self._keys.PrivateKey(key_bytes) (v, r, s, eth_signature_bytes) = sign_message_hash(key, msg_hash) return AttributeDict({ 'message': HexBytes(msg_bytes), 'messageHash': msg_hash, 'r': r, 's': s, 'v': v, 'signature': HexBytes(eth_signature_bytes), })
eth_initial_transaction = AttributeDict({ 'blockHash': HexBytes( '0x111346453964a0df4fcd50ac759231f4f90a858c6ab62255e7c75089b22b0b40'), 'blockNumber': 8388823, 'chainId': None, 'condition': None, 'creates': None, 'from': '0x999F348959E611F1E9eab2927c21E88E48e6Ef45', 'gas': 140502, 'gasPrice': 1000000000, 'hash': HexBytes( '0xcf64ef4d0449cf7a78d2be1c1f7225dffb11dded98a58d569ebcc6e883ce9f2b'), 'input': '0x7337c993000000000000000000000000000000000000000000000000000000005b782cfaed2e6fe492005de2dd82e84d38448' '467d632e81c000000000000000000000000000000000000000000000000d867f293ba129629a9f9355fa285b8d3711a90920000' '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 'nonce': 283, 'publicKey': HexBytes( '0x76c4f5810736d1d9b9964863abc339dce70ace058db5c820e5fdec26e0840f36f9adcb150e5216213bc301f3a6b71a178' 'c81ddd34a361d696c8cb03970590d4f'), 'r': HexBytes( '0x68e2bbdd4cb4e989854a87aa8964c65b3bf28f68183f73f905ab8841db690cbb'), 'raw': HexBytes( '0xf9013282011b843b9aca00830224d694ce07ab9477bc20790b88b398a2a9e0f626c7d26387038d7ea4c68000b8c47337c993000000' '000000000000000000000000000000000000000000000000005b782cfaed2e6fe492005de2dd82e84d38448467d632e81c0000000000' '00000000000000000000000000000000000000d867f293ba129629a9f9355fa285b8d3711a9092000000000000000000000000000000' '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' '0000000000000000000000000000000000000000000000000000001ca068e2bbdd4cb4e989854a87aa8964c65b3bf28f68183f73f905' 'ab8841db690cbba0227712dc5c204041cc8c44facac023c457725acc905b2db311b1176cd53836e0' ), 's': HexBytes( '0x227712dc5c204041cc8c44facac023c457725acc905b2db311b1176cd53836e0'), 'standardV': 1, 'to': '0xce07aB9477BC20790B88B398A2A9e0F626c7D263', 'transactionIndex': 1, 'v': 28, 'value': 1000000000000000 })
def test_attributedict_delitem_invalid(): container = AttributeDict({'a': 1}) with pytest.raises(TypeError): del container['a'] assert container['a'] == 1
def test_attributedict_equality(dict1, dict2): assert AttributeDict(dict1) == dict2 assert AttributeDict(dict1) == AttributeDict(dict2) assert dict1 == AttributeDict(dict2)
def test_attributedict_repr(): dict1 = AttributeDict({'a': 1}) dict2 = eval(repr(dict1)) assert dict1 == dict2
def test_attributedict_setattr_invalid(): container = AttributeDict({'a': 1}) with pytest.raises(TypeError): container.a = 0 assert container.a == 1
def test_attributedict_inequality(dict1, dict2): assert AttributeDict(dict1) != dict2 assert AttributeDict(dict1) != AttributeDict(dict2) assert dict1 != AttributeDict(dict2)
def test_attributedict_access(): container = AttributeDict({'a': 1}) assert container.a == 1
def client(self, *args, org_id=None, service_id=None, channel_id=None): client = MutableAttributeDict({}) # Determine org_id, service_id and channel_id for client _org_id = org_id _service_id = service_id _channel_id = channel_id if len(args) == 2: (_org_id, _service_id) = args if len(args) == 1: raise ValueError( "Please either provide both organization id and service id as positional arguments or none of them" ) if (_org_id is not None or _service_id is not None) and (org_id is not None or service_id is not None): raise ValueError( "Please provide organization id and service id either as positional arguments or as keyword arguments" ) if org_id is not None and _service_id is not None: _org_id = org_id _service_id = service_id if _org_id is None or _service_id is None: raise ValueError("""Could not instantiate client. Please provide at least an org_id and a service_id either as positional or keyword arguments""" ) # Get client metadata for service (found, registration_id, metadata_uri, tags) = self.registry_contract.functions.getServiceRegistrationById( bytes(_org_id, "utf-8"), bytes(_service_id, "utf-8")).call() client.metadata = AttributeDict( json.loads( self.ipfs_client.cat( metadata_uri.rstrip(b"\0").decode('ascii')[7:]))) default_group = AttributeDict(client.metadata.groups[0]) client.default_payment_address = default_group["payment_address"] default_channel_value = client.metadata.pricing["price_in_cogs"] * 100 default_channel_expiration = int( self.web3.eth.getBlock("latest").number + client.metadata.payment_expiration_threshold + (3600 * 24 * 7 / self.average_block_time)) service_endpoint = None for endpoint in client.metadata["endpoints"]: if (endpoint["group_name"] == default_group["group_name"]): service_endpoint = endpoint["endpoint"] break # Functions to get a funded channel with a combination of calls to the blockchain and to the daemon grpc_channel = self._get_base_grpc_channel(service_endpoint) channel_state_service_proto_path = str( cur_dir.joinpath("resources", "proto")) sys.path.insert(0, channel_state_service_proto_path) _state_service_pb2 = importlib.import_module("state_service_pb2") _state_service_pb2_grpc = importlib.import_module( "state_service_pb2_grpc") sys.path.remove(channel_state_service_proto_path) def _get_channel_state(channel_id): stub = _state_service_pb2_grpc.PaymentChannelStateServiceStub( grpc_channel) message = web3.Web3.soliditySha3(["uint256"], [channel_id]) signature = self.web3.eth.account.signHash( defunct_hash_message(message), self.signer_private_key).signature request = _state_service_pb2.ChannelStateRequest( channel_id=web3.Web3.toBytes(channel_id), signature=bytes(signature)) response = stub.GetChannelState(request) return { "current_nonce": int.from_bytes(response.current_nonce, byteorder="big"), "current_signed_amount": int.from_bytes(response.current_signed_amount, byteorder="big") } def _get_channel_states(): return [ dict( _get_channel_state(channel.channelId), **{ "channel_id": channel.channelId, "initial_amount": channel.amount, "expiration": channel.expiration }) for channel in self._get_channels( client.default_payment_address) ] def _client_open_channel(value, expiration): mpe_balance = self.mpe_contract.functions.balances( self.address).call() group_id = base64.b64decode(default_group.group_id) if value > mpe_balance: return (self.mpe_deposit_and_open_channel( default_group.payment_address, group_id, value - mpe_balance, expiration)) else: return (self.mpe_open_channel(default_group.payment_address, group_id, value, expiration)) def _client_add_funds(channel_id, amount): mpe_balance = self.mpe_contract.functions.balances( self.address).call() if value > mpe_balance: self.mpe_deposit(amount - mpe_balance) return (self.mpe_channel_add_funds(channel_id, amount)) def _client_extend_and_add_funds(channel_id, new_expiration, amount): mpe_balance = self.mpe_contract.functions.balances( self.address).call() if amount > mpe_balance: self.mpe_deposit(amount - mpe_balance) return (self.mpe_channel_extend_and_add_funds( channel_id, new_expiration, amount)) def _get_funded_channel(): channel_states = _get_channel_states() if len(channel_states) == 0: if self.allow_transactions is False: raise RuntimeError( 'No state channel found. Please open a new channel or set configuration parameter "allow_transactions=True" when creating Snet class instance' ) else: _client_open_channel(default_channel_value, default_channel_expiration) channel_states = _get_channel_states() funded_channels = list( filter( lambda state: state["initial_amount"] - state[ "current_signed_amount"] >= int( client.metadata.pricing["price_in_cogs"]), iter(channel_states))) if len(funded_channels) == 0: if self.allow_transactions is True: non_expired_unfunded_channels = list( filter( lambda state: state["expiration"] + client.metadata .payment_expiration_threshold > self.web3.eth. getBlock("latest").number, iter(channel_states))) if len(non_expired_unfunded_channels) == 0: channel_id = next(iter(channel_states))["channel_id"] _client_extend_and_add_funds( channel_id, default_channel_expiration, default_channel_value) return channel_id else: channel_id = next( iter(non_expired_unfunded_channels))["channel_id"] _client_add_funds(channel_id, default_channel_value) return channel_id else: raise RuntimeError( 'No funded channel found. Please open a new channel or fund an open one, or set configuration parameter "allow_transactions=True" when creating Snet class instance' ) valid_channels = list( filter( lambda state: state["expiration"] + client.metadata. payment_expiration_threshold > self.web3.eth.getBlock( "latest").number, iter(funded_channels))) if len(valid_channels) == 0: if self.allow_transactions is True: channel_id = next(iter(funded_channels))["channel_id"] self.mpe_channel_extend(channel_id, default_channel_expiration) return channel_id else: raise RuntimeError( 'No non-expired channel found. Please open a new channel or extend an open and funded one, or set configuration parameter "allow_transactions=True" when creating Snet class instance' ) else: channel_id = next(iter(valid_channels))["channel_id"] return channel_id if _channel_id is None: _channel_id = _get_funded_channel() # Import modules and add them to client grpc object libraries_base_path = self.libraries_base_path client_library_path = str( main_dir_path.joinpath(self.libraries_base_path, _org_id, _service_id)) sys.path.insert(0, client_library_path) grpc_modules = [] for module_path in Path(client_library_path).glob("**/*_pb2.py"): grpc_modules.append(module_path) for module_path in Path(client_library_path).glob("**/*_pb2_grpc.py"): grpc_modules.append(module_path) grpc_modules = list( map( lambda x: str( PurePath( Path(x).relative_to(client_library_path).parent. joinpath(PurePath(x).stem))), grpc_modules)) imported_modules = MutableAttributeDict({}) for grpc_module in grpc_modules: imported_module = importlib.import_module(grpc_module) imported_modules[grpc_module] = imported_module sys.path.remove(client_library_path) # Service channel utility methods def _get_service_call_metadata(channel_id): state = _get_channel_state(channel_id) amount = state["current_signed_amount"] + int( client.metadata.pricing["price_in_cogs"]) message = web3.Web3.soliditySha3( ["address", "uint256", "uint256", "uint256"], [ self.mpe_contract.address, channel_id, state["current_nonce"], amount ]) signature = bytes( self.web3.eth.account.signHash( defunct_hash_message(message), self.signer_private_key).signature) metadata = [("snet-payment-type", "escrow"), ("snet-payment-channel-id", str(channel_id)), ("snet-payment-channel-nonce", str(state["current_nonce"])), ("snet-payment-channel-amount", str(amount)), ("snet-payment-channel-signature-bin", signature)] return metadata # Client exports client.open_channel = lambda value=default_channel_value, expiration=default_channel_expiration: _client_open_channel( value, expiration) client.get_service_call_metadata = lambda: _get_service_call_metadata( _channel_id) client.grpc = imported_modules def intercept_call(client_call_details, request_iterator, request_streaming, response_streaming): metadata = [] if client_call_details.metadata is not None: metadata = list(client_call_details.metadata) metadata.extend(client.get_service_call_metadata()) client_call_details = _ClientCallDetails( client_call_details.method, client_call_details.timeout, metadata, client_call_details.credentials) return client_call_details, request_iterator, None client.grpc_channel = grpc.intercept_channel( grpc_channel, generic_client_interceptor.create(intercept_call)) return client
def get_event_data(event_abi, log_entry): """ Given an event ABI and a log entry for that event, return the decoded event data """ if event_abi['anonymous']: log_topics = log_entry['topics'] elif not log_entry['topics']: raise MismatchedABI( "Expected non-anonymous event to have 1 or more topics") elif event_abi_to_log_topic(event_abi) != log_entry['topics'][0]: raise MismatchedABI( "The event signature did not match the provided ABI") else: log_topics = log_entry['topics'][1:] log_topics_abi = get_indexed_event_inputs(event_abi) log_topic_normalized_inputs = normalize_event_input_types(log_topics_abi) log_topic_types = get_event_abi_types_for_decoding( log_topic_normalized_inputs) log_topic_names = get_abi_input_names({'inputs': log_topics_abi}) if len(log_topics) != len(log_topic_types): raise ValueError("Expected {0} log topics. Got {1}".format( len(log_topic_types), len(log_topics), )) log_data = hexstr_if_str(to_bytes, log_entry['data']) log_data_abi = exclude_indexed_event_inputs(event_abi) log_data_normalized_inputs = normalize_event_input_types(log_data_abi) log_data_types = get_event_abi_types_for_decoding( log_data_normalized_inputs) log_data_names = get_abi_input_names({'inputs': log_data_abi}) # sanity check that there are not name intersections between the topic # names and the data argument names. duplicate_names = set(log_topic_names).intersection(log_data_names) if duplicate_names: raise ValueError( "Invalid Event ABI: The following argument names are duplicated " "between event inputs: '{0}'".format(', '.join(duplicate_names))) decoded_log_data = decode_abi(log_data_types, log_data) normalized_log_data = map_abi_data(BASE_RETURN_NORMALIZERS, log_data_types, decoded_log_data) decoded_topic_data = [ decode_single(topic_type, topic_data) for topic_type, topic_data in zip(log_topic_types, log_topics) ] normalized_topic_data = map_abi_data(BASE_RETURN_NORMALIZERS, log_topic_types, decoded_topic_data) event_args = dict( itertools.chain( zip(log_topic_names, normalized_topic_data), zip(log_data_names, normalized_log_data), )) event_data = { 'args': event_args, 'event': event_abi['name'], 'logIndex': log_entry['logIndex'], 'transactionIndex': log_entry['transactionIndex'], 'transactionHash': log_entry['transactionHash'], 'address': log_entry['address'], 'blockHash': log_entry['blockHash'], 'blockNumber': log_entry['blockNumber'], } return AttributeDict.recursive(event_data)
def test_attributedict_set_in_recursive_dict(): data = {'mydict': {'myset': {'found'}}} attrdict = AttributeDict.recursive(data) assert 'found' in attrdict.mydict.myset
eth_initial_transaction = AttributeDict({ 'blockHash': HexBytes('0xebb8d4e62dc5b0732bee6e2c3946c5a972988f41fbac321eb73311930a936804'), 'blockNumber': 6600435, 'chainId': None, 'condition': None, 'creates': None, 'from': '0x999F348959E611F1E9eab2927c21E88E48e6Ef45', 'gas': 126221, 'gasPrice': 14959965017, 'hash': HexBytes('0x7221773115ded91f856cedb2032a529edabe0bab8785d07d901681512314ef41'), 'input': ( '0xeb8ae1ed000000000000000000000000000000000000000000000000000000005abe25ea10ff9' '72f3d8181f603aa7f6b4bc172de730fec2b00000000000000000000000000000000000000000000' '0000d867f293ba129629a9f9355fa285b8d3711a9092' ), 'nonce': 16, 'publicKey': HexBytes( '0x76c4f5810736d1d9b9964863abc339dce70ace058db5c820e5fdec26e0840f36f9adcb150e521' '6213bc301f3a6b71a178c81ddd34a361d696c8cb03970590d4f' ), 'r': HexBytes('0x7a4e11ea96640fb0ab255960cea6afcf16732246ce1dadeec52eb6a8d59c2e05'), 'raw': HexBytes( '0xf8ca1085037baef3598301ed0d949f7e5402ed0858ea0c5914d44b900a42c89547b80cb864eb8' 'ae1ed000000000000000000000000000000000000000000000000000000005abe25ea10ff972f3d' '8181f603aa7f6b4bc172de730fec2b000000000000000000000000000000000000000000000000d' '867f293ba129629a9f9355fa285b8d3711a90921ba07a4e11ea96640fb0ab255960cea6afcf1673' '2246ce1dadeec52eb6a8d59c2e05a06f47ffe2bc88915013295be61c503bc52762fe4f7826fa249' '0b2d302a11bff85' ), 's': HexBytes('0x6f47ffe2bc88915013295be61c503bc52762fe4f7826fa2490b2d302a11bff85'), 'standardV': 0, 'to': '0x9F7e5402ed0858Ea0C5914D44B900A42C89547B8', 'transactionIndex': 0, 'v': 27, 'value': 12 })
def test_attributedict_recursive_dict(): w = AttributeDict.recursive({'x': {'y': {'z': 8}}}) assert w.x.y.z == 8
def test_decode_transaction_logs(self): """ Mocking `web3.eth.Eth.getTransactionReceipt()` response and verifies decoding transaction works as expected. """ mocked_logs = [ AttributeDict({ 'address': '0xCBf1735Aad8C4B337903cD44b419eFE6538aaB40', 'topics': [ HexBytes('b76d0edd90c6a07aa3ff7a222d7f5933' 'e29c6acc660c059c97837f05c4ca1a84') ], 'data': '000000000000000000000000fe8a5f3a7bb446e1cb4566717691cd3139289ed4' 'b0230ab70b78e47050766089ea333f2ff7ad41c6f31e8bed8c2acfcb8e911841' '0000000000000000000000000000000000000000000000000000000000000000' '0000000000000000000000000000000000000000000000000000000000000100' '0000000000000000000000000000000000000000000000000000000000000140' '00000000000000000000000000000000000000000000000000000000000395f8' '1100000000000000000000000000000000000000000000000000000000000000' '00000000000000000000000000000000000000000000000000000004a817c800' '0000000000000000000000000000000000000000000000000000000000000006' '6e65737465640000000000000000000000000000000000000000000000000000' '00000000000000000000000000000000000000000000000000000000000001b4' '5b55524c5d205b276a736f6e2868747470733a2f2f6170692e72616e646f6d2e' '6f72672f6a736f6e2d7270632f312f696e766f6b65292e726573756c742e7261' '6e646f6d5b2273657269616c4e756d626572222c2264617461225d272c20275c' '6e7b226a736f6e727063223a22322e30222c226d6574686f64223a2267656e65' '726174655369676e6564496e746567657273222c22706172616d73223a7b2261' '70694b6579223a247b5b646563727970745d20424b6733544373376c6b7a4e72' '316b523670786a50434d32534f656a63466f6a55504d544f73426b432f343748' '485066317350326f78564c546a4e42752b736c523953675a797144746a564f56' '35597a67313269556b62756270304470636a434564654a54486e477743366744' '3732394755566f47766f393668757877526f5a6c436a594f3830725771325747' '596f522f4c433357616d704475767632426f3d7d2c226e223a312c226d696e22' '3a312c226d6178223a3130302c227265706c6163656d656e74223a747275652c' '2262617365223a3130247b5b6964656e746974795d20227d227d2c226964223a' '31247b5b6964656e746974795d20227d227d275d000000000000000000000000', }), AttributeDict({ 'address': '0xFE8a5f3a7Bb446e1cB4566717691cD3139289ED4', 'topics': [ HexBytes('1cb5bfc4e69cbacf65c8e05bdb84d7a3' '27bd6bb4c034ff82359aefd7443775c4'), HexBytes('b0230ab70b78e47050766089ea333f2f' 'f7ad41c6f31e8bed8c2acfcb8e911841'), HexBytes('00000000000000000000000066d4bacf' 'e61df23be813089a7a6d1a749a5c936a'), HexBytes('00000000000000000000000000000000' '0000000000000000016a98b78c556c34') ], 'data': '0000000000000000000000000000000000000000000000000007533f2ecb6c34' '000000000000000000000000000000000000000000000000016345785d8a0000' '0000000000000000000000000000000000000000000000000000000000000062', }) ] chain_id = ChainID.ROPSTEN transaction_hash = ( "0x330df22df6543c9816d80e582a4213b1fc11992f317be71775f49c3d853ed5be" ) with \ mock.patch('web3.eth.Eth.getTransactionReceipt') \ as m_getTransactionReceipt, \ mock.patch( 'etherscan.contracts.Contract.get_abi', side_effect=self.m_get_abi, autospec=True): m_getTransactionReceipt.return_value.logs = mocked_logs decoded_methods = TransactionDebugger.decode_transaction_logs( chain_id, transaction_hash) self.assertEqual(len(decoded_methods), 2) decoded_method = decoded_methods[0] self.assertEqual( decoded_method['method_info']['definition'], 'Log1(address,bytes32,uint256,string,string,uint256,bytes1,uint256)' ) decoded_method = decoded_methods[1] self.assertEqual( decoded_method['method_info']['definition'], 'LogBet(bytes32,address,uint256,uint256,uint256,uint256)')
def stub_block(timestamp): return AttributeDict({ 'timestamp': timestamp, 'number': 123, })
def test_attributedict_sequence_with_dict(sequence): data = sequence(['a', {'found': True}, 'c']) dict_in_sequence = AttributeDict.recursive(data) assert dict_in_sequence[1].found is True
def _castAttributeDict(self, maybe_dict): """Return an AttributeDict as is provided by web3 middleware.""" if is_dict(maybe_dict) and not isinstance(maybe_dict, AttributeDict): return AttributeDict.recursive(maybe_dict) else: return maybe_dict
def get_event_data(event_abi, log_entry): """ Given an event ABI and a log entry for that event, return the decoded event data """ if event_abi['anonymous']: log_topics = log_entry['topics'] elif not log_entry['topics']: raise MismatchedABI("Expected non-anonymous event to have 1 or more topics") elif event_abi_to_log_topic(event_abi) != log_entry['topics'][0]: raise MismatchedABI("The event signature did not match the provided ABI") else: log_topics = log_entry['topics'][1:] log_topics_abi = get_indexed_event_inputs(event_abi) log_topic_normalized_inputs = normalize_event_input_types(log_topics_abi) log_topic_types = get_event_abi_types_for_decoding(log_topic_normalized_inputs) log_topic_names = get_abi_input_names({'inputs': log_topics_abi}) if len(log_topics) != len(log_topic_types): raise ValueError("Expected {0} log topics. Got {1}".format( len(log_topic_types), len(log_topics), )) log_data = hexstr_if_str(to_bytes, log_entry['data']) log_data_abi = exclude_indexed_event_inputs(event_abi) log_data_normalized_inputs = normalize_event_input_types(log_data_abi) log_data_types = get_event_abi_types_for_decoding(log_data_normalized_inputs) log_data_names = get_abi_input_names({'inputs': log_data_abi}) # sanity check that there are not name intersections between the topic # names and the data argument names. duplicate_names = set(log_topic_names).intersection(log_data_names) if duplicate_names: raise ValueError( "Invalid Event ABI: The following argument names are duplicated " "between event inputs: '{0}'".format(', '.join(duplicate_names)) ) decoded_log_data = decode_abi(log_data_types, log_data) normalized_log_data = map_abi_data( BASE_RETURN_NORMALIZERS, log_data_types, decoded_log_data ) decoded_topic_data = [ decode_single(topic_type, topic_data) for topic_type, topic_data in zip(log_topic_types, log_topics) ] normalized_topic_data = map_abi_data( BASE_RETURN_NORMALIZERS, log_topic_types, decoded_topic_data ) event_args = dict(itertools.chain( zip(log_topic_names, normalized_topic_data), zip(log_data_names, normalized_log_data), )) event_data = { 'args': event_args, 'event': event_abi['name'], 'logIndex': log_entry['logIndex'], 'transactionIndex': log_entry['transactionIndex'], 'transactionHash': log_entry['transactionHash'], 'address': log_entry['address'], 'blockHash': log_entry['blockHash'], 'blockNumber': log_entry['blockNumber'], } return AttributeDict.recursive(event_data)