diff --git a/prdoc/pr_10919.prdoc b/prdoc/pr_10919.prdoc new file mode 100644 index 0000000000000..14276a1c03840 --- /dev/null +++ b/prdoc/pr_10919.prdoc @@ -0,0 +1,22 @@ +title: Add revive substrate runtime-api integration tests for call & instantiate +doc: +- audience: Runtime Dev + description: |- + ## Summary + - Add integration tests for revive runtime API + - Test Fibonacci contract deployment and execution via substrate APIs + + ## Changes + - Add test for Fibonacci contract call via runtime API + - Add test to verify large Fibonacci values run out of gas as expected + - Update dev-node runtime configuration for testing + + ## Test plan + - Run new integration tests + - Verify runtime API correctly handles contract deployment + - Verify gas limits are enforced correctly +crates: +- name: revive-dev-runtime + bump: patch +- name: pallet-revive-eth-rpc + bump: patch diff --git a/substrate/frame/revive/dev-node/runtime/src/lib.rs b/substrate/frame/revive/dev-node/runtime/src/lib.rs index 176d6b91a13c3..bcf2128dc8082 100644 --- a/substrate/frame/revive/dev-node/runtime/src/lib.rs +++ b/substrate/frame/revive/dev-node/runtime/src/lib.rs @@ -64,11 +64,12 @@ pub mod currency { pub mod genesis_config_presets { use super::*; use crate::{ - currency::DOLLARS, sp_keyring::Sr25519Keyring, Balance, BalancesConfig, + currency::DOLLARS, sp_keyring::Sr25519Keyring, Balance, BalancesConfig, ReviveConfig, RuntimeGenesisConfig, SudoConfig, }; use alloc::{vec, vec::Vec}; + use pallet_revive::is_eth_derived; use serde_json::Value; pub const ENDOWMENT: Balance = 10_000_000_000_001 * DOLLARS; @@ -103,14 +104,23 @@ pub mod genesis_config_presets { /// Returns a development genesis config preset. pub fn development_config_genesis() -> Value { + let endowed_accounts = well_known_accounts(); frame_support::build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { - balances: well_known_accounts() - .into_iter() + balances: endowed_accounts + .iter() + .cloned() .map(|id| (id, ENDOWMENT)) .collect::>(), }, sudo: SudoConfig { key: Some(Sr25519Keyring::Alice.to_account_id()) }, + revive: ReviveConfig { + mapped_accounts: endowed_accounts + .iter() + .filter(|x| !is_eth_derived(x)) + .cloned() + .collect(), + }, }) } diff --git a/substrate/frame/revive/rpc/src/tests.rs b/substrate/frame/revive/rpc/src/tests.rs index 269dffb8c7572..35e0e61e1f1aa 100644 --- a/substrate/frame/revive/rpc/src/tests.rs +++ b/substrate/frame/revive/rpc/src/tests.rs @@ -284,6 +284,7 @@ async fn run_all_eth_rpc_tests() -> anyhow::Result<()> { } run_tests!( + test_fibonacci_call_via_runtime_api, test_transfer, test_deploy_and_call, test_runtime_api_dry_run_addr_works, @@ -790,3 +791,105 @@ async fn test_runtime_pallets_address_upload_code(client: Arc) -> anyh Ok(()) } + +/// Test that deploys and calls the Fibonacci contract via Substrate APIs works +async fn test_fibonacci_call_via_runtime_api() -> anyhow::Result<()> { + use pallet_revive::precompiles::alloy::sol_types::SolCall; + use pallet_revive_fixtures::Fibonacci; + + let (bytes, _) = pallet_revive_fixtures::compile_module_with_type( + "Fibonacci", + pallet_revive_fixtures::FixtureType::Solc, + )?; + + let node_client = + OnlineClient::::from_url(SharedResources::node_rpc_url()).await?; + let signer = subxt_signer::sr25519::dev::alice(); + let origin: [u8; 32] = signer.public_key().0; + + // Deploy the Fibonacci contract via Substrate API + log::trace!(target: LOG_TARGET, "Deploying Fibonacci contract via Substrate API"); + let dry_run_result = node_client + .runtime_api() + .at_latest() + .await? + .call(subxt_client::apis().revive_api().instantiate( + subxt::utils::AccountId32(origin), + 0u128, // value + None, // gas_limit + None, // storage_deposit_limit + subxt_client::src_chain::runtime_types::pallet_revive::primitives::Code::Upload( + bytes.clone(), + ), + vec![], // data (constructor args) + None, // salt + )) + .await; + + assert!(dry_run_result.is_ok(), "Dry-run instantiate failed: {dry_run_result:?}"); + let dry_run = dry_run_result.unwrap(); + let instantiate_result = dry_run.result.expect("Dry-run should succeed"); + + log::trace!( + target: LOG_TARGET, + "Dry-run succeeded: address: {:?}, gas_consumed: {:?}, weight_required: {:?}", + instantiate_result.addr, + dry_run.gas_consumed, + dry_run.weight_required + ); + + // Now submit the actual instantiate extrinsic + let events = node_client + .tx() + .sign_and_submit_then_watch_default( + &subxt_client::tx().revive().instantiate_with_code( + 0u128, // value + dry_run.weight_required, // weight_limit from dry-run + u128::MAX, // storage_deposit_limit + bytes, // code + vec![], // data + None, // salt + ), + &subxt_signer::sr25519::dev::alice(), + ) + .await? + .wait_for_finalized_success() + .await?; + + // Extract the contract address from the Instantiated event + let instantiated_event = events + .find_first::()? + .expect("Instantiated event should be present"); + + let contract_address = instantiated_event.contract; + log::trace!(target: LOG_TARGET, "Contract deployed via Substrate at: {contract_address:?}"); + + // Verify that the dry-run predicted address matches the actual deployed address + assert_eq!( + instantiate_result.addr, contract_address, + "Dry-run predicted address should match actual deployed address" + ); + + // Call the deployed contract using runtime API + let call_data = Fibonacci::fibCall { n: 3u64 }.abi_encode(); + let call_payload = subxt_client::apis().revive_api().call( + subxt::utils::AccountId32(origin), + contract_address, + 0u128, // value + None, // gas_limit + None, // storage_deposit_limit + call_data, + ); + + let result = node_client.runtime_api().at_latest().await?.call(call_payload).await; + + assert!(result.is_ok(), "Contract call failed: {result:?}"); + let call_result = result.unwrap(); + let exec_result = call_result.result.expect("fib(3) should succeed"); + + let decoded = Fibonacci::fibCall::abi_decode_returns(&exec_result.data) + .expect("Failed to decode return value"); + assert_eq!(decoded, 2u64, "fib(3) should return 2"); + + Ok(()) +}