def monitor_settings(self): """Check if a modification of bridge settings is requested by seeing if the config file has been changed and try to update the bridge contract (gather 2/3 validators signatures). """ config_data = load_config_data(self.config_file_path) t_anchor, t_final = query_aergo_tempo(self.hera, self.aergo_bridge) unfreeze_fee = query_unfreeze_fee(self.hera, self.aergo_bridge) config_t_anchor = (config_data['networks'][self.aergo_net]['bridges'] [self.eth_net]['t_anchor']) if t_anchor != config_t_anchor: logger.info( '\"Anchoring periode update requested: %s\"', config_t_anchor) self.update_t_anchor(config_t_anchor) config_t_final = (config_data['networks'][self.aergo_net]['bridges'] [self.eth_net]['t_final']) if t_final != config_t_final: logger.info('\"Finality update requested: %s\"', config_t_final) self.update_t_final(config_t_final) config_unfreeze_fee = (config_data['networks'][self.aergo_net] ['bridges'][self.eth_net]['unfreeze_fee']) if unfreeze_fee != config_unfreeze_fee: logger.info( '\"Unfreeze fee update requested: %s\"', config_unfreeze_fee) self.update_unfreeze_fee(config_unfreeze_fee) if self.oracle_update: validators = query_aergo_validators(self.hera, self.aergo_oracle) config_validators = \ [val['addr'] for val in config_data['validators']] if validators != config_validators: logger.info( '\"Validator set update requested: %s\"', config_validators ) if self.update_validators(config_validators): self.val_connect.use_new_validators(config_data) oracle = query_aergo_oracle(self.hera, self.aergo_bridge) config_oracle = (config_data['networks'][self.aergo_net]['bridges'] [self.eth_net]['oracle']) if oracle != config_oracle: logger.info('\"Oracle change requested: %s\"', config_oracle) self.update_oracle(config_oracle)
def is_valid_eth_anchor(self, anchor) -> Optional[str]: """ An anchor is valid if : 1- it's height is finalized 2- it's root for that height is correct. 3- it's nonce is correct 4- it's height is higher than previous anchored height + t_anchor """ t_anchor, t_final = query_aergo_tempo(self.hera, self.aergo_oracle) # 1- get the last block height and check anchor height > LIB # lib = best_height - finalized_from best_height = self.web3.eth.blockNumber lib = best_height - t_final if anchor.height > lib: return ( "anchor height not finalized, got: {}, expected: {}".format( anchor.height, lib)) # 2- get contract state root at origin_height # and check equals anchor root root = self.web3.eth.getBlock(anchor.height).stateRoot if root != anchor.root: return ( "root doesn't match height {}, got: {}, expected: {}".format( lib, anchor.root.hex(), root)) # 3- check merkle bridge nonces are correct last_nonce_to = int( self.hera.query_sc_state(self.aergo_oracle, ["_sv__nonce"]).var_proofs[0].value) if last_nonce_to != anchor.destination_nonce: return ("anchor nonce invalid, got: {}, expected: {}".format( anchor.destination_nonce, last_nonce_to)) # 4- check anchored height comes after the previous one and t_anchor is # passed last_merged_height_from = int( self.hera.query_sc_state( self.aergo_oracle, ["_sv__anchorHeight"]).var_proofs[0].value) if last_merged_height_from + t_anchor > anchor.height: return ("anchor height too soon, got: {}, expected: {}".format( anchor.height, last_merged_height_from + t_anchor)) return None
def __init__( self, config_file_path: str, aergo_net: str, eth_net: str, eth_block_time: int, privkey_name: str = None, privkey_pwd: str = None, anchoring_on: bool = False, auto_update: bool = False, oracle_update: bool = False, aergo_gas_price: int = None, bridge_anchoring: bool = True ) -> None: threading.Thread.__init__(self, name="AergoProposerClient") if aergo_gas_price is None: aergo_gas_price = 0 self.aergo_gas_price = aergo_gas_price self.config_file_path = config_file_path config_data = load_config_data(self.config_file_path) self.eth_block_time = eth_block_time self.eth_net = eth_net self.aergo_net = aergo_net self.anchoring_on = anchoring_on self.auto_update = auto_update self.oracle_update = oracle_update self.bridge_anchoring = bridge_anchoring logger.info("\"Connect Aergo and Ethereum providers\"") self.hera = herapy.Aergo() self.hera.connect(config_data['networks'][aergo_net]['ip']) ip = config_data['networks'][eth_net]['ip'] self.web3 = Web3(Web3.HTTPProvider(ip)) eth_poa = config_data['networks'][eth_net]['isPOA'] if eth_poa: self.web3.middleware_onion.inject(geth_poa_middleware, layer=0) assert self.web3.isConnected() self.eth_bridge = (config_data['networks'][eth_net]['bridges'] [aergo_net]['addr']) self.aergo_bridge = (config_data['networks'][aergo_net]['bridges'] [eth_net]['addr']) self.aergo_oracle = (config_data['networks'][aergo_net]['bridges'] [eth_net]['oracle']) # get the current t_anchor and t_final for both sides of bridge self.t_anchor, self.t_final = query_aergo_tempo( self.hera, self.aergo_bridge ) logger.info( "\"%s <- %s (t_final=%s) : t_anchor=%s\"", aergo_net, eth_net, self.t_final, self.t_anchor ) if privkey_name is None: privkey_name = 'proposer' if privkey_pwd is None: privkey_pwd = getpass("Decrypt exported private key '{}'\n" "Password: "******"\"Connect to AergoValidators\"") self.val_connect = AergoValConnect( config_data, self.hera, self.aergo_oracle)
def __init__( self, config_file_path: str, aergo_net: str, eth_net: str, eth_block_time: int, privkey_name: str = None, privkey_pwd: str = None, anchoring_on: bool = False, auto_update: bool = False, oracle_update: bool = False, aergo_gas_price: int = None, bridge_anchoring: bool = True, root_path: str = './', eco: bool = False ) -> None: threading.Thread.__init__(self, name="AergoProposerClient") if aergo_gas_price is None: aergo_gas_price = 0 self.aergo_gas_price = aergo_gas_price self.config_file_path = config_file_path config_data = load_config_data(self.config_file_path) self.eth_block_time = eth_block_time self.eth_net = eth_net self.aergo_net = aergo_net self.anchoring_on = anchoring_on self.auto_update = auto_update self.oracle_update = oracle_update self.bridge_anchoring = bridge_anchoring self.eco = eco logger.info("\"Connect Aergo and Ethereum providers\"") self.hera = herapy.Aergo() self.hera.connect(config_data['networks'][aergo_net]['ip']) ip = config_data['networks'][eth_net]['ip'] self.web3 = Web3(Web3.HTTPProvider(ip)) eth_poa = config_data['networks'][eth_net]['isPOA'] if eth_poa: self.web3.middleware_onion.inject(geth_poa_middleware, layer=0) assert self.web3.isConnected() eth_bridge_abi_path = (config_data['networks'][eth_net]['bridges'] [aergo_net]['bridge_abi']) with open(root_path + eth_bridge_abi_path, "r") as f: eth_bridge_abi = f.read() self.eth_bridge_addr = (config_data['networks'][eth_net]['bridges'] [aergo_net]['addr']) self.eth_bridge = self.web3.eth.contract( address=self.eth_bridge_addr, abi=eth_bridge_abi ) self.aergo_bridge = (config_data['networks'][aergo_net]['bridges'] [eth_net]['addr']) self.aergo_oracle = (config_data['networks'][aergo_net]['bridges'] [eth_net]['oracle']) # get the current t_anchor and t_final for both sides of bridge self.t_anchor, self.t_final = query_aergo_tempo( self.hera, self.aergo_bridge ) logger.info( "\"%s <- %s (t_final=%s) : t_anchor=%s\"", aergo_net, eth_net, self.t_final, self.t_anchor ) if not anchoring_on and not auto_update: # if anchoring and auto update are off, use proposer as monitoring # system return if privkey_name is None: privkey_name = 'proposer' keystore_path = config_data["wallet"][privkey_name]['keystore'] with open(root_path + keystore_path, "r") as f: keystore = f.read() if privkey_pwd is None: while True: try: privkey_pwd = getpass( "Decrypt Aergo keystore: '{}'\nPassword: "******"\"Wrong password, try again\"") else: self.aergo_tx = AergoTx( self.hera, keystore, privkey_pwd, self.aergo_oracle, aergo_gas_price, self.t_anchor, eth_block_time ) logger.info("\"Connect to AergoValidators\"") self.val_connect = AergoValConnect( config_data, self.hera, self.aergo_oracle)
def check_bridge_status(root_path: str, config_data: Dict, aergo_net: str, eth_net: str, auto_update: bool, oracle_update: bool): logger.info("\"Connect Aergo and Ethereum\"") hera = herapy.Aergo() hera.connect(config_data['networks'][aergo_net]['ip']) ip = config_data['networks'][eth_net]['ip'] web3 = Web3(Web3.HTTPProvider(ip)) eth_poa = config_data['networks'][eth_net]['isPOA'] if eth_poa: web3.middleware_onion.inject(geth_poa_middleware, layer=0) assert web3.isConnected() # remember bridge contracts # eth bridge bridge_abi_path = ( config_data['networks'][eth_net]['bridges'][aergo_net]['bridge_abi']) with open(root_path + bridge_abi_path, "r") as f: bridge_abi = f.read() eth_bridge_addr = ( config_data['networks'][eth_net]['bridges'][aergo_net]['addr']) # eth oracle oracle_abi_path = ( config_data['networks'][eth_net]['bridges'][aergo_net]['oracle_abi']) with open(root_path + oracle_abi_path, "r") as f: oracle_abi = f.read() eth_oracle_addr = ( config_data['networks'][eth_net]['bridges'][aergo_net]['oracle']) # aergo contracts aergo_bridge = ( config_data['networks'][aergo_net]['bridges'][eth_net]['addr']) aergo_oracle = ( config_data['networks'][aergo_net]['bridges'][eth_net]['oracle']) # check validators are correct and warn the validator will vote for # a new validator set aergo_vals = query_aergo_validators(hera, aergo_oracle) eth_vals = query_eth_validators(web3, eth_oracle_addr, oracle_abi) logger.info("\"Current Aergo validators : %s\"", aergo_vals) logger.info("\"Current Ethereum validators : %s\"", eth_vals) # get the current t_anchor and t_final for both sides of bridge t_anchor_aergo, t_final_aergo = query_aergo_tempo(hera, aergo_bridge) t_anchor_eth, t_final_eth = query_eth_tempo(web3, eth_bridge_addr, bridge_abi) logger.info("\"%s <- %s (t_final=%s) : t_anchor=%s\"", aergo_net, eth_net, t_final_aergo, t_anchor_aergo) logger.info("\"%s (t_final=%s) -> %s : t_anchor=%s\"", aergo_net, t_final_eth, eth_net, t_anchor_eth) if auto_update: if oracle_update: logger.warning( "\"WARNING: This validator will vote for updating the oracle\"" ) logger.warning( "\"WARNING: This validator will vote for settings update in " "config.json\"") if len(aergo_vals) != len(eth_vals): logger.warning( "\"WARNING: different number of validators on both sides of " "the bridge\"") if len(config_data['validators']) != len(aergo_vals): logger.warning( "\"WARNING: This validator is voting for a new set of aergo " "validators\"") if len(config_data['validators']) != len(eth_vals): logger.warning( "\"WARNING: This validator is voting for a new set of eth " "validators\"") for i, validator in enumerate(config_data['validators']): try: if validator['addr'] != aergo_vals[i]: logger.warning( "\"WARNING: This validator is voting for a new set of " "aergo validators\"") except IndexError: # new validators index larger than current validators pass try: if validator['eth-addr'] != eth_vals[i]: logger.warning( "\"WARNING: This validator is voting for a new set of " "eth validators\"") except IndexError: # new validators index larger than current validators pass t_anchor_aergo_c = ( config_data['networks'][aergo_net]['bridges'][eth_net]['t_anchor']) t_final_aergo_c = ( config_data['networks'][aergo_net]['bridges'][eth_net]['t_final']) t_anchor_eth_c = ( config_data['networks'][eth_net]['bridges'][aergo_net]['t_anchor']) t_final_eth_c = ( config_data['networks'][eth_net]['bridges'][aergo_net]['t_final']) if t_anchor_aergo_c != t_anchor_aergo: logger.warning( "\"WARNING: This validator is voting to update anchoring" " periode on aergo\"") if t_final_aergo_c != t_final_aergo: logger.warning( "\"WARNING: This validator is voting to update finality of eth" " on aergo\"") if t_anchor_eth_c != t_anchor_eth: logger.warning( "\"WARNING: This validator is voting to update anchoring" " periode on eth\"") if t_final_eth_c != t_final_eth: logger.warning( "\"WARNING: This validator is voting to update finality of" " aergo on eth\"") aergo_id = query_aergo_id(hera, aergo_oracle) eth_id = query_eth_id(web3, eth_oracle_addr, oracle_abi) return aergo_id, eth_id