From 1a2aedae121fd9b0232f7a5b001e0929e154c32d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex?= Date: Mon, 3 Aug 2026 10:51:18 +0000 Subject: [PATCH] feat(operation): Split trait (#3234) --- c-bindings/src/api/wallet.rs | 4 +- core/benches/mantle_tx_components.rs | 12 +- core/src/block/deser.rs | 8 +- core/src/block/genesis.rs | 8 +- core/src/block/mod.rs | 33 +++--- core/src/header/mod.rs | 12 +- core/src/mantle/channel.rs | 6 +- core/src/mantle/fixtures/tx.rs | 4 +- core/src/mantle/ledger.rs | 51 +++++--- core/src/mantle/mod.rs | 2 +- .../mantle/ops/channel/channel_transfer.rs | 74 ++++++------ core/src/mantle/ops/channel/config.rs | 51 ++++---- core/src/mantle/ops/channel/deposit.rs | 65 +++++----- core/src/mantle/ops/channel/inscribe.rs | 55 ++++----- core/src/mantle/ops/channel/withdraw.rs | 60 +++++----- core/src/mantle/ops/leader_claim.rs | 73 ++++++------ core/src/mantle/ops/mod.rs | 8 +- core/src/mantle/ops/sdp/active.rs | 53 ++++----- core/src/mantle/ops/sdp/declare.rs | 111 +++++++----------- core/src/mantle/ops/sdp/withdraw.rs | 53 ++++----- core/src/mantle/ops/transfer.rs | 49 ++++---- core/src/mantle/traits/genesis.rs | 6 +- core/src/mantle/traits/mantle_tx.rs | 7 +- core/src/mantle/transactions/builder.rs | 14 +-- core/src/mantle/transactions/codec.rs | 46 ++++---- core/src/mantle/transactions/genesis_tx.rs | 10 +- core/src/mantle/transactions/mantle_tx.rs | 37 +++--- core/src/mantle/transactions/mod.rs | 2 +- .../mantle/transactions/signed_mantle_tx.rs | 33 +++--- core/src/mantle/transactions/verified_ops.rs | 5 +- core/src/sdp/mod.rs | 2 +- core/src/utils/merkle.rs | 8 +- .../sequencer/src/sequencer.rs | 4 +- .../tui-zone/src/run_commands/run_config.rs | 6 +- .../tui-zone/src/run_commands/run_withdraw.rs | 2 +- .../tui-zone/src/run_commands/unit_tests.rs | 14 +-- deployment/tui-zone/src/run_commands/utils.rs | 6 +- ledger/src/cryptarchia/mod.rs | 8 +- ledger/src/lib.rs | 21 ++-- ledger/src/mantle/mod.rs | 2 +- ledger/src/mantle/sdp/mod.rs | 48 ++++---- nodes/api-common/src/bodies/wallet.rs | 4 +- .../src/api/serializers/transactions.rs | 29 +++-- .../chain-service/src/sync/block_provider.rs | 4 +- services/wallet/src/lib.rs | 3 +- tests/src/common/fee_spec.rs | 1 + tests/src/common/wallet/scanner/accounting.rs | 16 +-- .../wallet/transaction/builder_funding.rs | 2 +- tests/src/common/wallet/transaction/intent.rs | 2 +- .../src/common/wallet/transaction/prepare.rs | 2 +- .../src/common/wallet/transaction/signing.rs | 6 +- .../tracked_transactions.rs | 4 +- .../src/cucumber/steps/manual_zone/support.rs | 4 +- tests/src/cucumber/steps/wallet_fund.rs | 2 +- .../src/workloads/inscription/workload.rs | 4 +- tools/config/src/consensus.rs | 4 +- tools/config/src/sdp.rs | 6 +- wallet/src/lib.rs | 8 +- zone-sdk/BRIDGING.md | 2 +- zone-sdk/src/sequencer/actor.rs | 10 +- zone-sdk/src/sequencer/block_fetch.rs | 5 +- zone-sdk/src/sequencer/client.rs | 10 +- zone-sdk/src/sequencer/handle.rs | 15 +-- zone-sdk/src/sequencer/state.rs | 6 +- zone-sdk/src/sequencer/tx_builder.rs | 18 +-- zone-sdk/src/sequencer/zone_sequencer.rs | 15 ++- zone-sdk/src/test_support.rs | 4 +- 67 files changed, 639 insertions(+), 620 deletions(-) diff --git a/c-bindings/src/api/wallet.rs b/c-bindings/src/api/wallet.rs index 517d0952a..a04a3b451 100644 --- a/c-bindings/src/api/wallet.rs +++ b/c-bindings/src/api/wallet.rs @@ -8,7 +8,7 @@ use lb_api_service::http::mempool; use lb_core::{ header::HeaderId as CoreHeaderId, mantle::{ - MantleTx, Note, NoteId as CoreNoteId, Op, OpProof, SignedMantleTx, + Note, NoteId as CoreNoteId, Op, OpProof, RawMantleTx, SignedMantleTx, gas::GasCost, ledger::{Inputs, Outputs}, ops::{ @@ -1330,7 +1330,7 @@ pub(crate) fn channel_deposit_sync( // inputs and the deposit's input note are owned by `funding_public_key`, so // one signature over the tx hash satisfies both op proofs. Do not "simplify" // this to `sign_tx`. - let tx = MantleTx([Op::Transfer(transfer), Op::ChannelDeposit(deposit)].into()); + let tx = RawMantleTx([Op::Transfer(transfer), Op::ChannelDeposit(deposit)].into()); let tx_hash = tx.hash(); let user_sig = api .sign_tx_with_zk(tx_hash, vec![funding_public_key]) diff --git a/core/benches/mantle_tx_components.rs b/core/benches/mantle_tx_components.rs index 834f0fb2c..99e1ba3d6 100644 --- a/core/benches/mantle_tx_components.rs +++ b/core/benches/mantle_tx_components.rs @@ -30,7 +30,7 @@ use logos_blockchain_core::{ transactions::{ codec::{decode_signed_mantle_tx, encode_signed_mantle_tx}, hash::TxHash, - mantle_tx::MantleTx, + mantle_tx::RawMantleTx, states::Unverified, }, }, @@ -54,9 +54,9 @@ const SIZES: &[usize] = &[ ]; // Helper fn to create an inscription `MantleTx`, no ledger inputs ot outputs. -fn make_inscription_tx(payload_size: usize) -> MantleTx { +fn make_inscription_tx(payload_size: usize) -> RawMantleTx { let signing_key = Ed25519Key::from_bytes(&[1; 32]); - MantleTx( + RawMantleTx( [Op::ChannelInscribe(InscriptionOp { channel_id: ChannelId::from([0xAA; 32]), inscription: Inscription::new_unchecked(vec![0xAB; payload_size]), @@ -107,7 +107,7 @@ fn bench_poseidon2_hash(bencher: Bencher, size: usize) { fn bench_blake2b_poseidon2_hash(bencher: Bencher, size: usize) { bencher .with_inputs(|| make_inscription_tx(size)) - .bench_values(|tx: MantleTx| { + .bench_values(|tx: RawMantleTx| { // Encoding is included here to compare fairly with the Poseidon2 hash function, // which includes it. let encoded = tx.encode(); @@ -156,7 +156,7 @@ fn bench_sign_c_mantle_tx_new_verify_ops_proofs_single_proof(bencher: Bencher, s let op_sig = signing_key.sign_payload(&txhash.as_signing_bytes()); (tx, op_sig) }) - .bench_values(|(tx, op_sig): (MantleTx, Ed25519Signature)| { + .bench_values(|(tx, op_sig): (RawMantleTx, Ed25519Signature)| { black_box(SignedMantleTx::new(tx, [OpProof::Ed25519Sig(op_sig)].into()).preverify()) }); } @@ -174,7 +174,7 @@ fn bench_sign_d_fully_empty(bencher: Bencher, size: usize) { let tx_hash = tx.hash(); (tx, tx_hash) }) - .bench_values(|(tx, tx_hash): (MantleTx, TxHash)| { + .bench_values(|(tx, tx_hash): (RawMantleTx, TxHash)| { let op_sig = signing_key.sign_payload(&tx_hash.as_signing_bytes()); black_box(SignedMantleTx::new(tx, [OpProof::Ed25519Sig(op_sig)].into()).preverify()) }); diff --git a/core/src/block/deser.rs b/core/src/block/deser.rs index 810800d7c..023519e41 100644 --- a/core/src/block/deser.rs +++ b/core/src/block/deser.rs @@ -5,10 +5,10 @@ mod tests { use crate::{ block::{Block, BlockTransactions, tests::create_proof}, - mantle::MantleTx, + mantle::RawMantleTx, }; - fn make_empty_block() -> Block { + fn make_empty_block() -> Block { let signing_key = Ed25519Key::from_bytes(&[0; 32]); Block::create( [0u8; 32].into(), @@ -24,7 +24,7 @@ mod tests { fn test_json_round_trip() { let block = make_empty_block(); let json = serde_json::to_string(&block).expect("JSON serialization should succeed"); - let restored: Block = + let restored: Block = serde_json::from_str(&json).expect("JSON deserialization should succeed"); assert_eq!(block.header().id(), restored.header().id()); assert_eq!(block.signature(), restored.signature()); @@ -64,7 +64,7 @@ mod tests { fn test_bincode_round_trip() { let block = make_empty_block(); let bytes = bincode::serialize(&block).expect("bincode serialization should succeed"); - let restored: Block = + let restored: Block = bincode::deserialize(&bytes).expect("bincode deserialization should succeed"); assert_eq!(block.header().id(), restored.header().id()); assert_eq!(block.signature(), restored.signature()); diff --git a/core/src/block/genesis.rs b/core/src/block/genesis.rs index 55f38c4df..64eaaf9f6 100644 --- a/core/src/block/genesis.rs +++ b/core/src/block/genesis.rs @@ -15,7 +15,7 @@ use crate::{ ops::{channel::inscribe::InscriptionOp, sdp::SDPDeclareOp, transfer::TransferOp}, transactions::{ GenesisTx, MAX_OPS_PER_TX, Ops, OpsProofs, VerificationError, genesis_tx, - mantle_tx::MantleTx, + mantle_tx::RawMantleTx, }, }, }; @@ -1250,7 +1250,7 @@ impl GenesisBlockBuilder { }) .expect("genesis transaction proofs are bounded"); } - let signed_tx = SignedMantleTx::new_trusted(MantleTx(capped_ops), ops_proofs); + let signed_tx = SignedMantleTx::new_trusted(RawMantleTx(capped_ops), ops_proofs); Ok(GenesisBlock::genesis(GenesisTx::from_tx(signed_tx)?)) } } @@ -1280,7 +1280,7 @@ mod tests { CryptarchiaParameter, GenesisTime, NoteId, ops::channel::{ChannelId, MsgId, inscribe::Inscription}, traits::genesis::GenesisTx as _, - transactions::states::Preverified, + transactions::{mantle_tx::MantleTx as _, states::Preverified}, }, sdp::{Locator, ProviderId, ServiceType}, }; @@ -1367,7 +1367,7 @@ mod tests { })) .expect("genesis transaction proofs are bounded"); - SignedMantleTx::new_trusted(MantleTx(Ops::new_unchecked(ops)), ops_proofs) + SignedMantleTx::new_trusted(RawMantleTx(Ops::new_unchecked(ops)), ops_proofs) } fn make_genesis_tx(extra_ops: Vec) -> GenesisTx { diff --git a/core/src/block/mod.rs b/core/src/block/mod.rs index a4fe3ddec..b44056b40 100644 --- a/core/src/block/mod.rs +++ b/core/src/block/mod.rs @@ -348,7 +348,7 @@ mod tests { ledger::{Note, Utxo}, ops::leader_claim::VoucherCm, traits::hashable, - transactions::{Ops, mantle_tx::MantleTx}, + transactions::{Ops, mantle_tx::RawMantleTx}, }, proofs::leader_proof::{LeaderPrivate, LeaderPublic}, }; @@ -412,8 +412,8 @@ mod tests { .expect("Proof generation should succeed") } - fn create_tx(count: usize) -> Vec { - iter::repeat_with(|| MantleTx(Ops::new_unchecked(vec![]))) + fn create_tx(count: usize) -> Vec { + iter::repeat_with(|| RawMantleTx(Ops::new_unchecked(vec![]))) .take(count) .collect() } @@ -443,7 +443,7 @@ mod tests { let parent_block = [0u8; 32].into(); let slot = Slot::from(42u64); let proof_of_leadership = create_proof(); - let transactions = BlockTransactions::::empty(); + let transactions = BlockTransactions::::empty(); let valid_signing_key = Ed25519Key::from_bytes(&[0; 32]); let valid_block = Block::create( @@ -483,7 +483,7 @@ mod tests { let signing_key = Ed25519Key::from_bytes(&[0; 32]); let transactions = BlockTransactions::empty(); - let _valid_block: Block = Block::create( + let _valid_block: Block = Block::create( parent_block, slot, proof_of_leadership.clone(), @@ -493,7 +493,7 @@ mod tests { .expect("Valid block should be created"); let transactions = BlockTransactions::try_from(create_tx(MAX_BLOCK_TRANSACTIONS)).unwrap(); - let _valid_block: Block = Block::create( + let _valid_block: Block = Block::create( parent_block, slot, proof_of_leadership, @@ -503,7 +503,7 @@ mod tests { .expect("Valid block should be created"); let invalid_transaction_inputs_result = - BlockTransactions::::try_from(create_tx(MAX_BLOCK_TRANSACTIONS + 1)); + BlockTransactions::::try_from(create_tx(MAX_BLOCK_TRANSACTIONS + 1)); assert!(invalid_transaction_inputs_result.is_err()); let error = invalid_transaction_inputs_result.unwrap_err(); @@ -550,7 +550,7 @@ mod tests { parent_block, Slot::from(42u64), create_proof(), - BlockTransactions::::try_from(create_tx(MAX_BLOCK_TRANSACTIONS)).unwrap(), + BlockTransactions::::try_from(create_tx(MAX_BLOCK_TRANSACTIONS)).unwrap(), &signing_key, ) .unwrap(); @@ -582,7 +582,7 @@ mod tests { [0u8; 32].into(), Slot::from(42u64), create_proof(), - BlockTransactions::::empty(), + BlockTransactions::::empty(), &signing_key, ) .unwrap() @@ -633,7 +633,7 @@ mod tests { let signing_key = Ed25519Key::from_bytes(&[0; 32]); let transactions = BlockTransactions::empty(); - let _valid_block: Block = Block::create( + let _valid_block: Block = Block::create( parent_block, slot, proof_of_leadership.clone(), @@ -678,8 +678,11 @@ mod tests { #[test] fn global_block_limits_are_reflective_in_block_transaction_bounds() { - assert_eq!(BlockTransactions::::MIN, 0); - assert_eq!(BlockTransactions::::MAX, MAX_BLOCK_TRANSACTIONS); + assert_eq!(BlockTransactions::::MIN, 0); + assert_eq!( + BlockTransactions::::MAX, + MAX_BLOCK_TRANSACTIONS + ); } #[test] @@ -688,7 +691,7 @@ mod tests { let proof = create_proof(); // Build a syntactically valid non-genesis block first. - let txs = BlockTransactions::::empty(); + let txs = BlockTransactions::::empty(); let key = Ed25519Key::from_bytes(&[0; 32]); let block_result = Block::create(parent_block, Slot::from(0u64), proof, txs, &key).unwrap_err(); @@ -710,7 +713,7 @@ mod tests { parent_block, Slot::from(1u64), proof.clone(), - BlockTransactions::::empty(), + BlockTransactions::::empty(), &key, ) .expect("valid non-genesis block"); @@ -729,7 +732,7 @@ mod tests { let err = Block::reconstruct( genesis_header, - BlockTransactions::::empty(), + BlockTransactions::::empty(), genesis_signature, ) .expect_err("genesis slot must be rejected by reconstruct path"); diff --git a/core/src/header/mod.rs b/core/src/header/mod.rs index 6b8bfe4ee..7586c9f4c 100644 --- a/core/src/header/mod.rs +++ b/core/src/header/mod.rs @@ -319,7 +319,7 @@ mod block_root_test_vectors { use super::*; use crate::{ mantle::{ - MantleTx, Note, Op, + Note, Op, RawMantleTx, channel::{SlotTimeframe, SlotTimeout}, ledger::{Inputs, NoteId, Outputs}, ops::{ @@ -351,14 +351,14 @@ mod block_root_test_vectors { ZkPublicKey::from(Fr::from(seed)) } - fn tx(op: Op) -> MantleTx { - MantleTx(Ops::new_unchecked(vec![op])) + fn tx(op: Op) -> RawMantleTx { + RawMantleTx(Ops::new_unchecked(vec![op])) } /// Builds one transaction per distinct mantle operation kind, each carrying /// a single operation. The instances mirror those used by the `OpId` test /// vectors so the two vector sets stay consistent. - fn one_tx_per_op() -> Vec<(&'static str, MantleTx)> { + fn one_tx_per_op() -> Vec<(&'static str, RawMantleTx)> { let activity = ActivityProof { epoch: Epoch::new(10), signing_key: ed25519_pk(1), @@ -487,7 +487,7 @@ mod block_root_test_vectors { ); // 1. Empty block: no transactions. - let empty: Vec = vec![]; + let empty: Vec = vec![]; let empty_root = merkle::calculate_block_root(&empty); println!("================================================================"); println!("vector 1 : empty block (0 transactions)"); @@ -495,7 +495,7 @@ mod block_root_test_vectors { // 2. One transaction per operation kind (one op each). let txs_with_names = one_tx_per_op(); - let txs: Vec = txs_with_names.iter().map(|(_, tx)| tx.clone()).collect(); + let txs: Vec = txs_with_names.iter().map(|(_, tx)| tx.clone()).collect(); println!("================================================================"); println!( "vector 2 : one transaction per op kind ({} transactions)", diff --git a/core/src/mantle/channel.rs b/core/src/mantle/channel.rs index 531fc0e25..24d5ff114 100644 --- a/core/src/mantle/channel.rs +++ b/core/src/mantle/channel.rs @@ -11,7 +11,7 @@ use crate::{ mantle::{ NoteId, channel_notes::{self, ChannelNotes}, - ledger::{self, Operation as _}, + ledger::{self, ExecutableOperation as _}, ops::channel::{ ChannelId, ChannelKeyIndex, MsgId, config::Keys, @@ -167,11 +167,11 @@ impl<'a> IntoIterator for &'a Channels { impl Channels { pub fn from_genesis(op: &InscriptionOp) -> Result<(Self, Vec), Error> { - let (ctx, events) = op.execute(InscriptionExecutionContext { + let (context, events) = op.execute(InscriptionExecutionContext { channels: Self::default(), block_slot: Slot::default(), })?; - Ok((ctx.channels, events)) + Ok((context.channels, events)) } #[must_use] diff --git a/core/src/mantle/fixtures/tx.rs b/core/src/mantle/fixtures/tx.rs index 257698420..74c9d52aa 100644 --- a/core/src/mantle/fixtures/tx.rs +++ b/core/src/mantle/fixtures/tx.rs @@ -3,10 +3,10 @@ use lb_codec::codec_fixtures; use lb_groth16::Fr; use crate::mantle::{ - MantleTx, NoteId, Op, ledger::Outputs, ops::transfer::TransferOp, transactions::Ops, + NoteId, Op, RawMantleTx, ledger::Outputs, ops::transfer::TransferOp, transactions::Ops, }; -codec_fixtures!(MantleTx, +codec_fixtures!(RawMantleTx, Self(Ops::empty()) => "00", Self([Op::Transfer(TransferOp { inputs: [NoteId(Fr::ZERO)].into(), outputs: Outputs::empty() })].into()) => "010001000000000000000000000000000000000000000000000000000000000000000000" ); diff --git a/core/src/mantle/ledger.rs b/core/src/mantle/ledger.rs index 7bb6f2b00..47291379f 100644 --- a/core/src/mantle/ledger.rs +++ b/core/src/mantle/ledger.rs @@ -39,29 +39,44 @@ pub type BoundedUtxos = UpperBoundedVec; pub type BoundedInputs = UpperBoundedVec; pub type BoundedOutputs = UpperBoundedVec; +pub mod verification_mode { + pub trait VerificationMode {} + + pub struct GenesisMode; + impl VerificationMode for GenesisMode {} + + pub struct StandardMode; + impl VerificationMode for StandardMode {} +} + // TODO: Specific proof type check? -pub trait Operation { - type PreverificationContext<'a> - where - Self: 'a; - type ExecutionContext<'a> - where - Self: 'a; +pub trait VerifiableOperation { + type PreverificationContext<'a>; + type VerificationContext<'a>; + type Error; - type VerificationError; - type ExecutionError; + fn preverify(&self, context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error>; - fn preverify( + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error>; +} + +pub trait ExecutableOperation { + type Context<'a>; + type Error; + + fn execute<'a>( &self, - context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError>; + context: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error>; +} - fn verify(&self, context: &VerificationContext) -> Result<(), Self::VerificationError>; - - fn execute( - &self, - context: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError>; +pub trait Operation: + VerifiableOperation + ExecutableOperation +{ +} +impl + ExecutableOperation, Mode: verification_mode::VerificationMode> + Operation for T +{ } pub type Utxos = UtxoTree; diff --git a/core/src/mantle/mod.rs b/core/src/mantle/mod.rs index 95f5dcea6..18a389ef1 100644 --- a/core/src/mantle/mod.rs +++ b/core/src/mantle/mod.rs @@ -13,7 +13,7 @@ pub use gas::{GasCalculator, GasConstants}; pub use ledger::{Note, NoteId, Utxo, Value}; pub use ops::{Op, OpProof}; pub use transactions::{ - CryptarchiaParameter, GenesisTime, SignedMantleTx, hash::TxHash, mantle_tx::MantleTx, + CryptarchiaParameter, GenesisTime, SignedMantleTx, hash::TxHash, mantle_tx::RawMantleTx, }; pub use crate::mantle::transactions::VerificationError; diff --git a/core/src/mantle/ops/channel/channel_transfer.rs b/core/src/mantle/ops/channel/channel_transfer.rs index 42200da0a..f0471aa12 100644 --- a/core/src/mantle/ops/channel/channel_transfer.rs +++ b/core/src/mantle/ops/channel/channel_transfer.rs @@ -6,7 +6,10 @@ use crate::{ mantle::{ TxHash, channel::{Channels, Error}, - ledger::{Inputs, Operation, Outputs, Utxo, Utxos}, + ledger::{ + ExecutableOperation, Inputs, Outputs, Utxo, Utxos, VerifiableOperation, + verification_mode, + }, ops::{ OpId, channel::{ChannelId, verification::verify_channel_multi_sig}, @@ -53,44 +56,32 @@ pub struct ChannelTransferExecutionContext { pub tx_hash: TxHash, } -impl Operation> for ChannelTransferOp { - type PreverificationContext<'a> - = () - where - Self: 'a; - type ExecutionContext<'a> - = ChannelTransferExecutionContext - where - Self: 'a; - type VerificationError = Error; - type ExecutionError = Error; +impl VerifiableOperation for ChannelTransferOp { + type PreverificationContext<'a> = (); + type VerificationContext<'a> = ChannelTransferValidationContext<'a>; + type Error = Error; - fn preverify( - &self, - _context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, _context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { // Check that the outputs are valid self.outputs.validate()?; Ok(()) } - fn verify( - &self, - ctx: &ChannelTransferValidationContext<'_>, - ) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { verify_channel_multi_sig( &self.channel_id, - ctx.proof, - ctx.tx_hash_view.as_bytes(), - ctx.helper, - ctx.op_index, + context.proof, + context.tx_hash_view.as_bytes(), + context.helper, + context.op_index, ) .map_err(|_error| Error::InvalidSignature)?; // FIXME: Discards error details // Check that the channel exist let channel = - ctx.channels + context + .channels .channel_state(&self.channel_id) .ok_or(Error::ChannelNotFound { channel_id: self.channel_id, @@ -98,21 +89,21 @@ impl Operation> for ChannelTransferOp { // Check that the inputs are valid and belong to the channel self.inputs.validate_in_channel( - ctx.locked_notes, - ctx.channels, + context.locked_notes, + context.channels, &self.channel_id, - ctx.utxos, + context.utxos, )?; // Check the balance is preserved - let input_amount = self.inputs.amount(ctx.utxos)?; + let input_amount = self.inputs.amount(context.utxos)?; let output_amount = self.outputs.amount()?; if input_amount != output_amount { return Err(Error::UnbalancedTransfer); } // Check there is enough signatures - let signatures = ctx.proof.signatures(); + let signatures = context.proof.signatures(); if signatures.len() != channel.transfer_threshold as usize { return Err(Error::ThresholdUnmet { channel_id: self.channel_id, @@ -127,7 +118,7 @@ impl Operation> for ChannelTransferOp { .accredited_keys .get(sig.channel_key_index as usize) .ok_or(Error::InvalidSignature)? - .verify(ctx.tx_hash_view.as_bytes(), &sig.signature) + .verify(context.tx_hash_view.as_bytes(), &sig.signature) .is_err() { return Err(Error::InvalidSignature); @@ -136,27 +127,32 @@ impl Operation> for ChannelTransferOp { Ok(()) } +} - fn execute( +impl ExecutableOperation for ChannelTransferOp { + type Context<'a> = ChannelTransferExecutionContext; + type Error = Error; + + fn execute<'a>( &self, - mut ctx: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { + mut context: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error> { // Remove the inputs from the ledger and from the channel. - ctx.utxos = self.inputs.execute(ctx.utxos)?; + context.utxos = self.inputs.execute(context.utxos)?; for note_id in self.inputs.iter() { - ctx.channels = ctx + context.channels = context .channels .unregister_channel_note(note_id, &self.channel_id)?; } // Add the outputs to the ledger and register them as channel notes. - ctx.utxos = self.outputs.execute(ctx.utxos, self); + context.utxos = self.outputs.execute(context.utxos, self); for utxo in self.utxos() { - ctx.channels = ctx + context.channels = context .channels .register_channel_note(&utxo.id(), &self.channel_id)?; } - Ok((ctx, Vec::new())) + Ok((context, Vec::new())) } } diff --git a/core/src/mantle/ops/channel/config.rs b/core/src/mantle/ops/channel/config.rs index 5f69ed936..0207fe269 100644 --- a/core/src/mantle/ops/channel/config.rs +++ b/core/src/mantle/ops/channel/config.rs @@ -9,7 +9,7 @@ use crate::{ events::TxEvent, mantle::{ channel::{ChannelState, Channels, Error, SlotTimeframe, SlotTimeout}, - ledger::Operation, + ledger::{ExecutableOperation, VerifiableOperation, verification_mode}, transactions::hash::TxHashView, }, proofs::channel_multi_sig_proof::ChannelMultiSigProof, @@ -48,22 +48,12 @@ pub struct ChannelConfigExecutionContext { pub block_slot: Slot, } -impl Operation> for ChannelConfigOp { - type PreverificationContext<'a> - = () - where - Self: 'a; - type ExecutionContext<'a> - = ChannelConfigExecutionContext - where - Self: 'a; - type VerificationError = Error; - type ExecutionError = Error; +impl VerifiableOperation for ChannelConfigOp { + type PreverificationContext<'a> = (); + type VerificationContext<'a> = ChannelConfigValidationContext<'a>; + type Error = Error; - fn preverify( - &self, - _context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, _context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { // Check config is well-formed if self.configuration_threshold == 0 || self.transfer_threshold == 0 || self.keys.is_empty() { @@ -73,18 +63,18 @@ impl Operation> for ChannelConfigOp { Ok(()) } - fn verify(&self, ctx: &ChannelConfigValidationContext<'_>) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { // Check that the indexes are unique and there is the same number of proof and // index. This is enforced by the proof structure that enforces it. - if let Some(channel) = ctx.channels.channel_state(&self.channel) { + if let Some(channel) = context.channels.channel_state(&self.channel) { // Check there is enough signatures - let signatures = ctx.proof.signatures(); + let signatures = context.proof.signatures(); if signatures.len() != channel.configuration_threshold as usize { return Err(Error::ThresholdUnmet { channel_id: self.channel, threshold: channel.configuration_threshold, - actual: ctx.proof.signatures().len(), + actual: context.proof.signatures().len(), }); } @@ -98,7 +88,7 @@ impl Operation> for ChannelConfigOp { sequencers: channel.accredited_keys.len(), index: signature.channel_key_index, })? - .verify(ctx.tx_hash_view.as_bytes(), &signature.signature) + .verify(context.tx_hash_view.as_bytes(), &signature.signature) .is_err() { return Err(Error::InvalidSignature); @@ -108,25 +98,30 @@ impl Operation> for ChannelConfigOp { Ok(()) } +} - fn execute( +impl ExecutableOperation for ChannelConfigOp { + type Context<'a> = ChannelConfigExecutionContext; + type Error = Error; + + fn execute<'a>( &self, - mut ctx: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { + mut context: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error> { let channel = ChannelState { accredited_keys: self.keys.clone().into(), configuration_threshold: self.configuration_threshold, tip_message: self.id(), - tip_slot: ctx.block_slot, + tip_slot: context.block_slot, tip_sequencer: 0, - tip_sequencer_starting_slot: ctx.block_slot, + tip_sequencer_starting_slot: context.block_slot, posting_timeframe: self.posting_timeframe.clone(), transfer_threshold: self.transfer_threshold, posting_timeout: self.posting_timeout.clone(), }; // if the channel doesn't exist, create it otherwise just update the config - ctx.channels = ctx.channels.set_channel_state(&self.channel, channel); - Ok((ctx, Vec::new())) + context.channels = context.channels.set_channel_state(&self.channel, channel); + Ok((context, Vec::new())) } } diff --git a/core/src/mantle/ops/channel/deposit.rs b/core/src/mantle/ops/channel/deposit.rs index a421142e9..b12989402 100644 --- a/core/src/mantle/ops/channel/deposit.rs +++ b/core/src/mantle/ops/channel/deposit.rs @@ -7,7 +7,10 @@ use crate::{ events::{DepositRecreatedNotes, TxEvent, TxEventPayload}, mantle::{ channel::{Channels, Error}, - ledger::{Inputs, InputsError, Operation, Outputs, Utxos}, + ledger::{ + ExecutableOperation, Inputs, InputsError, Outputs, Utxos, VerifiableOperation, + verification_mode, + }, ops::{OpId, channel::ChannelId}, transactions::hash::{TxHash, TxHashView}, }, @@ -62,70 +65,68 @@ pub struct DepositExecutionContext { pub tx_hash: TxHash, } -impl Operation> for DepositOp { - type PreverificationContext<'a> - = () - where - Self: 'a; - type ExecutionContext<'a> - = DepositExecutionContext - where - Self: 'a; - type VerificationError = Error; - type ExecutionError = Error; +impl VerifiableOperation for DepositOp { + type PreverificationContext<'a> = (); + type VerificationContext<'a> = DepositValidationContext<'a>; + type Error = Error; - fn preverify( - &self, - _context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, _context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { Ok(()) } - fn verify(&self, ctx: &DepositValidationContext<'_>) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { // Check that the channel exist - if !ctx.channels.contains_channel(&self.channel_id) { + if !context.channels.contains_channel(&self.channel_id) { return Err(Error::ChannelNotFound { channel_id: self.channel_id, }); } // Check that inputs are spendable and not already channel notes - self.inputs - .validate_not_in_channel(ctx.locked_notes, ctx.channels, ctx.utxos)?; + self.inputs.validate_not_in_channel( + context.locked_notes, + context.channels, + context.utxos, + )?; // Check the signature - let public_keys = self.inputs.get_pk(ctx.utxos)?; - if !ZkPublicKey::verify_multi(&public_keys, ctx.tx_hash_view.as_fr(), ctx.proof) { + let public_keys = self.inputs.get_pk(context.utxos)?; + if !ZkPublicKey::verify_multi(&public_keys, context.tx_hash_view.as_fr(), context.proof) { return Err(Error::InvalidSignature); } Ok(()) } +} - fn execute( +impl ExecutableOperation for DepositOp { + type Context<'a> = DepositExecutionContext; + type Error = Error; + + fn execute<'a>( &self, - mut ctx: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { + mut context: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error> { // Get the amount deposited for the event payload - let amount_deposited = self.inputs.amount(&ctx.utxos)?; - let outputs = self.outputs(&ctx.utxos)?; + let amount_deposited = self.inputs.amount(&context.utxos)?; + let outputs = self.outputs(&context.utxos)?; // Remove the inputs from the ledger. - ctx.utxos = self.inputs.execute(ctx.utxos)?; + context.utxos = self.inputs.execute(context.utxos)?; // Add the re-created notes to the ledger and register them as channel // notes. - ctx.utxos = outputs.execute(ctx.utxos, self); + context.utxos = outputs.execute(context.utxos, self); let mut note_ids = DepositRecreatedNotes::default(); for utxo in outputs.utxos(self) { - ctx.channels = ctx + context.channels = context .channels .register_channel_note(&utxo.id(), &self.channel_id)?; note_ids.try_push(utxo.id()).map_err(InputsError::from)?; } let events = std::iter::once(TxEvent::new( - ctx.tx_hash, + context.tx_hash, self.op_id(), TxEventPayload::Deposit { channel_id: self.channel_id, @@ -136,6 +137,6 @@ impl Operation> for DepositOp { )) .collect(); - Ok((ctx, events)) + Ok((context, events)) } } diff --git a/core/src/mantle/ops/channel/inscribe.rs b/core/src/mantle/ops/channel/inscribe.rs index cd9651fd7..885cae048 100644 --- a/core/src/mantle/ops/channel/inscribe.rs +++ b/core/src/mantle/ops/channel/inscribe.rs @@ -13,7 +13,7 @@ use crate::{ events::TxEvent, mantle::{ channel::{ChannelState, Channels, Error}, - ledger::Operation, + ledger::{ExecutableOperation, VerifiableOperation, verification_mode}, ops::channel::config::Keys, transactions::hash::TxHashView, }, @@ -60,22 +60,12 @@ pub struct InscriptionExecutionContext { pub block_slot: Slot, } -impl Operation> for InscriptionOp { - type PreverificationContext<'a> - = InscriptionPreverificationContext<'a> - where - Self: 'a; - type ExecutionContext<'a> - = InscriptionExecutionContext - where - Self: 'a; - type VerificationError = Error; - type ExecutionError = Error; +impl VerifiableOperation for InscriptionOp { + type PreverificationContext<'a> = InscriptionPreverificationContext<'a>; + type VerificationContext<'a> = InscriptionValidationContext<'a>; + type Error = Error; - fn preverify( - &self, - context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { // Check the signature self.signer .verify(context.tx_hash_view.as_bytes(), context.proof) @@ -84,10 +74,10 @@ impl Operation> for InscriptionOp { Ok(()) } - fn verify(&self, ctx: &InscriptionValidationContext<'_>) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { // Check if the channel exist otherwise the inscription is valid only if and // only if parent == ZERO - if let Some(channel) = ctx.channels.channel_state(&self.channel_id) { + if let Some(channel) = context.channels.channel_state(&self.channel_id) { // Check the parent corresponds to the payload if self.parent != channel.tip_message { return Err(Error::InvalidParent { @@ -99,7 +89,7 @@ impl Operation> for InscriptionOp { // Check that the signer is the authorized one if self.signer - != channel.accredited_keys[channel.round_robin(ctx.block_slot).0 as usize] + != channel.accredited_keys[channel.round_robin(context.block_slot).0 as usize] { return Err(Error::UnauthorizedSigner { channel_id: self.channel_id, @@ -117,13 +107,18 @@ impl Operation> for InscriptionOp { Ok(()) } +} - fn execute( +impl ExecutableOperation for InscriptionOp { + type Context<'a> = InscriptionExecutionContext; + type Error = Error; + + fn execute<'a>( &self, - mut ctx: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { + mut context: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error> { // if the channel doesn't exist, create it - let channel = ctx + let channel = context .channels .channel_state(&self.channel_id) .cloned() @@ -131,9 +126,9 @@ impl Operation> for InscriptionOp { accredited_keys: Keys::from(self.signer).into(), configuration_threshold: 1, tip_message: MsgId::root(), - tip_slot: ctx.block_slot, + tip_slot: context.block_slot, tip_sequencer: 0, - tip_sequencer_starting_slot: ctx.block_slot, + tip_sequencer_starting_slot: context.block_slot, posting_timeframe: 0.into(), transfer_threshold: crate::mantle::channel::DEFAULT_TRANSFER_THRESHOLD, posting_timeout: 0.into(), @@ -141,17 +136,19 @@ impl Operation> for InscriptionOp { // Update the channel sequencer, its starting slot, the tip message and the tip // slot - let (new_sequencer, new_starting_slot) = channel.round_robin(ctx.block_slot); + let (new_sequencer, new_starting_slot) = channel.round_robin(context.block_slot); let updated = ChannelState { tip_message: self.id(), accredited_keys: Arc::clone(&channel.accredited_keys), tip_sequencer: new_sequencer, tip_sequencer_starting_slot: new_starting_slot, - tip_slot: ctx.block_slot, + tip_slot: context.block_slot, ..channel }; - ctx.channels = ctx.channels.set_channel_state(&self.channel_id, updated); - Ok((ctx, Vec::new())) + context.channels = context + .channels + .set_channel_state(&self.channel_id, updated); + Ok((context, Vec::new())) } } diff --git a/core/src/mantle/ops/channel/withdraw.rs b/core/src/mantle/ops/channel/withdraw.rs index de3343587..ab7e497f4 100644 --- a/core/src/mantle/ops/channel/withdraw.rs +++ b/core/src/mantle/ops/channel/withdraw.rs @@ -6,7 +6,7 @@ use crate::{ mantle::{ TxHash, channel::{Channels, Error}, - ledger::{Inputs, Operation, Utxos}, + ledger::{ExecutableOperation, Inputs, Utxos, VerifiableOperation, verification_mode}, ops::{ OpId, channel::{ChannelId, verification::verify_channel_multi_sig}, @@ -45,38 +45,29 @@ pub struct WithdrawExecutionContext { pub tx_hash: TxHash, } -impl Operation> for ChannelWithdrawOp { - type PreverificationContext<'a> - = () - where - Self: 'a; - type ExecutionContext<'a> - = WithdrawExecutionContext - where - Self: 'a; - type VerificationError = Error; - type ExecutionError = Error; +impl VerifiableOperation for ChannelWithdrawOp { + type PreverificationContext<'a> = (); + type VerificationContext<'a> = WithdrawValidationContext<'a>; + type Error = Error; - fn preverify( - &self, - _context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, _context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { Ok(()) } - fn verify(&self, ctx: &WithdrawValidationContext<'_>) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { verify_channel_multi_sig( &self.channel_id, - ctx.proof, - ctx.tx_hash_view.as_bytes(), - ctx.helper, - ctx.op_index, + context.proof, + context.tx_hash_view.as_bytes(), + context.helper, + context.op_index, ) .map_err(|_error| Error::InvalidSignature)?; // FIXME: Discards error details // Check that the channel exists let channel = - ctx.channels + context + .channels .channel_state(&self.channel_id) .ok_or(Error::ChannelNotFound { channel_id: self.channel_id, @@ -84,14 +75,14 @@ impl Operation> for ChannelWithdrawOp { // Check that the inputs are valid and belong to the channel self.inputs.validate_in_channel( - ctx.locked_notes, - ctx.channels, + context.locked_notes, + context.channels, &self.channel_id, - ctx.utxos, + context.utxos, )?; // Check there is enough signatures - let signatures = ctx.proof.signatures(); + let signatures = context.proof.signatures(); if signatures.len() != channel.transfer_threshold as usize { return Err(Error::ThresholdUnmet { channel_id: self.channel_id, @@ -106,7 +97,7 @@ impl Operation> for ChannelWithdrawOp { .accredited_keys .get(sig.channel_key_index as usize) .ok_or(Error::InvalidSignature)? - .verify(ctx.tx_hash_view.as_bytes(), &sig.signature) + .verify(context.tx_hash_view.as_bytes(), &sig.signature) .is_err() { return Err(Error::InvalidSignature); @@ -115,19 +106,24 @@ impl Operation> for ChannelWithdrawOp { Ok(()) } +} - fn execute( +impl ExecutableOperation for ChannelWithdrawOp { + type Context<'a> = WithdrawExecutionContext; + type Error = Error; + + fn execute<'a>( &self, - mut ctx: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { + mut context: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error> { // Release the inputs from the channel. The notes keep their NoteId, // value and ZkPublicKey and stay in the ledger as regular notes. for note_id in self.inputs.iter() { - ctx.channels = ctx + context.channels = context .channels .unregister_channel_note(note_id, &self.channel_id)?; } - Ok((ctx, Vec::new())) + Ok((context, Vec::new())) } } diff --git a/core/src/mantle/ops/leader_claim.rs b/core/src/mantle/ops/leader_claim.rs index 1e83cdc64..a11780dbb 100644 --- a/core/src/mantle/ops/leader_claim.rs +++ b/core/src/mantle/ops/leader_claim.rs @@ -13,7 +13,7 @@ use crate::{ events::{TxEvent, TxEventPayload}, mantle::{ Note, Utxo, Value, - ledger::{Operation, Utxos}, + ledger::{ExecutableOperation, Utxos, VerifiableOperation, verification_mode}, ops::OpId, transactions::hash::{TxHash, TxHashView}, }, @@ -192,22 +192,12 @@ pub struct LeaderClaimExecutionContext { pub tx_hash: TxHash, } -impl Operation> for LeaderClaimOp { - type PreverificationContext<'a> - = LeaderClaimPreverificationContext<'a> - where - Self: 'a; - type ExecutionContext<'a> - = LeaderClaimExecutionContext - where - Self: 'a; - type VerificationError = LeaderClaimError; - type ExecutionError = LeaderClaimError; +impl VerifiableOperation for LeaderClaimOp { + type PreverificationContext<'a> = LeaderClaimPreverificationContext<'a>; + type VerificationContext<'a> = LeaderClaimVerificationContext<'a>; + type Error = LeaderClaimError; - fn preverify( - &self, - context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { let is_verified = context.proof.verify(&LeaderClaimPublic { voucher_nullifier: self.voucher_nullifier.into(), voucher_root: self.rewards_root.into(), @@ -221,46 +211,51 @@ impl Operation> for LeaderClaimOp { } } - fn verify(&self, ctx: &LeaderClaimVerificationContext<'_>) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { // Check that the nullifier isn't in the set - if ctx.nullifiers.contains(&self.voucher_nullifier) { + if context.nullifiers.contains(&self.voucher_nullifier) { return Err(LeaderClaimError::DuplicatedVoucherNullifier); } // Check that the voucher root is the same as in the ledger - if ctx.claimable_vouchers_root != &self.rewards_root { + if context.claimable_vouchers_root != &self.rewards_root { return Err(LeaderClaimError::VouchersRootMismatch); } // Check the proof of claim - if !ctx.proof.verify(&LeaderClaimPublic { + if !context.proof.verify(&LeaderClaimPublic { voucher_nullifier: self.voucher_nullifier.into(), - voucher_root: ctx.claimable_vouchers_root.0, - mantle_tx_hash: *ctx.tx_hash_view.as_fr(), + voucher_root: context.claimable_vouchers_root.0, + mantle_tx_hash: *context.tx_hash_view.as_fr(), }) { return Err(LeaderClaimError::InvalidPoC); } Ok(()) } +} - fn execute( +impl ExecutableOperation for LeaderClaimOp { + type Context<'a> = LeaderClaimExecutionContext; + type Error = LeaderClaimError; + + fn execute<'a>( &self, - mut ctx: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { + mut context: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error> { // Add the nullifier to the nullifier set - ctx.nullifiers = ctx.nullifiers.insert(self.voucher_nullifier, ()).0; + context.nullifiers = context.nullifiers.insert(self.voucher_nullifier, ()).0; // Distribute the reward - let utxo = self.utxo(ctx.reward_amount); - ctx.utxos = ctx.utxos.insert(utxo.id(), utxo).0; + let utxo = self.utxo(context.reward_amount); + context.utxos = context.utxos.insert(utxo.id(), utxo).0; // Remove the distributed rewards from the pool - ctx.claimable_rewards -= ctx.reward_amount; - let tx_hash = ctx.tx_hash; + context.claimable_rewards -= context.reward_amount; + let tx_hash = context.tx_hash; Ok(( - ctx, + context, vec![TxEvent::new( tx_hash, self.op_id(), @@ -309,14 +304,14 @@ mod tests { }; let nullifiers = VoucherNullifiers::new(); let tx_hash_view = TxHashView::from(tx_hash); - let ctx = LeaderClaimVerificationContext { + let context = LeaderClaimVerificationContext { nullifiers: &nullifiers, claimable_vouchers_root: &voucher_root, proof: &proof, tx_hash_view: &tx_hash_view, }; - assert_eq!(op.verify(&ctx), Ok(())); + assert_eq!(op.verify(&context), Ok(())); } #[test] @@ -331,7 +326,7 @@ mod tests { pk, }; - let (ctx, events) = op + let (context, events) = op .execute(LeaderClaimExecutionContext { nullifiers: VoucherNullifiers::new(), reward_amount, @@ -341,10 +336,10 @@ mod tests { }) .expect("leader claim execution should succeed"); - assert!(ctx.nullifiers.contains(&op.voucher_nullifier)); - assert_eq!(ctx.claimable_rewards, 62); + assert!(context.nullifiers.contains(&op.voucher_nullifier)); + assert_eq!(context.claimable_rewards, 62); assert_eq!( - ctx.utxos.get(&op.utxo(reward_amount).id()), + context.utxos.get(&op.utxo(reward_amount).id()), Some(op.utxo(reward_amount)) ); @@ -410,7 +405,7 @@ mod tests { }; let nullifiers = VoucherNullifiers::new(); let tx_hash_view = TxHashView::from(tx_hash); - let ctx = LeaderClaimVerificationContext { + let context = LeaderClaimVerificationContext { nullifiers: &nullifiers, claimable_vouchers_root: &voucher_root, proof: &proof, @@ -420,7 +415,7 @@ mod tests { // The proof is verified against `op.voucher_nullifier`, which does not // match the proven voucher -> rejected. A voucher cannot be claimed under // a substituted nullifier. - assert_eq!(op.verify(&ctx), Err(LeaderClaimError::InvalidPoC)); + assert_eq!(op.verify(&context), Err(LeaderClaimError::InvalidPoC)); } fn nullifier(secret: u64) -> VoucherNullifier { diff --git a/core/src/mantle/ops/mod.rs b/core/src/mantle/ops/mod.rs index ebef6eb86..6937882ce 100644 --- a/core/src/mantle/ops/mod.rs +++ b/core/src/mantle/ops/mod.rs @@ -297,7 +297,7 @@ mod mantle_test_vectors { use super::*; use crate::{ mantle::{ - MantleTx, Note, + Note, RawMantleTx, channel::{SlotTimeframe, SlotTimeout}, ledger::{Inputs, NoteId, Outputs}, ops::channel::{ChannelId, MsgId, config::Keys, deposit::Metadata}, @@ -430,7 +430,7 @@ mod mantle_test_vectors { println!(); } - fn print_tx_vector(label: &str, tx: &MantleTx) { + fn print_tx_vector(label: &str, tx: &RawMantleTx) { let payload = tx.encode(); let tx_hash = tx_hash_from_payload(&payload); // The hand-rolled computation must match the production `hash()`. @@ -476,12 +476,12 @@ mod mantle_test_vectors { fn generate_mantle_tx_hash_test_vectors() { println!(); // Empty transaction (zero operations). - print_tx_vector("empty (0 ops)", &MantleTx(Ops::new_unchecked(vec![]))); + print_tx_vector("empty (0 ops)", &RawMantleTx(Ops::new_unchecked(vec![]))); // Transaction holding one of every operation. print_tx_vector( "one of each operation (9 ops)", - &MantleTx(Ops::new_unchecked(sample_ops())), + &RawMantleTx(Ops::new_unchecked(sample_ops())), ); } } diff --git a/core/src/mantle/ops/sdp/active.rs b/core/src/mantle/ops/sdp/active.rs index 06318f8c4..e0ccd44f8 100644 --- a/core/src/mantle/ops/sdp/active.rs +++ b/core/src/mantle/ops/sdp/active.rs @@ -7,7 +7,7 @@ use super::{SDPActiveOp, SdpError}; use crate::{ events::TxEvent, mantle::{ - ledger::{Declarations, Operation}, + ledger::{Declarations, ExecutableOperation, VerifiableOperation, verification_mode}, transactions::hash::TxHashView, }, }; @@ -26,35 +26,25 @@ pub struct SDPActiveExecutionContext { pub declarations: Declarations, } -impl Operation> for SDPActiveOp { - type PreverificationContext<'a> - = () - where - Self: 'a; - type ExecutionContext<'a> - = SDPActiveExecutionContext - where - Self: 'a; - type VerificationError = SdpError; - type ExecutionError = SdpError; +impl VerifiableOperation for SDPActiveOp { + type PreverificationContext<'a> = (); + type VerificationContext<'a> = SDPActiveValidationContext<'a>; + type Error = SdpError; - fn preverify( - &self, - _context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, _context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { Ok(()) } - fn verify(&self, ctx: &SDPActiveValidationContext<'_>) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { // Check the declaration exists - let Some(declaration) = ctx.declarations.get(&self.declaration_id) else { + let Some(declaration) = context.declarations.get(&self.declaration_id) else { return Err(SdpError::DeclarationNotFound(self.declaration_id)); }; // Check the declaration hasn't been withdrawn // (Return error if `scheduled_withdrawal_epoch` epoch has passed) if let Some(withdraw_at) = declaration.withdraw_at - && withdraw_at <= ctx.epoch + && withdraw_at <= context.epoch { return Err(SdpError::DeclarationWithdrawn { declaration_id: self.declaration_id, @@ -71,26 +61,35 @@ impl Operation> for SDPActiveOp { } // Check the signature over the `zk_id` - if !ZkPublicKey::verify_multi(&[declaration.zk_id], ctx.tx_hash_view.as_fr(), ctx.proof) { + if !ZkPublicKey::verify_multi( + &[declaration.zk_id], + context.tx_hash_view.as_fr(), + context.proof, + ) { return Err(SdpError::InvalidZkSignature); } Ok(()) } +} + +impl ExecutableOperation for SDPActiveOp { + type Context<'a> = SDPActiveExecutionContext; + type Error = SdpError; // TODO: check service specific logic - fn execute( + fn execute<'a>( &self, - mut ctx: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { - let mut declaration = ctx + mut context: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error> { + let mut declaration = context .declarations .get(&self.declaration_id) .expect("The operation should have been validated"); - declaration.active = ctx.epoch; + declaration.active = context.epoch; declaration.nonce = self.nonce; - ctx.declarations = ctx + context.declarations = context .declarations .update(&self.declaration_id, declaration.clone()) .expect("the declaration is in the tree"); @@ -102,6 +101,6 @@ impl Operation> for SDPActiveOp { "updated declaration with active message" ); - Ok((ctx, Vec::new())) + Ok((context, Vec::new())) } } diff --git a/core/src/mantle/ops/sdp/declare.rs b/core/src/mantle/ops/sdp/declare.rs index 9f782f7af..9ad8e6190 100644 --- a/core/src/mantle/ops/sdp/declare.rs +++ b/core/src/mantle/ops/sdp/declare.rs @@ -7,7 +7,9 @@ use crate::{ mantle::{ Note, channel::Channels, - ledger::{Declarations, Operation, Utxos}, + ledger::{ + Declarations, ExecutableOperation, Utxos, VerifiableOperation, verification_mode, + }, transactions::hash::TxHashView, }, sdp::{Declaration, MinStake, locked_notes::LockedNotes}, @@ -25,7 +27,7 @@ trait SDPDeclareValidationExt { fn execute( &self, - ctx: SDPDeclareExecutionContext, + context: SDPDeclareExecutionContext, ) -> Result<(SDPDeclareExecutionContext, Vec), SdpError>; } @@ -70,22 +72,22 @@ impl SDPDeclareValidationExt for SDPDeclareOp { fn execute( &self, - mut ctx: SDPDeclareExecutionContext, + mut context: SDPDeclareExecutionContext, ) -> Result<(SDPDeclareExecutionContext, Vec), SdpError> { let declaration_id = self.id(); - let declaration = Declaration::new(ctx.epoch, self); - ctx.declarations = ctx.declarations.insert(declaration_id, declaration).0; - let utxo = ctx + let declaration = Declaration::new(context.epoch, self); + context.declarations = context.declarations.insert(declaration_id, declaration).0; + let utxo = context .utxo_tree .utxos() .get(&self.locked_note_id) .expect("The operation should have been checked") .0; - ctx.locked_notes = ctx + context.locked_notes = context .locked_notes .lock( - &ctx.min_stake, + &context.min_stake, self.service_type, declaration_id, utxo.note, @@ -93,7 +95,7 @@ impl SDPDeclareValidationExt for SDPDeclareOp { ) .map_err(|_| SdpError::UnexpectedError)?; - Ok((ctx, Vec::new())) + Ok((context, Vec::new())) } } @@ -155,28 +157,18 @@ pub struct SDPDeclareExecutionContext { pub min_stake: MinStake, } -impl Operation> for SDPDeclareOp { - type PreverificationContext<'a> - = SDPDeclarePreverificationContext<'a> - where - Self: 'a; - type ExecutionContext<'a> - = SDPDeclareExecutionContext - where - Self: 'a; - type VerificationError = SdpError; - type ExecutionError = SdpError; +impl VerifiableOperation for SDPDeclareOp { + type PreverificationContext<'a> = SDPDeclarePreverificationContext<'a>; + type VerificationContext<'a> = SDPDeclareVerificationContext<'a>; + type Error = SdpError; - fn preverify( - &self, - context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { self.preverify(context.tx_hash_view, context.proof_ed25519) } - fn verify(&self, ctx: &SDPDeclareVerificationContext<'_>) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { // Check that the note exist - let Some((utxo, _)) = ctx.utxo_tree.utxos().get(&self.locked_note_id) else { + let Some((utxo, _)) = context.utxo_tree.utxos().get(&self.locked_note_id) else { return Err(SdpError::InexistingNote(self.locked_note_id)); }; @@ -184,8 +176,8 @@ impl Operation> for SDPDeclareOp { let note = utxo.note; if !ZkPublicKey::verify_multi( &[note.pk, self.zk_id], - ctx.tx_hash_view.as_fr(), - ctx.proof_zk_signature, + context.tx_hash_view.as_fr(), + context.proof_zk_signature, ) { return Err(SdpError::InvalidZkSignature); } @@ -193,46 +185,26 @@ impl Operation> for SDPDeclareOp { SDPDeclareValidationExt::validate( self, note, - ctx.channels, - ctx.declarations, - ctx.locked_notes, - ctx.min_stake, + context.channels, + context.declarations, + context.locked_notes, + context.min_stake, ) } - - fn execute( - &self, - ctx: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { - SDPDeclareValidationExt::execute(self, ctx) - } } -impl Operation> for SDPDeclareOp { - type PreverificationContext<'a> - = SDPDeclarePreverificationContext<'a> - where - Self: 'a; - type ExecutionContext<'a> - = SDPDeclareExecutionContext - where - Self: 'a; - type VerificationError = SdpError; - type ExecutionError = SdpError; +impl VerifiableOperation for SDPDeclareOp { + type PreverificationContext<'a> = SDPDeclarePreverificationContext<'a>; + type VerificationContext<'a> = SDPDeclareGenesisValidationContext<'a>; + type Error = SdpError; - fn preverify( - &self, - context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { self.preverify(context.tx_hash_view, context.proof_ed25519) } - fn verify( - &self, - ctx: &SDPDeclareGenesisValidationContext<'_>, - ) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { // Check that the note exist - let Some((utxo, _)) = ctx.utxo_tree.utxos().get(&self.locked_note_id) else { + let Some((utxo, _)) = context.utxo_tree.utxos().get(&self.locked_note_id) else { return Err(SdpError::InexistingNote(self.locked_note_id)); }; let note = utxo.note; @@ -240,18 +212,23 @@ impl Operation> for SDPDeclareOp { SDPDeclareValidationExt::validate( self, note, - ctx.channels, - ctx.declarations, - ctx.locked_notes, - ctx.min_stake, + context.channels, + context.declarations, + context.locked_notes, + context.min_stake, ) } +} - fn execute( +impl ExecutableOperation for SDPDeclareOp { + type Context<'a> = SDPDeclareExecutionContext; + type Error = SdpError; + + fn execute<'a>( &self, - ctx: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { - SDPDeclareValidationExt::execute(self, ctx) + context: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error> { + SDPDeclareValidationExt::execute(self, context) } } diff --git a/core/src/mantle/ops/sdp/withdraw.rs b/core/src/mantle/ops/sdp/withdraw.rs index 102933ea9..020e9d2cf 100644 --- a/core/src/mantle/ops/sdp/withdraw.rs +++ b/core/src/mantle/ops/sdp/withdraw.rs @@ -7,7 +7,7 @@ use super::{SDPWithdrawOp, SdpError}; use crate::{ events::TxEvent, mantle::{ - ledger::{Declarations, Operation}, + ledger::{Declarations, ExecutableOperation, VerifiableOperation, verification_mode}, transactions::hash::TxHashView, }, sdp::{self, locked_notes::LockedNotes}, @@ -29,28 +29,18 @@ pub struct SDPWithdrawExecutionContext { pub epoch: Epoch, } -impl Operation> for SDPWithdrawOp { - type PreverificationContext<'a> - = () - where - Self: 'a; - type ExecutionContext<'a> - = SDPWithdrawExecutionContext - where - Self: 'a; - type VerificationError = SdpError; - type ExecutionError = SdpError; +impl VerifiableOperation for SDPWithdrawOp { + type PreverificationContext<'a> = (); + type VerificationContext<'a> = SDPWithdrawValidationContext<'a>; + type Error = SdpError; - fn preverify( - &self, - _context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, _context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { Ok(()) } - fn verify(&self, ctx: &SDPWithdrawValidationContext<'_>) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { // Check that the declaration exists - let Some(declaration) = ctx.declarations.get(&self.declaration_id) else { + let Some(declaration) = context.declarations.get(&self.declaration_id) else { return Err(SdpError::DeclarationNotFound(self.declaration_id)); }; @@ -63,7 +53,7 @@ impl Operation> for SDPWithdrawOp { } // Check that the locked note is locked for this service - if !ctx + if !context .locked_notes .is_locked_for_service(&self.locked_note_id, &declaration.service_type) { @@ -84,14 +74,14 @@ impl Operation> for SDPWithdrawOp { // Ensure locked note pk and zk_id attached to this declaration authorized this // Operation. - let note = ctx + let note = context .locked_notes .get(&self.locked_note_id) .expect("The Operation has been checked above"); if !ZkPublicKey::verify_multi( &[note.pk, declaration.zk_id], - ctx.tx_hash_view.as_fr(), - ctx.proof, + context.tx_hash_view.as_fr(), + context.proof, ) { return Err(SdpError::InvalidZkSignature); } @@ -106,12 +96,17 @@ impl Operation> for SDPWithdrawOp { Ok(()) } +} - fn execute( +impl ExecutableOperation for SDPWithdrawOp { + type Context<'a> = SDPWithdrawExecutionContext; + type Error = SdpError; + + fn execute<'a>( &self, - mut ctx: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { - let mut declaration = ctx + mut context: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error> { + let mut declaration = context .declarations .get(&self.declaration_id) .expect("The operation should have been validated"); @@ -122,9 +117,9 @@ impl Operation> for SDPWithdrawOp { // withdrawal because SDP uses the snapshot from `SNAPSHOT_FINALIZATION_DELAY` // epochs ago. // The note will be unlocked once the withdrawn epoch set here is reached. - declaration.withdraw_at = Some(ctx.epoch.strict_add(sdp::SNAPSHOT_FINALIZATION_DELAY)); + declaration.withdraw_at = Some(context.epoch.strict_add(sdp::SNAPSHOT_FINALIZATION_DELAY)); declaration.nonce = self.nonce; - ctx.declarations = ctx + context.declarations = context .declarations .update(&self.declaration_id, declaration.clone()) .expect("the declaration is in the tree"); @@ -137,6 +132,6 @@ impl Operation> for SDPWithdrawOp { "updated declaration with withdraw message" ); - Ok((ctx, Vec::new())) + Ok((context, Vec::new())) } } diff --git a/core/src/mantle/ops/transfer.rs b/core/src/mantle/ops/transfer.rs index 0ab922d57..9b8e97722 100644 --- a/core/src/mantle/ops/transfer.rs +++ b/core/src/mantle/ops/transfer.rs @@ -7,7 +7,10 @@ use crate::{ events::TxEvent, mantle::{ channel::Channels, - ledger::{self, Inputs, Operation, Outputs, Utxo, Utxos}, + ledger::{ + self, ExecutableOperation, Inputs, Outputs, Utxo, Utxos, VerifiableOperation, + verification_mode, + }, ops::OpId, transactions::hash::TxHashView, }, @@ -82,22 +85,12 @@ pub struct TransferValidationContext<'a> { pub proof: &'a ZkSignature, } -impl Operation> for TransferOp { - type PreverificationContext<'a> - = () - where - Self: 'a; - type ExecutionContext<'a> - = Utxos - where - Self: 'a; - type VerificationError = TransferError; - type ExecutionError = TransferError; +impl VerifiableOperation for TransferOp { + type PreverificationContext<'a> = (); + type VerificationContext<'a> = TransferValidationContext<'a>; + type Error = TransferError; - fn preverify( - &self, - _context: &Self::PreverificationContext<'_>, - ) -> Result<(), Self::VerificationError> { + fn preverify(&self, _context: &Self::PreverificationContext<'_>) -> Result<(), Self::Error> { // Ensure the inputs is non-empty if self.inputs.is_empty() { return Err(TransferError::NoInputTransfer); @@ -109,24 +102,32 @@ impl Operation> for TransferOp { Ok(()) } - fn verify(&self, ctx: &TransferValidationContext<'_>) -> Result<(), Self::ExecutionError> { + fn verify(&self, context: &Self::VerificationContext<'_>) -> Result<(), Self::Error> { // Validate Inputs - self.inputs - .validate_not_in_channel(ctx.locked_notes, ctx.channels, ctx.utxos)?; + self.inputs.validate_not_in_channel( + context.locked_notes, + context.channels, + context.utxos, + )?; // Check the transfer Proof - let pks = self.inputs.get_pk(ctx.utxos)?; - if !ZkPublicKey::verify_multi(&pks, ctx.tx_hash_view.as_fr(), ctx.proof) { + let pks = self.inputs.get_pk(context.utxos)?; + if !ZkPublicKey::verify_multi(&pks, context.tx_hash_view.as_fr(), context.proof) { return Err(TransferError::InvalidProof); } Ok(()) } +} - fn execute( +impl ExecutableOperation for TransferOp { + type Context<'a> = Utxos; + type Error = TransferError; + + fn execute<'a>( &self, - mut utxos: Self::ExecutionContext<'_>, - ) -> Result<(Self::ExecutionContext<'_>, Vec), Self::ExecutionError> { + mut utxos: Self::Context<'a>, + ) -> Result<(Self::Context<'a>, Vec), Self::Error> { // Remove inputs from the ledger utxos = self.inputs.execute(utxos)?; // Add outputs from the ledger diff --git a/core/src/mantle/traits/genesis.rs b/core/src/mantle/traits/genesis.rs index 00d734002..415a778ae 100644 --- a/core/src/mantle/traits/genesis.rs +++ b/core/src/mantle/traits/genesis.rs @@ -2,7 +2,7 @@ use crate::mantle::{ CryptarchiaParameter, OpProof, ops::{channel::inscribe::InscriptionOp, sdp::SDPDeclareOp, transfer::TransferOp}, traits::Hashable, - transactions::{hash::TxHash, mantle_tx::MantleTx}, + transactions::{hash::TxHash, mantle_tx::RawMantleTx}, }; /// A genesis transaction as specified in the @@ -12,7 +12,7 @@ pub trait GenesisTx: Hashable { fn genesis_inscription(&self) -> &InscriptionOp; fn cryptarchia_parameter(&self) -> CryptarchiaParameter; fn sdp_declarations(&self) -> impl Iterator; - fn mantle_tx(&self) -> &MantleTx; + fn mantle_tx(&self) -> &RawMantleTx; } impl GenesisTx for &T { @@ -32,7 +32,7 @@ impl GenesisTx for &T { T::sdp_declarations(self) } - fn mantle_tx(&self) -> &MantleTx { + fn mantle_tx(&self) -> &RawMantleTx { T::mantle_tx(self) } } diff --git a/core/src/mantle/traits/mantle_tx.rs b/core/src/mantle/traits/mantle_tx.rs index 3795295e0..a941c1f4f 100644 --- a/core/src/mantle/traits/mantle_tx.rs +++ b/core/src/mantle/traits/mantle_tx.rs @@ -1,14 +1,15 @@ use crate::mantle::{ GasCalculator, Op, OpProof, traits::{Hashable, StorageSize}, - transactions::{hash::TxHash, mantle_tx::MantleTx}, + transactions::{hash::TxHash, mantle_tx::RawMantleTx}, }; pub type OpWithProof<'a> = (&'a Op, &'a OpProof); +// TODO: Supertrait to MantleTx and propagate pub trait MantleTxWithProofs: Hashable + GasCalculator + StorageSize { /// Returns the underlying `MantleTx` that this transaction represents. - fn mantle_tx(&self) -> &MantleTx; + fn mantle_tx(&self) -> &RawMantleTx; /// Returns an iterator over the operations and their corresponding proofs /// in this transaction. @@ -16,7 +17,7 @@ pub trait MantleTxWithProofs: Hashable + GasCalculator + StorageS } impl MantleTxWithProofs for &T { - fn mantle_tx(&self) -> &MantleTx { + fn mantle_tx(&self) -> &RawMantleTx { T::mantle_tx(self) } diff --git a/core/src/mantle/transactions/builder.rs b/core/src/mantle/transactions/builder.rs index 6a5227f4f..298470fd6 100644 --- a/core/src/mantle/transactions/builder.rs +++ b/core/src/mantle/transactions/builder.rs @@ -14,7 +14,7 @@ use crate::{ channel::{ChannelId, withdraw::ChannelWithdrawOp}, transfer::TransferOp, }, - transactions::mantle_tx::{MantleTx, MantleTxContext}, + transactions::mantle_tx::{MantleTx as _, MantleTxContext, RawMantleTx}, }, proofs::channel_multi_sig_proof::ChannelMultiSigProof, }; @@ -52,7 +52,7 @@ impl From<(BoundedError, BoundedTag)> for TxBuilderError { } } -/// Builds a [`MantleTx`] incrementally. +/// Builds a [`RawMantleTx`] incrementally. /// /// The builder is intentionally free of any [`MantleTxContext`]: gas prices are /// tip-dependent, so the context is supplied as a parameter to the fee-aware @@ -62,7 +62,7 @@ impl From<(BoundedError, BoundedTag)> for TxBuilderError { /// HTTP) to be funded against a freshly fetched context. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MantleTxBuilder { - mantle_tx: MantleTx, + mantle_tx: RawMantleTx, ledger_inputs: BoundedUtxos, pending_transfer: TransferOp, // Maps a Proof to its Op by the Op Index @@ -80,7 +80,7 @@ impl MantleTxBuilder { #[must_use] pub fn new() -> Self { Self { - mantle_tx: MantleTx([].into()), + mantle_tx: RawMantleTx([].into()), ledger_inputs: BoundedUtxos::default(), pending_transfer: TransferOp::new(Inputs::empty(), Outputs::empty()), channel_multi_sig_proofs: HashMap::new(), @@ -234,8 +234,8 @@ impl MantleTxBuilder { } /// Predicts the minimum gas cost of the transaction once signed. - /// See [`MantleTx::minimum_total_gas_cost`] to understand why this is only - /// a minimum, not an exact cost. + /// See [`RawMantleTx::minimum_total_gas_cost`] to understand why this is + /// only a minimum, not an exact cost. pub fn minimum_gas_cost( &self, context: &MantleTxContext, @@ -302,7 +302,7 @@ impl MantleTxBuilder { // TODO: Change this to a `Result` if genesis tx already contains max number of // ops. - pub fn build(mut self) -> Result { + pub fn build(mut self) -> Result { if !self.pending_transfer.is_empty() { self.mantle_tx .0 diff --git a/core/src/mantle/transactions/codec.rs b/core/src/mantle/transactions/codec.rs index 2b1a0d6dc..71d65f73b 100644 --- a/core/src/mantle/transactions/codec.rs +++ b/core/src/mantle/transactions/codec.rs @@ -7,7 +7,7 @@ use crate::{ Op, SignedMantleTx, ops::codec::{decode_ops_proofs, encode_ops_proofs}, transactions::{ - mantle_tx::{MantleTx, MantleTxGasContext}, + mantle_tx::{MantleTx as _, MantleTxGasContext, RawMantleTx}, states::{Unverified, VerificationState}, }, }, @@ -18,7 +18,7 @@ pub fn decode_signed_mantle_tx( input: &[u8], ) -> Result<(&[u8], SignedMantleTx), DecodeError> { // SignedMantleTx = MantleTx OpsProofs - let (input, mantle_tx) = MantleTx::decode(input)?; + let (input, mantle_tx) = RawMantleTx::decode(input)?; let (input, ops_proofs) = decode_ops_proofs(input, mantle_tx.ops())?; let signed_tx = SignedMantleTx::new(mantle_tx, ops_proofs); @@ -43,7 +43,7 @@ pub fn encode_signed_mantle_tx(tx: &SignedMantleTx usize { +pub fn minimum_signed_mantle_tx_size(tx: &RawMantleTx, context: &MantleTxGasContext) -> usize { let mantle_tx_size = tx.encode().len(); let ops_proofs_size = tx @@ -168,7 +168,7 @@ mod tests { #[test] fn test_decode_signed_mantle_tx_empty() { - let mantle_tx = MantleTx(Ops::new_unchecked(vec![])); + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![])); let signed_tx = SignedMantleTx::new(mantle_tx, OpsProofs::empty()); @@ -195,7 +195,7 @@ mod tests { #[test] fn test_decode_signed_mantle_tx_with_inscribe() { let signing_key = Ed25519Key::from_bytes(&[4u8; 32]); - let mantle_tx = MantleTx( + let mantle_tx = RawMantleTx( [Op::ChannelInscribe(InscriptionOp { channel_id: ChannelId::from([0xAA; 32]), inscription: b"hello".into(), @@ -241,7 +241,7 @@ mod tests { #[test] fn test_decode_signed_mantle_tx_with_multiple_ops() { let signing_key = Ed25519Key::from_bytes(&[4u8; 32]); - let mantle_tx = MantleTx(Ops::new_unchecked(vec![ + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![ Op::ChannelInscribe(InscriptionOp { channel_id: ChannelId::from([0x11; 32]), inscription: b"first".into(), @@ -306,7 +306,7 @@ mod tests { }; let mantle_tx = - MantleTx(Ops::new_unchecked(vec![Op::ChannelInscribe(inscribe_op)])); + RawMantleTx(Ops::new_unchecked(vec![Op::ChannelInscribe(inscribe_op)])); let tx_hash = mantle_tx.hash(); let op_sig = signing_key.sign_payload(&tx_hash.as_signing_bytes()); @@ -350,13 +350,13 @@ mod tests { #[test] fn test_encode_decode_roundtrip_empty_tx() { // Create an empty MantleTx - let original_tx = MantleTx(Ops::new_unchecked(vec![])); + let original_tx = RawMantleTx(Ops::new_unchecked(vec![])); // Encode let encoded = original_tx.encode(); // Decode - let (remaining, decoded_tx) = MantleTx::decode(&encoded).unwrap(); + let (remaining, decoded_tx) = RawMantleTx::decode(&encoded).unwrap(); // Verify assert!(remaining.is_empty()); @@ -371,13 +371,13 @@ mod tests { let note_id = NoteId(BigUint::from(123u64).into()); let transfer_op = TransferOp::new(Inputs::new([note_id]), Outputs::new([note])); - let original_tx = MantleTx(Ops::new_unchecked(vec![Op::Transfer(transfer_op)])); + let original_tx = RawMantleTx(Ops::new_unchecked(vec![Op::Transfer(transfer_op)])); // Encode let encoded = original_tx.encode(); // Decode - let (remaining, decoded_tx) = MantleTx::decode(&encoded).unwrap(); + let (remaining, decoded_tx) = RawMantleTx::decode(&encoded).unwrap(); // Verify assert!(remaining.is_empty()); @@ -387,7 +387,7 @@ mod tests { #[test] fn test_encode_decode_roundtrip_signed_tx() { // Create a simple SignedMantleTx - let mantle_tx = MantleTx(Ops::new_unchecked(vec![])); + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![])); let original_tx = SignedMantleTx::new(mantle_tx, OpsProofs::empty()); // Encode @@ -404,7 +404,7 @@ mod tests { #[test] fn test_minimum_signed_mantle_tx_size_empty_tx() { // Create an empty MantleTx - let mantle_tx = MantleTx(Ops::new_unchecked(vec![])); + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![])); // Predict size let gas_context = @@ -429,7 +429,7 @@ mod tests { signer: signing_key.public_key(), }; - let mantle_tx = MantleTx(Ops::new_unchecked(vec![Op::ChannelInscribe(inscribe_op)])); + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![Op::ChannelInscribe(inscribe_op)])); // Predict size let gas_context = @@ -466,7 +466,7 @@ mod tests { transfer_threshold: 0, }; - let mantle_tx = MantleTx(Ops::new_unchecked(vec![Op::ChannelConfig(config_op)])); + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![Op::ChannelConfig(config_op)])); // Predict size let gas_context = @@ -515,7 +515,7 @@ mod tests { locked_note_id: locked_note.id(), }; - let mantle_tx = MantleTx(Ops::new_unchecked(vec![Op::SDPDeclare(sdp_declare_op)])); + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![Op::SDPDeclare(sdp_declare_op)])); // Predict size let gas_context = @@ -548,7 +548,7 @@ mod tests { locked_note_id, }; - let mantle_tx = MantleTx(Ops::new_unchecked(vec![Op::SDPWithdraw(sdp_withdraw_op)])); + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![Op::SDPWithdraw(sdp_withdraw_op)])); let tx_hash = mantle_tx.hash(); @@ -589,7 +589,7 @@ mod tests { metadata, }; - let mantle_tx = MantleTx(Ops::new_unchecked(vec![Op::SDPActive(sdp_active_op)])); + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![Op::SDPActive(sdp_active_op)])); let gas_context = MantleTxGasContext::new(HashMap::new(), HashMap::new(), GasPrices::new(0, 0)); @@ -643,7 +643,7 @@ mod tests { metadata: ActivityMetadata::Blend(Box::new(blend_proof)), }; - let mantle_tx = MantleTx(Ops::new_unchecked(vec![ + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![ Op::ChannelInscribe(inscribe_op), Op::ChannelConfig(config_op), Op::SDPActive(sdp_active_op), @@ -691,7 +691,7 @@ mod tests { Outputs::new([note1, note2]), ); - let mantle_tx = MantleTx(Ops::new_unchecked(vec![Op::Transfer(transfer_op)])); + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![Op::Transfer(transfer_op)])); // Predict size let gas_context = @@ -750,7 +750,7 @@ mod tests { .id(), }; - let mantle_tx = MantleTx(Ops::new_unchecked(vec![ + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![ Op::ChannelInscribe(inscribe_op), Op::ChannelConfig(config_op), Op::SDPDeclare(sdp_declare_op), @@ -794,7 +794,7 @@ mod tests { pk: ZkPublicKey::from(BigUint::from(0u64)), }; - let mantle_tx = MantleTx(Ops::new_unchecked(vec![Op::LeaderClaim(leader_claim_op)])); + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![Op::LeaderClaim(leader_claim_op)])); let empty_gas_context = MantleTxGasContext::new(HashMap::new(), HashMap::new(), GasPrices::new(0, 0)); @@ -828,7 +828,7 @@ mod tests { #[test] fn test_encode_decode_channel_withdraw_tx() { let signing_key = Ed25519Key::from_bytes(&[21u8; 32]); - let mantle_tx = MantleTx(Ops::new_unchecked(vec![Op::ChannelWithdraw( + let mantle_tx = RawMantleTx(Ops::new_unchecked(vec![Op::ChannelWithdraw( ChannelWithdrawOp { channel_id: ChannelId::from([0xAB; 32]), inputs: Inputs::new([ diff --git a/core/src/mantle/transactions/genesis_tx.rs b/core/src/mantle/transactions/genesis_tx.rs index c7bf3fd6c..973b90aca 100644 --- a/core/src/mantle/transactions/genesis_tx.rs +++ b/core/src/mantle/transactions/genesis_tx.rs @@ -19,7 +19,11 @@ use crate::{ transfer::TransferOp, }, traits::{GenesisTx as GenesisTxTrait, Hashable, hashable}, - transactions::{hash::TxHash, mantle_tx::MantleTx, states::Preverified}, + transactions::{ + hash::TxHash, + mantle_tx::{MantleTx as _, RawMantleTx}, + states::Preverified, + }, }, }; @@ -217,7 +221,7 @@ impl GenesisTxTrait for GenesisTx { }) } - fn mantle_tx(&self) -> &MantleTx { + fn mantle_tx(&self) -> &RawMantleTx { self.tx.mantle_tx() } } @@ -469,7 +473,7 @@ mod tests { let transfer_op = TransferOp::new(Inputs::empty(), Outputs::new([create_test_note(1000)])); let mut new_ops = vec![Op::Transfer(transfer_op)]; new_ops.append(&mut ops); - let mantle_tx = MantleTx(Ops::new_unchecked(new_ops)); + let mantle_tx = RawMantleTx(Ops::new_unchecked(new_ops)); let ops_proofs = OpsProofs::try_from(ops_proofs).unwrap(); let mut new_op_proofs = OpsProofs::from(OpProof::ZkSig( ZkKey::multi_sign(&[], &mantle_tx.hash().to_fr()).unwrap(), diff --git a/core/src/mantle/transactions/mantle_tx.rs b/core/src/mantle/transactions/mantle_tx.rs index ad7637a14..5cf58e005 100644 --- a/core/src/mantle/transactions/mantle_tx.rs +++ b/core/src/mantle/transactions/mantle_tx.rs @@ -23,9 +23,9 @@ use crate::{ static MANTLE_TX_HASH_V1_BYTES: LazyLock> = LazyLock::new(|| b"MANTLE_TXHASH_V1".to_vec()); #[derive(Clone, Debug, PartialEq, Eq, BinaryCodec)] -pub struct MantleTx(pub Ops); +pub struct RawMantleTx(pub Ops); -impl MantleTx { +impl RawMantleTx { /// Predicts the minimum total gas cost of the transaction once signed. /// /// See [`minimum_signed_mantle_tx_size`] for why this doesn't implement @@ -83,14 +83,15 @@ impl MantleTx { } transfers } +} - #[must_use] - pub const fn ops(&self) -> &Ops { +impl MantleTx for RawMantleTx { + fn ops(&self) -> &Ops { &self.0 } } -impl Hashable for MantleTx { +impl Hashable for RawMantleTx { //noinspection RsTypeCheck: The type is correct, but the linter is confused by // the closure. const HASHER: hashable::Hasher = |tx| { @@ -108,7 +109,7 @@ impl Hashable for MantleTx { } } -impl StorageSize for MantleTx { +impl StorageSize for RawMantleTx { fn storage_size(&self) -> usize { self.encode().len() } @@ -137,36 +138,36 @@ fn contextual_op_execution_gas( .checked_mul(Value::from(multiplier)) } -impl From> for MantleTx { +impl From> for RawMantleTx { fn from(signed_tx: SignedMantleTx) -> Self { signed_tx.mantle_tx } } #[derive(Serialize, Deserialize)] -struct MantleTxSerde { +struct RawMantleTxSerde { pub ops: Ops, } -impl From for MantleTx { - fn from(MantleTxSerde { ops }: MantleTxSerde) -> Self { +impl From for RawMantleTx { + fn from(RawMantleTxSerde { ops }: RawMantleTxSerde) -> Self { Self(ops) } } -impl From for MantleTxSerde { - fn from(MantleTx(ops): MantleTx) -> Self { +impl From for RawMantleTxSerde { + fn from(RawMantleTx(ops): RawMantleTx) -> Self { Self { ops } } } -impl Serialize for MantleTx { +impl Serialize for RawMantleTx { fn serialize(&self, serializer: S) -> Result where S: Serializer, { if serializer.is_human_readable() { - let tx_deser: MantleTxSerde = self.clone().into(); + let tx_deser: RawMantleTxSerde = self.clone().into(); tx_deser.serialize(serializer) } else { let bytes = self.encode(); @@ -175,13 +176,13 @@ impl Serialize for MantleTx { } } -impl<'de> Deserialize<'de> for MantleTx { +impl<'de> Deserialize<'de> for RawMantleTx { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { if deserializer.is_human_readable() { - ::deserialize(deserializer).map(Into::into) + ::deserialize(deserializer).map(Into::into) } else { let bytes: Vec = >::deserialize(deserializer)?; Self::decode(&bytes) @@ -247,3 +248,7 @@ impl MantleTxGasContext { self.gas_prices.clone() } } + +pub trait MantleTx { + fn ops(&self) -> &Ops; +} diff --git a/core/src/mantle/transactions/mod.rs b/core/src/mantle/transactions/mod.rs index 3bd39e299..cfc9c6a51 100644 --- a/core/src/mantle/transactions/mod.rs +++ b/core/src/mantle/transactions/mod.rs @@ -16,7 +16,7 @@ pub use gas::{GENESIS_EXECUTION_GAS_PRICE, GENESIS_STORAGE_GAS_PRICE, GasPrices} pub use genesis_tx::{CryptarchiaParameter, GenesisTime, GenesisTx}; pub use hash::TxHash; use lb_utils::bounded::UpperBoundedVec; -pub use mantle_tx::{MantleTx, MantleTxContext, MantleTxGasContext}; +pub use mantle_tx::{MantleTxContext, MantleTxGasContext, RawMantleTx}; pub use signed_mantle_tx::SignedMantleTx; pub use verification_helper::OperationVerificationHelper; pub use verified_ops::VerifiedOps; diff --git a/core/src/mantle/transactions/signed_mantle_tx.rs b/core/src/mantle/transactions/signed_mantle_tx.rs index 6955bf62b..927d750c6 100644 --- a/core/src/mantle/transactions/signed_mantle_tx.rs +++ b/core/src/mantle/transactions/signed_mantle_tx.rs @@ -5,9 +5,9 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::{ crypto::{Digest as _, Hasher}, mantle::{ - MantleTx, Value, VerificationError, + RawMantleTx, Value, VerificationError, gas::{Gas, GasCalculator, GasConstants, GasCost, GasOverflow}, - ledger::Operation, + ledger::{VerifiableOperation, verification_mode::StandardMode}, ops::{ Op, OpProof, channel::{ @@ -32,6 +32,7 @@ use crate::{ GasPrices, OperationVerificationHelper, OpsProofs, VerifiedOps, codec::{decode_signed_mantle_tx, encode_signed_mantle_tx}, hash::{TxHash, TxHashView}, + mantle_tx::MantleTx as _, states::{Preverified, Unverified, VerificationState}, }, }, @@ -41,7 +42,7 @@ use crate::{ // The current tests behave just like the old code. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SignedMantleTx { - pub(crate) mantle_tx: MantleTx, + pub(crate) mantle_tx: RawMantleTx, // TODO: make this more efficient ops_proofs: OpsProofs, state: PhantomData, @@ -70,7 +71,7 @@ impl SignedMantleTx { } #[must_use] - pub const fn mantle_tx(&self) -> &MantleTx { + pub const fn mantle_tx(&self) -> &RawMantleTx { &self.mantle_tx } @@ -80,14 +81,14 @@ impl SignedMantleTx { } #[must_use] - pub fn into_parts(self) -> (MantleTx, OpsProofs) { + pub fn into_parts(self) -> (RawMantleTx, OpsProofs) { (self.mantle_tx, self.ops_proofs) } } impl SignedMantleTx { #[must_use] - pub const fn new(mantle_tx: MantleTx, ops_proofs: OpsProofs) -> Self { + pub const fn new(mantle_tx: RawMantleTx, ops_proofs: OpsProofs) -> Self { Self { mantle_tx, ops_proofs, @@ -95,7 +96,7 @@ impl SignedMantleTx { } } - const fn ensure_one_proof_per_op(&self) -> Result<(), VerificationError> { + fn ensure_one_proof_per_op(&self) -> Result<(), VerificationError> { if self.mantle_tx.ops().len() == self.ops_proofs.len() { return Ok(()); } @@ -147,7 +148,7 @@ impl SignedMantleTx { tx_hash_view, proof_ed25519, }; - >::preverify(op, &context) + >::preverify(op, &context) .map_err(VerificationError::SDPVerificationError) } (Op::SDPWithdraw(op), OpProof::ZkSig(_proof)) => op @@ -225,7 +226,7 @@ impl SignedMantleTx { /// testing purposes only. #[must_use] #[doc(hidden)] - pub const fn new_trusted(mantle_tx: MantleTx, ops_proofs: OpsProofs) -> Self { + pub const fn new_trusted(mantle_tx: RawMantleTx, ops_proofs: OpsProofs) -> Self { Self { mantle_tx, ops_proofs, @@ -317,7 +318,7 @@ impl SignedMantleTx { declarations: helper.get_declarations_by_service(op.service_type)?, min_stake: helper.get_min_stake(), }; - op.verify(&context) + >::verify(op, &context) .map_err(VerificationError::SDPVerificationError) } (Op::SDPWithdraw(op), OpProof::ZkSig(proof)) => { @@ -391,7 +392,7 @@ impl Hashable for SignedMantleTx { } impl MantleTxWithProofs for SignedMantleTx { - fn mantle_tx(&self) -> &MantleTx { + fn mantle_tx(&self) -> &RawMantleTx { &self.mantle_tx } @@ -472,7 +473,7 @@ impl PreverifiedMantleTx for SignedMantleTx { #[derive(Serialize)] #[serde(rename = "SignedMantleTx")] struct SignedMantleTxSerde<'a> { - mantle_tx: &'a MantleTx, + mantle_tx: &'a RawMantleTx, ops_proofs: &'a [OpProof], } @@ -501,7 +502,7 @@ impl Serialize for SignedMantleTx { #[derive(Deserialize)] #[serde(rename = "SignedMantleTx")] struct OwnedSignedMantleTxSerde { - mantle_tx: MantleTx, + mantle_tx: RawMantleTx, ops_proofs: OpsProofs, } @@ -555,7 +556,7 @@ pub mod test_utils { use lb_key_management_system_keys::keys::Ed25519Key; use crate::mantle::{ - MantleTx, NoteId, Op, OpProof, SignedMantleTx, + NoteId, Op, OpProof, RawMantleTx, SignedMantleTx, channel::{ChannelState, SlotTimeframe, SlotTimeout}, ledger::Inputs, ops::channel::{ @@ -567,8 +568,8 @@ pub mod test_utils { }; #[must_use] - pub fn create_test_mantle_tx(ops: Vec) -> MantleTx { - MantleTx(Ops::new_unchecked(ops)) + pub fn create_test_mantle_tx(ops: Vec) -> RawMantleTx { + RawMantleTx(Ops::new_unchecked(ops)) } #[must_use] diff --git a/core/src/mantle/transactions/verified_ops.rs b/core/src/mantle/transactions/verified_ops.rs index ca59af121..9ec47b8d2 100644 --- a/core/src/mantle/transactions/verified_ops.rs +++ b/core/src/mantle/transactions/verified_ops.rs @@ -1,7 +1,10 @@ use crate::mantle::{ Op, OpProof, SignedMantleTx, VerificationError, traits::Hashable as _, - transactions::{OperationVerificationHelper, hash::TxHashView, states::Preverified}, + transactions::{ + OperationVerificationHelper, hash::TxHashView, mantle_tx::MantleTx as _, + states::Preverified, + }, }; pub struct VerifiedOps<'tx> { diff --git a/core/src/sdp/mod.rs b/core/src/sdp/mod.rs index f8247442e..4f8aede17 100644 --- a/core/src/sdp/mod.rs +++ b/core/src/sdp/mod.rs @@ -510,7 +510,7 @@ impl DeclarationMessage { DeclarationId(hasher.finalize().into()) } - pub fn preverify( + pub(crate) fn preverify( &self, tx_hash_view: &TxHashView, proof_eddsa_signature: &Ed25519Signature, diff --git a/core/src/utils/merkle.rs b/core/src/utils/merkle.rs index ab2ad5568..e396dfcef 100644 --- a/core/src/utils/merkle.rs +++ b/core/src/utils/merkle.rs @@ -38,11 +38,11 @@ mod tests { Note, Op, ledger::{Inputs, Outputs}, ops::transfer::TransferOp, - transactions::mantle_tx::MantleTx, + transactions::mantle_tx::RawMantleTx, }; - fn create_random_tx(seed: u32) -> MantleTx { - MantleTx( + fn create_random_tx(seed: u32) -> RawMantleTx { + RawMantleTx( [Op::Transfer(TransferOp::new( Inputs::empty(), Outputs::new([Note { @@ -77,7 +77,7 @@ mod tests { #[test] fn test_root_empty_elements() { - let txs: Vec = vec![]; + let txs: Vec = vec![]; let result = calculate_block_root(&txs); let expected = [0u8; 32]; diff --git a/deployment/l2-sequencer-archival-demo/sequencer/src/sequencer.rs b/deployment/l2-sequencer-archival-demo/sequencer/src/sequencer.rs index 5b9e57f5d..df53df772 100644 --- a/deployment/l2-sequencer-archival-demo/sequencer/src/sequencer.rs +++ b/deployment/l2-sequencer-archival-demo/sequencer/src/sequencer.rs @@ -14,7 +14,7 @@ use lb_core::{ }, traits::Hashable as _, transactions::{ - mantle_tx::MantleTx, + mantle_tx::{MantleTx as _, RawMantleTx}, states::{Unverified, VerificationState}, }, }, @@ -173,7 +173,7 @@ impl Sequencer { signer: verifying_key, }; - let inscribe_tx = MantleTx([Op::ChannelInscribe(inscribe_op)].into()); + let inscribe_tx = RawMantleTx([Op::ChannelInscribe(inscribe_op)].into()); let tx_hash = inscribe_tx.hash(); let signature_bytes = self diff --git a/deployment/tui-zone/src/run_commands/run_config.rs b/deployment/tui-zone/src/run_commands/run_config.rs index 3d5860ae4..f97c5bc95 100644 --- a/deployment/tui-zone/src/run_commands/run_config.rs +++ b/deployment/tui-zone/src/run_commands/run_config.rs @@ -9,7 +9,7 @@ use lb_core::{ config::{ChannelConfigOp, Keys}, }, traits::Hashable as _, - transactions::codec::encode_signed_mantle_tx, + transactions::{codec::encode_signed_mantle_tx, mantle_tx::MantleTx as _}, }, proofs::channel_multi_sig_proof::{ChannelMultiSigProof, IndexedSignature}, }; @@ -105,7 +105,7 @@ pub(crate) async fn run_config_prepare(args: ConfigPrepareArgs) -> RunResult<()> args.transfer_threshold, )?; let msg_id = config_op.id(); - let tx = lb_core::mantle::MantleTx([Op::ChannelConfig(config_op)].into()); + let tx = lb_core::mantle::RawMantleTx([Op::ChannelConfig(config_op)].into()); let tx_hash = tx.hash(); let intent = ConfigIntent { version: ZONE_FILE_TRANSFER_VERSION, @@ -384,7 +384,7 @@ fn validate_authorized_signer( .into()) } -fn validate_config_tx(tx: &lb_core::mantle::MantleTx, intent: &ConfigIntent) -> RunResult<()> { +fn validate_config_tx(tx: &lb_core::mantle::RawMantleTx, intent: &ConfigIntent) -> RunResult<()> { let config = tx .ops() .iter() diff --git a/deployment/tui-zone/src/run_commands/run_withdraw.rs b/deployment/tui-zone/src/run_commands/run_withdraw.rs index 4af76c1ab..310930e4c 100644 --- a/deployment/tui-zone/src/run_commands/run_withdraw.rs +++ b/deployment/tui-zone/src/run_commands/run_withdraw.rs @@ -5,7 +5,7 @@ use lb_core::{ Op, OpProof, SignedMantleTx, ops::channel::{ChannelId, ChannelKeyIndex}, traits::Hashable as _, - transactions::codec::encode_signed_mantle_tx, + transactions::{codec::encode_signed_mantle_tx, mantle_tx::MantleTx as _}, }, proofs::channel_multi_sig_proof::{ChannelMultiSigProof, IndexedSignature}, }; diff --git a/deployment/tui-zone/src/run_commands/unit_tests.rs b/deployment/tui-zone/src/run_commands/unit_tests.rs index be6d357ae..3f8180451 100644 --- a/deployment/tui-zone/src/run_commands/unit_tests.rs +++ b/deployment/tui-zone/src/run_commands/unit_tests.rs @@ -8,7 +8,7 @@ mod tests { use lb_codec::BinaryEncode as _; use lb_core::mantle::{ - MantleTx, Note, NoteId, Op, SignedMantleTx, Utxo, Value, + Note, NoteId, Op, RawMantleTx, SignedMantleTx, Utxo, Value, ledger::Inputs, ops::channel::{ ChannelId, MsgId, @@ -67,8 +67,8 @@ mod tests { Ed25519Key::from_bytes(&[byte; ED25519_SECRET_KEY_SIZE]) } - fn empty_mantle_tx() -> MantleTx { - MantleTx(Ops::try_from(Vec::new()).expect("empty ops must be valid")) + fn empty_mantle_tx() -> RawMantleTx { + RawMantleTx(Ops::try_from(Vec::new()).expect("empty ops must be valid")) } #[test] @@ -234,7 +234,7 @@ mod tests { parent: MsgId::root(), signer: inscriber.public_key(), }; - let tx = MantleTx( + let tx = RawMantleTx( Ops::try_from(vec![ Op::ChannelWithdraw(withdraw), Op::ChannelInscribe(inscribe), @@ -303,7 +303,7 @@ mod tests { #[test] fn withdraw_combine_rejects_too_few_signatures() { - let tx = MantleTx( + let tx = RawMantleTx( Ops::try_from(vec![Op::ChannelWithdraw(ChannelWithdrawOp { channel_id: ChannelId::from([9; 32]), inputs: Inputs::new([NoteId::from(Fr::from(1u64))]), @@ -349,7 +349,7 @@ mod tests { let channel_id = ChannelId::from([10; 32]); let signer = test_signing_key(2); let second_signer = test_signing_key(3); - let tx = MantleTx( + let tx = RawMantleTx( Ops::try_from(vec![Op::ChannelWithdraw(ChannelWithdrawOp { channel_id, inputs: Inputs::new([NoteId::from(Fr::from(1u64))]), @@ -423,7 +423,7 @@ mod tests { let channel_id = ChannelId::from([11; 32]); let signer = test_signing_key(2); let second_signer = test_signing_key(3); - let tx = MantleTx( + let tx = RawMantleTx( Ops::try_from(vec![Op::ChannelWithdraw(ChannelWithdrawOp { channel_id, inputs: Inputs::new([NoteId::from(Fr::from(1u64))]), diff --git a/deployment/tui-zone/src/run_commands/utils.rs b/deployment/tui-zone/src/run_commands/utils.rs index 87962e0e8..d7bcd2382 100644 --- a/deployment/tui-zone/src/run_commands/utils.rs +++ b/deployment/tui-zone/src/run_commands/utils.rs @@ -18,7 +18,7 @@ use lb_core::mantle::{ transfer::TransferOp, }, transactions::{ - codec::decode_signed_mantle_tx, hash::TxHash, mantle_tx::MantleTx, states::Unverified, + codec::decode_signed_mantle_tx, hash::TxHash, mantle_tx::RawMantleTx, states::Unverified, }, }; use lb_key_management_system_service::keys::{ @@ -228,9 +228,9 @@ pub fn decode_exported_utxos(funds: &WalletFundsExport) -> RunResult> } /// Decode a hex-encoded mantle transaction and reject trailing bytes. -pub fn decode_mantle_tx_hex(value: &str) -> RunResult { +pub fn decode_mantle_tx_hex(value: &str) -> RunResult { let bytes = decode_hex(value)?; - let (remaining, tx) = MantleTx::decode(&bytes).map_err(|error| format!("{error:?}"))?; + let (remaining, tx) = RawMantleTx::decode(&bytes).map_err(|error| format!("{error:?}"))?; if !remaining.is_empty() { return Err("mantle tx has trailing bytes".into()); } diff --git a/ledger/src/cryptarchia/mod.rs b/ledger/src/cryptarchia/mod.rs index 256304054..c93af752d 100644 --- a/ledger/src/cryptarchia/mod.rs +++ b/ledger/src/cryptarchia/mod.rs @@ -10,7 +10,7 @@ use lb_core::{ mantle::{ NoteId, Utxo, Value, gas::{Gas, GasConstants, GasCost, GasOverflow, GasPrice}, - ledger::Operation as _, + ledger::ExecutableOperation as _, ops::transfer::TransferOp, traits::GenesisTx, transactions::{GENESIS_EXECUTION_GAS_PRICE, GENESIS_STORAGE_GAS_PRICE}, @@ -745,9 +745,9 @@ pub mod tests { use lb_core::{ crypto::{Digest as _, Hasher}, mantle::{ - GasCalculator as _, MantleTx, Note, Op, + GasCalculator as _, Note, Op, OpProof::ZkSig, - SignedMantleTx, + RawMantleTx, SignedMantleTx, gas::MainnetGasConstants, ledger::{Inputs, Outputs}, ops::{leader_claim::VoucherCm, sdp::SDPDeclareOp}, @@ -1587,7 +1587,7 @@ pub mod tests { Inputs::try_new(inputs).expect("Invalid inputs size"), Outputs::try_new(outputs).expect("Invalid outputs size"), ); - let mantle_tx = MantleTx([Op::Transfer(transfer_op.clone())].into()); + let mantle_tx = RawMantleTx([Op::Transfer(transfer_op.clone())].into()); let transfer_sig = ZkKey::multi_sign(&sks, &mantle_tx.hash().to_fr()).unwrap(); let tx = SignedMantleTx::new(mantle_tx, [ZkSig(transfer_sig.clone())].into()); (tx, transfer_op, transfer_sig) diff --git a/ledger/src/lib.rs b/ledger/src/lib.rs index af53c237a..0483a48c8 100644 --- a/ledger/src/lib.rs +++ b/ledger/src/lib.rs @@ -17,7 +17,7 @@ use lb_core::{ mantle::{ NoteId, Op, Utxo, Value, VerificationError, gas::{Gas, GasConstants, GasCost, GasOverflow}, - ledger::Operation as _, + ledger::ExecutableOperation as _, ops::{ channel::{ channel_transfer::ChannelTransferExecutionContext, @@ -723,9 +723,9 @@ mod tests { use lb_core::{ events::TxEventPayload, mantle::{ - GasCalculator as _, MantleTx, Note, OpProof, SignedMantleTx, + GasCalculator as _, Note, OpProof, RawMantleTx, SignedMantleTx, gas::MainnetGasConstants, - ledger::{Inputs, Outputs, Utxos}, + ledger::{Inputs, Outputs, Utxos, VerifiableOperation as _}, ops::{ OpId as _, channel::{ @@ -743,6 +743,7 @@ mod tests { transactions::{ Ops, OpsProofs, hash::TxHashView, + mantle_tx::MantleTx as _, states::{Preverified, Unverified}, }, }, @@ -781,7 +782,7 @@ mod tests { Inputs::try_new(inputs).expect("Invalid inputs size"), Outputs::try_new(outputs).expect("Invalid outputs size"), ); - let mantle_tx = MantleTx([Op::Transfer(transfer_op)].into()); + let mantle_tx = RawMantleTx([Op::Transfer(transfer_op)].into()); let ops_proofs = [OpProof::ZkSig( ZkKey::multi_sign(sks, &mantle_tx.hash().to_fr()).unwrap(), )] @@ -856,7 +857,7 @@ mod tests { ops: Vec, signing_keys: Vec<&Key>, ) -> SignedMantleTx { - let mantle_tx = MantleTx(Ops::new_unchecked(ops.clone())); + let mantle_tx = RawMantleTx(Ops::new_unchecked(ops.clone())); let tx_hash = mantle_tx.hash(); let ops_proofs = signing_keys @@ -1054,7 +1055,7 @@ mod tests { transfer_threshold: 1, }; - let config_tx = MantleTx([Op::ChannelConfig(config_op.clone())].into()); + let config_tx = RawMantleTx([Op::ChannelConfig(config_op.clone())].into()); let config_tx_hash = config_tx.hash(); let config_proof = ChannelMultiSigProof::try_new( [IndexedSignature::new( @@ -1218,7 +1219,7 @@ mod tests { channel_id, inputs: Inputs::new([deposited]), }; - let withdraw_tx = MantleTx([Op::ChannelWithdraw(withdraw)].into()); + let withdraw_tx = RawMantleTx([Op::ChannelWithdraw(withdraw)].into()); let withdraw_tx_hash = withdraw_tx.hash(); let withdraw_proof = ChannelMultiSigProof::try_new( [IndexedSignature::new( @@ -1282,7 +1283,7 @@ mod tests { // Withdraw releases the channel note under the NoteId the deposit gave // it, so the original input never comes back to the ledger. - let withdraw_tx = MantleTx( + let withdraw_tx = RawMantleTx( [Op::ChannelWithdraw(ChannelWithdrawOp { channel_id, inputs: Inputs::new([deposited]), @@ -1357,7 +1358,7 @@ mod tests { inputs: Inputs::new([deposited]), }; let wrong_key = Ed25519Key::from_bytes(&[42; 32]); - let withdraw_tx = MantleTx([Op::ChannelWithdraw(withdraw)].into()); + let withdraw_tx = RawMantleTx([Op::ChannelWithdraw(withdraw)].into()); let withdraw_tx_hash = withdraw_tx.hash(); let invalid_proof = ChannelMultiSigProof::try_new( [IndexedSignature::new( @@ -1569,7 +1570,7 @@ mod tests { Op::ChannelConfig(config_op), Op::ChannelInscribe(inscribe_op3.clone()), ]; - let config_tx = MantleTx(Ops::new_unchecked(ops.clone())); + let config_tx = RawMantleTx(Ops::new_unchecked(ops.clone())); let config_tx_hash = config_tx.hash(); let config_proof = ChannelMultiSigProof::try_new( [IndexedSignature::new( diff --git a/ledger/src/mantle/mod.rs b/ledger/src/mantle/mod.rs index eaad3d91c..3eee08c90 100644 --- a/ledger/src/mantle/mod.rs +++ b/ledger/src/mantle/mod.rs @@ -8,7 +8,7 @@ use lb_core::{ events::TxEvent, mantle::{ NoteId, Value, - ledger::Operation as _, + ledger::ExecutableOperation as _, ops::{ channel::{ config::{ChannelConfigExecutionContext, ChannelConfigOp}, diff --git a/ledger/src/mantle/sdp/mod.rs b/ledger/src/mantle/sdp/mod.rs index 9c97c0b76..617dd81ac 100644 --- a/ledger/src/mantle/sdp/mod.rs +++ b/ledger/src/mantle/sdp/mod.rs @@ -11,10 +11,12 @@ use lb_core::{ mantle::{ NoteId, OpProof, Utxo, Value, channel::Channels, - ledger::{Declarations, Operation}, + ledger::{ + Declarations, ExecutableOperation, VerifiableOperation, verification_mode::GenesisMode, + }, ops::sdp::{ SDPActiveExecutionContext, SDPActiveOp, SDPDeclareExecutionContext, SDPDeclareOp, - SDPDeclareVerificationContext, SDPWithdrawExecutionContext, SDPWithdrawOp, + SDPWithdrawExecutionContext, SDPWithdrawOp, declare::SDPDeclareGenesisValidationContext, }, }, @@ -399,27 +401,29 @@ impl SdpLedger { // Validate SDP Declare // TODO: Genesis has a different verification flow than `SignedMantleTx`. - // Refactor into a type state. - op.verify(&SDPDeclareGenesisValidationContext { - utxo_tree, - channels, - locked_notes: &self.locked_notes, - declarations: service_state.declarations(), - min_stake: &config.min_stake, - })?; + // Refactor into a type state. + >::verify( + op, + &SDPDeclareGenesisValidationContext { + utxo_tree, + channels, + locked_notes: &self.locked_notes, + declarations: service_state.declarations(), + min_stake: &config.min_stake, + }, + )?; // Execute SDP Declare - let (result, events) = - >::execute( - op, - SDPDeclareExecutionContext { - utxo_tree: utxo_tree.clone(), - epoch: self.epoch, - declarations: service_state.declarations_clone(), - locked_notes: self.locked_notes.clone(), - min_stake: config.min_stake, - }, - )?; + let (result, events) = ::execute( + op, + SDPDeclareExecutionContext { + utxo_tree: utxo_tree.clone(), + epoch: self.epoch, + declarations: service_state.declarations_clone(), + locked_notes: self.locked_notes.clone(), + min_stake: config.min_stake, + }, + )?; self.locked_notes = result.locked_notes; service_state.update_declarations(result.declarations); @@ -436,7 +440,7 @@ impl SdpLedger { return Err(Error::ServiceNotFound(op.service_type)); }; - let (result, events) = >::execute( + let (result, events) = ::execute( op, SDPDeclareExecutionContext { utxo_tree: utxo_tree.clone(), diff --git a/nodes/api-common/src/bodies/wallet.rs b/nodes/api-common/src/bodies/wallet.rs index 73a83ad35..f15748d20 100644 --- a/nodes/api-common/src/bodies/wallet.rs +++ b/nodes/api-common/src/bodies/wallet.rs @@ -151,7 +151,7 @@ pub mod fund { mantle::{ OpProof, Value, gas::GasCost, - transactions::{MantleTx, builder::MantleTxBuilder}, + transactions::{RawMantleTx, builder::MantleTxBuilder}, }, }; use lb_key_management_system_keys::keys::ZkPublicKey; @@ -174,7 +174,7 @@ pub mod fund { pub tip: HeaderId, /// The funded transaction, with the fee transfer appended as the last /// op. All ops are still unsigned. - pub funded_tx: MantleTx, + pub funded_tx: RawMantleTx, /// Proof for the appended fee transfer, signed over the funded /// transaction hash. `None` if funding required no transfer (zero /// fee and no inputs pulled in). diff --git a/nodes/node/binary/src/api/serializers/transactions.rs b/nodes/node/binary/src/api/serializers/transactions.rs index 8cb3c8c64..f40ae8287 100644 --- a/nodes/node/binary/src/api/serializers/transactions.rs +++ b/nodes/node/binary/src/api/serializers/transactions.rs @@ -1,29 +1,38 @@ use lb_core::mantle::{ - MantleTx, SignedMantleTx, TxHash, - transactions::{Ops, OpsProofs, states::VerificationState}, + SignedMantleTx, TxHash, + traits::Hashable, + transactions::{Ops, OpsProofs, mantle_tx::MantleTx, states::VerificationState}, }; use serde::Serialize; #[derive(Serialize)] -#[serde(remote = "MantleTx")] -pub struct ApiTransactionSerializer { - #[serde(getter = "::hash")] +pub struct ApiTransactionSerializer<'tx> { hash: TxHash, - #[serde(getter = "MantleTx::ops")] - ops: Ops, + ops: &'tx Ops, +} + +impl<'tx, T> From<&'tx T> for ApiTransactionSerializer<'tx> +where + T: MantleTx + Hashable, +{ + fn from(tx: &'tx T) -> Self { + Self { + hash: tx.hash(), + ops: tx.ops(), + } + } } #[derive(Serialize)] pub struct ApiSignedTransaction<'tx> { - #[serde(with = "ApiTransactionSerializer")] - mantle_tx: &'tx MantleTx, + mantle_tx: ApiTransactionSerializer<'tx>, ops_proofs: &'tx OpsProofs, } impl<'tx, State: VerificationState> From<&'tx SignedMantleTx> for ApiSignedTransaction<'tx> { fn from(value: &'tx SignedMantleTx) -> Self { Self { - mantle_tx: value.mantle_tx(), + mantle_tx: value.mantle_tx().into(), ops_proofs: value.ops_proofs(), } } diff --git a/services/chain/chain-service/src/sync/block_provider.rs b/services/chain/chain-service/src/sync/block_provider.rs index d56ebbb98..1dfdf6fce 100644 --- a/services/chain/chain-service/src/sync/block_provider.rs +++ b/services/chain/chain-service/src/sync/block_provider.rs @@ -592,7 +592,7 @@ mod tests { crypto::ZkHasher, events::Events, mantle::{ - MantleTx, Note, SignedMantleTx, ledger::Utxo, ops::leader_claim::VoucherCm, + Note, RawMantleTx, SignedMantleTx, ledger::Utxo, ops::leader_claim::VoucherCm, transactions::states::Unverified, }, proofs::leader_proof::{LeaderPrivate, LeaderPublic}, @@ -1040,7 +1040,7 @@ mod tests { } ProviderResponse::Available(mut stream) => match stream.next().await { Some(Ok(bytes)) => { - let block: Block = Block::try_from(bytes).unwrap(); + let block: Block = Block::try_from(bytes).unwrap(); ( false, format!("Available(first_block={:?})", block.header().id()), diff --git a/services/wallet/src/lib.rs b/services/wallet/src/lib.rs index 371fde055..7350856a8 100644 --- a/services/wallet/src/lib.rs +++ b/services/wallet/src/lib.rs @@ -28,7 +28,8 @@ use lb_core::{ }, traits::{Hashable as _, MantleTxWithProofs}, transactions::{ - MantleTxBuilder, MantleTxContext, OpsProofs, TxBuilderError, states::Preverified, + MantleTxBuilder, MantleTxContext, OpsProofs, TxBuilderError, mantle_tx::MantleTx as _, + states::Preverified, }, }, proofs::leader_claim_proof::{Groth16LeaderClaimProof, LeaderClaimPrivate, LeaderClaimPublic}, diff --git a/tests/src/common/fee_spec.rs b/tests/src/common/fee_spec.rs index f54b814e0..73a3c9dee 100644 --- a/tests/src/common/fee_spec.rs +++ b/tests/src/common/fee_spec.rs @@ -25,6 +25,7 @@ use lb_core::mantle::{ transactions::{ GasPrices, MantleTxBuilder, MantleTxContext, MantleTxGasContext, codec::{encode_signed_mantle_tx, minimum_signed_mantle_tx_size}, + mantle_tx::MantleTx as _, states::{Preverified, VerificationState}, }, }; diff --git a/tests/src/common/wallet/scanner/accounting.rs b/tests/src/common/wallet/scanner/accounting.rs index a6edd54e5..ea1bf3b43 100644 --- a/tests/src/common/wallet/scanner/accounting.rs +++ b/tests/src/common/wallet/scanner/accounting.rs @@ -2,8 +2,10 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, hash_map::Entry}; use lb_common_http_client::ApiBlock; use lb_core::mantle::{ - NoteId, SignedMantleTx, TxHash, Utxo, ops::Op, traits::Hashable as _, - transactions::states::Unverified, + NoteId, SignedMantleTx, TxHash, Utxo, + ops::Op, + traits::Hashable as _, + transactions::{mantle_tx::MantleTx as _, states::Unverified}, }; use lb_key_management_system_service::keys::ZkPublicKey; @@ -221,7 +223,7 @@ mod tests { use lb_core::{ header::{ContentId, HeaderId}, mantle::{ - MantleTx, Note, SignedMantleTx, Utxo, + Note, RawMantleTx, SignedMantleTx, Utxo, ledger::{Inputs, Outputs}, ops::{ Op, @@ -270,7 +272,7 @@ mod tests { /// notes, so a transfer is what the accounting observes owned outputs from. fn transfer_tx(outputs: [Note; 2]) -> SignedMantleTx { SignedMantleTx::new( - MantleTx( + RawMantleTx( [Op::Transfer(TransferOp::new( Inputs::empty(), Outputs::new(outputs), @@ -347,7 +349,7 @@ mod tests { fn accounting_removes_spent_utxos() { let owned = utxo(10, 0, pk(1)); let spend = SignedMantleTx::new( - MantleTx( + RawMantleTx( [Op::ChannelDeposit(DepositOp { channel_id: ChannelId::from([0; 32]), inputs: Inputs::from([owned.id()]), @@ -381,11 +383,11 @@ mod tests { let locked = utxo(10, 0, pk(1)); let declaration = sdp_declaration(locked.id()); let declare_tx = SignedMantleTx::new( - MantleTx([Op::SDPDeclare(declaration.clone())].into()), + RawMantleTx([Op::SDPDeclare(declaration.clone())].into()), OpsProofs::empty(), ); let withdraw_tx = SignedMantleTx::new( - MantleTx( + RawMantleTx( [Op::SDPWithdraw(WithdrawMessage { declaration_id: declaration.id(), locked_note_id: locked.id(), diff --git a/tests/src/common/wallet/transaction/builder_funding.rs b/tests/src/common/wallet/transaction/builder_funding.rs index 5026b91ee..fb6978bed 100644 --- a/tests/src/common/wallet/transaction/builder_funding.rs +++ b/tests/src/common/wallet/transaction/builder_funding.rs @@ -375,7 +375,7 @@ mod tests { ChannelId, MsgId, inscribe::{Inscription, InscriptionOp}, }, - transactions::{GasPrices, MantleTxGasContext}, + transactions::{GasPrices, MantleTxGasContext, mantle_tx::MantleTx as _}, }; use lb_key_management_system_service::keys::Ed25519Key; use lb_testing_framework::configs::wallet::WalletAccount; diff --git a/tests/src/common/wallet/transaction/intent.rs b/tests/src/common/wallet/transaction/intent.rs index 781c063c3..7bc10e5f1 100644 --- a/tests/src/common/wallet/transaction/intent.rs +++ b/tests/src/common/wallet/transaction/intent.rs @@ -6,7 +6,7 @@ use lb_core::mantle::{ Note, Op, transactions::{ GENESIS_EXECUTION_GAS_PRICE, GasPrices, MantleTxBuilder, MantleTxContext, - MantleTxGasContext, + MantleTxGasContext, mantle_tx::MantleTx as _, }, }; use lb_key_management_system_service::keys::ZkPublicKey; diff --git a/tests/src/common/wallet/transaction/prepare.rs b/tests/src/common/wallet/transaction/prepare.rs index 3e78bdfcc..9cef54c38 100644 --- a/tests/src/common/wallet/transaction/prepare.rs +++ b/tests/src/common/wallet/transaction/prepare.rs @@ -155,7 +155,7 @@ fn wallet_reserved_inputs_from_inputs( } fn funding_inputs_from_transfers( - mantle_tx: &MantleTx, + mantle_tx: &impl MantleTx, input_utxos_by_note_id: &HashMap, ) -> Result, WalletTransactionError> { mantle_tx diff --git a/tests/src/common/wallet/transaction/signing.rs b/tests/src/common/wallet/transaction/signing.rs index e2a746b1f..c64adaec3 100644 --- a/tests/src/common/wallet/transaction/signing.rs +++ b/tests/src/common/wallet/transaction/signing.rs @@ -3,10 +3,10 @@ use std::collections::HashMap; use lb_core::mantle::{ - GasCalculator as _, MantleTx, NoteId, Op, OpProof, SignedMantleTx, TxHash, + GasCalculator as _, NoteId, Op, OpProof, RawMantleTx, SignedMantleTx, TxHash, gas::MainnetGasConstants, traits::Hashable as _, - transactions::{MantleTxBuilder, MantleTxContext, OpsProofs}, + transactions::{MantleTxBuilder, MantleTxContext, OpsProofs, mantle_tx::MantleTx as _}, }; use lb_key_management_system_service::keys::ZkKey; @@ -47,7 +47,7 @@ pub(super) fn sign_prepared_wallet_transaction( /// every input with the same wallet key. Suitable for transactions whose /// funding inputs all come from a single wallet account. pub fn transfer_proofs_for_funded_wallet_tx( - tx: &MantleTx, + tx: &RawMantleTx, signing_key: &ZkKey, ) -> Result { let tx_hash = tx.hash(); diff --git a/tests/src/cucumber/steps/manual_transactions/tracked_transactions.rs b/tests/src/cucumber/steps/manual_transactions/tracked_transactions.rs index e7efbac3d..96957d75c 100644 --- a/tests/src/cucumber/steps/manual_transactions/tracked_transactions.rs +++ b/tests/src/cucumber/steps/manual_transactions/tracked_transactions.rs @@ -6,7 +6,7 @@ use lb_core::mantle::{ ledger::{Inputs, Outputs}, ops::transfer::TransferOp, traits::Hashable as _, - transactions::{hash::TxHash, mantle_tx::MantleTx, states::Unverified}, + transactions::{hash::TxHash, mantle_tx::RawMantleTx, states::Unverified}, }; use lb_groth16::Fr; use lb_key_management_system_service::keys::{ZkKey, ZkPublicKey}; @@ -273,7 +273,7 @@ fn create_stateful_invalid_transaction() -> SignedMantleTx { } fn build_signed_transfer(transfer_op: TransferOp) -> SignedMantleTx { - let mantle_tx = MantleTx([Op::Transfer(transfer_op)].into()); + let mantle_tx = RawMantleTx([Op::Transfer(transfer_op)].into()); let transfer_proof = ZkKey::multi_sign(&[], &mantle_tx.hash().to_fr()) .expect("invalid transfer proof should still be constructible"); diff --git a/tests/src/cucumber/steps/manual_zone/support.rs b/tests/src/cucumber/steps/manual_zone/support.rs index 8858e5750..8db28fd51 100644 --- a/tests/src/cucumber/steps/manual_zone/support.rs +++ b/tests/src/cucumber/steps/manual_zone/support.rs @@ -14,7 +14,7 @@ use std::{ use futures::StreamExt as _; use lb_common_http_client::{CommonHttpClient, Slot}; use lb_core::mantle::{ - MantleTx, Note, Op, OpProof, Utxo, Value, + Note, Op, OpProof, RawMantleTx, Utxo, Value, gas::GasCost, ledger::{Inputs, Outputs, OutputsError}, ops::{ @@ -1905,7 +1905,7 @@ pub async fn publish_atomic_zone_withdraw( /// ZK keys. async fn sign_tx_zk( node_url: &Url, - tx: &MantleTx, + tx: &RawMantleTx, public_keys: Vec, ) -> Result { let request_url = diff --git a/tests/src/cucumber/steps/wallet_fund.rs b/tests/src/cucumber/steps/wallet_fund.rs index d50354e64..1b252477c 100644 --- a/tests/src/cucumber/steps/wallet_fund.rs +++ b/tests/src/cucumber/steps/wallet_fund.rs @@ -11,7 +11,7 @@ use lb_core::mantle::{ inscribe::{Inscription, InscriptionOp}, }, traits::Hashable as _, - transactions::builder::MantleTxBuilder, + transactions::{builder::MantleTxBuilder, mantle_tx::MantleTx as _}, }; use lb_http_api_common::bodies::wallet::fund::{WalletFundRequestBody, WalletFundResponseBody}; use lb_key_management_system_service::keys::{Ed25519Key, ZkPublicKey}; diff --git a/tests/testing_framework/src/workloads/inscription/workload.rs b/tests/testing_framework/src/workloads/inscription/workload.rs index 410300bce..8def34460 100644 --- a/tests/testing_framework/src/workloads/inscription/workload.rs +++ b/tests/testing_framework/src/workloads/inscription/workload.rs @@ -17,7 +17,7 @@ use lb_core::mantle::{ }, }, traits::Hashable as _, - transactions::{hash::TxHash, mantle_tx::MantleTx, states::Preverified}, + transactions::{hash::TxHash, mantle_tx::RawMantleTx, states::Preverified}, }; use lb_key_management_system_service::keys::Ed25519Key; use rand::{seq::SliceRandom as _, thread_rng}; @@ -392,7 +392,7 @@ fn build_inscription_transaction( }; let msg_id = op.id(); - let mantle_tx = MantleTx([Op::ChannelInscribe(op)].into()); + let mantle_tx = RawMantleTx([Op::ChannelInscribe(op)].into()); let tx_hash = mantle_tx.hash(); let ed25519_signature = channel diff --git a/tools/config/src/consensus.rs b/tools/config/src/consensus.rs index 9a2e8c510..78f357ea0 100644 --- a/tools/config/src/consensus.rs +++ b/tools/config/src/consensus.rs @@ -5,7 +5,7 @@ use lb_codec::BinaryEncode as _; use lb_core::{ block::genesis::{GenesisBlock, GenesisBlockBuilder}, mantle::{ - CryptarchiaParameter, GenesisTime, MantleTx, Note, NoteId, OpProof, Utxo, + CryptarchiaParameter, GenesisTime, Note, NoteId, OpProof, RawMantleTx, Utxo, ops::{ Op, OpId as _, channel::{ @@ -304,7 +304,7 @@ pub fn create_genesis_block_with_declarations( ops.push(Op::SDPDeclare(declaration)); } - let mantle_tx = MantleTx(Ops::new_unchecked(ops)); + let mantle_tx = RawMantleTx(Ops::new_unchecked(ops)); let mantle_tx_hash = mantle_tx.hash(); let mut ops_proofs = OpsProofs::from([ diff --git a/tools/config/src/sdp.rs b/tools/config/src/sdp.rs index 87733971f..f6e811a0c 100644 --- a/tools/config/src/sdp.rs +++ b/tools/config/src/sdp.rs @@ -1,7 +1,11 @@ use std::iter::repeat_n; use lb_core::{ - mantle::{Op, traits::GenesisTx as _, transactions::GenesisTx}, + mantle::{ + Op, + traits::GenesisTx as _, + transactions::{GenesisTx, mantle_tx::MantleTx as _}, + }, sdp::DeclarationId, }; diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 293bd727f..ba4e460c8 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -26,7 +26,9 @@ use lb_core::{ transfer::TransferOp, }, traits::MantleTxWithProofs, - transactions::{MAX_OPS_PER_TX, MantleTxContext, builder::MantleTxBuilder}, + transactions::{ + MAX_OPS_PER_TX, MantleTxContext, builder::MantleTxBuilder, mantle_tx::MantleTx as _, + }, }, proofs::leader_proof::LeaderProof as _, }; @@ -860,7 +862,7 @@ mod tests { use lb_core::{ crypto::ZkDigest as _, mantle::{ - MantleTx, Note, OpProof, SignedMantleTx, + Note, OpProof, RawMantleTx, SignedMantleTx, channel::Channels, gas::MainnetGasConstants as Gas, ledger::{Inputs, Outputs}, @@ -2304,7 +2306,7 @@ mod tests { .expect("test proofs should fit"); SignedMantleTx::new( - MantleTx(Ops::try_from(ops).expect("test operations should fit")), + RawMantleTx(Ops::try_from(ops).expect("test operations should fit")), proofs, ) } diff --git a/zone-sdk/BRIDGING.md b/zone-sdk/BRIDGING.md index 886a652b5..6f71421bd 100644 --- a/zone-sdk/BRIDGING.md +++ b/zone-sdk/BRIDGING.md @@ -148,7 +148,7 @@ if let Event::BlocksProcessed { finalized, .. } = event { When `withdraw_threshold > 1`, no single sequencer can authorize a withdraw alone. The Zone SDK exposes the lower-level building blocks for threshold coordination, and the proposing sequencer builds the `ChannelWithdrawOp` itself (instead of `WithdrawArg`) because it needs to commit to a specific `withdraw_nonce` before sharing the unsigned tx with the rest of the committee. -- `handle.prepare_tx(ops, inscription)` — build the unsigned `MantleTx` for arbitrary `ops` (including `ChannelWithdraw`) and return it plus this sequencer's own signature. +- `handle.prepare_tx(ops, inscription)` — build the unsigned `RawMantleTx` for arbitrary `ops` (including `ChannelWithdraw`) and return it plus this sequencer's own signature. - `handle.sign_tx(&tx)` — sign a transaction prepared elsewhere, e.g. one proposed by another committee member. - `handle.submit_signed_tx(signed_tx, msg_id)` — submit once the committee has gathered `ChannelState.withdraw_threshold` signatures. diff --git a/zone-sdk/src/sequencer/actor.rs b/zone-sdk/src/sequencer/actor.rs index 1844cfa4f..895df3b7f 100644 --- a/zone-sdk/src/sequencer/actor.rs +++ b/zone-sdk/src/sequencer/actor.rs @@ -595,7 +595,7 @@ mod tests { use lb_core::{ header::HeaderId, mantle::{ - MantleTx, Note, Op, SignedMantleTx, Utxo, + Note, Op, RawMantleTx, SignedMantleTx, Utxo, ledger::Inputs, ops::{ OpProof, @@ -607,7 +607,7 @@ mod tests { }, }, traits::Hashable as _, - transactions::{Ops, OpsProofs}, + transactions::{Ops, OpsProofs, mantle_tx::MantleTx as _}, }, }; use lb_key_management_system_service::keys::{Ed25519Key, ZkKey}; @@ -801,7 +801,7 @@ mod tests { parent: MsgId::root(), signer: Ed25519Key::from_bytes(&[0; 32]).public_key(), }; - let mantle_tx = MantleTx( + let mantle_tx = RawMantleTx( Ops::try_from(vec![ Op::ChannelWithdraw(withdraw_op.clone()), Op::ChannelInscribe(inscribe_op), @@ -839,7 +839,7 @@ mod tests { parent: MsgId::root(), signer: Ed25519Key::from_bytes(&[0; 32]).public_key(), }; - let mantle_tx = MantleTx(Ops::try_from(vec![Op::ChannelInscribe(inscribe_op)]).unwrap()); + let mantle_tx = RawMantleTx(Ops::try_from(vec![Op::ChannelInscribe(inscribe_op)]).unwrap()); let tx_hash = mantle_tx.hash(); let signed_tx = SignedMantleTx::new(mantle_tx, OpsProofs::empty()); @@ -864,7 +864,7 @@ mod tests { parent: MsgId::root(), signer: Ed25519Key::from_bytes(&[0; 32]).public_key(), }; - let mantle_tx = MantleTx(Ops::try_from(vec![Op::ChannelInscribe(inscribe_op)]).unwrap()); + let mantle_tx = RawMantleTx(Ops::try_from(vec![Op::ChannelInscribe(inscribe_op)]).unwrap()); let tx_hash = mantle_tx.hash(); let signed_tx = SignedMantleTx::new(mantle_tx, OpsProofs::empty()); diff --git a/zone-sdk/src/sequencer/block_fetch.rs b/zone-sdk/src/sequencer/block_fetch.rs index 2cbe3aa3d..02bcb4016 100644 --- a/zone-sdk/src/sequencer/block_fetch.rs +++ b/zone-sdk/src/sequencer/block_fetch.rs @@ -14,6 +14,7 @@ use lb_core::{ traits::Hashable as _, transactions::{ hash::TxHash, + mantle_tx::MantleTx as _, states::{Unverified, VerificationState}, }, }, @@ -776,7 +777,7 @@ fn touches_channel_tip( #[cfg(test)] mod tests { use lb_core::mantle::{ - MantleTx, NoteId, + NoteId, RawMantleTx, channel::{SlotTimeframe, SlotTimeout}, ledger::Inputs, ops::{ @@ -1160,7 +1161,7 @@ mod tests { } fn dummy_pending_tx(seed: u8) -> SignedMantleTx { - let mantle_tx = MantleTx( + let mantle_tx = RawMantleTx( [Op::ChannelInscribe(InscriptionOp { channel_id: [0u8; 32].into(), inscription: Inscription::new_unchecked(vec![seed]), diff --git a/zone-sdk/src/sequencer/client.rs b/zone-sdk/src/sequencer/client.rs index eefafb126..bee4d881f 100644 --- a/zone-sdk/src/sequencer/client.rs +++ b/zone-sdk/src/sequencer/client.rs @@ -2,7 +2,7 @@ use lb_core::mantle::{ SignedMantleTx, channel::{SlotTimeframe, SlotTimeout}, ops::channel::{MsgId, config::Keys, inscribe::Inscription}, - transactions::{Ops, mantle_tx::MantleTx, states::Unverified}, + transactions::{Ops, mantle_tx::RawMantleTx, states::Unverified}, }; use lb_key_management_system_service::keys::Ed25519Signature; use tokio::sync::{broadcast, mpsc, oneshot, watch}; @@ -129,14 +129,14 @@ impl SequencerClient { Self::recv(response_rx).await? } - /// Build a [`MantleTx`] for the given ops and an inscription message. + /// Build a [`RawMantleTx`] for the given ops and an inscription message. /// /// Async counterpart of [`super::SequencerHandle::prepare_tx`]. pub async fn prepare_tx( &self, ops: Ops, data: Inscription, - ) -> Result<(MantleTx, MsgId, Ed25519Signature), Error> { + ) -> Result<(RawMantleTx, MsgId, Ed25519Signature), Error> { let (response_tx, response_rx) = oneshot::channel(); self.send(ActorRequest::PrepareTx { ops, @@ -146,11 +146,11 @@ impl SequencerClient { Self::recv(response_rx).await? } - /// Sign a [`MantleTx`] using the sequencer's key. + /// Sign a [`RawMantleTx`] using the sequencer's key. /// /// Async counterpart of [`super::SequencerHandle::sign_tx`]. Clones `tx` /// internally so the call site can keep its borrow. - pub async fn sign_tx(&self, tx: &MantleTx) -> Result { + pub async fn sign_tx(&self, tx: &RawMantleTx) -> Result { let (response_tx, response_rx) = oneshot::channel(); self.send(ActorRequest::SignTx { tx: tx.clone(), diff --git a/zone-sdk/src/sequencer/handle.rs b/zone-sdk/src/sequencer/handle.rs index d2465b519..8854ea253 100644 --- a/zone-sdk/src/sequencer/handle.rs +++ b/zone-sdk/src/sequencer/handle.rs @@ -2,7 +2,7 @@ use lb_core::mantle::{ SignedMantleTx, channel::{SlotTimeframe, SlotTimeout}, ops::channel::{MsgId, config::Keys, inscribe::Inscription}, - transactions::{Ops, mantle_tx::MantleTx, states::Unverified}, + transactions::{Ops, mantle_tx::RawMantleTx, states::Unverified}, }; use lb_key_management_system_service::keys::Ed25519Signature; @@ -80,24 +80,25 @@ where self.sequencer.do_publish(data).await } - /// Build a [`MantleTx`] for the given ops and an inscription message, + /// Build a [`RawMantleTx`] for the given ops and an inscription message, /// without submitting it. /// - /// The returned [`MantleTx`] should be signed by all parties and submitted - /// via [`Self::submit_signed_tx`]. Does not mutate sequencer state. + /// The returned [`RawMantleTx`] should be signed by all parties and + /// submitted via [`Self::submit_signed_tx`]. Does not mutate sequencer + /// state. pub fn prepare_tx( &mut self, ops: Ops, data: Inscription, - ) -> Result<(MantleTx, MsgId, Ed25519Signature), Error> { + ) -> Result<(RawMantleTx, MsgId, Ed25519Signature), Error> { self.sequencer.do_prepare_tx(ops, data) } - /// Sign a [`MantleTx`] using the sequencer's key. + /// Sign a [`RawMantleTx`] using the sequencer's key. /// /// Useful when signing tx built by other sequencers (e.g. withdraw). Does /// not mutate sequencer state. - pub fn sign_tx(&mut self, tx: &MantleTx) -> Result { + pub fn sign_tx(&mut self, tx: &RawMantleTx) -> Result { self.sequencer.do_sign_tx(tx) } diff --git a/zone-sdk/src/sequencer/state.rs b/zone-sdk/src/sequencer/state.rs index 3a4df111c..5cdfb6349 100644 --- a/zone-sdk/src/sequencer/state.rs +++ b/zone-sdk/src/sequencer/state.rs @@ -9,7 +9,7 @@ use lb_core::{ channel::{ChannelId, MsgId, inscribe::Inscription}, }, traits::Hashable as _, - transactions::{hash::TxHash, states::Unverified}, + transactions::{hash::TxHash, mantle_tx::MantleTx as _, states::Unverified}, }, }; use rpds::HashTrieSetSync; @@ -1041,7 +1041,7 @@ impl TxState { #[cfg(test)] mod tests { use lb_core::mantle::{ - MantleTx, Op::ChannelInscribe, ops::channel::inscribe::InscriptionOp, + Op::ChannelInscribe, RawMantleTx, ops::channel::inscribe::InscriptionOp, traits::Hashable as _, transactions::OpsProofs, }; use lb_key_management_system_service::keys::Ed25519PublicKey; @@ -1050,7 +1050,7 @@ mod tests { use crate::test_support::header_id; fn make_dummy_tx(data: u8) -> SignedMantleTx { - let mantle_tx = MantleTx( + let mantle_tx = RawMantleTx( [ChannelInscribe(InscriptionOp { channel_id: [0u8; 32].into(), inscription: [data].into(), diff --git a/zone-sdk/src/sequencer/tx_builder.rs b/zone-sdk/src/sequencer/tx_builder.rs index afd9e8b8d..f54b0551d 100644 --- a/zone-sdk/src/sequencer/tx_builder.rs +++ b/zone-sdk/src/sequencer/tx_builder.rs @@ -11,7 +11,11 @@ use lb_core::{ }, }, traits::Hashable as _, - transactions::{MantleTxBuilder, Ops, OpsProofs, mantle_tx::MantleTx, states::Unverified}, + transactions::{ + MantleTxBuilder, Ops, OpsProofs, + mantle_tx::{MantleTx, RawMantleTx}, + states::Unverified, + }, }, proofs::channel_multi_sig_proof::{ChannelMultiSigProof, IndexedSignature}, }; @@ -33,14 +37,14 @@ pub(super) async fn fund_ops( node: &Node, funding: Option<&FundingConfig>, ops: Vec, -) -> Result<(MantleTx, Option), Error> +) -> Result<(RawMantleTx, Option), Error> where Node: adapter::Node + Sync, { let Some(funding) = funding else { let ops = Ops::try_from(ops) .map_err(|e| Error::Network(format!("too many ops in transaction: {e:?}")))?; - return Ok((MantleTx(ops), None)); + return Ok((RawMantleTx(ops), None)); }; let tx_builder = MantleTxBuilder::new() @@ -66,7 +70,7 @@ where /// funded transaction's op layout (funding appends the transfer as the last /// op; a fee-less transaction carries none). pub(super) fn attach_transfer_proof( - tx: &MantleTx, + tx: &impl MantleTx, mut channel_proofs: OpsProofs, transfer_proof: Option, ) -> Result { @@ -105,7 +109,7 @@ pub(super) fn attach_transfer_proof( reason = "Belongs to the atomic withdraw flow; restored with `do_publish_atomic_withdraw`." )] pub(super) fn build_atomic_withdraw_ops_proofs( - tx: &MantleTx, + tx: &impl MantleTx, own_key_index: ChannelKeyIndex, own_sig: Ed25519Signature, transfer_proof: Option<&OpProof>, @@ -261,7 +265,7 @@ pub(super) fn prepare_tx( signing_key: &Ed25519Key, inscription: Inscription, parent: MsgId, -) -> (MantleTx, MsgId, Ed25519Signature) { +) -> (RawMantleTx, MsgId, Ed25519Signature) { let inscription_op = InscriptionOp { channel_id, inscription, @@ -273,7 +277,7 @@ pub(super) fn prepare_tx( ops.try_push(Op::ChannelInscribe(inscription_op)).unwrap(); // TODO: fund tx - let tx = MantleTx(ops); + let tx = RawMantleTx(ops); let inscription_sig = sign_tx(tx.hash(), signing_key); diff --git a/zone-sdk/src/sequencer/zone_sequencer.rs b/zone-sdk/src/sequencer/zone_sequencer.rs index 2bc6122d7..6222886a2 100644 --- a/zone-sdk/src/sequencer/zone_sequencer.rs +++ b/zone-sdk/src/sequencer/zone_sequencer.rs @@ -15,7 +15,12 @@ use lb_core::{ channel::{ChannelState, SlotTimeframe, SlotTimeout}, ops::channel::{ChannelId, MsgId, config::Keys, inscribe::Inscription}, traits::Hashable as _, - transactions::{Ops, hash::TxHash, mantle_tx::MantleTx, states::Unverified}, + transactions::{ + Ops, + hash::TxHash, + mantle_tx::{MantleTx as _, RawMantleTx}, + states::Unverified, + }, }, }; use lb_key_management_system_service::keys::{Ed25519Key, Ed25519Signature}; @@ -169,10 +174,10 @@ pub(super) enum ActorRequest { PrepareTx { ops: Ops, data: Inscription, - response_tx: oneshot::Sender>, + response_tx: oneshot::Sender>, }, SignTx { - tx: MantleTx, + tx: RawMantleTx, response_tx: oneshot::Sender>, }, } @@ -891,7 +896,7 @@ where &self, ops: Ops, data: Inscription, - ) -> Result<(MantleTx, MsgId, Ed25519Signature), Error> { + ) -> Result<(RawMantleTx, MsgId, Ed25519Signature), Error> { self.ensure_ready()?; let parent = self.compute_publish_parent(); Ok(build_prepare_tx( @@ -903,7 +908,7 @@ where )) } - pub(super) fn do_sign_tx(&self, tx: &MantleTx) -> Result { + pub(super) fn do_sign_tx(&self, tx: &RawMantleTx) -> Result { self.ensure_ready()?; Ok(build_sign_tx(tx.hash(), &self.signing_key)) } diff --git a/zone-sdk/src/test_support.rs b/zone-sdk/src/test_support.rs index c24d9b8d5..ac99fc8c1 100644 --- a/zone-sdk/src/test_support.rs +++ b/zone-sdk/src/test_support.rs @@ -14,7 +14,7 @@ use lb_common_http_client::{ use lb_core::{ header::{ContentId, HeaderId}, mantle::{ - MantleTx, Op, SignedMantleTx, + Op, RawMantleTx, SignedMantleTx, channel::ChannelState, ops::{ OpProof, @@ -328,7 +328,7 @@ pub fn live_event(block: &ApiBlock) -> ProcessedBlockEvent { /// Suitable for tests that only care about op extraction, not verification. pub fn unverified_tx_with_ops(ops: Vec) -> SignedMantleTx { let n = ops.len(); - let mantle_tx = MantleTx(Ops::try_from(ops).expect("ops fit")); + let mantle_tx = RawMantleTx(Ops::try_from(ops).expect("ops fit")); SignedMantleTx::new( mantle_tx, OpsProofs::new_unchecked(vec![OpProof::Ed25519Sig(Ed25519Signature::zero()); n]),