From 09c6c11e506cf530deeca53c2dd153166885bbfc Mon Sep 17 00:00:00 2001 From: Miguel Paya <71258059+mpaya5@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:25:34 +0100 Subject: [PATCH] feat(api): add endpoint to retrieve consensus contract addresses (#746) * feat: add hardhat node and web3 package - Add Hardhat node service with Docker configuration - Configure hardhat.config.js with local network settings - Add 20 test accounts with 10,000 ETH each - Add web3 Python package to backend requirements - Update .gitignore for Hardhat artifacts and cache * feat: add contract and try script * feat: setup contract compilation and artifact handling - Add contract compilation setup with hardhat - Configure project structure for smart contracts - Fix file path for GhostContract.json artifact - Add necessary dependencies for contract compilation - Setup directory structure for contracts and artifacts * feat: creating hardhat transactions in consensus mechanism - Moved web3 python package into backend requirements file - Added access to compiled hardhat contract in jsonrpc service - Added database migration file so that a transaction has the ghost contract address of hardhat network - When a genlayer contract is deployed then a hardhat contract is deployed, both are linked in the CurrentState table - When a genlayer write method is executed then the new transaction gets the hardhat contract from the CurrentState table - When a genlayer transaction changes from status then a rollup transaction is created on the hardhat network - todo: remove rollup transaction table, put hardhat port in env, link genlayer account to hardhat account, check for out of gas, remove prints * feat: put hardhat port in env * feat: remove rollup transactions database table * feat: free transactions on hardhat, one hardhat account * test: add hardhat test with code from the transaction_processor * fix: resolve pre-commit error * test: moved test to integration tests, upgrade web3 version in requirements as it gave an import error * feat(hardhat): add genlayer-consensus contracts and setup compilation - Add genlayer-consensus smart contracts to hardhat/contracts directory - Update hardhat.config.js to enable new code generator (viaIR: true) - Add @openzeppelin/contracts and @openzeppelin/contracts-upgradeable dependencies - Configure hardhat Docker container for contract compilation - Verify hardhat node functionality in genlayer-studio This commit sets up the smart contract development environment with the necessary dependencies and configurations to compile and deploy genlayer-consensus contracts. * feat: deleted amm_adaptive.py - Deleted amm_adaptive.py because is not using the new syntax I will upload the new amm_adaptive.py whenevir will be ready * fix: contract interaction - Fixed contract deployment state updates for frontend synchronization - Improved contract method interactions and state management - Ensured proper state updates after contract value changes * fix: pre-commit errors solved * fix: solved black pre-commit issue * feat(hardhat): add deployment persistence to hardhat node - Add volume mappings for deployments, artifacts and cache - Implement manual deployment file saving - Update hardhat configuration for proper deployment paths This change ensures that deployed contracts persist between container restarts by saving deployment data to mounted volumes. Note: Block persistence still requires a different solution (Ganache/Geth/Besu) as Hardhat Network doesn't support state persistence. Related issue: #669 * refactor(hardhat): integrate start script into Dockerfile - Removed start.sh script - Incorporated start script directly into Dockerfile for streamlined execution * fix(db): remove duplicate ghost_contract_address assignment Removes redundant assignment of ghost_contract_address in transaction_from_dict function to improve code clarity and prevent potential inconsistencies * fix(security): address SonarCloud warnings and improve test coverage - Ensure non-root user is used in Dockerfile to enhance security - Adjust permissions to avoid overly permissive access (777) on /app directories - Add tests to cover deployment scripts, ensuring proper functionality and coverage * commit: Add contract initialization and consensus service integration - Updated deploy_contracts.js to handle contract initialization after deployment - Added initial ConsensusService integration into server.py - Enhanced Dockerfile.hardhat with proper permissions and chainId handling - Started implementation of consensus_service.py with basic contract interactions Note: consensus_service.py still needs additional work for complete contract initialization and interaction methods. * feat(deploy): integrate contract deployment and backend service Integrate complete contract deployment persistence in Hardhat and create ConsensusService for contract interaction. - Add deployFixture to Hardhat deployment script to handle contract initialization - Create ConsensusService class to interact with deployed contracts from backend - Add deploy_fixture method to ConsensusService as fallback initialization option - Ensure consistent contract initialization between Hardhat and backend - Load contract artifacts and addresses in ConsensusService from deployment files The deployFixture now handles all contract initialization during deployment, while ConsensusService provides a Python interface to interact with the deployed contracts. The deploy_fixture method in ConsensusService mirrors the Hardhat initialization logic but is kept as a fallback option. * feat(hardhat): implement contract deployment flow and deployment backup system - Add consensus contract deployment flow with proper initialization sequence - Implement contract backup system for deployment persistence - Add deployment verification to prevent unnecessary redeployments - Create copy-deployments script for managing contract backups - Configure hardhat to handle contract compilation and deployment * feat(hardhat): implement contract deployment flow and deployment backup system - Add consensus contract deployment flow with proper initialization sequence - Implement contract backup system for deployment persistence - Add deployment verification to prevent unnecessary redeployments - Create copy-deployments script for managing contract backups - Configure hardhat to handle contract compilation and deployment * refactor: remove ghost_contract_address redundant references - Remove ghost_contract_address from transactions_processor to avoid duplication - Remove unused ghost_contract_address from types.py * refactor: replace hardhat-deploy with hardhat-ignition - Migrate deployment process from hardhat-deploy to hardhat-ignition - Create DeployFixture module following TransactionFlow deployment order - Update deployment tests to ensure initialization and contract connecton as TransactionFlow * refactor: remove deployFixture from consensus_service.py - Remove unused deployFixture function from consensus_service.py - Deployment now handled entirely through hardhat-ignition * feat(rpc): add eth_getContract endpoint to retrieve deployed contract addresses - Add new RPC endpoint to fetch contract information by name - Return contract address for deployed contracts - Register endpoint in RPC server initialization * refactor(consensus): remove unused methods from ConsensusService - Remove _send_transaction helper method as it's not being used - Remove get_accounts method as it's no longer needes * feat(rpc): enhance eth_getContract endpoint with ABI and message handling - Add ABI to contract endpoint response for better contract interaction - Integrate MessageHandler into ConsensusService for improved logging * feat(hardhat): add bytecode to contract deployment data - Include bytecode in saved deployment JSON files - Included bytecode in the response for the eth_getContract endpoint * resolve: changed the endpoint name: eth_getContract -> sim_getContract * refactor: delete msg_handler in ConsensusService * refactor: delete msg_handler in ConsensusService * refactor: delete msg_handler in ConsensusService * resolve: changed the endpoint name: eth_getContract -> sim_getContract --------- Co-authored-by: kstroobants --- backend/protocol_rpc/endpoints.py | 32 +++++++++++++++++++++ backend/protocol_rpc/server.py | 1 + backend/rollup/consensus_service.py | 44 ++++++----------------------- hardhat/scripts/deploy.js | 7 ++--- 4 files changed, 44 insertions(+), 40 deletions(-) diff --git a/backend/protocol_rpc/endpoints.py b/backend/protocol_rpc/endpoints.py index b312d3ab..f3076aa2 100644 --- a/backend/protocol_rpc/endpoints.py +++ b/backend/protocol_rpc/endpoints.py @@ -12,6 +12,7 @@ from backend.database_handler.contract_snapshot import ContractSnapshot from backend.database_handler.llm_providers import LLMProviderRegistry +from backend.rollup.consensus_service import ConsensusService from backend.database_handler.models import Base from backend.domain.types import LLMProvider, Validator, TransactionType from backend.node.create_nodes.providers import ( @@ -528,6 +529,32 @@ def set_finality_window_time(consensus: ConsensusAlgorithm, time: int) -> None: consensus.set_finality_window_time(time) +def get_contract(consensus_service: ConsensusService, contract_name: str) -> dict: + """ + Get contract instance by name + + Args: + consensus_service: The consensus service instance + contract_name: Name of the contract to retrieve + + Returns: + dict: Contract information including address and ABI + """ + contract = consensus_service._load_contract(contract_name) + + if contract is None: + raise JSONRPCError( + message=f"Contract {contract_name} not found", + data={"contract_name": contract_name}, + ) + + return { + "address": contract["address"], + "abi": contract["abi"], + "bytecode": contract["bytecode"], + } + + def register_all_rpc_endpoints( jsonrpc: JSONRPC, msg_handler: MessageHandler, @@ -537,6 +564,7 @@ def register_all_rpc_endpoints( validators_registry: ValidatorsRegistry, llm_provider_registry: LLMProviderRegistry, consensus: ConsensusAlgorithm, + consensus_service: ConsensusService, ): register_rpc_endpoint = partial(generate_rpc_endpoint, jsonrpc, msg_handler) @@ -655,3 +683,7 @@ def register_all_rpc_endpoints( partial(set_finality_window_time, consensus), method_name="sim_setFinalityWindowTime", ) + register_rpc_endpoint( + partial(get_contract, consensus_service), + method_name="sim_getConsensusContract", + ) diff --git a/backend/protocol_rpc/server.py b/backend/protocol_rpc/server.py index 6c7ac612..1b96c47f 100644 --- a/backend/protocol_rpc/server.py +++ b/backend/protocol_rpc/server.py @@ -114,6 +114,7 @@ def create_app(): validators_registry, llm_provider_registry, consensus, + consensus_service, ) diff --git a/backend/rollup/consensus_service.py b/backend/rollup/consensus_service.py index 025755f7..572c8cb3 100644 --- a/backend/rollup/consensus_service.py +++ b/backend/rollup/consensus_service.py @@ -1,9 +1,9 @@ import json import os from web3 import Web3 -from eth_account import Account from typing import Optional from pathlib import Path +from backend.protocol_rpc.message_handler.types import EventType, EventScope, LogEvent class ConsensusService: @@ -20,21 +20,6 @@ def __init__(self): if not self.web3.is_connected(): raise ConnectionError(f"Failed to connect to Hardhat node at {hardhat_url}") - # Set up the default account (similar to ethers.getSigners()[0]) - self.owner = self.web3.eth.accounts[0] - self.web3.eth.default_account = self.owner - self.private_key = os.environ.get("HARDHAT_PRIVATE_KEY") - - # Load all required contracts - self.ghost_contract = self._load_contract("GhostContract") - self.ghost_factory_contract = self._load_contract("GhostFactory") - self.ghost_blueprint_contract = self._load_contract("GhostBlueprint") - self.consensus_manager_contract = self._load_contract("ConsensusManager") - self.mock_gen_staking_contract = self._load_contract("MockGenStaking") - self.queues_contract = self._load_contract("Queues") - self.transactions_contract = self._load_contract("Transactions") - self.consensus_main_contract = self._load_contract("ConsensusMain") - def _load_contract(self, contract_name: str) -> Optional[dict]: """ Load contract deployment data from Hardhat deployments @@ -52,36 +37,25 @@ def _load_contract(self, contract_name: str) -> Optional[dict]: ) if not deployment_path.exists(): - print( - f"CONSENSUS_SERVICE: Deployment file not found at {deployment_path}" - ) return None with open(deployment_path, "r") as f: deployment_data = json.load(f) - # Create contract instance - contract = self.web3.eth.contract( - address=deployment_data["address"], abi=deployment_data["abi"] - ) - print( - f"CONSENSUS_SERVICE: Loaded {contract_name} contract with address {contract.address}" - ) - - return contract + return { + "address": deployment_data["address"], + "abi": deployment_data["abi"], + "bytecode": deployment_data["bytecode"], + } except FileNotFoundError: print( - f"CONSENSUS_SERVICE: Warning: {contract_name} deployment file not found" + f"[CONSENSUS_SERVICE]: Deployment file not found at {deployment_path}" ) return None except json.JSONDecodeError as e: - print( - f"CONSENSUS_SERVICE: Error decoding {contract_name} deployment file: {str(e)}" - ) + print(f"[CONSENSUS_SERVICE]: Error decoding deployment file: {str(e)}") return None except Exception as e: - print( - f"CONSENSUS_SERVICE: Error loading {contract_name} contract: {str(e)}" - ) + print(f"[CONSENSUS_SERVICE] Error loading contract: {str(e)}") return None diff --git a/hardhat/scripts/deploy.js b/hardhat/scripts/deploy.js index 97fcdd42..f2dfd87e 100644 --- a/hardhat/scripts/deploy.js +++ b/hardhat/scripts/deploy.js @@ -11,16 +11,13 @@ const path = require("path"); async function saveDeployment(name, contract, folder = "deployments/localhost") { const deploymentData = { address: await contract.getAddress(), - abi: JSON.parse(contract.interface.formatJson()) + abi: JSON.parse(contract.interface.formatJson()), + bytecode: (await hre.artifacts.readArtifact(name)).bytecode }; await fs.ensureDir(folder); const savePath = path.join(folder, `${name}.json`); await fs.writeJson(savePath, deploymentData, { spaces: 2 }); - - const backupFolder = path.join("copy_deployments/localhost"); - await fs.ensureDir(backupFolder); - await fs.writeJson(path.join(backupFolder, `${name}.json`), deploymentData, { spaces: 2 }); } /**