From eaeb06dbbb1f6bf8f2a0ccbfda87fec0ffda558d Mon Sep 17 00:00:00 2001 From: Steve Degosserie <723552+stiiifff@users.noreply.github.com> Date: Thu, 12 Feb 2026 09:22:37 +0100 Subject: [PATCH 1/4] feat(contracts): deploy stagenet-hoodi AVS contracts, fix verification and update-metadata CLI (#439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Add stagenet-hoodi deployment artifacts (contract addresses, rewards info) and update Snowbridge config with latest validator set - Fix the `bun cli contracts verify` command to correctly verify all deployed contracts, including proxy contracts and Snowbridge dependencies - Fix the `bun cli contracts update-metadata` command to use the correct config file when `--environment` is specified ## Contract verification fixes The verification CLI hardcoded all contract source paths as `src/.sol`, which failed for: - **Snowbridge contracts** (Gateway, BeefyClient, AgentExecutor) — these live in `lib/snowbridge/contracts/src/` - **Gateway proxy** — the `Gateway` deployment address is actually a `GatewayProxy`, not the Gateway implementation. The implementation address needs to be resolved from the ERC1967 storage slot - **ServiceManager proxy** — was not being verified at all Changes: - Added `contractPath` field to `ContractToVerify` so each contract specifies its source location relative to the contracts directory - Added `guessConstructorArgs` option for proxy contracts with complex encoded init data (uses forge's `--guess-constructor-args`) - Gateway is now verified as two separate contracts: Gateway Implementation (address resolved from ERC1967 proxy slot) and GatewayProxy - ServiceManager proxy is now verified as `TransparentUpgradeableProxy` ## Update-metadata fix The `update-metadata` command was ignoring the `--environment` flag when selecting the deployments file: 1. The handler received a pre-built networkId (`"stagenet-hoodi"`) as the chain parameter, which `getChainDeploymentParams` couldn't resolve (falling back to anvil). Now chain and environment are passed separately. 2. Commander.js routed `--environment` to the parent contracts command, leaving `options.environment` undefined on the subcommand. Added the same parent-fallback logic already used for `--chain`. ## Test plan - [x] `bun typecheck` passes - [x] Ran `bun cli contracts verify --chain hoodi --environment stagenet` — all contracts verified successfully on Etherscan - [x] `bun cli contracts update-metadata --chain hoodi --environment stagenet` now reads the correct `stagenet-hoodi.json` deployments file --------- Co-authored-by: Claude Opus 4.6 --- contracts/config/stagenet-hoodi.json | 6 +- .../stagenet-hoodi-rewards-info.json | 1 + contracts/deployments/stagenet-hoodi.json | 1 + .../cli/handlers/contracts/update-metadata.ts | 11 +-- test/cli/handlers/contracts/verify.ts | 72 ++++++++++++++++--- test/cli/index.ts | 12 ++-- 6 files changed, 81 insertions(+), 22 deletions(-) create mode 100644 contracts/deployments/stagenet-hoodi-rewards-info.json create mode 100644 contracts/deployments/stagenet-hoodi.json diff --git a/contracts/config/stagenet-hoodi.json b/contracts/config/stagenet-hoodi.json index 0dbd84c1..a79ac73a 100644 --- a/contracts/config/stagenet-hoodi.json +++ b/contracts/config/stagenet-hoodi.json @@ -43,16 +43,16 @@ "randaoCommitDelay": 4, "randaoCommitExpiration": 24, "minNumRequiredSignatures": 3, - "startBlock": 1299215, + "startBlock": 1303065, "rewardsMessageOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd", - "initialValidatorSetId": 2179, + "initialValidatorSetId": 2186, "initialValidatorHashes": [ "0x07ce4f2cd558f4d4b529a3362b6ff7d616ca0893b53252dc62829b8218ea5c10", "0xaea5344f086d3be7c94cf3a47436bcbb98de23cf1ee773a9180cfecab0453a50", "0xcd3a33755b27fe810dfb780b3f1df1c25efa1bb826ca618e41022fa900876087", "0x4f4ce8cad711a4b33d15095091f8a98eaf9bfd1b39a9159e605cf5d6783cc667" ], - "nextValidatorSetId": 2180, + "nextValidatorSetId": 2187, "nextValidatorHashes": [ "0x07ce4f2cd558f4d4b529a3362b6ff7d616ca0893b53252dc62829b8218ea5c10", "0xaea5344f086d3be7c94cf3a47436bcbb98de23cf1ee773a9180cfecab0453a50", diff --git a/contracts/deployments/stagenet-hoodi-rewards-info.json b/contracts/deployments/stagenet-hoodi-rewards-info.json new file mode 100644 index 00000000..32bba16a --- /dev/null +++ b/contracts/deployments/stagenet-hoodi-rewards-info.json @@ -0,0 +1 @@ +{"RewardsAgent": "0x2E039a88838241d1Ac738cf2e3C5763ba12571e7","RewardsAgentOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd"} \ No newline at end of file diff --git a/contracts/deployments/stagenet-hoodi.json b/contracts/deployments/stagenet-hoodi.json new file mode 100644 index 00000000..72b70a20 --- /dev/null +++ b/contracts/deployments/stagenet-hoodi.json @@ -0,0 +1 @@ +{"network": "stagenet-hoodi","BeefyClient": "0xE65dc4eCA2Fd428361076e1f204731224CeB4292","AgentExecutor": "0x35d3FdCB19A246a1763421168dF69dA3dE207063","Gateway": "0xE9352f1488F12bFEd722c133C129ca5F467463d1","ServiceManager": "0xED73cCaF067cebC706B2B3a6cf2b9af2c696c6d3","ServiceManagerImplementation": "0x5E1DA2eE025Dac2F8c391Ac86ebA20bd34c32465","RewardsAgent": "0x2E039a88838241d1Ac738cf2e3C5763ba12571e7","DelegationManager": "0x867837a9722C512e0862d8c2E15b8bE220E8b87d","StrategyManager": "0xeE45e76ddbEDdA2918b8C7E3035cd37Eab3b5D41","AVSDirectory": "0xD58f6844f79eB1fbd9f7091d05f7cb30d3363926","RewardsCoordinator": "0x29e8572678e0c272350aa0b4B8f304E47EBcd5e7","AllocationManager": "0x95a7431400F362F3647a69535C5666cA0133CAA0","PermissionController": "0xdcCF401fD121d8C542E96BC1d0078884422aFAD2"} \ No newline at end of file diff --git a/test/cli/handlers/contracts/update-metadata.ts b/test/cli/handlers/contracts/update-metadata.ts index d6f49cba..5c7e50b2 100644 --- a/test/cli/handlers/contracts/update-metadata.ts +++ b/test/cli/handlers/contracts/update-metadata.ts @@ -1,7 +1,7 @@ import { logger, parseDeploymentsFile, printDivider } from "utils"; import { createPublicClient, createWalletClient, encodeFunctionData, http } from "viem"; import { privateKeyToAccount } from "viem/accounts"; -import { getChainDeploymentParams } from "../../../configs/contracts/config"; +import { buildNetworkId, getChainDeploymentParams } from "../../../configs/contracts/config"; import { dataHavenServiceManagerAbi } from "../../../contract-bindings/generated"; /** @@ -10,7 +10,7 @@ import { dataHavenServiceManagerAbi } from "../../../contract-bindings/generated export const updateAVSMetadataURI = async ( chain: string, uri: string, - opts: { execute?: boolean; avsOwnerKey?: string } = {} + opts: { execute?: boolean; avsOwnerKey?: string; environment?: string } = {} ) => { try { const execute = opts.execute ?? false; @@ -22,14 +22,15 @@ export const updateAVSMetadataURI = async ( throw new Error("AVS owner private key is required to execute this transaction"); } - // Get chain configuration + // Get chain configuration using base chain name, and build networkId for deployment file lookup + const networkId = buildNetworkId(chain, opts.environment); const deploymentParams = getChainDeploymentParams(chain); - logger.info(`šŸ«Ž Updating AVS metadata URI on ${chain} chain`); + logger.info(`šŸ«Ž Updating AVS metadata URI on ${networkId}`); logger.info(`Network: ${deploymentParams.network} (Chain ID: ${deploymentParams.chainId})`); logger.info(`RPC URL: ${deploymentParams.rpcUrl}`); logger.info(`New URI: ${uri}`); - const deployments = await parseDeploymentsFile(chain); + const deployments = await parseDeploymentsFile(networkId); const serviceManagerAddress = deployments.ServiceManager; if (!serviceManagerAddress) { diff --git a/test/cli/handlers/contracts/verify.ts b/test/cli/handlers/contracts/verify.ts index 5f369b43..5d6bf98b 100644 --- a/test/cli/handlers/contracts/verify.ts +++ b/test/cli/handlers/contracts/verify.ts @@ -14,8 +14,12 @@ interface ContractToVerify { name: string; address: string; artifactName: string; + /** Path to the contract source file relative to the contracts directory (e.g. "src/Foo.sol" or "lib/snowbridge/contracts/src/Bar.sol") */ + contractPath: string; constructorArgs: string[]; constructorArgTypes: string[]; + /** When true, uses forge's --guess-constructor-args instead of explicit args (useful for proxies with complex init data) */ + guessConstructorArgs?: boolean; } /** @@ -41,11 +45,17 @@ export const verifyContracts = async (options: ContractsVerifyOptions) => { const deployments = await parseDeploymentsFile(networkId); + // Resolve the Gateway implementation address from the ERC1967 proxy storage slot + const chainConfig = CHAIN_CONFIGS[options.chain as keyof typeof CHAIN_CONFIGS]; + const rpcUrl = options.rpcUrl || chainConfig.RPC_URL; + const gatewayImplAddress = await getProxyImplementation(deployments.Gateway, rpcUrl); + const contractsToVerify: ContractToVerify[] = [ { name: "ServiceManager Implementation", address: deployments.ServiceManagerImplementation, artifactName: "DataHavenServiceManager", + contractPath: "src/DataHavenServiceManager.sol", constructorArgs: [ deployments.RewardsCoordinator, deployments.PermissionController, @@ -54,16 +64,41 @@ export const verifyContracts = async (options: ContractsVerifyOptions) => { constructorArgTypes: ["address", "address", "address"] }, { - name: "Gateway", - address: deployments.Gateway, - artifactName: "Gateway", + name: "ServiceManager Proxy", + address: deployments.ServiceManager, + artifactName: "TransparentUpgradeableProxy", + contractPath: + "lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/contracts/proxy/transparent/TransparentUpgradeableProxy.sol", constructorArgs: [], - constructorArgTypes: [] + constructorArgTypes: [], + guessConstructorArgs: true + }, + ...(gatewayImplAddress + ? [ + { + name: "Gateway Implementation", + address: gatewayImplAddress, + artifactName: "Gateway", + contractPath: "lib/snowbridge/contracts/src/Gateway.sol", + constructorArgs: [deployments.BeefyClient, deployments.AgentExecutor], + constructorArgTypes: ["address", "address"] + } + ] + : []), + { + name: "Gateway Proxy", + address: deployments.Gateway, + artifactName: "GatewayProxy", + contractPath: "lib/snowbridge/contracts/src/GatewayProxy.sol", + constructorArgs: [], + constructorArgTypes: [], + guessConstructorArgs: true }, { name: "BeefyClient", address: deployments.BeefyClient, artifactName: "BeefyClient", + contractPath: "lib/snowbridge/contracts/src/BeefyClient.sol", constructorArgs: [], constructorArgTypes: [] }, @@ -71,11 +106,18 @@ export const verifyContracts = async (options: ContractsVerifyOptions) => { name: "AgentExecutor", address: deployments.AgentExecutor, artifactName: "AgentExecutor", + contractPath: "lib/snowbridge/contracts/src/AgentExecutor.sol", constructorArgs: [], constructorArgTypes: [] } ]; + if (!gatewayImplAddress) { + logger.warn( + "āš ļø Could not resolve Gateway implementation address from proxy, skipping Gateway implementation verification" + ); + } + try { logger.info("šŸ“‹ Contracts to verify:"); contractsToVerify.forEach((contract) => { @@ -109,17 +151,29 @@ export const verifyContracts = async (options: ContractsVerifyOptions) => { async function verifySingleContract(contract: ContractToVerify, options: ContractsVerifyOptions) { logger.info(`\nšŸ” Verifying ${contract.name} (${contract.address})...`); - const { address, artifactName, constructorArgs: args, constructorArgTypes: types } = contract; + const { + address, + artifactName, + contractPath, + constructorArgs: args, + constructorArgTypes: types, + guessConstructorArgs + } = contract; - const abiEncodedArgs = getEncodedConstructorArgs(args, types); - const constructorArgsStr = abiEncodedArgs ? `--constructor-args ${abiEncodedArgs}` : ""; + let constructorArgsStr: string; + if (guessConstructorArgs) { + constructorArgsStr = "--guess-constructor-args"; + } else { + const abiEncodedArgs = getEncodedConstructorArgs(args, types); + constructorArgsStr = abiEncodedArgs ? `--constructor-args ${abiEncodedArgs}` : ""; + } try { const chainConfig = CHAIN_CONFIGS[options.chain as keyof typeof CHAIN_CONFIGS]; const rpcUrl = options.rpcUrl || chainConfig.RPC_URL; const chainParameter = options.chain === "hoodi" ? "--chain-id 560048" : `--chain ${options.chain}`; - const verifyCommand = `forge verify-contract ${address} src/${artifactName}.sol:${artifactName} --rpc-url ${rpcUrl} ${chainParameter} ${constructorArgsStr} --watch`; + const verifyCommand = `forge verify-contract ${address} ${contractPath}:${artifactName} --rpc-url ${rpcUrl} ${chainParameter} ${constructorArgsStr} --watch`; logger.info(`Running: ${verifyCommand}`); @@ -142,7 +196,7 @@ async function verifySingleContract(contract: ContractToVerify, options: Contrac logger.info(`Check manually at: ${chainConfig.BLOCK_EXPLORER}address/${contract.address}`); logger.info("You can also try running the command manually from the contracts directory:"); const rpcUrl = options.rpcUrl || chainConfig.RPC_URL; - const manualCommand = `forge verify-contract ${contract.address} src/${contract.artifactName}.sol:${contract.artifactName} --rpc-url ${rpcUrl} --chain ${options.chain} ${constructorArgsStr}`; + const manualCommand = `forge verify-contract ${contract.address} ${contract.contractPath}:${contract.artifactName} --rpc-url ${rpcUrl} --chain ${options.chain} ${constructorArgsStr}`; logger.info(`cd ../contracts && ${manualCommand}`); } } diff --git a/test/cli/index.ts b/test/cli/index.ts index a9752657..71ad8781 100644 --- a/test/cli/index.ts +++ b/test/cli/index.ts @@ -360,12 +360,14 @@ contractsCommand if (!chain) { throw new Error("--chain parameter is required"); } - // Build network identifier with environment prefix if specified - const environment = options.environment; - const networkId = environment ? `${environment}-${chain}` : chain; - await updateAVSMetadataURI(networkId, options.uri, { + let environment = options.environment; + if (!environment && command.parent) { + environment = command.parent.getOptionValue("environment"); + } + await updateAVSMetadataURI(chain, options.uri, { execute: options.execute, - avsOwnerKey: options.avsOwnerKey + avsOwnerKey: options.avsOwnerKey, + environment }); }); From 7ecab6f7e7b32192779d030fc40f69c9d36484a5 Mon Sep 17 00:00:00 2001 From: Facundo Farall <37149322+ffarall@users.noreply.github.com> Date: Sat, 14 Feb 2026 18:54:38 -0300 Subject: [PATCH 2/4] build: :arrow_up: Upgrade to StorageHub 0.4.1 (#445) Upgrades to StorageHub version [v0.4.1](https://github.com/Moonsong-Labs/storage-hub/releases/tag/v0.4.1). This release include new CLI options as can be seen in the release notes. However, they come with sensible defaults, making it non-breaking. --- operator/Cargo.lock | 148 +++++++++++++++++------------------ operator/Cargo.toml | 68 ++++++++-------- operator/node/src/cli.rs | 40 ++++++++++ operator/node/src/service.rs | 5 ++ 4 files changed, 153 insertions(+), 108 deletions(-) diff --git a/operator/Cargo.lock b/operator/Cargo.lock index 1f6d04ec..53dc87d8 100644 --- a/operator/Cargo.lock +++ b/operator/Cargo.lock @@ -8639,8 +8639,8 @@ dependencies = [ [[package]] name = "pallet-bucket-nfts" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-benchmarking", "frame-support", @@ -8696,8 +8696,8 @@ dependencies = [ [[package]] name = "pallet-cr-randomness" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-support", "frame-system", @@ -8980,8 +8980,8 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-file-system" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "fp-account", "fp-evm", @@ -9220,8 +9220,8 @@ dependencies = [ [[package]] name = "pallet-file-system" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-benchmarking", "frame-support", @@ -9249,8 +9249,8 @@ dependencies = [ [[package]] name = "pallet-file-system-runtime-api" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "parity-scale-codec", "scale-info", @@ -9445,8 +9445,8 @@ dependencies = [ [[package]] name = "pallet-payment-streams" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-benchmarking", "frame-support", @@ -9465,8 +9465,8 @@ dependencies = [ [[package]] name = "pallet-payment-streams-runtime-api" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "parity-scale-codec", "scale-info", @@ -9493,8 +9493,8 @@ dependencies = [ [[package]] name = "pallet-proofs-dealer" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-benchmarking", "frame-support", @@ -9519,8 +9519,8 @@ dependencies = [ [[package]] name = "pallet-proofs-dealer-runtime-api" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "parity-scale-codec", "scale-info", @@ -9558,8 +9558,8 @@ dependencies = [ [[package]] name = "pallet-randomness" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-benchmarking", "frame-support", @@ -9696,8 +9696,8 @@ dependencies = [ [[package]] name = "pallet-storage-providers" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-benchmarking", "frame-support", @@ -9718,8 +9718,8 @@ dependencies = [ [[package]] name = "pallet-storage-providers-runtime-api" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "parity-scale-codec", "scale-info", @@ -13881,8 +13881,8 @@ dependencies = [ [[package]] name = "shc-actors-derive" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "once_cell", "proc-macro2", @@ -13894,8 +13894,8 @@ dependencies = [ [[package]] name = "shc-actors-framework" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "anyhow", "bincode", @@ -13913,8 +13913,8 @@ dependencies = [ [[package]] name = "shc-blockchain-service" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "anyhow", "array-bytes", @@ -13969,8 +13969,8 @@ dependencies = [ [[package]] name = "shc-blockchain-service-db" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "chrono", "diesel", @@ -13993,8 +13993,8 @@ dependencies = [ [[package]] name = "shc-client" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "anyhow", "array-bytes", @@ -14067,8 +14067,8 @@ dependencies = [ [[package]] name = "shc-common" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "anyhow", "bigdecimal", @@ -14131,8 +14131,8 @@ dependencies = [ [[package]] name = "shc-file-manager" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "bincode", "hash-db", @@ -14155,8 +14155,8 @@ dependencies = [ [[package]] name = "shc-file-transfer-service" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "anyhow", "array-bytes", @@ -14184,8 +14184,8 @@ dependencies = [ [[package]] name = "shc-fisherman-service" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "async-trait", "diesel", @@ -14215,8 +14215,8 @@ dependencies = [ [[package]] name = "shc-forest-manager" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "anyhow", "async-trait", @@ -14241,8 +14241,8 @@ dependencies = [ [[package]] name = "shc-indexer-db" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "bigdecimal", "chrono", @@ -14269,8 +14269,8 @@ dependencies = [ [[package]] name = "shc-indexer-service" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "anyhow", "array-bytes", @@ -14320,8 +14320,8 @@ dependencies = [ [[package]] name = "shc-rpc" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "array-bytes", "async-trait", @@ -14366,8 +14366,8 @@ dependencies = [ [[package]] name = "shc-telemetry" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -14383,8 +14383,8 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "shp-constants" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "sp-core", "sp-runtime", @@ -14392,8 +14392,8 @@ dependencies = [ [[package]] name = "shp-data-price-updater" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-support", "parity-scale-codec", @@ -14407,8 +14407,8 @@ dependencies = [ [[package]] name = "shp-file-key-verifier" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-support", "parity-scale-codec", @@ -14425,8 +14425,8 @@ dependencies = [ [[package]] name = "shp-file-metadata" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "hex", "num-bigint", @@ -14441,8 +14441,8 @@ dependencies = [ [[package]] name = "shp-forest-verifier" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-support", "parity-scale-codec", @@ -14458,16 +14458,16 @@ dependencies = [ [[package]] name = "shp-opaque" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "sp-runtime", ] [[package]] name = "shp-session-keys" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "async-trait", "parity-scale-codec", @@ -14481,8 +14481,8 @@ dependencies = [ [[package]] name = "shp-traits" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "frame-support", "parity-scale-codec", @@ -14495,8 +14495,8 @@ dependencies = [ [[package]] name = "shp-treasury-funding" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "log", "shp-traits", @@ -14506,8 +14506,8 @@ dependencies = [ [[package]] name = "shp-tx-implicits-runtime-api" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "parity-scale-codec", "scale-info", @@ -14519,8 +14519,8 @@ dependencies = [ [[package]] name = "shp-types" -version = "0.4.0" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" +version = "0.4.1" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.1#b5d6eb2ffa153d97e079d1fda382773b466f4702" dependencies = [ "sp-core", "sp-runtime", diff --git a/operator/Cargo.toml b/operator/Cargo.toml index ee2d6933..f58bd631 100644 --- a/operator/Cargo.toml +++ b/operator/Cargo.toml @@ -272,42 +272,42 @@ fc-storage = { git = "https://github.com/polkadot-evm/frontier", branch = "stabl # StorageHub ## Runtime -pallet-bucket-nfts = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -pallet-cr-randomness = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -pallet-file-system = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -pallet-file-system-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -pallet-payment-streams = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -pallet-payment-streams-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -pallet-proofs-dealer = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -pallet-proofs-dealer-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -pallet-randomness = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -pallet-storage-providers = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -pallet-storage-providers-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shp-constants = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shp-data-price-updater = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shp-file-key-verifier = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shp-file-metadata = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shp-forest-verifier = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shp-traits = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shp-treasury-funding = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-bucket-nfts = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +pallet-cr-randomness = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +pallet-file-system = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +pallet-file-system-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +pallet-payment-streams = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +pallet-payment-streams-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +pallet-proofs-dealer = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +pallet-proofs-dealer-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +pallet-randomness = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +pallet-storage-providers = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +pallet-storage-providers-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shp-constants = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shp-data-price-updater = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shp-file-key-verifier = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shp-file-metadata = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shp-forest-verifier = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shp-traits = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shp-treasury-funding = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } ## Client -shc-actors-derive = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-actors-framework = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-blockchain-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-client = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-common = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-file-manager = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-file-transfer-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-fisherman-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-forest-manager = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-indexer-db = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-indexer-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shc-rpc = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shp-opaque = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shp-tx-implicits-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } -shp-types = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-actors-derive = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-actors-framework = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-blockchain-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-client = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-common = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-file-manager = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-file-transfer-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-fisherman-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-forest-manager = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-indexer-db = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-indexer-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shc-rpc = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shp-opaque = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shp-tx-implicits-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } +shp-types = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } ## Precompiles -pallet-evm-precompile-file-system = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-evm-precompile-file-system = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.1", default-features = false } # Static linking diff --git a/operator/node/src/cli.rs b/operator/node/src/cli.rs index 51a881a4..af0ac8af 100644 --- a/operator/node/src/cli.rs +++ b/operator/node/src/cli.rs @@ -242,6 +242,16 @@ pub struct ProviderConfigurations { #[arg(long, default_value = "60")] pub extrinsic_retry_timeout: Option, + /// Mortality period for extrinsics in number of blocks. + /// + /// Determines how long a submitted transaction remains valid before expiring. + /// Must be a power of 2 between 4 and `BlockHashCount` (4096). Non-power-of-2 values + /// will be rounded up to the next valid power of 2. Lower values mean transactions + /// expire faster, which helps recover from stuck nonces after block reorgs, but also + /// reduces the window for a transaction to be included on-chain. + #[arg(long, value_name = "BLOCKS", default_value = "256", value_parser = clap::value_parser!(u32).range(4..))] + pub extrinsic_mortality: Option, + /// On blocks that are multiples of this number, the blockchain service will trigger the catch of proofs. #[arg(long, default_value = "4")] pub check_for_pending_proofs_period: Option, @@ -607,6 +617,11 @@ impl ProviderConfigurations { bs_changed = true; } + if let Some(extrinsic_mortality) = self.extrinsic_mortality { + bs_options.extrinsic_mortality = Some(extrinsic_mortality); + bs_changed = true; + } + if let Some(check_for_pending_proofs_period) = self.check_for_pending_proofs_period { bs_options.check_for_pending_proofs_period = Some(check_for_pending_proofs_period); bs_changed = true; @@ -790,6 +805,16 @@ pub struct FishermanConfigurations { help_heading = "Fisherman Strategy Options" )] pub fisherman_ttl_threshold_seconds: Option, + + /// Mortality period for extrinsics in number of blocks. + /// + /// Determines how long a submitted transaction remains valid before expiring. + /// Must be a power of 2 between 4 and BlockHashCount (4096). Non-power-of-2 values + /// will be rounded to the nearest valid power of 2. Lower values mean transactions + /// expire faster, which helps recover from stuck nonces after block reorgs, but also + /// reduces the window for a transaction to be included. + #[arg(long, value_name = "BLOCKS", default_value = "256", value_parser = clap::value_parser!(u32).range(4..))] + pub fisherman_extrinsic_mortality: Option, } impl FishermanConfigurations { @@ -810,6 +835,20 @@ impl FishermanConfigurations { FishermanOrdering::Randomized => FileOrdering::Randomized, }; + // Build blockchain_service options + let mut blockchain_service = None; + let mut bs_options = BlockchainServiceOptions::default(); + let mut bs_changed = false; + + if let Some(extrinsic_mortality) = self.fisherman_extrinsic_mortality { + bs_options.extrinsic_mortality = Some(extrinsic_mortality); + bs_changed = true; + } + + if bs_changed { + blockchain_service = Some(bs_options); + } + Some(FishermanOptions { database_url: self .fisherman_database_url @@ -823,6 +862,7 @@ impl FishermanConfigurations { maintenance_mode: false, // Skipping maintenance mode for now filtering, ordering, + blockchain_service, }) } else { None diff --git a/operator/node/src/service.rs b/operator/node/src/service.rs index 710d6e81..b203e7c7 100644 --- a/operator/node/src/service.rs +++ b/operator/node/src/service.rs @@ -1305,6 +1305,11 @@ where // Set the indexer db pool builder.with_indexer_db_pool(Some(db_pool)); + // Configure blockchain service options for the fisherman + if let Some(c) = fisherman_options.blockchain_service.clone() { + builder.with_blockchain_service_config(c); + } + // Spawn the fisherman service builder .with_fisherman(client.clone(), &fisherman_options) From 990df7ccce049b4e93a2e5e246e85077e2d9eced Mon Sep 17 00:00:00 2001 From: Steve Degosserie <723552+stiiifff@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:43:35 +0200 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E2=9C=A8=20Bump=20client=20version?= =?UTF-8?q?=20to=20v0.24.0=20and=20runtime=20to=20RT1300=20(#446)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operator/Cargo.lock | 120 +++++++++++++-------------- operator/Cargo.toml | 2 +- operator/runtime/mainnet/src/lib.rs | 2 +- operator/runtime/stagenet/src/lib.rs | 2 +- operator/runtime/testnet/src/lib.rs | 2 +- test/.papi/metadata/datahaven.scale | Bin 630941 -> 630941 bytes 6 files changed, 64 insertions(+), 64 deletions(-) diff --git a/operator/Cargo.lock b/operator/Cargo.lock index 53dc87d8..5ffba155 100644 --- a/operator/Cargo.lock +++ b/operator/Cargo.lock @@ -1521,7 +1521,7 @@ dependencies = [ "pallet-message-queue", "parity-scale-codec", "scale-info", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "sp-core", "sp-runtime", "sp-std", @@ -2607,7 +2607,7 @@ dependencies = [ [[package]] name = "datahaven-mainnet-runtime" -version = "0.23.0" +version = "0.24.0" dependencies = [ "alloy-core", "bridge-hub-common 0.13.1", @@ -2719,8 +2719,8 @@ dependencies = [ "shp-treasury-funding", "shp-tx-implicits-runtime-api", "smallvec", - "snowbridge-beacon-primitives 0.23.0", - "snowbridge-core 0.23.0", + "snowbridge-beacon-primitives 0.24.0", + "snowbridge-core 0.24.0", "snowbridge-inbound-queue-primitives", "snowbridge-merkle-tree", "snowbridge-outbound-queue-primitives", @@ -2763,7 +2763,7 @@ dependencies = [ [[package]] name = "datahaven-node" -version = "0.23.0" +version = "0.24.0" dependencies = [ "async-channel 1.9.0", "clap", @@ -2876,7 +2876,7 @@ dependencies = [ [[package]] name = "datahaven-runtime-common" -version = "0.23.0" +version = "0.24.0" dependencies = [ "alloy-core", "fp-account", @@ -2910,7 +2910,7 @@ dependencies = [ [[package]] name = "datahaven-stagenet-runtime" -version = "0.23.0" +version = "0.24.0" dependencies = [ "alloy-core", "bridge-hub-common 0.13.1", @@ -3022,8 +3022,8 @@ dependencies = [ "shp-treasury-funding", "shp-tx-implicits-runtime-api", "smallvec", - "snowbridge-beacon-primitives 0.23.0", - "snowbridge-core 0.23.0", + "snowbridge-beacon-primitives 0.24.0", + "snowbridge-core 0.24.0", "snowbridge-inbound-queue-primitives", "snowbridge-merkle-tree", "snowbridge-outbound-queue-primitives", @@ -3066,7 +3066,7 @@ dependencies = [ [[package]] name = "datahaven-testnet-runtime" -version = "0.23.0" +version = "0.24.0" dependencies = [ "alloy-core", "bridge-hub-common 0.13.1", @@ -3178,8 +3178,8 @@ dependencies = [ "shp-treasury-funding", "shp-tx-implicits-runtime-api", "smallvec", - "snowbridge-beacon-primitives 0.23.0", - "snowbridge-core 0.23.0", + "snowbridge-beacon-primitives 0.24.0", + "snowbridge-core 0.24.0", "snowbridge-inbound-queue-primitives", "snowbridge-merkle-tree", "snowbridge-outbound-queue-primitives", @@ -3371,7 +3371,7 @@ dependencies = [ [[package]] name = "dhp-bridge" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-support", "frame-system", @@ -3379,7 +3379,7 @@ dependencies = [ "pallet-datahaven-native-transfer", "pallet-external-validators", "parity-scale-codec", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "snowbridge-inbound-queue-primitives", "sp-core", "sp-std", @@ -8716,7 +8716,7 @@ dependencies = [ [[package]] name = "pallet-datahaven-native-transfer" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -8724,7 +8724,7 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "snowbridge-outbound-queue-primitives", "sp-core", "sp-io", @@ -8828,7 +8828,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-balances-erc20" -version = "0.23.0" +version = "0.24.0" dependencies = [ "fp-evm", "frame-support", @@ -8851,7 +8851,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-batch" -version = "0.23.0" +version = "0.24.0" dependencies = [ "evm", "fp-evm", @@ -8890,7 +8890,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-call-permit" -version = "0.23.0" +version = "0.24.0" dependencies = [ "evm", "fp-evm", @@ -8956,7 +8956,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-datahaven-native-transfer" -version = "0.23.0" +version = "0.24.0" dependencies = [ "evm", "fp-evm", @@ -8970,7 +8970,7 @@ dependencies = [ "parity-scale-codec", "precompile-utils", "scale-info", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "snowbridge-outbound-queue-primitives", "sp-core", "sp-io", @@ -9049,7 +9049,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-proxy" -version = "0.23.0" +version = "0.24.0" dependencies = [ "evm", "fp-evm", @@ -9093,7 +9093,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-registry" -version = "0.23.0" +version = "0.24.0" dependencies = [ "fp-evm", "frame-support", @@ -9144,7 +9144,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "snowbridge-outbound-queue-primitives", "sp-core", "sp-io", @@ -9154,7 +9154,7 @@ dependencies = [ [[package]] name = "pallet-external-validators" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9178,7 +9178,7 @@ dependencies = [ [[package]] name = "pallet-external-validators-rewards" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9191,7 +9191,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "scale-info", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "snowbridge-outbound-queue-primitives", "sp-core", "sp-io", @@ -9417,7 +9417,7 @@ dependencies = [ [[package]] name = "pallet-outbound-commitment-store" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-support", "frame-system", @@ -9541,7 +9541,7 @@ dependencies = [ [[package]] name = "pallet-proxy-genesis-companion" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-support", "frame-system", @@ -9652,7 +9652,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -14800,7 +14800,7 @@ dependencies = [ [[package]] name = "snowbridge-beacon-primitives" -version = "0.23.0" +version = "0.24.0" dependencies = [ "byte-slice-cast", "frame-support", @@ -14845,7 +14845,7 @@ dependencies = [ [[package]] name = "snowbridge-core" -version = "0.23.0" +version = "0.24.0" dependencies = [ "bp-relayers", "ethabi-decode", @@ -14922,8 +14922,8 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "snowbridge-beacon-primitives 0.23.0", - "snowbridge-core 0.23.0", + "snowbridge-beacon-primitives 0.24.0", + "snowbridge-core 0.24.0", "snowbridge-verification-primitives", "sp-core", "sp-io", @@ -14936,7 +14936,7 @@ dependencies = [ [[package]] name = "snowbridge-merkle-tree" -version = "0.23.0" +version = "0.24.0" dependencies = [ "array-bytes", "hex", @@ -14977,7 +14977,7 @@ dependencies = [ [[package]] name = "snowbridge-outbound-queue-primitives" -version = "0.23.0" +version = "0.24.0" dependencies = [ "alloy-core", "ethabi-decode", @@ -14989,7 +14989,7 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "scale-info", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "snowbridge-verification-primitives", "sp-arithmetic", "sp-core", @@ -15003,12 +15003,12 @@ dependencies = [ [[package]] name = "snowbridge-outbound-queue-v2-runtime-api" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-support", "parity-scale-codec", "scale-info", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "snowbridge-merkle-tree", "snowbridge-outbound-queue-primitives", "sp-api", @@ -15018,7 +15018,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-ethereum-client" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -15031,8 +15031,8 @@ dependencies = [ "scale-info", "serde", "serde_json", - "snowbridge-beacon-primitives 0.23.0", - "snowbridge-core 0.23.0", + "snowbridge-beacon-primitives 0.24.0", + "snowbridge-core 0.24.0", "snowbridge-ethereum 0.3.0", "snowbridge-inbound-queue-primitives", "snowbridge-pallet-ethereum-client-fixtures", @@ -15048,8 +15048,8 @@ name = "snowbridge-pallet-ethereum-client-fixtures" version = "0.9.0" dependencies = [ "hex-literal 0.3.4", - "snowbridge-beacon-primitives 0.23.0", - "snowbridge-core 0.23.0", + "snowbridge-beacon-primitives 0.24.0", + "snowbridge-core 0.24.0", "snowbridge-inbound-queue-primitives", "sp-core", "sp-std", @@ -15057,7 +15057,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-inbound-queue-v2" -version = "0.23.0" +version = "0.24.0" dependencies = [ "alloy-core", "bp-relayers", @@ -15071,8 +15071,8 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "snowbridge-beacon-primitives 0.23.0", - "snowbridge-core 0.23.0", + "snowbridge-beacon-primitives 0.24.0", + "snowbridge-core 0.24.0", "snowbridge-inbound-queue-primitives", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-inbound-queue-v2-fixtures", @@ -15093,8 +15093,8 @@ name = "snowbridge-pallet-inbound-queue-v2-fixtures" version = "0.10.0" dependencies = [ "hex-literal 0.3.4", - "snowbridge-beacon-primitives 0.23.0", - "snowbridge-core 0.23.0", + "snowbridge-beacon-primitives 0.24.0", + "snowbridge-core 0.24.0", "snowbridge-inbound-queue-primitives", "sp-core", "sp-std", @@ -15124,7 +15124,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-outbound-queue-v2" -version = "0.23.0" +version = "0.24.0" dependencies = [ "alloy-core", "bp-relayers", @@ -15138,8 +15138,8 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "snowbridge-beacon-primitives 0.23.0", - "snowbridge-core 0.23.0", + "snowbridge-beacon-primitives 0.24.0", + "snowbridge-core 0.24.0", "snowbridge-inbound-queue-primitives", "snowbridge-merkle-tree", "snowbridge-outbound-queue-primitives", @@ -15170,7 +15170,7 @@ dependencies = [ "parity-scale-codec", "polkadot-primitives", "scale-info", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "snowbridge-outbound-queue-primitives", "snowbridge-pallet-outbound-queue", "sp-core", @@ -15183,7 +15183,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-system-v2" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -15195,7 +15195,7 @@ dependencies = [ "parity-scale-codec", "polkadot-primitives", "scale-info", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "snowbridge-outbound-queue-primitives", "snowbridge-pallet-outbound-queue-v2", "snowbridge-pallet-system", @@ -15211,10 +15211,10 @@ dependencies = [ [[package]] name = "snowbridge-system-v2-runtime-api" -version = "0.23.0" +version = "0.24.0" dependencies = [ "parity-scale-codec", - "snowbridge-core 0.23.0", + "snowbridge-core 0.24.0", "sp-api", "sp-std", "staging-xcm", @@ -15222,7 +15222,7 @@ dependencies = [ [[package]] name = "snowbridge-test-utils" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -15242,12 +15242,12 @@ dependencies = [ [[package]] name = "snowbridge-verification-primitives" -version = "0.23.0" +version = "0.24.0" dependencies = [ "frame-support", "parity-scale-codec", "scale-info", - "snowbridge-beacon-primitives 0.23.0", + "snowbridge-beacon-primitives 0.24.0", "sp-core", "sp-std", ] diff --git a/operator/Cargo.toml b/operator/Cargo.toml index f58bd631..92fd4f46 100644 --- a/operator/Cargo.toml +++ b/operator/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" homepage = "https://datahaven.xyz/" license = "GPL-3" repository = "https://github.com/datahavenxyz/datahaven" -version = "0.23.0" +version = "0.24.0" [workspace] members = [ diff --git a/operator/runtime/mainnet/src/lib.rs b/operator/runtime/mainnet/src/lib.rs index ceb8f373..660a630e 100644 --- a/operator/runtime/mainnet/src/lib.rs +++ b/operator/runtime/mainnet/src/lib.rs @@ -142,7 +142,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 200 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 1200, + spec_version: 1300, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/operator/runtime/stagenet/src/lib.rs b/operator/runtime/stagenet/src/lib.rs index 2bc4d60b..b78a265e 100644 --- a/operator/runtime/stagenet/src/lib.rs +++ b/operator/runtime/stagenet/src/lib.rs @@ -145,7 +145,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 200 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 1200, + spec_version: 1300, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/operator/runtime/testnet/src/lib.rs b/operator/runtime/testnet/src/lib.rs index 88ef82eb..0f1d0dd5 100644 --- a/operator/runtime/testnet/src/lib.rs +++ b/operator/runtime/testnet/src/lib.rs @@ -142,7 +142,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 200 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 1200, + spec_version: 1300, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/test/.papi/metadata/datahaven.scale b/test/.papi/metadata/datahaven.scale index 7f390905fafa57c798c2baadf955d19e40869bd2..0b0e1f95055fe0354d34781473f2ae7b744ca896 100644 GIT binary patch delta 44 ucmbPxP;Kr(wT2eP7N!>F7M3ln+Gb25tnIpHAj}5D>_E)1UDu4WAPWFHkqnmr delta 44 vcmbPxP;Kr(wT2eP7N!>F7M3ln+Gb1}SlV^XSb>-gh}nUdW4o>yXF(PKN*WC^ From b45009f62df5ead38ecc61abeddc9da463fcc434 Mon Sep 17 00:00:00 2001 From: Steve Degosserie <723552+stiiifff@users.noreply.github.com> Date: Tue, 17 Feb 2026 22:38:38 +0200 Subject: [PATCH 4/4] test: Port Moonbeam Moonwall E2E test suites to DataHaven (#436) ## Summary - Port 16 Moonbeam Moonwall E2E test suites to DataHaven, covering proxy, multisig, sudo, txpool, receipts, precompiles, polkadot-js API, and node RPC - Adapt all tests for DataHaven runtime differences: 2 inherents (timestamp + randomness) instead of Moonbeam's 4, different event ordering/counts from fee/treasury handling, no PoV constraints - Use resilient event-based lookups (`.find()`/`.some()`) instead of hardcoded array indices throughout - Add supporting Solidity contracts: `FailingConstructor`, `StorageLoop` ## New test suites | Suite | Category | Tests | |-------|----------|-------| | `test-proxy-balance` | common/proxy | Proxy with Balances type | | `test-proxy` | stagenet/proxy | Add/remove proxy, delayed & announced proxy (7 tests) | | `test-multisigs` | stagenet/multisig | Creation, approval, execution, cancellation | | `test-sudo` | stagenet/sudo | Dispatch, sudoAs, set/remove key | | `test-polkadot-api` | stagenet/polkadot-js | Genesis, headers, transfers, extrinsics, events | | `test-polkadot-chain-info` | stagenet/polkadot-js | Chain name/type queries | | `test-node-rpc-peer` | stagenet/node-rpc | `system_peers` RPC | | `test-precompile-bn128-bounds` | stagenet/precompile | bn128 add/mul/pairing edge cases | | `test-receipt` | stagenet/receipt | Receipt fields, effective gas price | | `test-receipt-revert` | stagenet/receipt | Receipt for reverted transactions | | `test-evm-store-storage-growth` | stagenet/storage-growth | EVM SSTORE storage growth | | `test-txpool-future` | stagenet/txpool | Future transaction pool | | `test-txpool-pending` | stagenet/txpool | Pending transaction pool | --------- Co-authored-by: Claude Opus 4.6 --- .../contracts/src/FailingConstructor.sol | 8 + test/moonwall/contracts/src/StorageLoop.sol | 17 ++ .../common/test-proxy/test-proxy-balance.ts | 80 ++++++ .../dev/stagenet/multisig/test-multisigs.ts | 184 +++++++++++++ .../stagenet/node-rpc/test-node-rpc-peer.ts | 21 ++ .../stagenet/polkadot-js/test-polkadot-api.ts | 141 ++++++++++ .../polkadot-js/test-polkadot-chain-info.ts | 21 ++ .../test-precompile-bn128-bounds.ts | 80 ++++++ .../suites/dev/stagenet/proxy/test-proxy.ts | 257 ++++++++++++++++++ .../stagenet/receipt/test-receipt-revert.ts | 33 +++ .../dev/stagenet/receipt/test-receipt.ts | 61 +++++ .../test-evm-store-storage-growth.ts | 76 ++++++ .../suites/dev/stagenet/sudo/test-sudo.ts | 123 +++++++++ .../dev/stagenet/txpool/test-txpool-future.ts | 69 +++++ .../stagenet/txpool/test-txpool-pending.ts | 84 ++++++ 15 files changed, 1255 insertions(+) create mode 100644 test/moonwall/contracts/src/FailingConstructor.sol create mode 100644 test/moonwall/contracts/src/StorageLoop.sol create mode 100644 test/moonwall/suites/dev/common/test-proxy/test-proxy-balance.ts create mode 100644 test/moonwall/suites/dev/stagenet/multisig/test-multisigs.ts create mode 100644 test/moonwall/suites/dev/stagenet/node-rpc/test-node-rpc-peer.ts create mode 100644 test/moonwall/suites/dev/stagenet/polkadot-js/test-polkadot-api.ts create mode 100644 test/moonwall/suites/dev/stagenet/polkadot-js/test-polkadot-chain-info.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128-bounds.ts create mode 100644 test/moonwall/suites/dev/stagenet/proxy/test-proxy.ts create mode 100644 test/moonwall/suites/dev/stagenet/receipt/test-receipt-revert.ts create mode 100644 test/moonwall/suites/dev/stagenet/receipt/test-receipt.ts create mode 100644 test/moonwall/suites/dev/stagenet/storage-growth/test-evm-store-storage-growth.ts create mode 100644 test/moonwall/suites/dev/stagenet/sudo/test-sudo.ts create mode 100644 test/moonwall/suites/dev/stagenet/txpool/test-txpool-future.ts create mode 100644 test/moonwall/suites/dev/stagenet/txpool/test-txpool-pending.ts diff --git a/test/moonwall/contracts/src/FailingConstructor.sol b/test/moonwall/contracts/src/FailingConstructor.sol new file mode 100644 index 00000000..a30781c9 --- /dev/null +++ b/test/moonwall/contracts/src/FailingConstructor.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.3; + +contract FailingConstructor { + constructor() { + require(false); + } +} diff --git a/test/moonwall/contracts/src/StorageLoop.sol b/test/moonwall/contracts/src/StorageLoop.sol new file mode 100644 index 00000000..fcab6859 --- /dev/null +++ b/test/moonwall/contracts/src/StorageLoop.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.8.2 <0.9.0; + +contract StorageLoop { + mapping(uint256 => uint256) public map; + mapping(uint256 => uint256) public map2; + + function store(uint16 n) public { + for (uint16 i = 0; i < n; i++) { + map[i] = i + 1; + } + } + + function store2(uint256 i) public { + map2[i] = i; + } +} diff --git a/test/moonwall/suites/dev/common/test-proxy/test-proxy-balance.ts b/test/moonwall/suites/dev/common/test-proxy/test-proxy-balance.ts new file mode 100644 index 00000000..371bb80d --- /dev/null +++ b/test/moonwall/suites/dev/common/test-proxy/test-proxy-balance.ts @@ -0,0 +1,80 @@ +import { describeSuite, expect } from "@moonwall/cli"; +import { + ALITH_ADDRESS, + CHARLETH_ADDRESS, + baltathar, +} from "@moonwall/util"; + +describeSuite({ + id: "D010502", + title: "Proxy: Balances", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + it({ + id: "T01", + title: "should accept known proxy", + test: async () => { + const beforeCharlieBalance = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + const { result } = await context.createBlock( + context.polkadotJs().tx.proxy.addProxy(baltathar.address, "Balances" as any, 0) + ); + const proxyAdded = result!.events.find( + ({ event }) => event.method === "ProxyAdded" + ); + expect(proxyAdded).to.not.be.undefined; + expect(proxyAdded!.event.data[2].toString()).to.be.eq("Balances"); //ProxyType + expect(result!.events.some(({ event }) => event.method === "ExtrinsicSuccess")).to.be.true; + + const { result: result2 } = await context.createBlock( + context + .polkadotJs() + .tx.proxy.proxy( + ALITH_ADDRESS, + null, + context.polkadotJs().tx.balances.transferAllowDeath(CHARLETH_ADDRESS, 100) + ) + .signAsync(baltathar) + ); + + const proxyExecuted = result2!.events.find( + ({ event }) => event.method === "ProxyExecuted" + ); + expect(proxyExecuted).to.not.be.undefined; + expect(proxyExecuted!.event.data[0].toString()).to.be.eq("Ok"); + expect(result2!.events.some(({ event }) => event.method === "ExtrinsicSuccess")).to.be + .true; + const afterCharlieBalance = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + expect(afterCharlieBalance - beforeCharlieBalance).to.be.eq(100n); + }, + }); + + it({ + id: "T02", + title: "shouldn't accept other proxy types", + test: async () => { + await context.createBlock( + context.polkadotJs().tx.proxy.addProxy(baltathar.address, "Balances", 0) + ); + + const { result } = await context.createBlock( + context + .polkadotJs() + .tx.proxy.proxy( + ALITH_ADDRESS, + null, + context.polkadotJs().tx.system.remark("0x") + ) + .signAsync(baltathar) + ); + + const proxyExecuted = result!.events.find( + ({ event }) => event.method === "ProxyExecuted" + ); + expect(proxyExecuted).to.not.be.undefined; + // Balances proxy type should not allow system.remark + expect(proxyExecuted!.event.data[0].toString()).to.not.be.eq("Ok"); + expect(result!.events.some(({ event }) => event.method === "ExtrinsicSuccess")).to.be.true; + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/multisig/test-multisigs.ts b/test/moonwall/suites/dev/stagenet/multisig/test-multisigs.ts new file mode 100644 index 00000000..f760607b --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/multisig/test-multisigs.ts @@ -0,0 +1,184 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { blake2AsHex, createKeyMulti } from "@polkadot/util-crypto"; +import { u8aToHex } from "@polkadot/util"; +import { + ALITH_ADDRESS, + BALTATHAR_ADDRESS, + CHARLETH_ADDRESS, + DOROTHY_ADDRESS, + alith, + baltathar, +} from "@moonwall/util"; + +// This test cases in this suite are dependent on each other, and must be run in order. +// TODO: Make the test cases atomic + +describeSuite({ + id: "D022101", + title: "Multisigs - perform multisigs operations", + foundationMethods: "dev", + testCases: ({ context, it }) => { + let threshold: number; + let call: any; + let encodedCall: string; + let encodedCallHash: string; + + // multisig accountId + let encodedMultisigId: Uint8Array; + let multisigId: string; + + beforeAll(async function () { + // set threshold and create multisig accountId + threshold = 2; + encodedMultisigId = createKeyMulti([ALITH_ADDRESS, BALTATHAR_ADDRESS, CHARLETH_ADDRESS], 2); + multisigId = u8aToHex(encodedMultisigId.slice(0, 20)); + + // encode and hash the call we want to dispatch as a multisig operation + call = context.polkadotJs().tx.balances.transferKeepAlive(DOROTHY_ADDRESS, 20); + encodedCall = call.method.toHex(); + encodedCallHash = blake2AsHex(encodedCall); + }); + + it({ + id: "T01", + title: "Should create a multisig operation with asMulti", + test: async () => { + // set signatories + const otherSignatories = [BALTATHAR_ADDRESS, CHARLETH_ADDRESS]; + const block = await context.createBlock( + context + .polkadotJs() + .tx.multisig.asMulti(threshold, otherSignatories, null, encodedCall, {}) + .signAsync(alith) + ); + + // check the event 'NewMultisig' was emitted + const records = await context.polkadotJs().query.system.events(); + const events = records.filter( + ({ event }) => event.section === "multisig" && event.method === "NewMultisig" + ); + expect(events).to.have.lengthOf(1); + expect(block.result!.successful).to.be.true; + }, + }); + + it({ + id: "T02", + title: "Should be able to approve a multisig operation with approveAsMulti", + test: async function () { + // signatories (sorted) + const otherSignatories = [CHARLETH_ADDRESS, ALITH_ADDRESS]; + // create a new multisig operation + await context.createBlock( + context + .polkadotJs() + .tx.multisig.asMulti(threshold, otherSignatories, null, encodedCall, {}) + .signAsync(alith), + { allowFailures: true } + ); + + // take the info of the new multisig operation saved in storage + const multisigInfo = await context + .polkadotJs() + .query.multisig.multisigs(multisigId, encodedCallHash); + const block = await context.createBlock( + context + .polkadotJs() + .tx.multisig.approveAsMulti( + threshold, + otherSignatories, + multisigInfo.unwrap().when, + encodedCallHash, + {} + ) + .signAsync(baltathar), + { allowFailures: true } + ); + + // check the event 'MultisigApproval' was emitted + const records = await context.polkadotJs().query.system.events(); + const events = records.filter( + ({ event }) => event.section === "multisig" && event.method === "MultisigApproval" + ); + expect(events).to.have.lengthOf(1); + expect(block.result!.successful).to.be.true; + }, + }); + + it({ + id: "T03", + title: "Should be able to cancel a multisig operation", + test: async () => { + const otherSignatories = [BALTATHAR_ADDRESS, CHARLETH_ADDRESS]; + // create a new multisig operation + await context.createBlock( + context + .polkadotJs() + .tx.multisig.asMulti(threshold, otherSignatories, null, encodedCall, {}) + .signAsync(alith), + { allowFailures: true } + ); + + // take the info of the new multisig operation saved in storage + const multisigInfo = await context + .polkadotJs() + .query.multisig.multisigs(multisigId, encodedCallHash); + const block = await context.createBlock( + context + .polkadotJs() + .tx.multisig.cancelAsMulti( + threshold, + otherSignatories, + multisigInfo.unwrap().when, + encodedCallHash + ) + .signAsync(alith) + ); + + const records = await context.polkadotJs().query.system.events(); + const events = records.filter( + ({ event }) => event.section === "multisig" && event.method === "MultisigCancelled" + ); + expect(events, "event 'MultisigCancelled' was not emitted").to.have.lengthOf(1); + expect(block.result!.successful).to.be.true; + }, + }); + + it({ + id: "T04", + title: "Should fail if signatories are out of order", + test: async () => { + const otherSignatories = [CHARLETH_ADDRESS, BALTATHAR_ADDRESS]; + const block = await context.createBlock( + context + .polkadotJs() + .tx.multisig.asMulti(threshold, otherSignatories, null, encodedCall, {}) + .signAsync(alith), + { allowFailures: true } + ); + expect(block.result!.error!.name, "signatories (they are not sorted)").to.equal( + "SignatoriesOutOfOrder" + ); + expect(block.result!.successful).to.be.false; + }, + }); + + it({ + id: "T05", + title: "Should fail if sender is present in signatories", + test: async () => { + // signatories (with sender in signatories) + const otherSignatories = [ALITH_ADDRESS, BALTATHAR_ADDRESS]; + const block = await context.createBlock( + context + .polkadotJs() + .tx.multisig.asMulti(threshold, otherSignatories, null, encodedCall, {}) + .signAsync(alith), + { allowFailures: true } + ); + expect(block.result!.error!.name).to.equal("SenderInSignatories"); + expect(block.result!.successful).to.be.false; + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/node-rpc/test-node-rpc-peer.ts b/test/moonwall/suites/dev/stagenet/node-rpc/test-node-rpc-peer.ts new file mode 100644 index 00000000..e699fd94 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/node-rpc/test-node-rpc-peer.ts @@ -0,0 +1,21 @@ +import { customDevRpcRequest, describeSuite, expect } from "@moonwall/cli"; + +describeSuite({ + id: "D022201", + title: "Node - RPC", + foundationMethods: "dev", + testCases: ({ context, it }) => { + it({ + id: "T01", + title: "should report peer count in hex", + test: async function () { + // this tests that the "net_peerCount" response comes back in hex and not decimal. + // related: frontier commits 677548c and 78fb3bc + const result = await customDevRpcRequest("net_peerCount", []); + + expect(result).to.be.equal("0x0"); + expect(typeof result).to.be.equal("string"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/polkadot-js/test-polkadot-api.ts b/test/moonwall/suites/dev/stagenet/polkadot-js/test-polkadot-api.ts new file mode 100644 index 00000000..524a16c3 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/polkadot-js/test-polkadot-api.ts @@ -0,0 +1,141 @@ +import { describeSuite, expect } from "@moonwall/cli"; +import { ALITH_ADDRESS, GLMR, generateKeyringPair } from "@moonwall/util"; + +describeSuite({ + id: "D022501", + title: "Polkadot API", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + it({ + id: "T01", + title: "should return genesis block", + test: async function () { + const lastHeader = await context.polkadotJs().rpc.chain.getHeader(); + expect(Number(lastHeader.number) >= 0).to.be.true; + }, + }); + + it({ + id: "T02", + title: "should return latest header number", + test: async function () { + await context.createBlock(); + const lastHeader = await context.polkadotJs().rpc.chain.getHeader(); + expect(lastHeader.number.toNumber()).to.be.at.least(1); + }, + }); + + it({ + id: "T03", + title: "transfers should be stored on chain", + test: async function () { + const randomAddress = generateKeyringPair().address as `0x${string}`; + await context.createBlock( + context.polkadotJs().tx.balances.transferAllowDeath(randomAddress, 2n * GLMR) + ); + + expect(BigInt(await context.viem().getBalance({ address: randomAddress }))).to.equal( + 2n * GLMR + ); + }, + }); + + it({ + id: "T04", + title: "should appear in extrinsics", + test: async function () { + const randomAddress = generateKeyringPair().address as `0x${string}`; + await context.createBlock( + context.polkadotJs().tx.balances.transferAllowDeath(randomAddress, 2n * GLMR) + ); + const signedBlock = await context.polkadotJs().rpc.chain.getBlock(); + + // Expecting 3 extrinsics so far: + // timestamp, author, the parachain validation data, randomness, and the balances transfer. + expect(signedBlock.block.extrinsics).to.be.of.length(3); + + signedBlock.block.extrinsics.forEach((ex, index) => { + const { + method: { args, method, section }, + } = ex; + const message = `${section}.${method}(${args.map((a) => a.toString()).join(", ")})`; + + switch (index) { + case 0: + expect(message.substring(0, 13)).to.eq(`timestamp.set`); + break; + case 1: + expect(message.toLocaleLowerCase()).to.match(/^randomness\.setbaberandomness/); + break; + case 2: + expect(message).to.eq( + `balances.transferAllowDeath(${randomAddress}, 2000000000000000000)` + ); + expect(ex.signer.toString()).to.eq(ALITH_ADDRESS); + break; + default: + throw new Error(`Unexpected extrinsic: ${message}`); + } + }); + }, + }); + + it({ + id: "T05", + title: "should appear in events", + test: async function () { + // Generating two transfers to ensure treasury account exists + const randomAddress = generateKeyringPair().address as `0x${string}`; + await context.createBlock( + context.polkadotJs().tx.balances.transferAllowDeath(randomAddress, 2n * GLMR) + ); + + const randomAddress2 = generateKeyringPair().address as `0x${string}`; + await context.createBlock( + context.polkadotJs().tx.balances.transferAllowDeath(randomAddress2, 2n * GLMR) + ); + const signedBlock = await context.polkadotJs().rpc.chain.getBlock(); + const apiAt = await context.polkadotJs().at(signedBlock.block.header.hash); + const allRecords = await apiAt.query.system.events(); + + // map between the extrinsics and events + signedBlock.block.extrinsics.forEach((_, index) => { + // filter the specific events based on the phase and then the + // index of our extrinsic in the block + const events = allRecords + .filter(({ phase }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index)) + .map(({ event }) => event); + + switch (index) { + // First 2 events: + // timestamp.set:: system.ExtrinsicSuccess + // randomness.setBabeRandomness:: system.ExtrinsicSuccess + case 0: + case 1: + expect(events).to.be.of.length(1); + expect(context.polkadotJs().events.system.ExtrinsicSuccess.is(events[0])).to.be.true; + break; + // balances.transferAllowDeath emits: system.NewAccount, balances.Endowed, + // balances.Transfer, (other events), system.ExtrinsicSuccess + case 2: + log(events.map((e) => `${e.section}.${e.method}`).join(" - ")); + expect(events.length).to.be.at.least(7); + expect(events.some((e) => context.polkadotJs().events.system.NewAccount.is(e))).to.be + .true; + expect(events.some((e) => context.polkadotJs().events.balances.Endowed.is(e))).to.be + .true; + expect(events.some((e) => context.polkadotJs().events.balances.Transfer.is(e))).to.be + .true; + // ExtrinsicSuccess should be the last event + expect( + context.polkadotJs().events.system.ExtrinsicSuccess.is(events[events.length - 1]) + ).to.be.true; + break; + default: + throw new Error(`Unexpected extrinsic`); + } + }); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/polkadot-js/test-polkadot-chain-info.ts b/test/moonwall/suites/dev/stagenet/polkadot-js/test-polkadot-chain-info.ts new file mode 100644 index 00000000..64b5455a --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/polkadot-js/test-polkadot-chain-info.ts @@ -0,0 +1,21 @@ +import { customDevRpcRequest, describeSuite, expect } from "@moonwall/cli"; + +describeSuite({ + id: "D022502", + title: "Web3Api Information", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + it({ + id: "T01", + title: "should include client version", + test: async function () { + const version = (await customDevRpcRequest("web3_clientVersion", [])) as string; + const specName = context.polkadotJs().runtimeVersion.specName.toString(); + const specVersion = context.polkadotJs().runtimeVersion.specVersion.toString(); + const implVersion = context.polkadotJs().runtimeVersion.implVersion.toString(); + const expected = `${specName}/v${specVersion}.${implVersion}/fc-rpc-2.0.0-dev`; + expect(version).toBe(expected); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128-bounds.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128-bounds.ts new file mode 100644 index 00000000..4ee0e5b8 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128-bounds.ts @@ -0,0 +1,80 @@ +import { describeSuite } from "@moonwall/cli"; +import { createViemTransaction, sendRawTransaction } from "@moonwall/util"; +/* + * These test cases trigger bugs in the bn128 precompiles which perform a from_slice() + * call on unchecked input. + * + * Fixed by: + * https://github.com/paritytech/frontier/pull/394 + */ + +describeSuite({ + id: "D022703", + title: "Precompiles - bn128 bounds", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + it({ + id: "T01", + title: "should fail gracefully (case 1)", + test: async () => { + // some call data which will cause bn128 to be called with insufficient input. this + // presumably was generated through fuzzing. + const data = + ("0x608060405234801561001057600080fd5b5060008060405180608001604052807f2243525c5eda" + + "1401003c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703815260207f6e01001d33be6da800" + + "00002bcc35964723180eed75f91a010001007d48f195c91581526020017f18b18acfb4c2c30276db" + + "5411000000000000000b4691610c5d3b00010001b17f81526020017f063c909c4720840cb5134cb9" + + "f546c80200579d040100d32efc0d288197f37266815246475b6100bf61044d505b6040604b808460" + + "006007600019f1925082610142576000000000acd401000000000000000000000000000000000000" + + "000000000000000000008152600401808060200182810300192c748252601e8152602001887f656c" + + "6c967074000381327572766520616464ad74696f6e206661696c6564000081525060200191500000" + + "000000000009fd5b7f2bd3e6d0f3b142924f5ca7b49ce5b9d585420400ae5648e61d02268b1a0a9f" + + "b7816000600202020202020202020202020202020202fd0202020203020202020202020202020202" + + "0202fb02020a02020202020202020202020202020202020202020202020202020202020202020202" + + "02020202020200000000000000000a1c000000000000000000000000000000000000000901010037" + + "190100000000000000000000f81a0100000002020202020202020202020202020202020202028a30" + + "a82123b27db75200aedc4a45a0e84fbd1f9f3621350bb778119630350eb7a7e613058daf51e9f514" + + "8ea65715eaac3d8019f80498112fc4860a020202020202020202fd02020202020202020202020202" + + "02020202020202020202020202020202020202020202020202020202020202020202020202020212" + + "02020202020202020202010202fd0202020202020202020202020202020202020202020202020202" + + "020202020202020202020202005f02d2020202020202020202020202020202020202020202020202" + + "02020202020202020202020302020202020202020202020202020202020202020202020202020202" + + "0302020202020202020202020202") as `0x${string}`; + + const tx = await createViemTransaction(context, { + to: "0x0000000000000000000000000000000000000007", + data, + skipEstimation: true, + }); + + await sendRawTransaction(context, tx); + + // we expect that the node hasn't crashed by here. without a fix, the previous web3 request + // would have been sufficient to crash our node. now it fails with "ExhaustsResources". if + // we can create a block, we must not have crashed. + await context.createBlock(); + }, + }); + + it({ + id: "T02", + title: "should fail gracefully (case 2)", + test: async () => { + // similar to the above call data, although triggers a slightly different bug + + const tx = await createViemTransaction(context, { + to: "0x0000000000000000000000000000000000000007", + data: "0x0000000000000000000000000000000000000000050000000000008303d0300d901401", + skipEstimation: true, + }); + + await sendRawTransaction(context, tx); + + // we expect that the node hasn't crashed by here. without a fix, the previous web3 request + // would have been sufficient to crash our node. now it fails with "ExhaustsResources". if + // we can create a block, we must not have crashed. + await context.createBlock(); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/proxy/test-proxy.ts b/test/moonwall/suites/dev/stagenet/proxy/test-proxy.ts new file mode 100644 index 00000000..5c313bef --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/proxy/test-proxy.ts @@ -0,0 +1,257 @@ +import { beforeEach, describeSuite, expect } from "@moonwall/cli"; +import { + ALITH_ADDRESS, + CHARLETH_ADDRESS, + type KeyringPair, + alith, + generateKeyringPair, +} from "@moonwall/util"; + +// In these tests Alith will allow signer to perform calls on her behalf. +// Charleth is used as a target account when making transfers. + +describeSuite({ + id: "D022902", + title: "Proxy - proxy", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + let signer: KeyringPair; + + beforeEach(async () => { + signer = generateKeyringPair("ethereum"); + + await context.createBlock( + context.polkadotJs().tx.balances.transferAllowDeath(signer.address, 5n * 10n ** 18n) + ); + }); + + it({ + id: "T01", + title: "shouldn't accept unknown proxy", + test: async function () { + const beforeCharlethBalance = await context + .viem() + .getBalance({ address: CHARLETH_ADDRESS }); + + const expectEvents = [context.polkadotJs().events.system.ExtrinsicFailed]; + + await context.createBlock( + context + .polkadotJs() + .tx.proxy.proxy( + ALITH_ADDRESS, + null, + context.polkadotJs().tx.balances.transferAllowDeath(CHARLETH_ADDRESS, 100) + ) + .signAsync(signer), + { expectEvents, signer: alith, allowFailures: true } + ); + const afterCharlethBalance = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + expect(afterCharlethBalance - beforeCharlethBalance).to.be.eq(0n); + }, + }); + + it({ + id: "T02", + title: "should accept known proxy", + test: async () => { + const beforeCharlethBalance = await context + .viem() + .getBalance({ address: CHARLETH_ADDRESS }); + + const events1 = [ + context.polkadotJs().events.system.ExtrinsicSuccess, + context.polkadotJs().events.proxy.ProxyAdded, + ]; + + const { result } = await context.createBlock( + context.polkadotJs().tx.proxy.addProxy(signer.address, "Any", 0), + { signer: alith, expectEvents: events1 } + ); + const proxyAdded = result?.events.find(({ event }) => + context.polkadotJs().events.proxy.ProxyAdded.is(event) + ); + expect(proxyAdded).to.not.be.undefined; + expect(proxyAdded!.event.data[2].toString()).to.be.eq("Any"); //ProxyType + + const events2 = [ + context.polkadotJs().events.system.ExtrinsicSuccess, + context.polkadotJs().events.proxy.ProxyExecuted, + ]; + + const { result: result2 } = await context.createBlock( + context + .polkadotJs() + .tx.proxy.proxy( + alith.address, + null, + context.polkadotJs().tx.balances.transferAllowDeath(CHARLETH_ADDRESS, 100) + ) + .signAsync(signer), + { signer: alith, expectEvents: events2 } + ); + const proxyExecuted = result2?.events.find(({ event }) => + context.polkadotJs().events.proxy.ProxyExecuted.is(event) + ); + expect(proxyExecuted).to.not.be.undefined; + expect(proxyExecuted!.event.data[0].toString()).to.be.eq("Ok"); + const afterCharlethBalance = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + expect(afterCharlethBalance - beforeCharlethBalance).to.be.eq(100n); + }, + }); + + it({ + id: "T03", + title: "shouldn't accept removed proxy", + test: async () => { + const beforeCharlethBalance = await context + .viem() + .getBalance({ address: CHARLETH_ADDRESS }); + + await context.createBlock( + context.polkadotJs().tx.proxy.addProxy(signer.address, "Any", 0), + { signer: alith, allowFailures: false } + ); + + await context.createBlock( + context.polkadotJs().tx.proxy.removeProxy(signer.address, "Any", 0), + { signer: alith, allowFailures: false } + ); + + await context.createBlock( + context + .polkadotJs() + .tx.proxy.proxy( + alith.address, + null, + context.polkadotJs().tx.balances.transferAllowDeath(CHARLETH_ADDRESS, 100) + ) + .signAsync(signer), + { signer: alith, expectEvents: [context.polkadotJs().events.system.ExtrinsicFailed] } + ); + const afterCharlethBalance = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + expect(afterCharlethBalance - beforeCharlethBalance).to.be.eq(0n); + }, + }); + + it({ + id: "T04", + title: "shouldn't accept instant for delayed proxy", + test: async () => { + const beforeCharlethBalance = await context + .viem() + .getBalance({ address: CHARLETH_ADDRESS }); + + await context.createBlock( + context.polkadotJs().tx.proxy.addProxy(signer.address, "Any", 2), + { signer: alith, allowFailures: false } + ); + + await context.createBlock( + context + .polkadotJs() + .tx.proxy.proxy( + alith.address, + null, + context.polkadotJs().tx.balances.transferAllowDeath(CHARLETH_ADDRESS, 100) + ) + .signAsync(signer), + { signer: alith, expectEvents: [context.polkadotJs().events.system.ExtrinsicFailed] } + ); + const afterCharlethBalance = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + expect(afterCharlethBalance - beforeCharlethBalance).to.be.eq(0n); + }, + }); + + it({ + id: "T05", + title: "shouldn't accept early delayed proxy", + test: async () => { + const beforeCharlethBalance = await context + .viem() + .getBalance({ address: CHARLETH_ADDRESS }); + const { result } = await context.createBlock( + context.polkadotJs().tx.proxy.addProxy(signer.address, "Any", 6), + { signer: alith, allowFailures: false } + ); + result?.events.forEach(({ event }) => log(`1${event.method}(${event.data})`)); + + const transfer = context.polkadotJs().tx.balances.transferAllowDeath(CHARLETH_ADDRESS, 100); + + const { result: result2 } = await context.createBlock( + context.polkadotJs().tx.proxy.announce(alith.address, transfer.hash).signAsync(signer), + { + signer: alith, + expectEvents: [context.polkadotJs().events.proxy.Announced], + allowFailures: false, + } + ); + result2?.events.forEach(({ event }) => log(`2${event.method}(${event.data})`)); + + const { result: result3 } = await context.createBlock( + context + .polkadotJs() + .tx.proxy.proxyAnnounced(signer.address, alith.address, null, transfer) + .signAsync(signer), + { + signer: alith, + expectEvents: [context.polkadotJs().events.system.ExtrinsicFailed], + } + ); + result3?.events.forEach(({ event }) => log(`3${event.method}(${event.data})`)); + const afterCharlethBalance = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + expect(afterCharlethBalance - beforeCharlethBalance).to.be.eq(0n); + }, + }); + + it({ + id: "T06", + title: "should accept on-time delayed proxy ", + test: async () => { + const beforeCharlethBalance = await context + .viem() + .getBalance({ address: CHARLETH_ADDRESS }); + await context.createBlock( + context.polkadotJs().tx.proxy.addProxy(signer.address, "Any", 2), + { signer: alith, allowFailures: false } + ); + + const transfer = context.polkadotJs().tx.balances.transferAllowDeath(CHARLETH_ADDRESS, 100); + const u8a = transfer.method.toU8a(); + const transfer_hash = transfer.registry.hash(u8a).toHex(); + + const { result: result2 } = await context.createBlock( + context.polkadotJs().tx.proxy.announce(alith.address, transfer_hash).signAsync(signer), + { + signer: alith, + expectEvents: [context.polkadotJs().events.proxy.Announced], + allowFailures: false, + } + ); + const announced = result2?.events.find(({ event }) => + context.polkadotJs().events.proxy.Announced.is(event) + ); + expect(announced).to.not.be.undefined; + expect(announced!.event.data[2].toHex()).to.eq(transfer_hash); + + await context.createBlock(); + await context.createBlock(); + + // On time. + const { result: result3 } = await context.createBlock( + context + .polkadotJs() + .tx.proxy.proxyAnnounced(signer.address, alith.address, null, transfer) + .signAsync(signer), + { + signer: alith, + expectEvents: [context.polkadotJs().events.proxy.ProxyExecuted], + allowFailures: false, + } + ); + const afterCharlethBalance = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + expect(afterCharlethBalance - beforeCharlethBalance).to.be.eq(100n); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/receipt/test-receipt-revert.ts b/test/moonwall/suites/dev/stagenet/receipt/test-receipt-revert.ts new file mode 100644 index 00000000..37890c0d --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/receipt/test-receipt-revert.ts @@ -0,0 +1,33 @@ +import { describeSuite, expect } from "@moonwall/cli"; +import { getAddress } from "viem"; + +describeSuite({ + id: "D023101", + title: "Receipt - Revert", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + it({ + id: "T01", + title: "should generate a receipt for a reverted transaction", + test: async function () { + const { hash } = await context.deployContract!("FailingConstructor", { gas: 300000n }); + const receipt = await context.viem().getTransactionReceipt({ hash }); + + expect(receipt.status).toBe("reverted"); + expect(receipt.blockNumber).toBe(1n); + expect(getAddress(receipt.contractAddress!)).toBe( + getAddress("0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3") + ); + expect(receipt.cumulativeGasUsed).toBe(54604n); + expect(getAddress(receipt.from!)).toBe( + getAddress("0xf24ff3a9cf04c71dbc94d0b566f7a27b94566cac") + ); + expect(receipt.gasUsed).toBe(54604n); + expect(receipt.logs).toStrictEqual([]); + expect(receipt.transactionHash).toBe(hash); + expect(receipt.to).toBe(null); + expect(receipt.transactionIndex).toBe(0); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/receipt/test-receipt.ts b/test/moonwall/suites/dev/stagenet/receipt/test-receipt.ts new file mode 100644 index 00000000..130e70d4 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/receipt/test-receipt.ts @@ -0,0 +1,61 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { BALTATHAR_ADDRESS, alith } from "@moonwall/util"; + +describeSuite({ + id: "D023103", + title: "Receipt - Contract", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + let txHash: string; + let eventContract: `0x${string}`; + + beforeAll(async () => { + const { contractAddress, hash } = await context.deployContract!("EventEmitter"); + eventContract = contractAddress; + txHash = hash; + }); + + it({ + id: "T01", + title: "Should generate receipt", + test: async function () { + const block = await context.viem().getBlock({ blockNumber: 1n }); + const receipt = await context + .viem() + .getTransactionReceipt({ hash: txHash as `0x${string}` }); + + expect(receipt.blockHash).toBe(block.hash); + expect(receipt.blockNumber).toBe(block.number); + expect(receipt.from).toBe(alith.address.toLowerCase()); + expect(receipt.logs.length).toBe(1); + expect(receipt.logs[0].address).toBe(eventContract); + expect(receipt.logs[0].blockHash).toBe(block.hash); + }, + }); + + it({ + id: "T02", + title: "should calculate effective gas price", + test: async function () { + const maxFeePerGas = 10_000_000_000n * 2n; + + const rawTxn = await context.createTxn!({ + gas: 21000n, + libraryType: "viem", + maxFeePerGas: maxFeePerGas, + maxPriorityFeePerGas: maxFeePerGas, + to: BALTATHAR_ADDRESS, + data: "0x", + txnType: "eip1559", + }); + await context.createBlock(rawTxn); + + const block = await context.viem().getBlock(); + const receipt = await context + .viem() + .getTransactionReceipt({ hash: block.transactions[0] as `0x${string}` }); + expect(receipt.effectiveGasPrice).to.be.eq(maxFeePerGas); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/storage-growth/test-evm-store-storage-growth.ts b/test/moonwall/suites/dev/stagenet/storage-growth/test-evm-store-storage-growth.ts new file mode 100644 index 00000000..964dd79f --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/storage-growth/test-evm-store-storage-growth.ts @@ -0,0 +1,76 @@ +// TODO: Constants (storage growth ratios, limits) may need adjustment for DataHaven's runtime configuration +import { + TransactionTypes, + beforeAll, + deployCreateCompiledContract, + describeSuite, +} from "@moonwall/cli"; +import { createEthersTransaction } from "@moonwall/util"; +import { expectEVMResult, expectOk } from "../../../../helpers"; +import { type Abi, encodeFunctionData } from "viem"; + +describeSuite({ + id: "D023403", + title: "Storage growth limit - New Entries", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + let storageLoopAddress: `0x${string}`; + let storageLoopAbi: Abi; + // Number of bytes added to storage for a new entry. + const ACCOUNT_STORAGE_SIZE = 116; + // Ratio of gas to storage growth. (BlockGasLimit (15_000_000) / BlockStorageLimit (40kb)) + const GAS_LIMIT_STORAGE_GROWTH_RATIO = 366; + beforeAll(async () => { + const { contractAddress, abi } = await deployCreateCompiledContract(context, "StorageLoop"); + storageLoopAddress = contractAddress; + storageLoopAbi = abi; + + await context.createBlock(); + }); + + for (const txnType of TransactionTypes) { + it({ + id: `T0${TransactionTypes.indexOf(txnType) + 1}`, + title: "should out of gas when gas provided is not enough to cover storage growth", + test: async function () { + // Tx is creating 5 new storage entries. So, required gas is: + // (5 * ACCOUNT_STORAGE_SIZE) * GAS_LIMIT_STORAGE_GROWTH_RATIO = 212_280 + // Execute tx with insufficient gas limit + const rawSigned = await createEthersTransaction(context, { + to: storageLoopAddress, + data: encodeFunctionData({ + abi: storageLoopAbi, + functionName: "store", + // for each transaction type, we add 5 new storage entries + args: [5 + 5 * TransactionTypes.indexOf(txnType)], + }), + gasLimit: 212_270, + }); + + const { result } = await context.createBlock(rawSigned); + // Check that the transaction failed with an out of gas error + expectEVMResult(result!.events, "Error", "OutOfGas"); + }, + }); + + it({ + id: `T0${TransactionTypes.indexOf(txnType) + 4}`, + title: "should successfully execute when updating existing storage entries (no growth)", + test: async function () { + // Update 5 existing storage entries. So, required gas should be less than 212_280 + const rawSigned = await createEthersTransaction(context, { + to: storageLoopAddress, + data: encodeFunctionData({ + abi: storageLoopAbi, + functionName: "store", + args: [5], + }), + gasLimit: 50_000, + }); + + await expectOk(context.createBlock(rawSigned)); + }, + }); + } + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/sudo/test-sudo.ts b/test/moonwall/suites/dev/stagenet/sudo/test-sudo.ts new file mode 100644 index 00000000..b134f7cb --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/sudo/test-sudo.ts @@ -0,0 +1,123 @@ +import { describeSuite, expect } from "@moonwall/cli"; +import { + ALITH_ADDRESS, + DEFAULT_GENESIS_BALANCE, + baltathar, + generateKeyringPair, +} from "@moonwall/util"; +import { ALITH_GENESIS_TRANSFERABLE_BALANCE, verifyLatestBlockFees } from "../../../../helpers"; +import { CHARLETH_ADDRESS, ETHAN_ADDRESS } from "@moonwall/util"; + +describeSuite({ + id: "D023601", + title: "Sudo - successful forceSetBalance", + foundationMethods: "dev", + testCases: ({ context, it }) => { + it({ + id: "T01", + title: "should be able to call sudo with the right account", + test: async function () { + const { result } = await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.balances.forceSetBalance(ETHAN_ADDRESS, 0)) + ); + + const account = await context.polkadotJs().query.system.account(ETHAN_ADDRESS); + expect(account.data.free.toBigInt()).toBe(0n); + + // DataHaven has 1 more event (NewChallengeSeed) than Moonbeam, + // coming from the StorageHub ProofsDealer pallet. + expect(result!.events.length).to.eq(8); + console.log(result!.events.map((e) => e.event.toHuman())); + expect(context.polkadotJs().events.balances.BalanceSet.is(result!.events[3].event)).to.be + .true; + expect(context.polkadotJs().events.sudo.Sudid.is(result!.events[4].event)).to.be.true; + expect(context.polkadotJs().events.balances.Deposit.is(result!.events[5].event)).to.be.true; + expect(context.polkadotJs().events.system.ExtrinsicSuccess.is(result!.events[7].event)).to + .be.true; + + expect( + await context.viem().getBalance({ address: ALITH_ADDRESS }), + "diff should be null for sudo - funds are sent back" + ).to.equal(ALITH_GENESIS_TRANSFERABLE_BALANCE); + }, + }); + + it({ + id: "T02", + title: "should charge the correct amount of gas when calling sudo", + test: async function () { + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo( + context + .polkadotJs() + .tx.balances.forceSetBalance(CHARLETH_ADDRESS, DEFAULT_GENESIS_BALANCE) + ) + ); + + await verifyLatestBlockFees(context); + }, + }); + + it({ + id: "T03", + title: "should NOT be able to call sudo with another account than sudo account", + test: async function () { + const baltathar_before = await context.polkadotJs().query.system.account(CHARLETH_ADDRESS); + const { result } = await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.balances.forceSetBalance(CHARLETH_ADDRESS, 0)) + .signAsync(baltathar), + { allowFailures: true } + ); + + // Check that balance didn't change + const account = await context.polkadotJs().query.system.account(CHARLETH_ADDRESS); + expect(account.data.free.toBigInt()).toBe(DEFAULT_GENESIS_BALANCE); + + expect(result!.events.length === 8).to.be.true; + expect(context.polkadotJs().events.system.NewAccount.is(result!.events[3].event)).to.be + .true; + expect(context.polkadotJs().events.balances.Endowed.is(result!.events[4].event)).to.be.true; + expect(context.polkadotJs().events.system.ExtrinsicFailed.is(result!.events[7].event)).to.be + .true; + }, + }); + + it({ + id: "T04", + title: "should not be able to call sudo with no funds", + test: async function () { + const newSigner = generateKeyringPair(); + + await context.createBlock(context.polkadotJs().tx.sudo.setKey(newSigner.address), { + allowFailures: false, + }); + + expect((await context.polkadotJs().query.sudo.key()).unwrap().toString()).toBe( + newSigner.address + ); + + await expect( + async () => + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.balances.forceSetBalance(CHARLETH_ADDRESS, 0)) + .signAsync(newSigner) + ) + ).rejects.toThrowError( + "1010: Invalid Transaction: Inability to pay some fees , e.g. account balance too low" + ); + + expect(await context.viem().getBalance({ address: CHARLETH_ADDRESS })).to.equal( + DEFAULT_GENESIS_BALANCE + ); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/txpool/test-txpool-future.ts b/test/moonwall/suites/dev/stagenet/txpool/test-txpool-future.ts new file mode 100644 index 00000000..6f6ced8f --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/txpool/test-txpool-future.ts @@ -0,0 +1,69 @@ +import { beforeAll, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { alith, createEthersTransaction, sendRawTransaction } from "@moonwall/util"; +import { encodeDeployData, toHex } from "viem"; + +describeSuite({ + id: "D023802", + title: "TxPool - Future Ethereum transaction", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + let txHash: string; + let deployData: string; + + beforeAll(async () => { + const { abi, bytecode } = fetchCompiledContract("MultiplyBy7"); + deployData = encodeDeployData({ + abi, + bytecode, + }); + + const rawTxn = await createEthersTransaction(context, { + data: deployData, + gasLimit: 1048576, + nonce: 1, // future nonce + }); + txHash = await sendRawTransaction(context, rawTxn); + }); + + it({ + id: "T01", + title: "should appear in the txpool inspection", + test: async function () { + const inspect = (await context + .viem() + .transport.request({ method: "txpool_inspect" })) as any; + // web3 rpc returns lowercase + const data = inspect.queued[alith.address.toLowerCase()][toHex(1)]; + expect(data).to.not.be.undefined; + expect(data).to.be.equal( + "0x0000000000000000000000000000000000000000: 0 wei + 1048576 gas x 10000000000 wei" + ); + }, + }); + + it({ + id: "T02", + title: "should appear in the txpool content", + test: async function () { + const content = (await context + .viem() + .transport.request({ method: "txpool_content" })) as any; + // web3 rpc returns lowercase + const data = content.queued[alith.address.toLowerCase()][toHex(1)]; + expect(data).toMatchObject({ + blockHash: null, + blockNumber: null, + from: alith.address.toLowerCase(), + gas: "0x100000", + gasPrice: "0x2540be400", + hash: txHash, + input: deployData, + nonce: toHex(1), + to: null, + transactionIndex: null, + value: "0x0", + }); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/txpool/test-txpool-pending.ts b/test/moonwall/suites/dev/stagenet/txpool/test-txpool-pending.ts new file mode 100644 index 00000000..c2f86188 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/txpool/test-txpool-pending.ts @@ -0,0 +1,84 @@ +import { beforeAll, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { ALITH_ADDRESS, createEthersTransaction, sendRawTransaction } from "@moonwall/util"; +import { encodeDeployData, toHex } from "viem"; + +describeSuite({ + id: "D023807", + title: "TxPool - Pending Ethereum transaction", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + let txHash: string; + + beforeAll(async () => { + const { abi, bytecode } = fetchCompiledContract("MultiplyBy7"); + const deployData = encodeDeployData({ + abi, + bytecode, + }); + + const rawTxn = await createEthersTransaction(context, { + data: deployData, + gasLimit: 1048576, + nonce: 0, + }); + txHash = await sendRawTransaction(context, rawTxn); + }); + + it({ + id: "T01", + title: "should appear in the txpool inspection", + test: async function () { + const inspect = (await context + .viem() + .transport.request({ method: "txpool_inspect" })) as any; + + // web3 rpc returns lowercase + const data = inspect.pending[ALITH_ADDRESS.toLowerCase()][toHex(0)]; + expect(data).to.not.be.undefined; + expect(data).to.be.equal( + "0x0000000000000000000000000000000000000000: 0 wei + 1048576 gas x 10000000000 wei" + ); + }, + }); + + it({ + id: "T02", + title: "should be marked as pending", + test: async function () { + const pendingTransaction = await context + .viem() + .getTransaction({ hash: txHash as `0x${string}` }); + + // pending transactions do not know yet to which block they belong to + expect(pendingTransaction).to.include({ + blockNumber: null, + hash: txHash, + }); + }, + }); + + it({ + id: "T03", + title: "should appear in the txpool content", + test: async function () { + const content = (await context + .viem() + .transport.request({ method: "txpool_content" })) as any; + + // web3 rpc returns lowercase + const data = content.pending[ALITH_ADDRESS.toLowerCase()][toHex(0)]; + expect(data).to.include({ + blockHash: null, + blockNumber: null, + from: ALITH_ADDRESS.toLowerCase(), + gas: "0x100000", + gasPrice: "0x2540be400", + hash: txHash, + nonce: toHex(0), + to: null, + value: "0x0", + }); + }, + }); + }, +});