From a06b58437a6ca2b8b4835d232c1816b4bcc29c78 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Thu, 23 Jul 2026 15:03:49 +0700 Subject: [PATCH 1/2] Fix for API change on chain multisig approve now has call binding --- src/chain/quantus_subxt.rs | 403 ++++++++++++++++++++----- src/cli/exercise/scenarios/multisig.rs | 9 +- src/cli/multisig.rs | 24 +- src/cli/wormhole.rs | 2 +- src/collect_rewards_lib.rs | 2 +- src/config/mod.rs | 2 +- src/quantus_metadata.scale | Bin 146337 -> 148707 bytes 7 files changed, 358 insertions(+), 84 deletions(-) diff --git a/src/chain/quantus_subxt.rs b/src/chain/quantus_subxt.rs index b07e4cc..0ff0bae 100644 --- a/src/chain/quantus_subxt.rs +++ b/src/chain/quantus_subxt.rs @@ -1565,10 +1565,10 @@ pub mod api { "query_call_info", types::QueryCallInfo { call, len }, [ - 32u8, 234u8, 47u8, 160u8, 157u8, 142u8, 236u8, 40u8, 222u8, 22u8, 75u8, - 92u8, 244u8, 90u8, 235u8, 228u8, 137u8, 249u8, 207u8, 89u8, 87u8, - 244u8, 41u8, 20u8, 102u8, 145u8, 48u8, 54u8, 164u8, 114u8, 122u8, - 182u8, + 236u8, 179u8, 121u8, 45u8, 163u8, 162u8, 34u8, 178u8, 209u8, 79u8, + 52u8, 219u8, 68u8, 182u8, 97u8, 219u8, 73u8, 251u8, 70u8, 59u8, 64u8, + 168u8, 120u8, 180u8, 167u8, 201u8, 167u8, 243u8, 129u8, 233u8, 219u8, + 140u8, ], ) } @@ -1586,10 +1586,9 @@ pub mod api { "query_call_fee_details", types::QueryCallFeeDetails { call, len }, [ - 135u8, 197u8, 88u8, 132u8, 222u8, 66u8, 219u8, 154u8, 162u8, 155u8, - 161u8, 58u8, 176u8, 115u8, 33u8, 214u8, 171u8, 221u8, 230u8, 21u8, - 60u8, 15u8, 36u8, 117u8, 182u8, 70u8, 181u8, 108u8, 125u8, 72u8, 252u8, - 40u8, + 252u8, 97u8, 2u8, 156u8, 196u8, 243u8, 70u8, 237u8, 248u8, 84u8, 199u8, + 239u8, 252u8, 191u8, 212u8, 127u8, 115u8, 240u8, 6u8, 118u8, 134u8, + 144u8, 50u8, 63u8, 32u8, 215u8, 0u8, 31u8, 77u8, 120u8, 162u8, 55u8, ], ) } @@ -1980,6 +1979,9 @@ pub mod api { pub fn scheduler(&self) -> scheduler::storage::StorageApi { scheduler::storage::StorageApi } + pub fn utility(&self) -> utility::storage::StorageApi { + utility::storage::StorageApi + } pub fn reversible_transfers(&self) -> reversible_transfers::storage::StorageApi { reversible_transfers::storage::StorageApi } @@ -2068,9 +2070,9 @@ pub mod api { .hash(); runtime_metadata_hash == [ - 0u8, 140u8, 5u8, 40u8, 237u8, 46u8, 42u8, 0u8, 228u8, 200u8, 1u8, 226u8, 132u8, - 185u8, 140u8, 253u8, 54u8, 127u8, 56u8, 200u8, 164u8, 237u8, 198u8, 151u8, 133u8, - 33u8, 239u8, 56u8, 190u8, 69u8, 228u8, 26u8, + 17u8, 27u8, 154u8, 214u8, 45u8, 173u8, 211u8, 92u8, 142u8, 98u8, 221u8, 90u8, 77u8, + 55u8, 227u8, 93u8, 181u8, 184u8, 117u8, 132u8, 94u8, 193u8, 34u8, 100u8, 60u8, 0u8, + 52u8, 15u8, 91u8, 247u8, 16u8, 225u8, ] } pub mod system { @@ -3198,9 +3200,10 @@ pub mod api { "Events", (), [ - 12u8, 200u8, 85u8, 114u8, 205u8, 35u8, 13u8, 101u8, 195u8, 51u8, 123u8, - 145u8, 74u8, 61u8, 217u8, 71u8, 184u8, 61u8, 132u8, 83u8, 214u8, 117u8, - 222u8, 18u8, 13u8, 229u8, 195u8, 147u8, 223u8, 19u8, 66u8, 105u8, + 13u8, 93u8, 203u8, 141u8, 208u8, 101u8, 248u8, 136u8, 160u8, 153u8, + 118u8, 135u8, 207u8, 11u8, 73u8, 51u8, 253u8, 137u8, 82u8, 57u8, 46u8, + 108u8, 144u8, 243u8, 99u8, 155u8, 153u8, 82u8, 114u8, 59u8, 251u8, + 89u8, ], ) } @@ -7317,9 +7320,10 @@ pub mod api { "batch", types::Batch { calls }, [ - 91u8, 52u8, 14u8, 228u8, 168u8, 75u8, 218u8, 19u8, 121u8, 112u8, 126u8, - 236u8, 87u8, 77u8, 194u8, 140u8, 196u8, 15u8, 84u8, 117u8, 80u8, 101u8, - 217u8, 62u8, 28u8, 83u8, 226u8, 233u8, 176u8, 2u8, 158u8, 79u8, + 158u8, 27u8, 226u8, 243u8, 42u8, 43u8, 44u8, 250u8, 239u8, 116u8, + 186u8, 179u8, 26u8, 182u8, 183u8, 240u8, 77u8, 60u8, 151u8, 112u8, + 192u8, 172u8, 170u8, 249u8, 62u8, 90u8, 206u8, 6u8, 191u8, 122u8, + 230u8, 223u8, ], ) } @@ -7349,10 +7353,9 @@ pub mod api { call: ::subxt::ext::subxt_core::alloc::boxed::Box::new(call), }, [ - 78u8, 239u8, 51u8, 124u8, 113u8, 201u8, 252u8, 171u8, 182u8, 113u8, - 155u8, 193u8, 125u8, 113u8, 241u8, 199u8, 215u8, 237u8, 55u8, 121u8, - 247u8, 255u8, 41u8, 35u8, 206u8, 143u8, 157u8, 90u8, 203u8, 106u8, - 252u8, 91u8, + 232u8, 178u8, 106u8, 7u8, 242u8, 190u8, 47u8, 25u8, 95u8, 243u8, 223u8, + 197u8, 207u8, 81u8, 85u8, 50u8, 189u8, 8u8, 46u8, 161u8, 43u8, 177u8, + 8u8, 100u8, 91u8, 111u8, 114u8, 13u8, 107u8, 109u8, 167u8, 122u8, ], ) } @@ -7378,9 +7381,10 @@ pub mod api { "batch_all", types::BatchAll { calls }, [ - 9u8, 111u8, 191u8, 88u8, 100u8, 224u8, 185u8, 197u8, 208u8, 26u8, - 198u8, 42u8, 215u8, 246u8, 54u8, 120u8, 3u8, 148u8, 182u8, 44u8, 233u8, - 60u8, 26u8, 128u8, 101u8, 87u8, 141u8, 143u8, 9u8, 197u8, 184u8, 64u8, + 221u8, 235u8, 105u8, 128u8, 85u8, 243u8, 162u8, 1u8, 165u8, 70u8, + 240u8, 215u8, 127u8, 33u8, 148u8, 25u8, 171u8, 87u8, 221u8, 35u8, + 229u8, 225u8, 78u8, 255u8, 85u8, 137u8, 173u8, 109u8, 82u8, 117u8, + 211u8, 253u8, ], ) } @@ -7403,10 +7407,10 @@ pub mod api { call: ::subxt::ext::subxt_core::alloc::boxed::Box::new(call), }, [ - 252u8, 234u8, 74u8, 189u8, 195u8, 91u8, 133u8, 238u8, 206u8, 171u8, - 160u8, 141u8, 58u8, 250u8, 221u8, 113u8, 192u8, 201u8, 95u8, 103u8, - 209u8, 0u8, 110u8, 235u8, 38u8, 225u8, 79u8, 73u8, 19u8, 75u8, 138u8, - 146u8, + 23u8, 202u8, 157u8, 79u8, 233u8, 175u8, 242u8, 28u8, 247u8, 216u8, + 148u8, 138u8, 31u8, 93u8, 217u8, 242u8, 248u8, 122u8, 242u8, 165u8, + 67u8, 225u8, 105u8, 241u8, 40u8, 81u8, 232u8, 151u8, 182u8, 113u8, + 23u8, 130u8, ], ) } @@ -7432,9 +7436,9 @@ pub mod api { "force_batch", types::ForceBatch { calls }, [ - 130u8, 189u8, 219u8, 64u8, 240u8, 89u8, 49u8, 86u8, 163u8, 42u8, 52u8, - 8u8, 152u8, 154u8, 147u8, 236u8, 209u8, 30u8, 243u8, 194u8, 123u8, - 97u8, 97u8, 239u8, 97u8, 57u8, 104u8, 191u8, 235u8, 221u8, 1u8, 227u8, + 66u8, 39u8, 4u8, 20u8, 8u8, 43u8, 141u8, 163u8, 45u8, 232u8, 83u8, + 196u8, 105u8, 42u8, 231u8, 170u8, 236u8, 30u8, 98u8, 43u8, 224u8, 73u8, + 8u8, 208u8, 50u8, 116u8, 2u8, 37u8, 226u8, 157u8, 249u8, 193u8, ], ) } @@ -7457,9 +7461,10 @@ pub mod api { weight, }, [ - 22u8, 169u8, 85u8, 171u8, 100u8, 38u8, 252u8, 11u8, 198u8, 55u8, 150u8, - 190u8, 59u8, 5u8, 143u8, 123u8, 107u8, 23u8, 202u8, 0u8, 237u8, 255u8, - 77u8, 19u8, 113u8, 77u8, 133u8, 52u8, 176u8, 110u8, 214u8, 221u8, + 222u8, 150u8, 13u8, 118u8, 150u8, 13u8, 56u8, 211u8, 198u8, 109u8, 5u8, + 43u8, 189u8, 147u8, 215u8, 167u8, 99u8, 160u8, 224u8, 20u8, 224u8, + 245u8, 163u8, 253u8, 173u8, 205u8, 98u8, 170u8, 7u8, 235u8, 249u8, + 71u8, ], ) } @@ -7499,9 +7504,9 @@ pub mod api { fallback: ::subxt::ext::subxt_core::alloc::boxed::Box::new(fallback), }, [ - 210u8, 174u8, 69u8, 89u8, 81u8, 251u8, 211u8, 77u8, 44u8, 88u8, 137u8, - 153u8, 221u8, 116u8, 10u8, 183u8, 158u8, 145u8, 229u8, 108u8, 168u8, - 123u8, 100u8, 162u8, 121u8, 201u8, 158u8, 103u8, 96u8, 96u8, 89u8, 4u8, + 167u8, 172u8, 213u8, 196u8, 158u8, 194u8, 170u8, 10u8, 101u8, 93u8, + 54u8, 153u8, 1u8, 145u8, 163u8, 46u8, 245u8, 161u8, 87u8, 93u8, 107u8, + 209u8, 114u8, 45u8, 38u8, 185u8, 84u8, 192u8, 227u8, 225u8, 74u8, 52u8, ], ) } @@ -7524,10 +7529,10 @@ pub mod api { call: ::subxt::ext::subxt_core::alloc::boxed::Box::new(call), }, [ - 68u8, 190u8, 238u8, 69u8, 88u8, 148u8, 111u8, 242u8, 200u8, 228u8, - 47u8, 232u8, 101u8, 173u8, 104u8, 140u8, 30u8, 113u8, 236u8, 193u8, - 48u8, 47u8, 116u8, 169u8, 97u8, 77u8, 17u8, 82u8, 37u8, 190u8, 158u8, - 66u8, + 66u8, 104u8, 192u8, 138u8, 153u8, 134u8, 170u8, 122u8, 87u8, 234u8, + 155u8, 110u8, 225u8, 138u8, 166u8, 181u8, 136u8, 86u8, 200u8, 224u8, + 153u8, 180u8, 186u8, 125u8, 126u8, 75u8, 22u8, 179u8, 194u8, 145u8, + 187u8, 89u8, ], ) } @@ -7670,6 +7675,67 @@ pub mod api { const EVENT: &'static str = "IfElseFallbackCalled"; } } + pub mod storage { + use super::runtime_types; + pub mod types { + use super::runtime_types; + pub mod known_derivatives { + use super::runtime_types; + pub type KnownDerivatives = (); + pub type Param0 = ::subxt::ext::subxt_core::utils::AccountId32; + } + } + pub struct StorageApi; + impl StorageApi { + #[doc = " Derivative pseudonym accounts that have been used with [`Pallet::as_derivative`] at least"] + #[doc = " once and have therefore been revealed to the wormhole soundness counter."] + pub fn known_derivatives_iter( + &self, + ) -> ::subxt::ext::subxt_core::storage::address::StaticAddress< + (), + types::known_derivatives::KnownDerivatives, + (), + (), + ::subxt::ext::subxt_core::utils::Yes, + > { + ::subxt::ext::subxt_core::storage::address::StaticAddress::new_static( + "Utility", + "KnownDerivatives", + (), + [ + 7u8, 191u8, 89u8, 10u8, 69u8, 99u8, 173u8, 186u8, 52u8, 139u8, 104u8, + 183u8, 71u8, 107u8, 206u8, 3u8, 88u8, 160u8, 238u8, 53u8, 84u8, 31u8, + 171u8, 151u8, 88u8, 158u8, 134u8, 111u8, 200u8, 72u8, 151u8, 158u8, + ], + ) + } + #[doc = " Derivative pseudonym accounts that have been used with [`Pallet::as_derivative`] at least"] + #[doc = " once and have therefore been revealed to the wormhole soundness counter."] + pub fn known_derivatives( + &self, + _0: types::known_derivatives::Param0, + ) -> ::subxt::ext::subxt_core::storage::address::StaticAddress< + ::subxt::ext::subxt_core::storage::address::StaticStorageKey< + types::known_derivatives::Param0, + >, + types::known_derivatives::KnownDerivatives, + ::subxt::ext::subxt_core::utils::Yes, + (), + (), + > { + ::subxt::ext::subxt_core::storage::address::StaticAddress::new_static( + "Utility", + "KnownDerivatives", + ::subxt::ext::subxt_core::storage::address::StaticStorageKey::new(_0), + [ + 7u8, 191u8, 89u8, 10u8, 69u8, 99u8, 173u8, 186u8, 52u8, 139u8, 104u8, + 183u8, 71u8, 107u8, 206u8, 3u8, 88u8, 160u8, 238u8, 53u8, 84u8, 31u8, + 171u8, 151u8, 88u8, 158u8, 134u8, 111u8, 200u8, 72u8, 151u8, 158u8, + ], + ) + } + } + } pub mod constants { use super::runtime_types; pub struct ConstantsApi; @@ -11603,9 +11669,10 @@ pub mod api { call: ::subxt::ext::subxt_core::alloc::boxed::Box::new(call), }, [ - 83u8, 241u8, 200u8, 95u8, 176u8, 73u8, 86u8, 254u8, 102u8, 49u8, 113u8, - 102u8, 55u8, 250u8, 115u8, 98u8, 254u8, 96u8, 27u8, 19u8, 132u8, 79u8, - 207u8, 104u8, 114u8, 167u8, 142u8, 208u8, 170u8, 125u8, 198u8, 136u8, + 32u8, 36u8, 63u8, 177u8, 5u8, 182u8, 210u8, 189u8, 81u8, 193u8, 176u8, + 20u8, 83u8, 199u8, 186u8, 115u8, 92u8, 187u8, 93u8, 205u8, 144u8, + 105u8, 119u8, 144u8, 115u8, 144u8, 205u8, 45u8, 118u8, 100u8, 182u8, + 193u8, ], ) } @@ -16606,22 +16673,33 @@ pub mod api { )] #[doc = "Approve a proposed transaction"] #[doc = ""] + #[doc = "The approver must resubmit the proposal's inner call bytes; the approval is"] + #[doc = "only valid if they are byte-equal to the payload stored at `proposal_id`."] + #[doc = "This binds the approver's signature to the actual call being approved, so"] + #[doc = "offline/cold-wallet signers can decode and inspect what they are signing"] + #[doc = "instead of trusting an opaque proposal id."] + #[doc = ""] #[doc = "If this approval brings the total approvals to or above the threshold,"] #[doc = "the proposal status changes to `Approved` and can be executed via `execute()`."] #[doc = ""] #[doc = "Parameters:"] #[doc = "- `multisig_address`: The multisig account"] #[doc = "- `proposal_id`: ID (nonce) of the proposal to approve"] + #[doc = "- `call`: The encoded inner call of the proposal (must match the stored payload)"] #[doc = ""] #[doc = "Weight: Charges for MAX call size, refunds based on actual"] pub struct Approve { pub multisig_address: approve::MultisigAddress, pub proposal_id: approve::ProposalId, + pub call: approve::Call, } pub mod approve { use super::runtime_types; pub type MultisigAddress = ::subxt::ext::subxt_core::utils::AccountId32; pub type ProposalId = ::core::primitive::u32; + pub type Call = runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >; } impl ::subxt::ext::subxt_core::blocks::StaticExtrinsic for Approve { const PALLET: &'static str = "Multisig"; @@ -16839,27 +16917,35 @@ pub mod api { } #[doc = "Approve a proposed transaction"] #[doc = ""] + #[doc = "The approver must resubmit the proposal's inner call bytes; the approval is"] + #[doc = "only valid if they are byte-equal to the payload stored at `proposal_id`."] + #[doc = "This binds the approver's signature to the actual call being approved, so"] + #[doc = "offline/cold-wallet signers can decode and inspect what they are signing"] + #[doc = "instead of trusting an opaque proposal id."] + #[doc = ""] #[doc = "If this approval brings the total approvals to or above the threshold,"] #[doc = "the proposal status changes to `Approved` and can be executed via `execute()`."] #[doc = ""] #[doc = "Parameters:"] #[doc = "- `multisig_address`: The multisig account"] #[doc = "- `proposal_id`: ID (nonce) of the proposal to approve"] + #[doc = "- `call`: The encoded inner call of the proposal (must match the stored payload)"] #[doc = ""] #[doc = "Weight: Charges for MAX call size, refunds based on actual"] pub fn approve( &self, multisig_address: types::approve::MultisigAddress, proposal_id: types::approve::ProposalId, + call: types::approve::Call, ) -> ::subxt::ext::subxt_core::tx::payload::StaticPayload { ::subxt::ext::subxt_core::tx::payload::StaticPayload::new_static( "Multisig", "approve", - types::Approve { multisig_address, proposal_id }, + types::Approve { multisig_address, proposal_id, call }, [ - 9u8, 56u8, 186u8, 135u8, 222u8, 23u8, 37u8, 64u8, 123u8, 199u8, 205u8, - 29u8, 216u8, 128u8, 37u8, 185u8, 170u8, 121u8, 75u8, 100u8, 198u8, - 80u8, 16u8, 249u8, 170u8, 91u8, 162u8, 201u8, 215u8, 81u8, 87u8, 190u8, + 17u8, 24u8, 48u8, 140u8, 188u8, 27u8, 94u8, 183u8, 209u8, 243u8, 139u8, + 55u8, 59u8, 212u8, 220u8, 78u8, 227u8, 235u8, 36u8, 122u8, 0u8, 125u8, + 98u8, 45u8, 234u8, 134u8, 196u8, 93u8, 140u8, 152u8, 171u8, 135u8, ], ) } @@ -17559,46 +17645,98 @@ pub mod api { #[encode_as_type( crate_path = ":: subxt :: ext :: subxt_core :: ext :: scale_encode" )] - #[doc = "Verify an aggregated wormhole proof and process all transfers in the batch."] + #[doc = "Verify a private-batch wormhole proof and process all exits in the batch."] #[doc = ""] #[doc = "Returns `DispatchResultWithPostInfo` to allow weight correction on early failures."] #[doc = "If validation fails before ZK verification, we return minimal weight."] #[doc = "If ZK verification fails, we return full weight since the work was done."] - pub struct VerifyAggregatedProof { - pub proof_bytes: verify_aggregated_proof::ProofBytes, + pub struct VerifyPrivateBatch { + pub proof_bytes: verify_private_batch::ProofBytes, + } + pub mod verify_private_batch { + use super::runtime_types; + pub type ProofBytes = + ::subxt::ext::subxt_core::alloc::vec::Vec<::core::primitive::u8>; + } + impl ::subxt::ext::subxt_core::blocks::StaticExtrinsic for VerifyPrivateBatch { + const PALLET: &'static str = "Wormhole"; + const CALL: &'static str = "verify_private_batch"; + } + #[derive( + :: subxt :: ext :: subxt_core :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: subxt_core :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + #[decode_as_type( + crate_path = ":: subxt :: ext :: subxt_core :: ext :: scale_decode" + )] + #[encode_as_type( + crate_path = ":: subxt :: ext :: subxt_core :: ext :: scale_encode" + )] + #[doc = "Verify a public-batch wormhole proof and process all valid exit segments."] + #[doc = ""] + #[doc = "Invalid segments (already-spent nullifiers) are denied individually; dummy-padded"] + #[doc = "segments (all-zero nullifiers) are skipped silently. A portion of the burn bucket"] + #[doc = "is minted to the proof's `aggregator_address`; if that mint fails (e.g. the"] + #[doc = "account doesn't exist and the rebate is below the existential deposit) the"] + #[doc = "rebate is burned instead of failing the users' exits."] + pub struct VerifyPublicBatch { + pub proof_bytes: verify_public_batch::ProofBytes, } - pub mod verify_aggregated_proof { + pub mod verify_public_batch { use super::runtime_types; pub type ProofBytes = ::subxt::ext::subxt_core::alloc::vec::Vec<::core::primitive::u8>; } - impl ::subxt::ext::subxt_core::blocks::StaticExtrinsic for VerifyAggregatedProof { + impl ::subxt::ext::subxt_core::blocks::StaticExtrinsic for VerifyPublicBatch { const PALLET: &'static str = "Wormhole"; - const CALL: &'static str = "verify_aggregated_proof"; + const CALL: &'static str = "verify_public_batch"; } } pub struct TransactionApi; impl TransactionApi { - #[doc = "Verify an aggregated wormhole proof and process all transfers in the batch."] + #[doc = "Verify a private-batch wormhole proof and process all exits in the batch."] #[doc = ""] #[doc = "Returns `DispatchResultWithPostInfo` to allow weight correction on early failures."] #[doc = "If validation fails before ZK verification, we return minimal weight."] #[doc = "If ZK verification fails, we return full weight since the work was done."] - pub fn verify_aggregated_proof( + pub fn verify_private_batch( &self, - proof_bytes: types::verify_aggregated_proof::ProofBytes, - ) -> ::subxt::ext::subxt_core::tx::payload::StaticPayload< - types::VerifyAggregatedProof, - > { + proof_bytes: types::verify_private_batch::ProofBytes, + ) -> ::subxt::ext::subxt_core::tx::payload::StaticPayload + { ::subxt::ext::subxt_core::tx::payload::StaticPayload::new_static( "Wormhole", - "verify_aggregated_proof", - types::VerifyAggregatedProof { proof_bytes }, + "verify_private_batch", + types::VerifyPrivateBatch { proof_bytes }, [ - 122u8, 42u8, 194u8, 153u8, 22u8, 66u8, 199u8, 173u8, 19u8, 107u8, - 232u8, 147u8, 77u8, 40u8, 115u8, 208u8, 183u8, 77u8, 46u8, 149u8, 56u8, - 225u8, 146u8, 168u8, 49u8, 248u8, 42u8, 195u8, 180u8, 91u8, 75u8, - 102u8, + 138u8, 4u8, 114u8, 57u8, 176u8, 171u8, 151u8, 90u8, 93u8, 19u8, 57u8, + 176u8, 66u8, 92u8, 244u8, 111u8, 52u8, 102u8, 210u8, 239u8, 220u8, + 14u8, 150u8, 134u8, 251u8, 90u8, 34u8, 237u8, 117u8, 125u8, 115u8, + 69u8, + ], + ) + } + #[doc = "Verify a public-batch wormhole proof and process all valid exit segments."] + #[doc = ""] + #[doc = "Invalid segments (already-spent nullifiers) are denied individually; dummy-padded"] + #[doc = "segments (all-zero nullifiers) are skipped silently. A portion of the burn bucket"] + #[doc = "is minted to the proof's `aggregator_address`; if that mint fails (e.g. the"] + #[doc = "account doesn't exist and the rebate is below the existential deposit) the"] + #[doc = "rebate is burned instead of failing the users' exits."] + pub fn verify_public_batch( + &self, + proof_bytes: types::verify_public_batch::ProofBytes, + ) -> ::subxt::ext::subxt_core::tx::payload::StaticPayload + { + ::subxt::ext::subxt_core::tx::payload::StaticPayload::new_static( + "Wormhole", + "verify_public_batch", + types::VerifyPublicBatch { proof_bytes }, + [ + 169u8, 13u8, 181u8, 227u8, 212u8, 168u8, 177u8, 140u8, 2u8, 34u8, 84u8, + 69u8, 178u8, 235u8, 250u8, 24u8, 126u8, 60u8, 52u8, 76u8, 79u8, 7u8, + 110u8, 1u8, 77u8, 209u8, 62u8, 137u8, 212u8, 98u8, 93u8, 4u8, ], ) } @@ -17691,6 +17829,52 @@ pub mod api { const PALLET: &'static str = "Wormhole"; const EVENT: &'static str = "ProofVerified"; } + #[derive( + :: subxt :: ext :: subxt_core :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: subxt_core :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + #[decode_as_type(crate_path = ":: subxt :: ext :: subxt_core :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: subxt_core :: ext :: scale_encode")] + #[doc = "The block author's share of the wormhole exit volume fee was minted."] + #[doc = ""] + #[doc = "NOTE: keep this as the last variant — indexers decode events by their"] + #[doc = "position in this enum, so existing variants must never be reordered."] + pub struct MinerVolumeFeePaid { + pub miner: miner_volume_fee_paid::Miner, + pub amount: miner_volume_fee_paid::Amount, + } + pub mod miner_volume_fee_paid { + use super::runtime_types; + pub type Miner = ::subxt::ext::subxt_core::utils::AccountId32; + pub type Amount = ::core::primitive::u128; + } + impl ::subxt::ext::subxt_core::events::StaticEvent for MinerVolumeFeePaid { + const PALLET: &'static str = "Wormhole"; + const EVENT: &'static str = "MinerVolumeFeePaid"; + } + #[derive( + :: subxt :: ext :: subxt_core :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: subxt_core :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + #[decode_as_type(crate_path = ":: subxt :: ext :: subxt_core :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: subxt_core :: ext :: scale_encode")] + #[doc = "Some segments of an exit bundle were denied (their nullifiers were already"] + #[doc = "used, e.g. because the underlying private batch landed on-chain separately)."] + #[doc = "The remaining segments were processed normally."] + pub struct SegmentsDenied { + pub indices: segments_denied::Indices, + } + pub mod segments_denied { + use super::runtime_types; + pub type Indices = + ::subxt::ext::subxt_core::alloc::vec::Vec<::core::primitive::u32>; + } + impl ::subxt::ext::subxt_core::events::StaticEvent for SegmentsDenied { + const PALLET: &'static str = "Wormhole"; + const EVENT: &'static str = "SegmentsDenied"; + } } pub mod storage { use super::runtime_types; @@ -17970,6 +18154,24 @@ pub mod api { ], ) } + #[doc = " For public-batch proofs, the proportion of the burn bucket redirected to the"] + #[doc = " aggregator instead of being destroyed. The miner's share is unchanged."] + #[doc = " Example: Permill::from_percent(50) means half the burn portion goes to the aggregator."] + pub fn volume_fees_aggregator_rate( + &self, + ) -> ::subxt::ext::subxt_core::constants::address::StaticAddress< + runtime_types::sp_arithmetic::per_things::Permill, + > { + ::subxt::ext::subxt_core::constants::address::StaticAddress::new_static( + "Wormhole", + "VolumeFeesAggregatorRate", + [ + 65u8, 93u8, 120u8, 165u8, 204u8, 81u8, 159u8, 163u8, 93u8, 135u8, + 114u8, 121u8, 147u8, 35u8, 215u8, 213u8, 4u8, 223u8, 83u8, 37u8, 225u8, + 200u8, 189u8, 156u8, 140u8, 36u8, 58u8, 46u8, 42u8, 232u8, 155u8, 0u8, + ], + ) + } } } } @@ -20859,17 +21061,27 @@ pub mod api { #[codec(index = 2)] #[doc = "Approve a proposed transaction"] #[doc = ""] + #[doc = "The approver must resubmit the proposal's inner call bytes; the approval is"] + #[doc = "only valid if they are byte-equal to the payload stored at `proposal_id`."] + #[doc = "This binds the approver's signature to the actual call being approved, so"] + #[doc = "offline/cold-wallet signers can decode and inspect what they are signing"] + #[doc = "instead of trusting an opaque proposal id."] + #[doc = ""] #[doc = "If this approval brings the total approvals to or above the threshold,"] #[doc = "the proposal status changes to `Approved` and can be executed via `execute()`."] #[doc = ""] #[doc = "Parameters:"] #[doc = "- `multisig_address`: The multisig account"] #[doc = "- `proposal_id`: ID (nonce) of the proposal to approve"] + #[doc = "- `call`: The encoded inner call of the proposal (must match the stored payload)"] #[doc = ""] #[doc = "Weight: Charges for MAX call size, refunds based on actual"] approve { multisig_address: ::subxt::ext::subxt_core::utils::AccountId32, proposal_id: ::core::primitive::u32, + call: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, }, #[codec(index = 3)] #[doc = "Cancel a proposed transaction (only by proposer)"] @@ -21023,6 +21235,9 @@ pub mod api { #[codec(index = 24)] #[doc = "Call weight exceeds MaxInnerCallWeight limit"] CallWeightExceedsLimit, + #[codec(index = 25)] + #[doc = "Provided call does not match the stored proposal payload"] + CallMismatch, } #[derive( :: subxt :: ext :: subxt_core :: ext :: scale_decode :: DecodeAsType, @@ -23429,12 +23644,24 @@ pub mod api { #[doc = "Contains a variant per dispatchable extrinsic that this pallet has."] pub enum Call { #[codec(index = 2)] - #[doc = "Verify an aggregated wormhole proof and process all transfers in the batch."] + #[doc = "Verify a private-batch wormhole proof and process all exits in the batch."] #[doc = ""] #[doc = "Returns `DispatchResultWithPostInfo` to allow weight correction on early failures."] #[doc = "If validation fails before ZK verification, we return minimal weight."] #[doc = "If ZK verification fails, we return full weight since the work was done."] - verify_aggregated_proof { + verify_private_batch { + proof_bytes: + ::subxt::ext::subxt_core::alloc::vec::Vec<::core::primitive::u8>, + }, + #[codec(index = 3)] + #[doc = "Verify a public-batch wormhole proof and process all valid exit segments."] + #[doc = ""] + #[doc = "Invalid segments (already-spent nullifiers) are denied individually; dummy-padded"] + #[doc = "segments (all-zero nullifiers) are skipped silently. A portion of the burn bucket"] + #[doc = "is minted to the proof's `aggregator_address`; if that mint fails (e.g. the"] + #[doc = "account doesn't exist and the rebate is below the existential deposit) the"] + #[doc = "rebate is burned instead of failing the users' exits."] + verify_public_batch { proof_bytes: ::subxt::ext::subxt_core::alloc::vec::Vec<::core::primitive::u8>, }, @@ -23455,27 +23682,35 @@ pub mod api { #[codec(index = 0)] InvalidPublicInputs, #[codec(index = 1)] + #[doc = "No segment of the bundle is spendable: every non-dummy segment contains a"] + #[doc = "nullifier that is already used (or the single segment of a private-batch"] + #[doc = "proof does)."] NullifierAlreadyUsed, #[codec(index = 2)] - BlockNotFound, + #[doc = "The bundle contains only dummy (all-zero) padding segments, so there is"] + #[doc = "nothing to exit. Distinct from [`Error::NullifierAlreadyUsed`], which is a"] + #[doc = "replay of real segments."] + NoValidSegments, #[codec(index = 3)] - AggregatedVerifierNotAvailable, + BlockNotFound, #[codec(index = 4)] - AggregatedProofDeserializationFailed, + VerifierNotAvailable, #[codec(index = 5)] - AggregatedVerificationFailed, + ProofDeserializationFailed, #[codec(index = 6)] - InvalidAggregatedPublicInputs, + ProofVerificationFailed, #[codec(index = 7)] + InvalidProofPublicInputs, + #[codec(index = 8)] #[doc = "The volume fee rate in the proof doesn't match the configured rate"] InvalidVolumeFeeRate, - #[codec(index = 8)] + #[codec(index = 9)] #[doc = "Transfer amount is below the minimum required"] TransferAmountBelowMinimum, - #[codec(index = 9)] + #[codec(index = 10)] #[doc = "Only native asset (asset_id = 0) is supported in this version"] NonNativeAssetNotSupported, - #[codec(index = 10)] + #[codec(index = 11)] #[doc = "Soundness invariant violated: total wormhole exits would exceed the value that could"] #[doc = "possibly have been deposited into wormhole addresses. This indicates a potential"] #[doc = "soundness bug in the ZK proof system, so the exit is rejected."] @@ -23526,6 +23761,22 @@ pub mod api { [::core::primitive::u8; 32usize], >, }, + #[codec(index = 3)] + #[doc = "The block author's share of the wormhole exit volume fee was minted."] + #[doc = ""] + #[doc = "NOTE: keep this as the last variant — indexers decode events by their"] + #[doc = "position in this enum, so existing variants must never be reordered."] + MinerVolumeFeePaid { + miner: ::subxt::ext::subxt_core::utils::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 4)] + #[doc = "Some segments of an exit bundle were denied (their nullifiers were already"] + #[doc = "used, e.g. because the underlying private batch landed on-chain separately)."] + #[doc = "The remaining segments were processed normally."] + SegmentsDenied { + indices: ::subxt::ext::subxt_core::alloc::vec::Vec<::core::primitive::u32>, + }, } } } diff --git a/src/cli/exercise/scenarios/multisig.rs b/src/cli/exercise/scenarios/multisig.rs index 319412a..e2b3925 100644 --- a/src/cli/exercise/scenarios/multisig.rs +++ b/src/cli/exercise/scenarios/multisig.rs @@ -79,8 +79,13 @@ async fn lifecycle(ctx: &mut ExerciseCtx) -> Result { submit_ok(ctx, &signer_a, propose_call).await?; let proposal_id = latest_proposal_id(ctx, &multisig_id).await?; - let approve_call = - quantus_subxt::api::tx().multisig().approve(multisig_id.clone(), proposal_id); + let approve_call = quantus_subxt::api::tx().multisig().approve( + multisig_id.clone(), + proposal_id, + quantus_subxt::api::runtime_types::bounded_collections::bounded_vec::BoundedVec( + call_data.clone(), + ), + ); submit_ok(ctx, &signer_b, approve_call).await?; let execute_call = diff --git a/src/cli/multisig.rs b/src/cli/multisig.rs index 6a3bb7c..2f6931c 100644 --- a/src/cli/multisig.rs +++ b/src/cli/multisig.rs @@ -626,7 +626,21 @@ pub async fn approve_proposal( multisig_address: subxt::ext::subxt_core::utils::AccountId32, proposal_id: u32, ) -> crate::error::Result { - let approve_tx = quantus_subxt::api::tx().multisig().approve(multisig_address, proposal_id); + // The chain binds each approval to the proposal's inner call payload, so fetch + // the stored call bytes and resubmit them with the approval. + let latest_block_hash = quantus_client.get_latest_block().await?; + let storage_at = quantus_client.client().storage().at(latest_block_hash); + let proposal_query = quantus_subxt::api::storage() + .multisig() + .proposals(multisig_address.clone(), proposal_id); + let proposal = storage_at.fetch(&proposal_query).await?.ok_or_else(|| { + crate::error::QuantusError::Generic(format!("Proposal {} does not exist", proposal_id)) + })?; + + let approve_tx = + quantus_subxt::api::tx() + .multisig() + .approve(multisig_address, proposal_id, proposal.call); let execution_mode = ExecutionMode { finalized: false, wait_for_transaction: false }; let tx_hash = crate::cli::common::submit_transaction( @@ -1699,8 +1713,12 @@ async fn handle_approve( )); } - // Build transaction - let approve_tx = quantus_subxt::api::tx().multisig().approve(multisig_address, proposal_id); + // Build transaction. The chain binds each approval to the proposal's inner call + // payload, so resubmit the exact call bytes stored at this proposal id. + let approve_tx = + quantus_subxt::api::tx() + .multisig() + .approve(multisig_address, proposal_id, proposal.call); // Always wait for transaction confirmation let approve_execution_mode = ExecutionMode { wait_for_transaction: true, ..execution_mode }; diff --git a/src/cli/wormhole.rs b/src/cli/wormhole.rs index f1d8422..8e73aba 100644 --- a/src/cli/wormhole.rs +++ b/src/cli/wormhole.rs @@ -1297,7 +1297,7 @@ pub async fn submit_unsigned_verify_aggregated_proof( ) -> crate::error::Result<(IncludedAt, subxt::utils::H256, subxt::utils::H256)> { use subxt::tx::TxStatus; - let verify_tx = quantus_node::api::tx().wormhole().verify_aggregated_proof(proof_bytes); + let verify_tx = quantus_node::api::tx().wormhole().verify_private_batch(proof_bytes); let unsigned_tx = quantus_client.client().tx().create_unsigned(&verify_tx).map_err(|e| { crate::error::QuantusError::Generic(format!("Failed to create unsigned tx: {}", e)) diff --git a/src/collect_rewards_lib.rs b/src/collect_rewards_lib.rs index 1c26990..81b4d31 100644 --- a/src/collect_rewards_lib.rs +++ b/src/collect_rewards_lib.rs @@ -890,7 +890,7 @@ async fn submit_and_get_events( } // Submit unsigned tx - let verify_tx = quantus_node::api::tx().wormhole().verify_aggregated_proof(proof_bytes); + let verify_tx = quantus_node::api::tx().wormhole().verify_private_batch(proof_bytes); let unsigned_tx = quantus_client.client().tx().create_unsigned(&verify_tx).map_err(|e| { diff --git a/src/config/mod.rs b/src/config/mod.rs index a11c6c4..0a0a722 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -8,7 +8,7 @@ pub struct CompatibleRuntime { /// Supported runtime / transaction version pairs. pub const COMPATIBLE_RUNTIMES: &[CompatibleRuntime] = - &[CompatibleRuntime { spec_version: 134, transaction_version: 2 }]; + &[CompatibleRuntime { spec_version: 135, transaction_version: 3 }]; /// Check whether a runtime version pair is supported by this CLI. pub fn is_runtime_compatible(spec_version: u32, transaction_version: u32) -> bool { diff --git a/src/quantus_metadata.scale b/src/quantus_metadata.scale index 2ab234bd92cc2f1039e519b878b3173540a03bcd..b224987cc23776f984a5fd9ea2610a77d05b58b7 100644 GIT binary patch delta 6370 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 i77mwpP-r|qLgq^p7+cchtS+n@kuKv{JStsowfq Date: Fri, 24 Jul 2026 14:19:09 +0800 Subject: [PATCH 2/2] update cmpatibility --- src/config/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/mod.rs b/src/config/mod.rs index 2f5baf2..5ec9c21 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -11,6 +11,7 @@ pub const COMPATIBLE_RUNTIMES: &[CompatibleRuntime] = &[ CompatibleRuntime { spec_version: 134, transaction_version: 2 }, CompatibleRuntime { spec_version: 135, transaction_version: 2 }, CompatibleRuntime { spec_version: 135, transaction_version: 3 }, + CompatibleRuntime { spec_version: 136, transaction_version: 3 }, ]; /// Check whether a runtime version pair is supported by this CLI.