Skip to content

feat(rpc): support native multisig simulation - #7239

Draft
joshieDo wants to merge 7 commits into
agent/native-multisig-activationfrom
agent/native-multisig-rpc-wallet
Draft

feat(rpc): support native multisig simulation#7239
joshieDo wants to merge 7 commits into
agent/native-multisig-activationfrom
agent/native-multisig-rpc-wallet

Conversation

@joshieDo

Copy link
Copy Markdown
Member

Adds request hints and state-derived recursive mock approvals for conservative multisig gas estimation, while preserving access-key simulation and fill/sign behavior.

Stacked on #7238; extracted from #4069.

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

✅ Changelog found on PR.

Edit changelog

@joshieDo
joshieDo force-pushed the agent/native-multisig-rpc-wallet branch 2 times, most recently from 5a15408 to 13c1bc9 Compare August 19, 2026 16:53
@joshieDo
joshieDo force-pushed the agent/native-multisig-rpc-wallet branch from 13c1bc9 to f023ebe Compare August 19, 2026 17:05
@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

📊 Tempo Precompiles Coverage

precompiles

Coverage: 6459/11165 lines (57.85%)

File details
File Lines Coverage
src/account_keychain/dispatch.rs 16/42 38.10%
src/account_keychain/mod.rs 315/899 35.04%
src/address_registry/dispatch.rs 21/27 77.78%
src/address_registry/mod.rs 50/60 83.33%
src/current_committee/dispatch.rs 0/9 0.00%
src/current_committee/mod.rs 0/18 0.00%
src/dispatch.rs 126/152 82.89%
src/error.rs 52/173 30.06%
src/ip_validation.rs 10/10 100.00%
src/lib.rs 128/148 86.49%
src/native_multisig/auth.rs 0/179 0.00%
src/native_multisig/dispatch.rs 0/15 0.00%
src/native_multisig/mod.rs 3/348 0.86%
src/nonce/dispatch.rs 6/7 85.71%
src/nonce/mod.rs 44/58 75.86%
src/receive_policy_guard/dispatch.rs 0/12 0.00%
src/receive_policy_guard/mod.rs 0/139 0.00%
src/signature_verifier/dispatch.rs 15/23 65.22%
src/signature_verifier/mod.rs 13/55 23.64%
src/stablecoin_dex/dispatch.rs 59/71 83.10%
src/stablecoin_dex/mod.rs 1002/1097 91.34%
src/stablecoin_dex/order/mod.rs 120/154 77.92%
src/stablecoin_dex/order/storage.rs 178/249 71.49%
src/stablecoin_dex/orderbook.rs 183/242 75.62%
src/storage/actions.rs 11/90 12.22%
src/storage/evm.rs 390/440 88.64%
src/storage/hashmap.rs 0/249 0.00%
src/storage/mod.rs 27/69 39.13%
src/storage/packing.rs 68/93 73.12%
src/storage/thread_local.rs 200/300 66.67%
src/storage/types/array.rs 0/72 0.00%
src/storage/types/bytes_like.rs 135/179 75.42%
src/storage/types/cache.rs 65/122 53.28%
src/storage/types/mapping.rs 27/48 56.25%
src/storage/types/mod.rs 47/71 66.20%
src/storage/types/primitives.rs 21/24 87.50%
src/storage/types/set.rs 28/192 14.58%
src/storage/types/slot.rs 80/101 79.21%
src/storage/types/vec.rs 103/261 39.46%
src/storage_credits/accounting.rs 79/92 85.87%
src/storage_credits/dispatch.rs 16/16 100.00%
src/storage_credits/mod.rs 139/193 72.02%
src/tip20/dispatch.rs 64/73 87.67%
src/tip20/mod.rs 657/801 82.02%
src/tip20/rewards.rs 242/260 93.08%
src/tip20/roles.rs 85/88 96.59%
src/tip20_channel_reserve/dispatch.rs 0/42 0.00%
src/tip20_channel_reserve/mod.rs 3/548 0.55%
src/tip20_factory/dispatch.rs 9/11 81.82%
src/tip20_factory/mod.rs 117/140 83.57%
src/tip403_registry/dispatch.rs 29/46 63.04%
src/tip403_registry/mod.rs 347/544 63.79%
src/tip_fee_manager/amm.rs 295/465 63.44%
src/tip_fee_manager/dispatch.rs 35/37 94.59%
src/tip_fee_manager/mod.rs 57/155 36.77%
src/validator_config/dispatch.rs 18/28 64.29%
src/validator_config/mod.rs 171/227 75.33%
src/validator_config_v2/dispatch.rs 30/33 90.91%
src/validator_config_v2/mod.rs 523/581 90.02%
src/zone_factory/dispatch.rs 0/15 0.00%
src/zone_factory/mod.rs 0/215 0.00%
src/zone_factory/portal.rs 0/57 0.00%

contracts

Coverage: 1/202 lines (0.50%)

File details
File Lines Coverage
src/lib.rs 1/1 100.00%
src/precompiles/receive_policy_guard.rs 0/78 0.00%
src/precompiles/storage_credits.rs 0/7 0.00%
src/precompiles/tip20.rs 0/50 0.00%
src/precompiles/tip20_channel_reserve.rs 0/27 0.00%
src/precompiles/tip403_registry.rs 0/9 0.00%
src/precompiles/validator_config_v2.rs 0/3 0.00%
src/precompiles/zone_factory.rs 0/27 0.00%

Total: 6460/11367 lines (56.83%)

📦 Download full HTML report

@joshieDo

Copy link
Copy Markdown
Member Author

cyclops audit fast note="this belongs to the GH stack #7243 and the TIP 1061 is specified in the PR 7242. any bug not introduced by this PR should be clearly marked"

@tempoxyz-bot tempoxyz-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👁️ Cyclops Review

This change adds state-derived recursive mock approvals so RPC simulation can estimate native-multisig transactions conservatively. One verified low-severity correctness issue remains: the duplicated quorum cost model omits a real intrinsic-gas charge and can under-estimate valid nested-multisig transactions.

Reviewer Callouts
  • Simulation context boundary: ExecutionContext::Simulation skips native-multisig authorization, and empty transaction encoding can mark a transaction as RPC simulation. This behavior predates the PR, but a human should confirm no consensus or payload-building path can construct a TempoTxEnv with empty encoding or its default simulation context.
  • WebAuthn hint ranking: Explicit WebAuthn hints are ranked at maximum WebAuthn gas while mock construction uses the supplied key-data size. The node currently emits only UnknownPrimitive or Multisig, but in-process or SDK hint producers could violate the conservative-ranking invariant.
  • Signed-request round trip: From<AASigned> for TempoTransactionRequest emits multisig_signature_count while leaving from unset. Confirm no server or SDK path feeds that request back into simulation, where it now fails with multisig account cannot be zero.

@joshieDo
joshieDo force-pushed the agent/native-multisig-rpc-wallet branch from f023ebe to e575087 Compare August 19, 2026 19:08
@joshieDo
joshieDo force-pushed the agent/native-multisig-rpc-wallet branch from e575087 to 35fbe63 Compare August 19, 2026 21:02
@joshieDo

Copy link
Copy Markdown
Member Author

cyclops audit fast note="this belongs to the GH stack #7243 and the TIP 1061 is specified in the PR 7242. any bug not introduced by this PR should be clearly marked"

@tempoxyz-bot tempoxyz-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👁️ Cyclops Review

This change adds native-multisig gas modeling to Tempo RPC simulations. The state-aware call path is careful and conservative, but simulateV1 bypasses it and can understate intrinsic gas; hint derivation also adds bounded attacker-shaped work before EVM gas accounting.

Reviewer Callouts
  • Contract-creation calls from registered multisigs: Populating a hint makes has_aa_fields() true, while AA conversion requires a non-empty calls list. Verify that eth_call with no to still simulates CREATE rather than failing with empty calls list.
  • Conservative owner key assumptions: The state-derived path prices unknown owners as maximum-size WebAuthn. Confirm that this intentional overestimate does not make otherwise valid simulations fail the caller balance check.

@joshieDo
joshieDo force-pushed the agent/native-multisig-rpc-wallet branch from 35fbe63 to 04d8e75 Compare August 20, 2026 11:13
@joshieDo
joshieDo force-pushed the agent/native-multisig-rpc-wallet branch 2 times, most recently from b37ee58 to c511a17 Compare August 20, 2026 13:56
@joshieDo

Copy link
Copy Markdown
Member Author

cyclops audit fast note="this belongs to the GH stack #7243 and the TIP 1061 is specified in the PR 7242. any bug not introduced by this PR should be clearly marked"

@joshieDo
joshieDo force-pushed the agent/native-multisig-rpc-wallet branch from c511a17 to 6f64329 Compare August 20, 2026 14:31

@tempoxyz-bot tempoxyz-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👁️ Cyclops Review

Rechecked against current head 6f643292333cac24a649cc8c8d019e0e4067a466 after head drift. Three actionable findings remain.

Reviewer Callouts
  • Conservative primitive-owner pricing: State-derived hints still emit UnknownPrimitive for every non-multisig owner, pricing each as maximal WebAuthn. A common registered 1-of-1 secp256k1 estimate is inflated by about 35,720 gas; consider a way to provide exact per-owner key hints.
  • Hint-builder work bound: A state override can create the maximum nested owner graph without on-chain setup cost, causing thousands of storage reads and millions of bounded DP iterations for one RPC call. Benchmark and rate-limit this path.
  • Storage-reader invariant parity: The RPC reader uses InitMultisig::validate() while the precompile uses validate_for_account(account). Keep these readers aligned so future state sources cannot make self-owned configurations acceptable only to simulation.

mut request: TempoTransactionRequest,
mut db: impl Database<Error: Into<EthApiError>>,
) -> Result<TxEnvFor<Self::Evm>, Self::Error> {
populate_native_multisig_simulation_hints(&mut request, &mut db)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 [SECURITY] Block simulation bypasses state-aware multisig hint construction

This preprocessing runs only through Call::create_txn_env; eth_simulateV1 and tempo_simulateV1 resolve raw requests through TryIntoSimTx. A normal registered-multisig request therefore stays non-AA and omits the TIP-1061 surcharge, while a request with multisigSignatureCount fabricates primitive approvals without loading the stored threshold or nested owner tree. Under T11 simulation this can report success and gas for an authorization shape that cannot execute on chain.

Recommended Fix:
Run the same state-aware preprocessing for each transaction at its simulated block position before build_simulate_v1_transaction, or reject count-only registered-multisig simulation when state-aware preprocessing is unavailable.

Comment thread crates/node/src/rpc/mod.rs
Comment thread crates/alloy/src/rpc/revm_compat.rs
@joshieDo
joshieDo force-pushed the agent/native-multisig-rpc-wallet branch from 6f64329 to e8a27db Compare August 20, 2026 15:26
@joshieDo
joshieDo force-pushed the agent/native-multisig-rpc-wallet branch from e8a27db to fe282e5 Compare August 20, 2026 16:12
@joshieDo
joshieDo force-pushed the agent/native-multisig-rpc-wallet branch from fe282e5 to e060cd3 Compare August 20, 2026 16:28
@joshieDo

Copy link
Copy Markdown
Member Author

cyclops audit fast note="this belongs to the GH stack #7243 and the TIP 1061 is specified in the PR 7242. any bug not introduced by this PR should be clearly marked"

@tempoxyz-bot tempoxyz-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👁️ Cyclops Review

This change adds native-multisig gas modeling to Tempo RPC simulations. After deduplicating against prior reviews, one verified Low-severity correctness issue remains: the simulateV1 bootstrap path uses a prefix-greedy quorum model that can undercharge intrinsic gas or reject valid multisig configurations.

Reviewer Callouts
  • Cross-RPC quorum parity: Confirm the same multisig bootstrap configuration produces a conservative approval count across eth_simulateV1, tempo_simulateV1, and eth_estimateGas after the fix.

};

let account = init.account().map_err(MultisigConfigError::as_str)?;
let signature_count = multisig_signature_count_for_threshold(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 [SECURITY] Bootstrap simulation uses a prefix-greedy quorum model

create_mock_native_multisig_sig sizes the mock approval list with multisig_signature_count_for_threshold, which accumulates owners in address order and stops at the first quorum. Consensus accepts any ascending subset of at most eight owners. A valid [2,1,1] configuration at threshold 2 is therefore modeled with one approval even though a two-approval quorum is valid and costs more gas. Conversely, eight weight-1 owners followed by a weight-100 owner at threshold 100 is a valid configuration, but this helper returns TooManySignatures before considering the heavy owner. Thus eth_simulateV1 and tempo_simulateV1 can understate intrinsic gas or reject valid bootstrap requests.

Recommended Fix:
Reuse the worst-case quorum selector used by the state-aware estimation path, or move an equivalent selector into a shared module and use it in both paths. Add regression tests for both configurations above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants