def to_dict(self): return { 'type': self.__class__.__name__, 'message_identifier': self.message_identifier, 'payment_identifier': self.payment_identifier, 'nonce': self.nonce, 'token_network_address': to_normalized_address(self.token_network_address), 'token': to_normalized_address(self.token), 'channel': to_normalized_address(self.channel), 'transferred_amount': self.transferred_amount, 'locked_amount': self.locked_amount, 'recipient': to_normalized_address(self.recipient), 'locksroot': data_encoder(self.locksroot), 'signature': data_encoder(self.signature), }
def to_dict(self): return { 'type': self.__class__.__name__, 'message_identifier': self.message_identifier, 'secret': data_encoder(self.secret), 'signature': data_encoder(self.signature) }
def format_data_for_call(sender='', to='', value=0, data='', startgas=GAS_PRICE, gasprice=GAS_PRICE): """ Helper to format the transaction data. """ json_data = {} if sender is not None: json_data['from'] = address_encoder(sender) if to is not None: json_data['to'] = data_encoder(to) if value is not None: json_data['value'] = quantity_encoder(value) if gasprice is not None: json_data['gasPrice'] = quantity_encoder(gasprice) if startgas is not None: json_data['gas'] = quantity_encoder(startgas) if data is not None: json_data['data'] = data_encoder(data) return json_data
def to_dict(self): return { 'type': self.__class__.__name__, 'message_identifier': self.message_identifier, 'payment_identifier': self.payment_identifier, 'secrethash': data_encoder(self.secrethash), 'amount': self.amount, 'signature': data_encoder(self.signature) }
def eth_sendTransaction( self, nonce=None, sender='', to='', value=0, data='', gasPrice=GAS_PRICE, gas=GAS_PRICE): """ Creates new message call transaction or a contract creation, if the data field contains code. Args: sender (address): The 20 bytes address the transaction is sent from. to (address): DATA, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. gas (int): Gas provided for the transaction execution. It will return unused gas. gasPrice (int): gasPrice used for each unit of gas paid. value (int): Value sent with this transaction. data (bin): The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. nonce (int): This allows to overwrite your own pending transactions that use the same nonce. """ if to == '' and data.isalnum(): warnings.warn( 'Verify that the data parameter is _not_ hex encoded, if this is the case ' 'the data will be double encoded and result in unexpected ' 'behavior.' ) if to == '0' * 40: warnings.warn('For contract creation the empty string must be used.') if sender is None: raise ValueError('sender needs to be provided.') json_data = { 'to': data_encoder(normalize_address(to, allow_blank=True)), 'value': quantity_encoder(value), 'gasPrice': quantity_encoder(gasPrice), 'gas': quantity_encoder(gas), 'data': data_encoder(data), 'from': address_encoder(sender), } if nonce is not None: json_data['nonce'] = quantity_encoder(nonce) res = self.call('eth_sendTransaction', json_data) return data_decoder(res)
def to_dict(self): return { 'type': self.__class__.__name__, 'message_identifier': self.message_identifier, 'payment_identifier': self.payment_identifier, 'secret': data_encoder(self.secret), 'nonce': self.nonce, 'channel': address_encoder(self.channel), 'transferred_amount': self.transferred_amount, 'locked_amount': self.locked_amount, 'locksroot': data_encoder(self.locksroot), 'signature': data_encoder(self.signature), }
def test_api_open_close_and_settle_channel( api_backend, token_addresses, reveal_timeout, ): # let's create a new channel partner_address = '0x61C808D82A3Ac53231750daDc13c777b59310bD9' token_address = token_addresses[0] settle_timeout = 1650 channel_data_obj = { 'partner_address': partner_address, 'token_address': to_checksum_address(token_address), 'settle_timeout': settle_timeout, } request = grequests.put( api_url_for( api_backend, 'channelsresource', ), json=channel_data_obj, ) response = request.send().response balance = 0 assert_proper_response(response, status_code=HTTPStatus.CREATED) response = response.json() expected_response = channel_data_obj expected_response['balance'] = balance expected_response['state'] = CHANNEL_STATE_OPENED expected_response['reveal_timeout'] = reveal_timeout channel_identifier = data_encoder( calculate_channel_identifier( api_backend[1].raiden_api.raiden.address, to_canonical_address(partner_address), )) expected_response['channel_identifier'] = channel_identifier assert response == expected_response # let's close the channel request = grequests.patch( api_url_for( api_backend, 'channelsresourcebytokenandpartneraddress', token_address=token_address, partner_address=partner_address, ), json={'state': CHANNEL_STATE_CLOSED}, ) response = request.send().response assert_proper_response(response) expected_response = { 'channel_identifier': channel_identifier, 'partner_address': partner_address, 'token_address': to_checksum_address(token_address), 'settle_timeout': settle_timeout, 'reveal_timeout': reveal_timeout, 'state': CHANNEL_STATE_CLOSED, 'balance': balance, } assert response.json() == expected_response
def to_dict(self): return { 'type': self.__class__.__name__, 'sender': to_normalized_address(self.sender), 'message_identifier': self.message_identifier, 'signature': data_encoder(self.signature), }
def send_transaction(self, sender: address, to: address, value: int = 0, data: bytes = b'', startgas: int = None, nonce: int = None): """ Helper to send signed messages. This method will use the `privkey` provided in the constructor to locally sign the transaction. This requires an extended server implementation that accepts the variables v, r, and s. """ if not self.privkey and not sender: raise ValueError('Either privkey or sender needs to be supplied.') if self.privkey: privkey_address = privatekey_to_address(self.privkey) sender = sender or privkey_address if sender != privkey_address: raise ValueError('sender for a different privkey.') if nonce is None: nonce = self.nonce(sender) else: if nonce is None: nonce = 0 startgas = self.check_startgas(startgas) tx = Transaction(nonce, self.gasprice(), startgas, to=to, value=value, data=data) if self.privkey: tx.sign(self.privkey) result = self.call( 'eth_sendRawTransaction', data_encoder(rlp.encode(tx)), ) return result[2 if result.startswith('0x') else 0:] else: # rename the fields to match the eth_sendTransaction signature tx_dict = tx.to_dict() tx_dict.pop('hash') tx_dict['sender'] = sender tx_dict['gasPrice'] = tx_dict.pop('gasprice') tx_dict['gas'] = tx_dict.pop('startgas') res = self.eth_sendTransaction(**tx_dict) assert len(res) in (20, 32) return hexlify(res)
def to_dict(self): return { 'type': self.__class__.__name__, 'amount': self.amount, 'expiration': self.expiration, 'secrethash': data_encoder(self.secrethash), }
def format_data_for_call(sender: address = b'', to: address = b'', value: int = 0, data: bytes = b'', startgas: int = GAS_LIMIT, gasprice: int = GAS_PRICE): """ Helper to format the transaction data. """ return { 'from': address_encoder(sender), 'to': data_encoder(to), 'value': quantity_encoder(value), 'gasPrice': quantity_encoder(gasprice), 'gas': quantity_encoder(startgas), 'data': data_encoder(data) }
def _login_or_register(self): # password is signed server address password = data_encoder(self._sign(self._server_url.encode())) seed = int.from_bytes(self._sign(b'seed')[-32:], 'big') rand = Random() # deterministic, random secret for username suffixes rand.seed(seed) # try login and register on first 5 possible accounts for i in range(5): base_username = to_normalized_address(self._raiden_service.address) username = base_username if i: username = f'{username}.{rand.randint(0, 0xffffffff):08x}' try: self._client.login_with_password(username, password) self.log.info( 'LOGIN', homeserver=self._server_url, username=username, ) break except MatrixRequestError as ex: if ex.code != 403: raise self.log.debug( 'Could not login. Trying register', homeserver=self._server_url, username=username, ) try: self._client.register_with_password(username, password) self.log.info( 'REGISTER', homeserver=self._server_url, username=username, ) break except MatrixRequestError as ex: if ex.code != 400: raise self.log.debug('Username taken. Continuing') continue else: raise ValueError('Could not register or login!') # TODO: persist access_token, to avoid generating a new login every time name = data_encoder(self._sign(self._client.user_id.encode())) self._client.get_user(self._client.user_id).set_display_name(name)
def format_data_for_call( sender: address = b'', to: address = b'', value: int = 0, data: bytes = b'', startgas: int = GAS_PRICE, gasprice: int = GAS_PRICE): """ Helper to format the transaction data. """ return { 'from': address_encoder(sender), 'to': data_encoder(to), 'value': quantity_encoder(value), 'gasPrice': quantity_encoder(gasprice), 'gas': quantity_encoder(startgas), 'data': data_encoder(data) }
def send_transaction( self, sender: address, to: address, value: int = 0, data: bytes = b'', startgas: int = 0, gasprice: int = GAS_PRICE, nonce: Optional[int] = None): """ Helper to send signed messages. This method will use the `privkey` provided in the constructor to locally sign the transaction. This requires an extended server implementation that accepts the variables v, r, and s. """ if not self.privkey and not sender: raise ValueError('Either privkey or sender needs to be supplied.') if self.privkey: privkey_address = privatekey_to_address(self.privkey) sender = sender or privkey_address if sender != privkey_address: raise ValueError('sender for a different privkey.') if nonce is None: nonce = self.nonce(sender) else: if nonce is None: nonce = 0 if not startgas: startgas = self.gaslimit() - 1 tx = Transaction(nonce, gasprice, startgas, to=to, value=value, data=data) if self.privkey: tx.sign(self.privkey) result = self.call( 'eth_sendRawTransaction', data_encoder(rlp.encode(tx)), ) return result[2 if result.startswith('0x') else 0:] else: # rename the fields to match the eth_sendTransaction signature tx_dict = tx.to_dict() tx_dict.pop('hash') tx_dict['sender'] = sender tx_dict['gasPrice'] = tx_dict.pop('gasprice') tx_dict['gas'] = tx_dict.pop('startgas') res = self.eth_sendTransaction(**tx_dict) assert len(res) in (20, 32) return hexlify(res)
def poll(self, transaction_hash: bytes, confirmations: int = None): """ Wait until the `transaction_hash` is applied or rejected. Args: transaction_hash: Transaction hash that we are waiting for. confirmations: Number of block confirmations that we will wait for. """ if transaction_hash.startswith(b'0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior', ) if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encoded)', ) transaction_hash = data_encoder(transaction_hash) # used to check if the transaction was removed, this could happen # if gas price is too low: # # > Transaction (acbca3d6) below gas price (tx=1 Wei ask=18 # > Shannon). All sequential txs from this address(7d0eae79) # > will be ignored # last_result = None while True: # Could return None for a short period of time, until the # transaction is added to the pool transaction = self.web3.eth.getTransaction(transaction_hash) # if the transaction was added to the pool and then removed if transaction is None and last_result is not None: raise Exception('invalid transaction, check gas price') # the transaction was added to the pool and mined if transaction and transaction['blockNumber'] is not None: break last_result = transaction gevent.sleep(.5) if confirmations: # this will wait for both APPLIED and REVERTED transactions transaction_block = transaction['blockNumber'] confirmation_block = transaction_block + confirmations block_number = self.block_number() while block_number < confirmation_block: gevent.sleep(.5) block_number = self.block_number()
def to_dict(self): return { 'type': self.__class__.__name__, 'message_identifier': self.message_identifier, 'payment_identifier': self.payment_identifier, 'nonce': self.nonce, 'registry_address': address_encoder(self.registry_address), 'token': address_encoder(self.token), 'channel': address_encoder(self.channel), 'transferred_amount': self.transferred_amount, 'locked_amount': self.locked_amount, 'recipient': address_encoder(self.recipient), 'locksroot': data_encoder(self.locksroot), 'lock': self.lock.to_dict(), 'target': address_encoder(self.target), 'initiator': address_encoder(self.initiator), 'fee': self.fee, 'signature': data_encoder(self.signature), }
def eth_getTransactionByHash(self, transaction_hash): """ Returns the information about a transaction requested by transaction hash. """ if transaction_hash.startswith('0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior') if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encoded)') transaction_hash = data_encoder(transaction_hash) return self.call('eth_getTransactionByHash', transaction_hash)
def check_transaction_threw(client, transaction_hash): """Check if the transaction threw/reverted or if it executed properly Returns None in case of success and the transaction receipt if the transaction's status indicator is 0x0. """ encoded_transaction = data_encoder(unhexlify(transaction_hash)) receipt = client.call('eth_getTransactionReceipt', encoded_transaction) if 'status' not in receipt: raise ValueError( 'Transaction receipt does not contain a status field. Upgrade your client' ) if receipt['status'] == '0x0': return receipt return None
def eth_getTransactionByHash(self, transaction_hash: bytes): """ Returns the information about a transaction requested by transaction hash. """ if transaction_hash.startswith(b'0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior' ) if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encoded)' ) transaction_hash = data_encoder(transaction_hash) return self.call('eth_getTransactionByHash', transaction_hash)
def send_transaction( self, to: Address, value: int = 0, data: bytes = b'', startgas: int = None, ): """ Helper to send signed messages. This method will use the `privkey` provided in the constructor to locally sign the transaction. This requires an extended server implementation that accepts the variables v, r, and s. """ if to == b'' and data.isalnum(): warnings.warn( 'Verify that the data parameter is _not_ hex encoded, if this is the case ' 'the data will be double encoded and result in unexpected ' 'behavior.') if to == b'0' * 40: warnings.warn( 'For contract creation the empty string must be used.') nonce = self.nonce() startgas = self.check_startgas(startgas) tx = Transaction( nonce, self.gasprice(), startgas, to=to, value=value, data=data, ) tx.sign(self.privkey) result = self.rpccall_with_retry( 'eth_sendRawTransaction', data_encoder(rlp.encode(tx)), ) return result[2 if result.startswith('0x') else 0:]
def eth_getTransactionReceipt(self, transaction_hash): """ Returns the receipt of a transaction by transaction hash. Args: transaction_hash: Hash of a transaction. Returns: A dict representing the transaction receipt object, or null when no receipt was found. """ if transaction_hash.startswith('0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior') if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encoded)') transaction_hash = data_encoder(transaction_hash) return self.call('eth_getTransactionReceipt', transaction_hash)
def eth_getTransactionReceipt(self, transaction_hash: bytes) -> Dict: """ Returns the receipt of a transaction by transaction hash. Args: transaction_hash: Hash of a transaction. Returns: A dict representing the transaction receipt object, or null when no receipt was found. """ if transaction_hash.startswith(b'0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior' ) if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encoded)' ) transaction_hash = data_encoder(transaction_hash) return self.call('eth_getTransactionReceipt', transaction_hash)
def test_api_deposit_limit( api_backend, token_addresses, reveal_timeout, ): # let's create a new channel and deposit exactly the limit amount first_partner_address = '0x61C808D82A3Ac53231750daDc13c777b59310bD9' token_address = token_addresses[0] settle_timeout = 1650 balance_working = MAX_TOKENS_DEPLOY * (10**2) # token has two digits channel_data_obj = { 'partner_address': first_partner_address, 'token_address': to_checksum_address(token_address), 'settle_timeout': settle_timeout, 'reveal_timeout': reveal_timeout, 'balance': balance_working, } request = grequests.put( api_url_for( api_backend, 'channelsresource', ), json=channel_data_obj, ) response = request.send().response assert_proper_response(response, HTTPStatus.CREATED) response = response.json() expected_response = channel_data_obj expected_response['balance'] = balance_working expected_response['state'] = CHANNEL_STATE_OPENED first_channel_identifier = data_encoder( calculate_channel_identifier( api_backend[1].raiden_api.raiden.address, to_canonical_address(first_partner_address), )) expected_response['channel_identifier'] = first_channel_identifier assert response == expected_response # now let's open a channel and deposit a bit more than the limit second_partner_address = '0x29FA6cf0Cce24582a9B20DB94Be4B6E017896038' balance_failing = balance_working + 1 # token has two digits channel_data_obj = { 'partner_address': second_partner_address, 'token_address': to_checksum_address(token_address), 'settle_timeout': settle_timeout, 'reveal_timeout': reveal_timeout, 'balance': balance_failing, } request = grequests.put( api_url_for( api_backend, 'channelsresource', ), json=channel_data_obj, ) response = request.send().response assert_proper_response(response, HTTPStatus.EXPECTATION_FAILED) response = response.json() assert response[ 'errors'] == 'The deposit of 10001 is bigger than the current limit of 10000'
def _serialize(self, value, attr, obj): return data_encoder(value)
def test_api_open_and_deposit_channel( api_backend, token_addresses, reveal_timeout, ): # let's create a new channel first_partner_address = '0x61C808D82A3Ac53231750daDc13c777b59310bD9' token_address = token_addresses[0] settle_timeout = 1650 channel_data_obj = { 'partner_address': first_partner_address, 'token_address': to_checksum_address(token_address), 'settle_timeout': settle_timeout, 'reveal_timeout': reveal_timeout, } request = grequests.put( api_url_for( api_backend, 'channelsresource', ), json=channel_data_obj, ) response = request.send().response assert_proper_response(response, HTTPStatus.CREATED) response = response.json() expected_response = channel_data_obj expected_response['balance'] = 0 expected_response['state'] = CHANNEL_STATE_OPENED first_channel_identifier = data_encoder( calculate_channel_identifier( api_backend[1].raiden_api.raiden.address, to_canonical_address(first_partner_address), )) expected_response['channel_identifier'] = first_channel_identifier assert response == expected_response # now let's open a channel and make a deposit too second_partner_address = '0x29FA6cf0Cce24582a9B20DB94Be4B6E017896038' balance = 100 channel_data_obj = { 'partner_address': second_partner_address, 'token_address': to_checksum_address(token_address), 'settle_timeout': settle_timeout, 'reveal_timeout': reveal_timeout, 'balance': balance, } request = grequests.put( api_url_for( api_backend, 'channelsresource', ), json=channel_data_obj, ) response = request.send().response assert_proper_response(response, HTTPStatus.CREATED) response = response.json() expected_response = channel_data_obj expected_response['balance'] = balance expected_response['state'] = CHANNEL_STATE_OPENED second_channel_identifier = data_encoder( calculate_channel_identifier( api_backend[1].raiden_api.raiden.address, to_canonical_address(second_partner_address), )) expected_response['channel_identifier'] = second_channel_identifier assert response == expected_response # let's deposit on the first channel request = grequests.patch( api_url_for( api_backend, 'channelsresourcebytokenandpartneraddress', token_address=token_address, partner_address=first_partner_address, ), json={'total_deposit': balance}, ) response = request.send().response assert_proper_response(response) response = response.json() expected_response = { 'channel_identifier': first_channel_identifier, 'partner_address': first_partner_address, 'token_address': to_checksum_address(token_address), 'settle_timeout': settle_timeout, 'reveal_timeout': reveal_timeout, 'state': CHANNEL_STATE_OPENED, 'balance': balance, } assert response == expected_response # finally let's try querying for the second channel request = grequests.get( api_url_for( api_backend, 'channelsresourcebytokenandpartneraddress', token_address=token_address, partner_address=second_partner_address, ), ) response = request.send().response assert_proper_response(response) response = response.json() expected_response = { 'channel_identifier': second_channel_identifier, 'partner_address': second_partner_address, 'token_address': to_checksum_address(token_address), 'settle_timeout': settle_timeout, 'reveal_timeout': reveal_timeout, 'state': CHANNEL_STATE_OPENED, 'balance': balance, } assert response == expected_response
def poll(self, transaction_hash, confirmations=None, timeout=None): """ Wait until the `transaction_hash` is applied or rejected. If timeout is None, this could wait indefinitely! Args: transaction_hash (hash): Transaction hash that we are waiting for. confirmations (int): Number of block confirmations that we will wait for. timeout (float): Timeout in seconds, raise an Excpetion on timeout. """ if transaction_hash.startswith('0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior') if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encoded)') transaction_hash = data_encoder(transaction_hash) deadline = None if timeout: deadline = gevent.Timeout(timeout) deadline.start() try: # used to check if the transaction was removed, this could happen # if gas price is too low: # # > Transaction (acbca3d6) below gas price (tx=1 Wei ask=18 # > Shannon). All sequential txs from this address(7d0eae79) # > will be ignored # last_result = None while True: # Could return None for a short period of time, until the # transaction is added to the pool transaction = self.call('eth_getTransactionByHash', transaction_hash) # if the transaction was added to the pool and then removed if transaction is None and last_result is not None: raise Exception('invalid transaction, check gas price') # the transaction was added to the pool and mined if transaction and transaction['blockNumber'] is not None: break last_result = transaction gevent.sleep(.5) if confirmations: # this will wait for both APPLIED and REVERTED transactions transaction_block = quantity_decoder( transaction['blockNumber']) confirmation_block = transaction_block + confirmations block_number = self.blocknumber() while block_number < confirmation_block: gevent.sleep(.5) block_number = self.blocknumber() except gevent.Timeout: raise Exception('timeout when polling for transaction') finally: if deadline: deadline.cancel()
def poll( self, transaction_hash: bytes, confirmations: Optional[int] = None, timeout: Optional[float] = None): """ Wait until the `transaction_hash` is applied or rejected. If timeout is None, this could wait indefinitely! Args: transaction_hash: Transaction hash that we are waiting for. confirmations: Number of block confirmations that we will wait for. timeout: Timeout in seconds, raise an Excpetion on timeout. """ if transaction_hash.startswith(b'0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior' ) if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encoded)' ) transaction_hash = data_encoder(transaction_hash) deadline = None if timeout: deadline = gevent.Timeout(timeout) deadline.start() try: # used to check if the transaction was removed, this could happen # if gas price is too low: # # > Transaction (acbca3d6) below gas price (tx=1 Wei ask=18 # > Shannon). All sequential txs from this address(7d0eae79) # > will be ignored # last_result = None while True: # Could return None for a short period of time, until the # transaction is added to the pool transaction = self.call('eth_getTransactionByHash', transaction_hash) # if the transaction was added to the pool and then removed if transaction is None and last_result is not None: raise Exception('invalid transaction, check gas price') # the transaction was added to the pool and mined if transaction and transaction['blockNumber'] is not None: break last_result = transaction gevent.sleep(.5) if confirmations: # this will wait for both APPLIED and REVERTED transactions transaction_block = quantity_decoder(transaction['blockNumber']) confirmation_block = transaction_block + confirmations block_number = self.block_number() while block_number < confirmation_block: gevent.sleep(.5) block_number = self.block_number() except gevent.Timeout: raise Exception('timeout when polling for transaction') finally: if deadline: deadline.cancel()
def _serialize(value, attr, obj): # pylint: disable=unused-argument return data_encoder(value)
def to_dict(self): return { 'type': self.__class__.__name__, 'delivered_message_identifier': self.delivered_message_identifier, 'signature': data_encoder(self.signature) }
def _serialize(self, value, attr, obj): # pylint: disable=no-self-use return data_encoder(value)