def catch_up(self, to_block: int): from_block = SwapTrackerObject.last_processed('Ethereum') + 1 self.logger.debug(f'Starting to catch up from block {from_block}') if self.config.eth_start_block > from_block: self.logger.debug( f'Due to config fast forwarding to block {self.config.eth_start_block}' ) from_block = self.config.eth_start_block SwapTrackerObject.update_last_processed('Ethereum', from_block) if to_block <= 0 or to_block < from_block: return self.logger.debug(f'Catching up to current block: {to_block}') evt_filter = self.contract.contract.events.Swap.createFilter( fromBlock=from_block, toBlock=to_block) for event in evt_filter.get_all_entries(): self._handle(event) evt_filter = self.contract.contract.events.SwapToken.createFilter( fromBlock=from_block, toBlock=to_block) for event in evt_filter.get_all_entries(): self._handle(event) # for event_name in self.contract.tracked_event(): # for event in self.event_listener.events_in_range(event_name, from_block, to_block): # self.logger.info(f'Found new event at block: {event["blockNumber"]}') SwapTrackerObject.update_last_processed('Ethereum', to_block)
def test_3_confirm_tx(web3_provider, ethr_signers, configuration: Config, erc20_contract, ethr_leader): secret_token_addr = TokenPairing.objects().get(src_network="Ethereum", src_coin="ERC").dst_address assert increase_block_number(web3_provider, configuration.eth_confirmations) # To allow the new EthrSigner to "catch up", we start it after the event submission event in Ethereum # ethr_signers[-1].start() sleep(configuration.sleep_interval + 3) # Validate the tx is confirmed in the smart contract last_nonce = SwapTrackerObject.last_processed(src=secret_token_addr) assert last_nonce > -1 # account for fee fee = erc20_contract.contract.functions.balanceOf(PAYABLE_ADDRESS).call() assert fee > 0 assert TRANSFER_AMOUNT_ERC == erc20_contract.contract.functions.balanceOf( ethr_leader.signer.address).call() + fee assert increase_block_number(web3_provider, configuration.eth_confirmations) sleep(configuration.sleep_interval) last_nonce = SwapTrackerObject.last_processed(secret_token_addr) swap = Swap.objects().get(src_tx_hash=f'{last_nonce}|{secret_token_addr}') assert swap.status == Status.SWAP_CONFIRMED
def test_3_confirm_and_finalize_eth_tx(web3_provider, ethr_signers, configuration: Config): # To allow the new EthrSigner to "catch up", we start it after the event submission event in Ethereum secret_token_addr = TokenPairing.objects().get(src_network="Ethereum", src_coin="ETH").dst_address prev_bal = web3_provider.eth.getBalance(zero_address, "latest") prev_bal_fee = web3_provider.eth.getBalance(PAYABLE_ADDRESS, "latest") ethr_signers[-1].start() sleep(1) assert increase_block_number(web3_provider, configuration.eth_confirmations) sleep(configuration.sleep_interval * 5) # Validate the tx is confirmed in the smart contract last_nonce = SwapTrackerObject.last_processed(secret_token_addr) # ethr_signers[-1].signer.multisig_contract.contract.functions.confirmations(last_nonce, # ethr_signers[-1].account).call() assert last_nonce >= 0 bal_fee = web3_provider.eth.getBalance(PAYABLE_ADDRESS, "latest") assert bal_fee > prev_bal_fee bal = web3_provider.eth.getBalance(zero_address, "latest") assert bal > prev_bal last_nonce = SwapTrackerObject.last_processed(secret_token_addr) swap = Swap.objects().get(src_tx_hash=f'{last_nonce}|{secret_token_addr}') assert swap.status == Status.SWAP_CONFIRMED configuration.eth_start_block = web3_provider.eth.blockNumber
def test_2_swap_s20_to_eth(setup, web3_provider, ethr_leader, configuration: Config, ethr_signers, scrt_signers): swap_contract_addr = configuration['scrt_swap_address'] secret_token_addr = TokenPairing.objects().get(src_network="Ethereum", src_coin="ETH").dst_address # start the eth signers for signer in ethr_signers[:-1]: signer.start() # Generate swap tx on secret network swap = {"send": {"amount": str(TRANSFER_AMOUNT_ETH), "msg": base64.standard_b64encode(zero_address.encode()).decode(), "recipient": swap_contract_addr}} sleep(configuration['sleep_interval']) last_nonce = SwapTrackerObject.last_processed(secret_token_addr) print(f"last processed before: {last_nonce}") tx_hash = run(f"secretcli tx compute execute {secret_token_addr} " f"'{json.dumps(swap)}' --from t1 --gas 300000 -y", shell=True, stdout=PIPE, stderr=PIPE) tx_hash = json.loads(tx_hash.stdout)['txhash'] sleep(configuration['sleep_interval'] + 6) assert query_data_success(tx_hash) != {} last_nonce_after = SwapTrackerObject.last_processed(secret_token_addr) print(f"last processed before: {last_nonce_after}") assert last_nonce + 1 == last_nonce_after # Give ethr signers time to handle the secret20 swap tx increase_block_number(web3_provider, configuration['eth_confirmations']) sleep(configuration['sleep_interval'] + 5)
def _handle(self, event: AttributeDict): """Extracts tx data from @event and add unsigned_tx to db""" if not self.contract.verify_destination(event): return amount = str(self.contract.extract_amount(event)) try: block_number, tx_hash, recipient, token = self.contract.parse_swap_event( event) if token is None: token = 'native' except ValueError: return try: s20 = self._get_s20(token) mint = mint_json(amount, tx_hash, recipient, s20.address) unsigned_tx = create_unsigned_tx(self.config.scrt_swap_address, mint, self.config.chain_id, self.config.enclave_key, self.config.swap_code_hash, self.multisig.address) tx = Swap(src_tx_hash=tx_hash, status=Status.SWAP_UNSIGNED, unsigned_tx=unsigned_tx, src_coin=token, dst_coin=s20.name, dst_address=s20.address, src_network="Ethereum", sequence=self.sequence, amount=amount) tx.save(force_insert=True) self.sequence = self.sequence + 1 self.logger.info( f"saved new Ethereum -> Secret transaction {tx_hash}, for {amount} {s20.name}" ) # SwapTrackerObject.update_last_processed(src=Source.ETH.value, update_val=block_number) except (IndexError, AttributeError, KeyError) as e: self.logger.error(f"Failed on tx {tx_hash}, block {block_number}, " f"due to error: {e}") except RuntimeError as e: self.logger.error( f"Failed to create swap tx for eth hash {tx_hash}, block {block_number}. Error: {e}" ) except NotUniqueError as e: self.logger.error( f"Tried to save duplicate TX, might be a catch up issue - {e}") # return block_number, tx_hash, recipient, s20 SwapTrackerObject.update_last_processed('Ethereum', block_number)
def _scan_swap(self): """ Scans secret network contract for swap events """ self.logger.info( f'Starting for account {self.signer.address} with tokens: {self.token_map=}' ) while not self.stop_event.is_set(): for token in self.token_map: try: swap_tracker = SwapTrackerObject.get_or_create(src=token) next_nonce = swap_tracker.nonce + 1 self.logger.debug( f'Scanning token {token} for query #{next_nonce}') swap_data = query_scrt_swap( next_nonce, self.config["scrt_swap_address"], token) self._handle_swap(swap_data, token, self.token_map[token].address) swap_tracker.nonce = next_nonce swap_tracker.save() next_nonce += 1 except CalledProcessError as e: if b'ERROR: query result: encrypted: Failed to get swap for token' not in e.stderr: self.logger.error( f"Failed to query swap: stdout: {e.stdout} stderr: {e.stderr}" ) # if b'ERROR: query result: encrypted: Failed to get swap for key' not in e.stderr: self.stop_event.wait(self.config['sleep_interval'])
def choose_starting_block(self) -> int: """Returns the block from which we start scanning Ethereum for new tx""" obj = SwapTrackerObject.get_or_create(src=signer_id(self.account)) if obj.nonce == -1: obj.update(nonce=self.config.eth_start_block) return obj.nonce
def run(self): self.logger.info("Starting") # todo: fix so tracker doesn't start from 0 from_block = SwapTrackerObject.get_or_create(src="Ethereum").nonce self.event_listener.register(self.confirmer.submit, ['Submission'], from_block=from_block) self.event_listener.register(self.confirmer.withdraw, ['Withdraw'], from_block=from_block) self.event_listener.register(self.confirmer.failed_withdraw, ['WithdrawFailure'], from_block=from_block) self.event_listener.start() self._scan_swap()
def test_2_swap_s20_to_erc(web3_provider, ethr_leader, configuration: Config, ethr_signers, erc20_contract): swap_contract_addr = configuration['scrt_swap_address'] secret_token_addr = TokenPairing.objects().get(src_network="Ethereum", src_coin="ERC").dst_address # for signer in ethr_signers[:-1]: # signer.start() # Generate swap tx on secret network swap = {"send": {"amount": str(TRANSFER_AMOUNT_ERC), "msg": base64.b64encode(ethr_leader.signer.address.encode()).decode(), "recipient": swap_contract_addr}} last_nonce = SwapTrackerObject.last_processed(src=secret_token_addr) tx_hash = run(f"secretcli tx compute execute {secret_token_addr} " f"'{json.dumps(swap)}' --from t1 -b block -y --gas 300000", shell=True, stdout=PIPE, stderr=PIPE) tx_hash = json.loads(tx_hash.stdout)['txhash'] print(f'{tx_hash=}') # Verify that leader recognized the burn tx assert increase_block_number(web3_provider, configuration['eth_confirmations']) sleep(configuration['sleep_interval']) assert last_nonce + 1 == SwapTrackerObject.last_processed(src=secret_token_addr)
def _set_tx_result(self, nonce, token, success=True): try: swap = self.get_swap(nonce, token) except Exception as e: self.logger.error(f'Error handling swap {build_hash(nonce, token)}: {e}') return if swap.status != Status.SWAP_SUBMITTED: return if success: swap.update(status=Status.SWAP_CONFIRMED) else: swap.update(status=Status.SWAP_FAILED) obj = SwapTrackerObject.get_or_create(self._confirmer_id(token)) obj.update(nonce=nonce)
def _scan_swap(self): """ Scans secret network contract for swap events """ self.logger.info(f'Starting for account {self.signer.address} with tokens: {self.token_map=}') while not self.stop_event.is_set(): num_of_tokens = TokenPairing.objects(src_network=self.network).count() if num_of_tokens != len(self.token_map.keys()): self._refresh_token_map() self.logger.info(f'Refreshed tracked tokens. Now tracking {len(self.token_map.keys())} tokens') for transaction in Swap.objects(status=Status.SWAP_RETRY, src_network="Secret"): # self._handle_swap(swap_data, token, self.token_map[token].address) try: token, nonce = _parse_db_tx(transaction) swap_data = query_scrt_swap(nonce, self.config.scrt_swap_address, token) # self._retry(transaction) self._handle_swap(swap_data, token, self.token_map[token].address, True) except Exception as e: # pylint: disable=broad-except self.logger.error(f'Failed to retry swap: {e}') transaction.update(status=Status.SWAP_FAILED) for token in self.token_map: try: swap_tracker = SwapTrackerObject.get_or_create(src=token) next_nonce = swap_tracker.nonce + 1 self.logger.debug(f'Scanning token {token} for query #{next_nonce}') swap_data = query_scrt_swap(next_nonce, self.config.scrt_swap_address, token) self._handle_swap(swap_data, token, self.token_map[token].address) swap_tracker.nonce = next_nonce swap_tracker.save() next_nonce += 1 except CalledProcessError as e: if b'ERROR: query result: encrypted: Failed to get swap for token' not in e.stderr: self.logger.error(f"Failed to query swap: stdout: {e.stdout} stderr: {e.stderr}") # if b'ERROR: query result: encrypted: Failed to get swap for key' not in e.stderr: self.stop_event.wait(self.config.sleep_interval)
def update_tracker_object(self, submission_event): obj = SwapTrackerObject.objects().get(src=signer_id(self.account)) obj.update(nonce=submission_event["blockNumber"])