def __init__( self, jsonrpc_client: JSONRPCClient, user_deposit_address: UserDepositAddress, contract_manager: ContractManager, proxy_manager: "ProxyManager", ) -> None: if not is_binary_address(user_deposit_address): raise ValueError("Expected binary address format for token nework") check_address_has_code( client=jsonrpc_client, address=Address(user_deposit_address), contract_name=CONTRACT_USER_DEPOSIT, expected_code=decode_hex( contract_manager.get_runtime_hexcode(CONTRACT_USER_DEPOSIT)), ) self.client = jsonrpc_client self.address = user_deposit_address self.node_address = self.client.address self.contract_manager = contract_manager self.gas_measurements = gas_measurements( self.contract_manager.contracts_version) self.proxy_manager = proxy_manager self.proxy = jsonrpc_client.new_contract_proxy( abi=self.contract_manager.get_contract_abi(CONTRACT_USER_DEPOSIT), contract_address=Address(user_deposit_address), ) self.deposit_lock = RLock()
def test_custom_token(custom_token: Contract, web3: Web3, contracts_manager: ContractManager) -> None: """See custom_token.address contains the expected code""" blockchain_bytecode = web3.eth.get_code(custom_token.address) compiled_bytecode = contracts_manager.get_runtime_hexcode( CONTRACT_CUSTOM_TOKEN) assert blockchain_bytecode.hex() == compiled_bytecode
def test_human_standard_token(human_standard_token: Contract, web3: Web3, contracts_manager: ContractManager) -> None: """See human_standard_token.address contains the expected code""" blockchain_bytecode = web3.eth.get_code(human_standard_token.address) compiled_bytecode = contracts_manager.get_runtime_hexcode( CONTRACT_HUMAN_STANDARD_TOKEN) assert blockchain_bytecode.hex() == compiled_bytecode
def __init__( self, jsonrpc_client: JSONRPCClient, monitoring_service_address: MonitoringServiceAddress, contract_manager: ContractManager, block_identifier: BlockIdentifier, ): if not is_binary_address(monitoring_service_address): raise ValueError("Expected binary address for monitoring service") check_address_has_code_handle_pruned_block( client=jsonrpc_client, address=Address(monitoring_service_address), contract_name=CONTRACT_MONITORING_SERVICE, expected_code=decode_hex( contract_manager.get_runtime_hexcode(CONTRACT_MONITORING_SERVICE) ), given_block_identifier=block_identifier, ) proxy = jsonrpc_client.new_contract_proxy( abi=contract_manager.get_contract_abi(CONTRACT_MONITORING_SERVICE), contract_address=Address(monitoring_service_address), ) self.address = monitoring_service_address self.client = jsonrpc_client self.contract_manager = contract_manager self.node_address = self.client.address self.proxy = proxy
def __init__( self, jsonrpc_client: JSONRPCClient, service_registry_address: Address, contract_manager: ContractManager, ): if not is_binary_address(service_registry_address): raise ValueError("Expected binary address for service registry") self.contract_manager = contract_manager check_address_has_code( client=jsonrpc_client, address=service_registry_address, contract_name=CONTRACT_SERVICE_REGISTRY, expected_code=decode_hex( contract_manager.get_runtime_hexcode( CONTRACT_SERVICE_REGISTRY)), ) proxy = jsonrpc_client.new_contract_proxy( abi=self.contract_manager.get_contract_abi( CONTRACT_SERVICE_REGISTRY), contract_address=service_registry_address, ) self.address = service_registry_address self.proxy = proxy self.client = jsonrpc_client self.node_address = self.client.address
def __init__( self, jsonrpc_client: JSONRPCClient, one_to_n_address: OneToNAddress, contract_manager: ContractManager, block_identifier: BlockIdentifier, ): if not is_binary_address(one_to_n_address): raise ValueError("Expected binary address for monitoring service") self.contract_manager = contract_manager check_address_has_code_handle_pruned_block( client=jsonrpc_client, address=Address(one_to_n_address), contract_name=CONTRACT_ONE_TO_N, expected_code=decode_hex( contract_manager.get_runtime_hexcode(CONTRACT_ONE_TO_N)), given_block_identifier=block_identifier, ) proxy = jsonrpc_client.new_contract_proxy( abi=self.contract_manager.get_contract_abi(CONTRACT_ONE_TO_N), contract_address=Address(one_to_n_address), ) self.address = one_to_n_address self.proxy = proxy self.client = jsonrpc_client self.node_address = self.client.address
def __init__( self, jsonrpc_client: JSONRPCClient, user_deposit_address: UserDepositAddress, contract_manager: ContractManager, proxy_manager: "ProxyManager", block_identifier: BlockIdentifier, ) -> None: if not is_binary_address(user_deposit_address): raise ValueError("Expected binary address format for token nework") check_address_has_code_handle_pruned_block( client=jsonrpc_client, address=Address(user_deposit_address), contract_name=CONTRACT_USER_DEPOSIT, expected_code=decode_hex( contract_manager.get_runtime_hexcode(CONTRACT_USER_DEPOSIT)), given_block_identifier=block_identifier, ) self.client = jsonrpc_client self.address = user_deposit_address self.node_address = self.client.address self.contract_manager = contract_manager self.gas_measurements = gas_measurements( self.contract_manager.contracts_version) self.proxy_manager = proxy_manager self.proxy = jsonrpc_client.new_contract_proxy( abi=self.contract_manager.get_contract_abi(CONTRACT_USER_DEPOSIT), contract_address=Address(user_deposit_address), ) # Keeps track of the current in-flight deposits, to avoid sending # unecessary transactions. self._inflight_deposits: Dict[Address, InflightDeposit] = dict() # Don't allow concurrent withdraw_plan and withdraw calls. # This simplifies the precondition checks. self._withdraw_lock = Lock()
def __init__( self, jsonrpc_client: JSONRPCClient, secret_registry_address: SecretRegistryAddress, contract_manager: ContractManager, block_identifier: BlockIdentifier, ) -> None: if not is_binary_address(secret_registry_address): raise ValueError( "Expected binary address format for secret registry") self.contract_manager = contract_manager check_address_has_code_handle_pruned_block( client=jsonrpc_client, address=Address(secret_registry_address), contract_name=CONTRACT_SECRET_REGISTRY, expected_code=decode_hex( contract_manager.get_runtime_hexcode( CONTRACT_SECRET_REGISTRY)), given_block_identifier=block_identifier, ) proxy = jsonrpc_client.new_contract_proxy( abi=self.contract_manager.get_contract_abi( CONTRACT_SECRET_REGISTRY), contract_address=Address(secret_registry_address), ) # There should be only one smart contract deployed, to avoid race # conditions for on-chain unlocks. self.address = secret_registry_address self.proxy = proxy self.client = jsonrpc_client self.node_address = self.client.address # The dictionary of open transactions is used to avoid sending a # transaction for the same secret more than once. This requires # synchronization for the local threads. self.open_secret_transactions: Dict[Secret, AsyncResult] = dict() self._open_secret_transactions_lock = Semaphore()
class ContractVerifier: def __init__(self, web3: Web3, contracts_version: Optional[str] = None): self.web3 = web3 self.contracts_version = contracts_version self.precompiled_path = contracts_precompiled_path( self.contracts_version) self.contract_manager = ContractManager(self.precompiled_path) def verify_deployed_contracts_in_filesystem(self) -> None: chain_id = ChainID(self.web3.eth.chain_id) deployment_data = get_contracts_deployment_info( chain_id=chain_id, version=self.contract_manager.contracts_version, module=DeploymentModule.RAIDEN, ) deployment_file_path = contracts_deployed_path( chain_id=chain_id, version=self.contract_manager.contracts_version) if deployment_data is None: raise RuntimeError( f"Deployment data cannot be found at {deployment_file_path}") if self.verify_deployment_data(deployment_data): print( f"Deployment info from {deployment_file_path} has been verified " "and it is CORRECT.") def verify_deployed_service_contracts_in_filesystem( self, token_address: HexAddress, user_deposit_whole_balance_limit: int, token_network_registry_address: HexAddress, ) -> None: chain_id = ChainID(self.web3.eth.chain_id) deployment_data = get_contracts_deployment_info( chain_id=chain_id, version=self.contract_manager.contracts_version, module=DeploymentModule.SERVICES, ) deployment_file_path = contracts_deployed_path( chain_id=chain_id, version=self.contract_manager.contracts_version, services=True) if deployment_data is None: raise RuntimeError( f"Deployment data cannot be found at {deployment_file_path}") if self.verify_service_contracts_deployment_data( token_address=token_address, user_deposit_whole_balance_limit= user_deposit_whole_balance_limit, deployed_contracts_info=deployment_data, token_network_registry_address=token_network_registry_address, ): print( f"Deployment info from {deployment_file_path} has been verified " "and it is CORRECT.") def store_and_verify_deployment_info_raiden( self, deployed_contracts_info: DeployedContracts) -> None: self._store_deployment_info(deployment_info=deployed_contracts_info, services=False) self.verify_deployed_contracts_in_filesystem() def store_and_verify_deployment_info_services( self, deployed_contracts_info: DeployedContracts, token_address: HexAddress, user_deposit_whole_balance_limit: int, token_network_registry_address: HexAddress, ) -> None: self._store_deployment_info(services=True, deployment_info=deployed_contracts_info) self.verify_deployed_service_contracts_in_filesystem( token_address=token_address, user_deposit_whole_balance_limit=user_deposit_whole_balance_limit, token_network_registry_address=token_network_registry_address, ) def _store_deployment_info(self, services: bool, deployment_info: DeployedContracts) -> None: deployment_file_path = contracts_deployed_path( chain_id=ChainID(self.web3.eth.chain_id), version=self.contracts_version, services=services, ) with deployment_file_path.open(mode="w") as target_file: target_file.write(json.dumps(deployment_info, indent=2)) print( f'Deployment information for chain id = {deployment_info["chain_id"]} ' f" has been updated at {deployment_file_path}.") def verify_deployment_data(self, deployment_data: DeployedContracts) -> bool: chain_id = self.web3.eth.chain_id if self.contract_manager.contracts_version != deployment_data[ "contracts_version"]: raise RuntimeError("Version string mismatch.") if chain_id != deployment_data["chain_id"]: raise RuntimeError("chain id mismatch.") secret_registry, _ = self._verify_deployed_contract( deployment_data=deployment_data, contract_name=CONTRACT_SECRET_REGISTRY) token_network_registry, constructor_arguments = self._verify_deployed_contract( deployment_data=deployment_data, contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY) # We need to also check the constructor parameters against the chain if (to_checksum_address(token_network_registry.functions. secret_registry_address().call()) != secret_registry.address): raise RuntimeError( "secret_registry_address onchain has an unexpected value.") if len(constructor_arguments) != 5: raise RuntimeError( "TokenNetworkRegistry received a wrong number of constructor arguments." ) if secret_registry.address != constructor_arguments[0]: raise RuntimeError( "TokenNetworkRegistry's constructor received a different SecretRegistry address." ) if token_network_registry.functions.chain_id().call( ) != constructor_arguments[1]: raise RuntimeError("TokenNetwork remembers a wrong chain_id.") assert (token_network_registry.functions.settlement_timeout_min().call( ) == constructor_arguments[2]) assert (token_network_registry.functions.settlement_timeout_max().call( ) == constructor_arguments[3]) return True def _verify_deployed_contract( self, deployment_data: DeployedContracts, contract_name: str) -> Tuple[Contract, List[Any]]: """Verify deployment info against the chain Verifies: - the runtime bytecode - precompiled data against the chain - information stored in deployment_*.json against the chain, except for the constructor arguments, which have to be checked separately. Returns: (onchain_instance, constructor_arguments) """ contract_instance = self.contract_instance_from_deployment_data( deployment_data, contract_name) contracts = deployment_data["contracts"] # Check blockchain transaction hash & block information receipt = self.web3.eth.getTransactionReceipt( contracts[contract_name]["transaction_hash"]) if receipt["blockNumber"] != contracts[contract_name]["block_number"]: raise RuntimeError( f'We have block_number {contracts[contract_name]["block_number"]} in the ' f'deployment info, but {receipt["blockNumber"]} in the transaction receipt ' "from web3.") if receipt["gasUsed"] != contracts[contract_name]["gas_cost"]: raise RuntimeError( f'We have gasUsed {contracts[contract_name]["gas_cost"]} in the deployment info, ' f'but {receipt["gasUsed"]} in the transaction receipt from web3.' ) if receipt["contractAddress"] != contracts[contract_name]["address"]: raise RuntimeError( f'We have contractAddress {contracts[contract_name]["address"]} in the deployment' f' info but {receipt["contractAddress"]} in the transaction receipt from web3.' ) # Check that the deployed bytecode matches the precompiled data blockchain_bytecode = self.web3.eth.get_code( contract_instance.address).hex() compiled_bytecode = self.contract_manager.get_runtime_hexcode( contract_name) if contract_name == CONTRACT_TOKEN_NETWORK_REGISTRY: # We need to link the libs into the contract bytecode. # As this is run in the tests in fake file systems, do poor mans linking here compiled_bytecode = link_bytecode( unlinked_bytecode=compiled_bytecode, library_identifier=LIBRARY_TOKEN_NETWORK_UTILS_LINK_KEY, library_address=contracts[LIBRARY_TOKEN_NETWORK_UTILS] ["address"], ) if blockchain_bytecode == compiled_bytecode: print(f"{contract_name} at {contract_instance.address} " f"matches the compiled data from contracts.json") else: raise RuntimeError( f"{contract_name} at {contract_instance.address} has wrong code" ) return contract_instance, contracts[contract_name][ "constructor_arguments"] def contract_instance_from_deployment_data( self, deployment_data: DeployedContracts, contract_name: str) -> Contract: contracts = deployment_data["contracts"] contract_address = contracts[contract_name]["address"] contract_instance = self.web3.eth.contract( abi=self.contract_manager.get_contract_abi(contract_name), address=contract_address) return contract_instance def verify_service_contracts_deployment_data( self, token_address: HexAddress, user_deposit_whole_balance_limit: int, token_network_registry_address: HexAddress, deployed_contracts_info: DeployedContracts, ) -> bool: chain_id = self.web3.eth.chain_id assert deployed_contracts_info is not None if self.contract_manager.contracts_version != deployed_contracts_info[ "contracts_version"]: raise RuntimeError("Version string mismatch") if chain_id != deployed_contracts_info["chain_id"]: raise RuntimeError("chain_id mismatch") ( service_registry, service_registry_constructor_arguments, ) = self._verify_deployed_contract( deployment_data=deployed_contracts_info, contract_name=CONTRACT_SERVICE_REGISTRY) ( user_deposit, user_deposit_constructor_arguments, ) = self._verify_deployed_contract( deployment_data=deployed_contracts_info, contract_name=CONTRACT_USER_DEPOSIT) one_to_n, one_to_n_constructor_arguments = self._verify_deployed_contract( deployment_data=deployed_contracts_info, contract_name=CONTRACT_ONE_TO_N) monitoring_service, ms_constructor_arguments = self._verify_deployed_contract( deployed_contracts_info, CONTRACT_MONITORING_SERVICE) _verify_service_registry_deployment( service_registry=service_registry, constructor_arguments=service_registry_constructor_arguments, token_address=token_address, ) _verify_user_deposit_deployment( user_deposit=user_deposit, constructor_arguments=user_deposit_constructor_arguments, token_address=token_address, user_deposit_whole_balance_limit=user_deposit_whole_balance_limit, one_to_n_address=one_to_n.address, monitoring_service_address=monitoring_service.address, ) _verify_monitoring_service_deployment( monitoring_service=monitoring_service, constructor_arguments=ms_constructor_arguments, token_address=token_address, service_registry_address=service_registry.address, user_deposit_address=user_deposit.address, token_network_registry_address=token_network_registry_address, ) _verify_one_to_n_deployment( one_to_n=one_to_n, constructor_arguments=one_to_n_constructor_arguments, user_deposit_address=user_deposit.address, chain_id=chain_id, service_registry_address=service_registry.address, ) return True
class ContractVerifier: def __init__(self, web3: Web3, contracts_version: Optional[str] = None): self.web3 = web3 self.contracts_version = contracts_version self.precompiled_path = contracts_precompiled_path( self.contracts_version) self.contract_manager = ContractManager(self.precompiled_path) def verify_deployed_contracts_in_filesystem(self) -> None: chain_id = int(self.web3.version.network) deployment_data = get_contracts_deployment_info( chain_id=chain_id, version=self.contract_manager.contracts_version, module=DeploymentModule.RAIDEN, ) deployment_file_path = contracts_deployed_path( chain_id=chain_id, version=self.contract_manager.contracts_version) if deployment_data is None: raise RuntimeError( f"Deployment data cannot be found at {deployment_file_path}") if self.verify_deployment_data(deployment_data): print( f"Deployment info from {deployment_file_path} has been verified" "and it is CORRECT.") def verify_deployed_service_contracts_in_filesystem( self, token_address: HexAddress, user_deposit_whole_balance_limit: int) -> None: chain_id = int(self.web3.version.network) deployment_data = get_contracts_deployment_info( chain_id=chain_id, version=self.contract_manager.contracts_version, module=DeploymentModule.SERVICES, ) deployment_file_path = contracts_deployed_path( chain_id=chain_id, version=self.contract_manager.contracts_version, services=True) if deployment_data is None: raise RuntimeError( f"Deployment data cannot be found at {deployment_file_path}") if self.verify_service_contracts_deployment_data( token_address=token_address, user_deposit_whole_balance_limit= user_deposit_whole_balance_limit, deployed_contracts_info=deployment_data, ): print( f"Deployment info from {deployment_file_path} has been verified " "and it is CORRECT.") def store_and_verify_deployment_info_raiden( self, deployed_contracts_info: DeployedContracts) -> None: self._store_deployment_info(deployment_info=deployed_contracts_info, services=False) self.verify_deployed_contracts_in_filesystem() def store_and_verify_deployment_info_services( self, deployed_contracts_info: DeployedContracts, token_address: HexAddress, user_deposit_whole_balance_limit: int, ) -> None: self._store_deployment_info(services=True, deployment_info=deployed_contracts_info) self.verify_deployed_service_contracts_in_filesystem( token_address=token_address, user_deposit_whole_balance_limit=user_deposit_whole_balance_limit, ) def _store_deployment_info(self, services: bool, deployment_info: DeployedContracts) -> None: deployment_file_path = contracts_deployed_path( chain_id=int(self.web3.version.network), version=self.contracts_version, services=services, ) with deployment_file_path.open(mode="w") as target_file: target_file.write(json.dumps(deployment_info)) print( f'Deployment information for chain id = {deployment_info["chain_id"]} ' f" has been updated at {deployment_file_path}.") def verify_deployment_data(self, deployment_data: DeployedContracts) -> bool: chain_id = int(self.web3.version.network) assert deployment_data is not None if self.contract_manager.version_string != deployment_data[ "contracts_version"]: raise RuntimeError("Version string mismatch.") if chain_id != deployment_data["chain_id"]: raise RuntimeError("chain id mismatch.") self._verify_deployed_contract( deployment_data=deployment_data, contract_name=CONTRACT_ENDPOINT_REGISTRY) secret_registry, _ = self._verify_deployed_contract( deployment_data=deployment_data, contract_name=CONTRACT_SECRET_REGISTRY) token_network_registry, constructor_arguments = self._verify_deployed_contract( deployment_data=deployment_data, contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY) # We need to also check the constructor parameters against the chain assert (to_checksum_address( token_network_registry.functions.secret_registry_address().call()) == secret_registry.address) assert secret_registry.address == constructor_arguments[0] assert token_network_registry.functions.chain_id().call( ) == constructor_arguments[1] assert (token_network_registry.functions.settlement_timeout_min().call( ) == constructor_arguments[2]) assert (token_network_registry.functions.settlement_timeout_max().call( ) == constructor_arguments[3]) return True def _verify_deployed_contract(self, deployment_data: DeployedContracts, contract_name: str) -> Contract: """ Verify deployment info against the chain Verifies: - the runtime bytecode - precompiled data against the chain - information stored in deployment_*.json against the chain, except for the constructor arguments, which have to be checked separately. Returns: (onchain_instance, constructor_arguments) """ contracts = deployment_data["contracts"] contract_address = contracts[contract_name]["address"] contract_instance = self.web3.eth.contract( abi=self.contract_manager.get_contract_abi(contract_name), address=contract_address) # Check blockchain transaction hash & block information receipt = self.web3.eth.getTransactionReceipt( contracts[contract_name]["transaction_hash"]) if receipt["blockNumber"] != contracts[contract_name]["block_number"]: raise RuntimeError( f'We have block_number {contracts[contract_name]["block_number"]} in the ' f'deployment info, but {receipt["blockNumber"]} in the transaction receipt' "from web3.") if receipt["gasUsed"] != contracts[contract_name]["gas_cost"]: raise RuntimeError( f'We have gasUsed {contracts[contract_name]["gas_cost"]} in the deployment info, ' f'but {receipt["gasUsed"]} in the transaction receipt from web3.' ) if receipt["contractAddress"] != contracts[contract_name]["address"]: raise RuntimeError( f'We have contractAddress {contracts[contract_name]["address"]} in the deployment' f' info but {receipt["contractAddress"]} in the transaction receipt from web3.' ) # Check that the deployed bytecode matches the precompiled data blockchain_bytecode = self.web3.eth.getCode(contract_address).hex() compiled_bytecode = self.contract_manager.get_runtime_hexcode( contract_name) if blockchain_bytecode == compiled_bytecode: print(f"{contract_name} at {contract_address} " f"matches the compiled data from contracts.json") else: raise RuntimeError( f"{contract_name} at {contract_address} has wrong code") # Check the contract version version = contract_instance.functions.contract_version().call() # It's an assert because the caller of this function should have checked this. assert version == deployment_data["contracts_version"], ( f'got {version} expected {deployment_data["contracts_version"]}. ' "contract_manager has contracts_version" f"{self.contract_manager.contracts_version}") return contract_instance, contracts[contract_name][ "constructor_arguments"] def verify_service_contracts_deployment_data( self, token_address: HexAddress, user_deposit_whole_balance_limit: int, deployed_contracts_info: DeployedContracts, ) -> bool: chain_id = int(self.web3.version.network) assert deployed_contracts_info is not None if self.contract_manager.version_string != deployed_contracts_info[ "contracts_version"]: raise RuntimeError("Version string mismatch") if chain_id != deployed_contracts_info["chain_id"]: raise RuntimeError("chain_id mismatch") service_registry, service_registry_constructor_arguments = self._verify_deployed_contract( deployment_data=deployed_contracts_info, contract_name=CONTRACT_SERVICE_REGISTRY) user_deposit, user_deposit_constructor_arguments = self._verify_deployed_contract( deployment_data=deployed_contracts_info, contract_name=CONTRACT_USER_DEPOSIT) one_to_n, one_to_n_constructor_arguments = self._verify_deployed_contract( deployment_data=deployed_contracts_info, contract_name=CONTRACT_ONE_TO_N) monitoring_service, ms_constructor_arguments = self._verify_deployed_contract( deployed_contracts_info, CONTRACT_MONITORING_SERVICE) _verify_service_registry_deployment( service_registry=service_registry, constructor_arguments=service_registry_constructor_arguments, token_address=token_address, ) _verify_user_deposit_deployment( user_deposit=user_deposit, constructor_arguments=user_deposit_constructor_arguments, token_address=token_address, user_deposit_whole_balance_limit=user_deposit_whole_balance_limit, one_to_n_address=one_to_n.address, monitoring_service_address=monitoring_service.address, ) _verify_monitoring_service_deployment( monitoring_service=monitoring_service, constructor_arguments=ms_constructor_arguments, token_address=token_address, service_registry_address=service_registry.address, user_deposit_address=user_deposit.address, ) _verify_one_to_n_deployment( one_to_n=one_to_n, constructor_arguments=one_to_n_constructor_arguments, user_deposit_address=user_deposit.address, chain_id=chain_id, ) return True