def prepare_confirmation_transaction(self, transfer_event): nonce = self.get_next_nonce() self.logger.debug( f"Preparing confirmation transaction for address " f"{transfer_event.args['from']} for {transfer_event.args.value} " f"coins (nonce {nonce}, chain {self.w3.eth.chainId})") # hard code gas limit to avoid executing the transaction (which would fail as the sender # address is not defined before signing the transaction, but the contract asserts that # it's a validator) transaction = self.home_bridge_contract.functions.confirmTransfer( compute_transfer_hash(transfer_event), transfer_event.transactionHash, transfer_event.args.value, transfer_event.args["from"], ).buildTransaction({ "gasPrice": self.gas_price, "nonce": nonce, "gas": CONFIRMATION_TRANSACTION_GAS_LIMIT, }) # The signing step should not fail, but we want to exit this execution path early, # therefore it's inside the try block signed_transaction = self.w3.eth.account.sign_transaction( transaction, self.private_key) return signed_transaction
def test_transaction_sending( confirmation_sender, w3_home, tester_home, home_bridge_contract, transfer_event, validator_address, ): transaction = confirmation_sender.prepare_confirmation_transaction( transfer_event, nonce=confirmation_sender.get_next_nonce(), chain_id=int(w3_home.eth.chainId), ) confirmation_sender.send_confirmation_transaction(transaction) assert transaction == confirmation_sender.pending_transaction_queue.peek() tester_home.mine_block() receipt = w3_home.eth.getTransactionReceipt(transaction.hash) assert receipt is not None events = home_bridge_contract.events.Confirmation.getLogs( fromBlock=receipt.blockNumber, toBlock=receipt.blockNumber) assert len(events) == 1 event_args = events[0].args assert event_args.transferHash == compute_transfer_hash(transfer_event) assert event_args.transactionHash == transfer_event.transactionHash assert event_args.amount == transfer_event.args.value assert event_args.recipient == transfer_event.args["from"] assert event_args.validator == validator_address
def prepare_confirmation_transaction(self, transfer_event, nonce: int, chain_id: int): transfer_hash = compute_transfer_hash(transfer_event) transaction_hash = transfer_event.transactionHash amount = transfer_event.args.value recipient = transfer_event.args["from"] logger.info( "confirmTransfer(transferHash=%s transactionHash=%s amount=%s recipient=%s) with nonce=%s, chain_id=%s", transfer_hash.hex(), transaction_hash.hex(), amount, recipient, nonce, chain_id, ) # hard code gas limit to avoid executing the transaction (which would fail as the sender # address is not defined before signing the transaction, but the contract asserts that # it's a validator) transaction = self.home_bridge_contract.functions.confirmTransfer( transferHash=transfer_hash, transactionHash=transaction_hash, amount=amount, recipient=recipient, ).buildTransaction({ "gasPrice": self.gas_price, "nonce": nonce, "gas": CONFIRMATION_TRANSACTION_GAS_LIMIT, "chainId": chain_id, }) signed_transaction = self.w3.eth.account.sign_transaction( transaction, self.private_key) return signed_transaction
def test_recorder_does_not_plan_completed_transfer(recorder, hashes): transfer_event = make_transfer_event(transaction_hash=next(hashes)) completion_event = make_transfer_hash_event( COMPLETION_EVENT_NAME, compute_transfer_hash(transfer_event), next(hashes)) recorder.apply_event(transfer_event) recorder.apply_event(completion_event) assert len(recorder.pull_transfers_to_confirm()) == 0
def test_recorder_does_not_plan_completed_transfer(recorder, transfer_hash, hashes): transfer_event = get_transfer_hash_event( TRANSFER_EVENT_NAME, transfer_hash, next(hashes) ) completion_event = get_transfer_hash_event( COMPLETION_EVENT_NAME, compute_transfer_hash(transfer_event), next(hashes) ) recorder.apply_proper_event(transfer_event) recorder.apply_proper_event(completion_event) assert len(recorder.pull_transfers_to_confirm()) == 0
def apply_proper_event(self, event: AttributeDict) -> None: event_name = event.event if event_name == TRANSFER_EVENT_NAME: transfer_hash = compute_transfer_hash(event) self.transfer_hashes.add(transfer_hash) self.transfer_events[transfer_hash] = event elif event_name == CONFIRMATION_EVENT_NAME: transfer_hash = Hash32(bytes(event.args.transferHash)) assert len(transfer_hash) == 32 self.confirmation_hashes.add(transfer_hash) elif event_name == COMPLETION_EVENT_NAME: transfer_hash = Hash32(bytes(event.args.transferHash)) assert len(transfer_hash) == 32 self.completion_hashes.add(transfer_hash) else: raise ValueError(f"Got unknown event {event}")
def _apply_web3_event(self, event: AttributeDict) -> None: event_name = event.event if event_name == TRANSFER_EVENT_NAME: if event.args.value == 0 or is_same_address( event.args["from"], ZERO_ADDRESS): logger.warning(f"skipping event {event}") return transfer_hash = compute_transfer_hash(event) self.transfer_hashes.add(transfer_hash) self.transfer_events[transfer_hash] = event elif event_name == CONFIRMATION_EVENT_NAME: transfer_hash = Hash32(bytes(event.args.transferHash)) assert len(transfer_hash) == 32 self.confirmation_hashes.add(transfer_hash) elif event_name == COMPLETION_EVENT_NAME: transfer_hash = Hash32(bytes(event.args.transferHash)) assert len(transfer_hash) == 32 self.completion_hashes.add(transfer_hash) else: raise ValueError(f"Got unknown event {event}")
def test_transfer_hash_computation(transfer_event): transfer_hash = compute_transfer_hash(transfer_event) assert transfer_event.logIndex == 5 assert transfer_hash == keccak(transfer_event.transactionHash + b"\x05")