diff --git a/core/src/mantle/channel.rs b/core/src/mantle/channel.rs index aff774953..13303b10a 100644 --- a/core/src/mantle/channel.rs +++ b/core/src/mantle/channel.rs @@ -9,12 +9,17 @@ use crate::{ mantle::{ NoteId, channel_notes::{self, ChannelNotes}, - ledger::{self, ExecutableOperation as _}, - ops::channel::{ - ChannelId, ChannelKeyIndex, MsgId, - config::Keys, - inscribe::{InscriptionExecutionContext, InscriptionOp}, + ledger, + ledger::verification_mode::GenesisMode, + ops::{ + SignedOperation, + channel::{ + ChannelId, ChannelKeyIndex, MsgId, + config::Keys, + inscribe::{InscriptionExecutionContext, InscriptionOp}, + }, }, + transactions::states::Verified, }, }; @@ -133,11 +138,15 @@ impl Default for Channels { } impl Channels { - pub fn from_genesis(op: &InscriptionOp) -> Result<(Self, Vec), Error> { - let (context, events) = op.execute(InscriptionExecutionContext { - channels: Self::default(), - block_slot: Slot::default(), - })?; + pub fn from_genesis( + signed_operation: SignedOperation, + ) -> Result<(Self, Vec), Error> { + let (context, events) = signed_operation + .execute(InscriptionExecutionContext { + channels: Self::default(), + block_slot: Slot::default(), + }) + .map_err(|(_signed_operation, error)| error)?; Ok((context.channels, events)) } @@ -233,8 +242,8 @@ impl ChannelState { #[cfg(test)] mod tests { use ark_ff::AdditiveGroup as _; - use lb_groth16::Fr; - use lb_key_management_system_keys::keys::{Ed25519Key, ZkKey}; + use lb_groth16::{CompressedGroth16Proof, Fr}; + use lb_key_management_system_keys::keys::{Ed25519Key, ZkKey, ZkSignature}; use lb_utils::blake_rng::RngCore as _; use rand::thread_rng; @@ -243,7 +252,7 @@ mod tests { events::{DepositNote, TxEventPayload}, mantle::{ Note, Utxo, Value, - ledger::Utxos, + ledger::{Utxos, verification_mode::StandardMode}, ops::{ OpId as _, channel::{ @@ -252,8 +261,9 @@ mod tests { withdraw::{ChannelWithdrawOp, WithdrawExecutionContext}, }, }, - transactions::{GasPrices, mantle_tx::MantleTxGasContext}, + transactions::{GasPrices, tx_list::ops::OpsGasContext}, }, + proofs::channel_multi_sig_proof::{ChannelMultiSigProof, IndexedSignatures}, }; fn test_public_key(seed: u8) -> PublicKey { @@ -356,7 +366,7 @@ mod tests { channel_notes: ChannelNotes::new(), }; - let gas_context = MantleTxGasContext::from_channels(&channels, GasPrices::new(0, 0)); + let gas_context = OpsGasContext::from_channels(&channels, GasPrices::new(0, 0)); assert_eq!(gas_context.transfer_threshold(&first_id), Some(1)); assert_eq!(gas_context.transfer_threshold(&second_id), Some(2)); @@ -376,10 +386,14 @@ mod tests { inputs: [note_id].into(), metadata: Metadata::empty(), }; + let empty_proof = ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])); + let signed_deposit: SignedOperation<_, _, StandardMode> = + SignedOperation::new(deposit_op, empty_proof).into_state_trusted(); + let operation = signed_deposit.operation().clone(); let utxo_tree = utxo_tree(vec![utxo]); - let (updated, events) = deposit_op + let (updated, events) = signed_deposit .execute(DepositExecutionContext { channels, utxos: utxo_tree, @@ -389,7 +403,7 @@ mod tests { // The deposited note is consumed and re-created as a channel note // under a new NoteId. - let deposited = Utxo::new(deposit_op.op_id(), 0, utxo.note).id(); + let deposited = Utxo::new(operation.op_id(), 0, utxo.note).id(); assert!(!updated.utxos.contains(¬e_id)); assert!(!updated.channels.is_channel_note(¬e_id)); assert!(updated.utxos.contains(&deposited)); @@ -419,10 +433,10 @@ mod tests { panic!("events should include deposit event") }; assert_eq!(*tx_hash, [0; 32].into()); - assert_eq!(*op_id, deposit_op.op_id()); - assert_eq!(*event_channel_id, deposit_op.channel_id); + assert_eq!(*op_id, operation.op_id()); + assert_eq!(*event_channel_id, operation.channel_id); assert_eq!(*amount, utxo.note.value); - assert_eq!(*metadata, deposit_op.metadata); + assert_eq!(*metadata, operation.metadata); assert_eq!( notes.clone().into_inner(), vec![DepositNote { @@ -445,8 +459,12 @@ mod tests { inputs: [first.id(), second.id()].into(), metadata: Metadata::empty(), }; + let empty_proof = ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])); + let signed_deposit: SignedOperation<_, _, StandardMode> = + SignedOperation::new(deposit_op, empty_proof).into_state_trusted(); + let op_id = signed_deposit.operation().op_id(); - let (updated, _) = deposit_op + let (updated, _) = signed_deposit .execute(DepositExecutionContext { channels: Channels::new(), utxos: utxo_tree(vec![first, second]), @@ -456,8 +474,8 @@ mod tests { // Each input is re-created at its own output index, so the two notes // get distinct identifiers even though they share an OpId. - let first_deposited = Utxo::new(deposit_op.op_id(), 0, first.note).id(); - let second_deposited = Utxo::new(deposit_op.op_id(), 1, second.note).id(); + let first_deposited = Utxo::new(op_id, 0, first.note).id(); + let second_deposited = Utxo::new(op_id, 1, second.note).id(); assert_ne!(first_deposited, second_deposited); assert!(!updated.utxos.contains(&first.id())); @@ -485,8 +503,11 @@ mod tests { channel_id, inputs: [note_id].into(), }; + let empty_proof = ChannelMultiSigProof::new(IndexedSignatures::try_from(vec![]).unwrap()); + let signed_withdraw: SignedOperation<_, _, StandardMode> = + SignedOperation::new(withdraw_op, empty_proof).into_state_trusted(); - let (updated, events) = withdraw_op + let (updated, events) = signed_withdraw .execute(WithdrawExecutionContext { channels, tx_hash: [1; 32].into(), @@ -508,15 +529,21 @@ mod tests { channel_id, inputs: [note_id].into(), }; + let empty_proof = ChannelMultiSigProof::new(IndexedSignatures::try_from(vec![]).unwrap()); + let signed_withdraw: SignedOperation<_, _, StandardMode> = + SignedOperation::new(withdraw_op, empty_proof).into_state_trusted(); - let result = withdraw_op.execute(WithdrawExecutionContext { + let result = signed_withdraw.execute(WithdrawExecutionContext { channels, tx_hash: [0; 32].into(), }); assert!(matches!( result, - Err(Error::ChannelNotes(channel_notes::Error::NotInChannel(_))) + Err(( + _, + Error::ChannelNotes(channel_notes::Error::NotInChannel(_)) + )) )); } diff --git a/core/src/mantle/mod.rs b/core/src/mantle/mod.rs index 57b3a3163..4cdd0f9e8 100644 --- a/core/src/mantle/mod.rs +++ b/core/src/mantle/mod.rs @@ -1,4 +1,6 @@ pub mod channel; +mod channel_notes; +mod fixtures; pub mod gas; pub mod ledger; pub mod mock; @@ -6,14 +8,9 @@ pub mod ops; pub mod traits; pub mod transactions; -mod channel_notes; -mod fixtures; - pub use gas::{GasProfile, TxGasCalculator}; pub use ledger::{Note, NoteId, Utxo, Value}; -pub use ops::{Op, OpProof}; -pub use transactions::{ - CryptarchiaParameter, GenesisTime, MantleTransaction, hash::TxHash, mantle_tx::RawMantleTx, -}; +pub use ops::{Op, OpProof, OpProofRef, OpRef}; +pub use transactions::{CryptarchiaParameter, GenesisTime, SignedOps, hash::TxHash}; pub use crate::mantle::transactions::VerificationError; diff --git a/ledger/src/cryptarchia/mod.rs b/ledger/src/cryptarchia/mod.rs index 494e90fe7..2cc1ac386 100644 --- a/ledger/src/cryptarchia/mod.rs +++ b/ledger/src/cryptarchia/mod.rs @@ -12,10 +12,9 @@ use lb_core::{ mantle::{ NoteId, Utxo, Value, gas::{Gas, GasCost, GasOverflow, GasPrice, GasProfile}, - ledger::ExecutableOperation as _, - ops::{pow::PowTarget, transfer::TransferOp}, - traits::GenesisTx, - transactions::{GENESIS_EXECUTION_GAS_PRICE, GENESIS_STORAGE_GAS_PRICE}, + ledger::verification_mode::{GenesisMode, StandardMode}, + ops::{SignedOperation, pow::PowTarget, transfer::TransferOp}, + transactions::{GENESIS_EXECUTION_GAS_PRICE, GENESIS_STORAGE_GAS_PRICE, states::Verified}, }, proofs::leader_proof::{self, LeaderPublic}, sdp::Declarations, @@ -556,17 +555,18 @@ impl LedgerState { pub fn try_apply_transfer( mut self, - transfer_op: &TransferOp, + signed_operation: SignedOperation, ) -> Result<(Self, Balance, Vec), LedgerError> { + let operation = signed_operation.operation(); // Compute the balance - let balance = transfer_op + let balance = operation .balance(&self.utxos) .map_err(mantle::Error::Transfer)?; //execute the transfer - let (result, events) = transfer_op + let (result, events) = signed_operation .execute(self.utxos) - .map_err(mantle::Error::Transfer)?; + .map_err(|(_signed_operation, error)| mantle::Error::Transfer(error))?; self.utxos = result; Ok((self, balance, events)) } @@ -697,13 +697,16 @@ impl LedgerState { } pub fn from_genesis_tx( - tx: impl GenesisTx, + transfer: &SignedOperation, config: &Config, epoch_nonce: Fr, ) -> Result> { - let transfer_op = tx.genesis_transfer(); - if !transfer_op.inputs.is_empty() { - let first_input = transfer_op + let operation = transfer.operation(); + + // This transfer has not yet been verified despite the state saying so. + // This is its verification. + if !operation.inputs.is_empty() { + let first_input = operation .inputs .iter() .next() @@ -712,7 +715,7 @@ impl LedgerState { return Err(LedgerError::InputInGenesis(first_input)); } - Ok(Self::from_utxos(transfer_op.utxos(), config, epoch_nonce)) + Ok(Self::from_utxos(operation.utxos(), config, epoch_nonce)) } pub fn from_utxos(utxos: impl IntoIterator, config: &Config, nonce: Fr) -> Self { @@ -832,23 +835,25 @@ pub mod tests { use lb_core::{ crypto::{Digest as _, Hasher}, mantle::{ - MantleTransaction, Note, Op, + Note, Op, OpProof::ZkSig, - RawMantleTx, TxGasCalculator as _, + SignedOps, TxGasCalculator as _, gas::MainnetGasProfile, ledger::{Inputs, Outputs}, - ops::{leader_claim::VoucherCm, sdp::SDPDeclareOp}, + ops::{ZkAndEd25519Proof, leader_claim::VoucherCm, sdp::SDPDeclareOp}, traits::Hashable as _, transactions::{ - GasPrices, + GasPrices, OpProofs, Ops, states::{Preverified, Unverified}, }, }, sdp::{Declaration, DeclarationId, Locator, ServiceParameters, ServiceType}, }; use lb_cryptarchia_engine::EpochConfig; - use lb_groth16::{AdditiveGroup as _, ModulusShift}; - use lb_key_management_system_keys::keys::{Ed25519Key, Ed25519PublicKey, ZkKey, ZkSignature}; + use lb_groth16::{AdditiveGroup as _, CompressedGroth16Proof, ModulusShift}; + use lb_key_management_system_keys::keys::{ + Ed25519Key, Ed25519PublicKey, Ed25519Signature, ZkKey, ZkSignature, + }; use lb_utils::math::{NonNegativeF64, NonNegativeRatio}; use num_bigint::BigUint; use rand::{RngCore as _, thread_rng}; @@ -983,7 +988,7 @@ pub mod tests { slot, &proof, &UncleSlots::default(), - std::iter::empty::<&MantleTransaction>(), + std::iter::empty::>(), )?; ledger.commit_update(id, state); Ok(id) @@ -1205,6 +1210,14 @@ pub mod tests { zk_id: zk_key.to_public_key(), locked_note_id: sdp_utxo.id(), }; + + let proof = ZkAndEd25519Proof { + zk_sig: ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ed25519_sig: Ed25519Signature::zero(), + }; + let signed_operation = + SignedOperation::<_, _, StandardMode>::new(declare_op.clone(), proof) + .into_state_trusted(); let config = ledger.config().clone(); let block_ledger = ledger.states.get_mut(&id).unwrap(); @@ -1212,7 +1225,7 @@ pub mod tests { .mantle_ledger .clone() .try_apply_sdp_declaration( - &declare_op, + signed_operation, block_ledger.cryptarchia_ledger.latest_utxos(), &config, ) @@ -1883,7 +1896,7 @@ pub mod tests { fn create_tx_with_transfer( inputs: &[(&ZkKey, &Utxo)], outputs: Vec, - ) -> (MantleTransaction, TransferOp, ZkSignature) { + ) -> (SignedOps, TransferOp, ZkSignature) { let sks = inputs .iter() .map(|(sk, _)| (*sk).clone()) @@ -1893,9 +1906,10 @@ pub mod tests { Inputs::try_new(inputs).expect("Invalid inputs size"), Outputs::try_new(outputs).expect("Invalid outputs size"), ); - let mantle_tx = RawMantleTx([Op::Transfer(transfer_op.clone())].into()); + let mantle_tx = Ops::from([Op::Transfer(transfer_op.clone())]); let transfer_sig = ZkKey::multi_sign(&sks, &mantle_tx.hash().to_fr()).unwrap(); - let tx = MantleTransaction::new(mantle_tx, [ZkSig(transfer_sig.clone())].into()); + let op_proofs = OpProofs::from([ZkSig(transfer_sig.clone())]); + let tx = SignedOps::from_parts(mantle_tx, op_proofs).unwrap(); (tx, transfer_op, transfer_sig) } @@ -1913,13 +1927,15 @@ pub mod tests { let output_note = Note::new(200, output_note_sk.to_public_key()); let ledger_state = LedgerState::from_utxos([input_utxo], &config(), Fr::ZERO); - let (tx, transfer_op, _transfer_sig) = create_tx_with_transfer( + let (tx, transfer_op, transfer_proof) = create_tx_with_transfer( &[(¬e_sk, &input_utxo), (¬e_sk, &input_utxo)], vec![output_note], ); - + let signed_operation = + SignedOperation::new(transfer_op, transfer_proof).into_state_trusted(); let _fees = tx.total_gas_cost::(&GasPrices::new(0, 0)); - let result = ledger_state.try_apply_transfer::<(), MainnetGasProfile>(&transfer_op); + + let result = ledger_state.try_apply_transfer::<(), MainnetGasProfile>(signed_operation); assert!(result.is_err()); } @@ -1940,12 +1956,14 @@ pub mod tests { let output_note2 = Note::new(3000, output_note2_sk.to_public_key()); let ledger_state = LedgerState::from_utxos([input_utxo], &config(), Fr::ZERO); - let (tx, transfer_op, _transfer_sig) = + let (tx, transfer_op, transfer_proof) = create_tx_with_transfer(&[(¬e_sk, &input_utxo)], vec![output_note1, output_note2]); + let signed_operation = + SignedOperation::new(transfer_op, transfer_proof).into_state_trusted(); let _fees = tx.total_gas_cost::(&GasPrices::new(0, 0)); let (new_state, balance, events) = ledger_state - .try_apply_transfer::<(), MainnetGasProfile>(&transfer_op) + .try_apply_transfer::<(), MainnetGasProfile>(signed_operation) .unwrap(); assert_eq!( @@ -1967,7 +1985,7 @@ pub mod tests { assert!(new_state.utxos.contains(&output_utxo2.id())); // The new outputs can be spent in future transactions - let (tx, transfer_op, _transfer_sig) = create_tx_with_transfer( + let (tx, transfer_op, transfer_proof) = create_tx_with_transfer( &[ (&output_note1_sk, &output_utxo1), (&output_note2_sk, &output_utxo2), @@ -1975,9 +1993,11 @@ pub mod tests { vec![], ); + let signed_operation = + SignedOperation::new(transfer_op, transfer_proof).into_state_trusted(); let _fees = tx.total_gas_cost::(&GasPrices::new(0, 0)); let (final_state, final_balance, events) = new_state - .try_apply_transfer::<(), MainnetGasProfile>(&transfer_op) + .try_apply_transfer::<(), MainnetGasProfile>(signed_operation) .unwrap(); assert_eq!( @@ -2026,11 +2046,14 @@ pub mod tests { ]; for non_existent_utxo in invalid_utxos { - let (_tx, transfer_op, _transfer_sig) = + let (_tx, transfer_op, transfer_proof) = create_tx_with_transfer(&[(&ZkKey::zero(), &non_existent_utxo)], vec![]); + + let signed_operation = + SignedOperation::new(transfer_op, transfer_proof).into_state_trusted(); let result = ledger_state .clone() - .try_apply_transfer::<(), MainnetGasProfile>(&transfer_op); + .try_apply_transfer::<(), MainnetGasProfile>(signed_operation); assert!(matches!(result, Err(LedgerError::Mantle(_)))); } } @@ -2048,21 +2071,25 @@ pub mod tests { let output_note = Note::new(1, Fr::from(BigUint::from(2u8)).into()); let ledger_state = LedgerState::from_utxos([input_utxo], &config(), Fr::ZERO); - let (_tx, transfer_op, _transfer_sig) = + let (_tx, transfer_op, transfer_proof) = create_tx_with_transfer(&[(&input_sk, &input_utxo)], vec![output_note, output_note]); + let signed_operation = + SignedOperation::new(transfer_op, transfer_proof).into_state_trusted(); let (_, balance, events) = ledger_state .clone() - .try_apply_transfer::<(), MainnetGasProfile>(&transfer_op) + .try_apply_transfer::<(), MainnetGasProfile>(signed_operation) .unwrap(); assert_eq!(balance, -1); assert!(events.is_empty()); - let (_tx, transfer_op, _transfer_sig) = + let (_tx, transfer_op, transfer_proof) = create_tx_with_transfer(&[(&input_sk, &input_utxo)], vec![output_note]); + let signed_operation = + SignedOperation::new(transfer_op, transfer_proof).into_state_trusted(); assert_eq!( ledger_state - .try_apply_transfer::<(), MainnetGasProfile>(&transfer_op,) + .try_apply_transfer::<(), MainnetGasProfile>(signed_operation) .unwrap() .1, 0 @@ -2080,11 +2107,13 @@ pub mod tests { }; let ledger_state = LedgerState::from_utxos([input_utxo], &config(), Fr::ZERO); - let (tx, transfer_op, _transfer_sig) = + let (tx, transfer_op, transfer_proof) = create_tx_with_transfer(&[(&input_sk, &input_utxo)], vec![]); + let signed_operation = + SignedOperation::new(transfer_op, transfer_proof).into_state_trusted(); let _fees = tx.total_gas_cost::(&GasPrices::new(0, 0)); - let result = ledger_state.try_apply_transfer::<(), MainnetGasProfile>(&transfer_op); + let result = ledger_state.try_apply_transfer::<(), MainnetGasProfile>(signed_operation); assert!(result.is_ok()); let (new_state, balance, events) = result.unwrap(); diff --git a/ledger/src/cryptarchia/test/tsi_simulation.rs b/ledger/src/cryptarchia/test/tsi_simulation.rs index e30fae053..9f4cd0f22 100644 --- a/ledger/src/cryptarchia/test/tsi_simulation.rs +++ b/ledger/src/cryptarchia/test/tsi_simulation.rs @@ -26,8 +26,9 @@ use std::{num::NonZero, sync::Arc}; use lb_core::{ crypto::{Digest as _, Hasher}, mantle::{ - MantleTransaction, Note, Utxo, Value, + Note, SignedOps, Utxo, Value, gas::MainnetGasProfile, + ledger::verification_mode::StandardMode, transactions::{ GENESIS_EXECUTION_GAS_PRICE, GENESIS_STORAGE_GAS_PRICE, states::Preverified, }, @@ -384,7 +385,7 @@ fn apply_block_to_ledger( slot, &proof, uncle_slots, - std::iter::empty::<&MantleTransaction>(), + std::iter::empty::>(), ) .expect("ledger update"); ledger.commit_update(id, state); diff --git a/ledger/src/lib.rs b/ledger/src/lib.rs index 44ae201cc..3249630b5 100644 --- a/ledger/src/lib.rs +++ b/ledger/src/lib.rs @@ -16,10 +16,11 @@ use lb_core::{ crypto::Hash as BlockHash, events::{Events, HeaderEvent, TxEvent, TxEventPayload}, mantle::{ - NoteId, Op, Utxo, Value, VerificationError, + NoteId, TxGasCalculator, Utxo, Value, VerificationError, gas::{Gas, GasCost, GasOverflow, GasProfile}, - ledger::ExecutableOperation as _, + ledger::verification_mode::StandardMode, ops::{ + SignedOp, channel::{ channel_transfer::ChannelTransferExecutionContext, deposit::DepositExecutionContext, withdraw::WithdrawExecutionContext, @@ -27,8 +28,13 @@ use lb_core::{ leader_claim::LeaderClaimExecutionContext, pow::{ClaimPoWRewardExecutionContext, PowReward}, }, - traits::{GenesisTx, MantleTxWithProofs, PreverifiedMantleTx}, - transactions::{GasPrices, MantleTxGasContext, hash::TxHash, mantle_tx::MantleTxContext}, + traits::{GenesisTx, PreverifiedMantleTransaction, genesis::GenesisOps}, + transactions::{ + GasPrices, + hash::TxHash, + states::Verified, + tx_list::ops::{OpsContext, OpsGasContext}, + }, }, proofs::leader_proof, }; @@ -152,17 +158,17 @@ where /// /// On success, a new [`LedgerState`] is returned, which can then be /// committed by calling [`Self::commit_update`]. - pub fn prepare_update<'tx, Tx, LeaderProof, Profile>( + pub fn prepare_update( &self, id: Id, parent_id: Id, slot: Slot, proof: &LeaderProof, uncle_slots: &UncleSlots, - txs: impl Iterator, + txs: impl Iterator, ) -> Result<(Id, LedgerState, Events), LedgerError> where - Tx: PreverifiedMantleTx + 'tx, + Tx: PreverifiedMantleTransaction + TxGasCalculator + Clone, LeaderProof: leader_proof::LeaderProof, Profile: GasProfile, Id: Into, @@ -227,17 +233,17 @@ pub struct LedgerState { } impl LedgerState { - fn try_update<'tx, Tx, LeaderProof, Id, Profile>( + fn try_update( self, block_id: Id, slot: Slot, proof: &LeaderProof, uncle_slots: &UncleSlots, - txs: impl Iterator, + txs: impl Iterator, config: &Config, ) -> Result<(Self, Events), LedgerError> where - Tx: PreverifiedMantleTx + 'tx, + Tx: PreverifiedMantleTransaction + TxGasCalculator + Clone, LeaderProof: leader_proof::LeaderProof, Profile: GasProfile, Id: Into, @@ -447,13 +453,13 @@ impl LedgerState { } /// Apply the contents of an update to the ledger state. - pub fn try_apply_contents<'tx, Tx, Id, Profile: GasProfile>( + pub fn try_apply_contents( mut self, config: &Config, - txs: impl Iterator, + txs: impl Iterator, ) -> Result<(Self, Vec), LedgerError> where - Tx: PreverifiedMantleTx + 'tx, + Tx: PreverifiedMantleTransaction + TxGasCalculator + Clone, { let mut total_block_execution_gas: Gas = 0.into(); let mut total_block_storage_gas: Gas = 0.into(); @@ -464,7 +470,8 @@ impl LedgerState { for tx in txs { let balance; let events; - (self, balance, events) = self.try_apply_tx::<_, _, Profile>(config, tx)?; + + (self, balance, events) = self.try_apply_tx::<_, _, Profile>(config, tx.clone())?; tx_events.extend(events); let gas_prices = GasPrices { @@ -538,9 +545,16 @@ impl LedgerState { config: &Config, epoch_nonce: Fr, ) -> Result<(Self, Vec), LedgerError> { - let cryptarchia_ledger = CryptarchiaLedger::from_genesis_tx(&tx, config, epoch_nonce)?; + let GenesisOps { + transfer, + inscription, + declarations, + } = tx.into_genesis_ops(); + let cryptarchia_ledger = + CryptarchiaLedger::from_genesis_tx(&transfer, config, epoch_nonce)?; let (mantle_ledger, events) = MantleLedger::from_genesis_tx( - tx, + inscription, + declarations, config, cryptarchia_ledger.latest_utxos(), cryptarchia_ledger.epoch_state(), @@ -609,9 +623,9 @@ impl LedgerState { } #[must_use] - pub fn tx_context(&self) -> MantleTxContext { - MantleTxContext { - gas_context: MantleTxGasContext::from_channels( + pub fn tx_context(&self) -> OpsContext { + OpsContext { + gas_context: OpsGasContext::from_channels( self.mantle_ledger().channels(), self.get_gas_prices(), ), @@ -625,56 +639,57 @@ impl LedgerState { )] fn try_apply_op( mut self, - op: &Op, + signed_op: SignedOp, config: &Config, tx_hash: &TxHash, mut balance: Balance, mut tx_events: Vec, ) -> Result<(Self, Balance, Vec), LedgerError> { - match op { - Op::ChannelInscribe(op) => { - let (result, events) = self - .mantle_ledger - .try_apply_channel_inscription(op, self.cryptarchia_ledger.slot)?; + match signed_op { + SignedOp::ChannelInscribe(signed_operation) => { + let (result, events) = self.mantle_ledger.try_apply_channel_inscription( + signed_operation, + self.cryptarchia_ledger.slot, + )?; self.mantle_ledger = result; tx_events.extend(events); } - Op::ChannelConfig(op) => { + SignedOp::ChannelConfig(signed_operation) => { let (result, events) = self .mantle_ledger - .try_apply_channel_config(op, self.cryptarchia_ledger.slot)?; + .try_apply_channel_config(signed_operation, self.cryptarchia_ledger.slot)?; self.mantle_ledger = result; tx_events.extend(events); } - Op::ChannelDeposit(op) => { + SignedOp::ChannelDeposit(signed_operation) => { let channels = self.mantle_ledger.channels(); let utxos = self.cryptarchia_ledger.latest_utxos(); // Execute the Deposit - let (result, events) = op + let (result, events) = signed_operation .execute(DepositExecutionContext { channels: channels.clone(), utxos: utxos.clone(), tx_hash: *tx_hash, }) - .map_err(mantle::Error::Channel)?; + .map_err(|(_signed_operation, error)| mantle::Error::Channel(error))?; self.mantle_ledger = self.mantle_ledger.update_channels(result.channels); self.cryptarchia_ledger = self.cryptarchia_ledger.update_utxos(result.utxos); tx_events.extend(events); } - Op::ChannelWithdraw(op) => { + SignedOp::ChannelWithdraw(signed_operation) => { let channels = self.mantle_ledger.channels(); - let (result, events) = op + let (result, events) = signed_operation .execute(WithdrawExecutionContext { channels: channels.clone(), tx_hash: *tx_hash, }) - .map_err(mantle::Error::Channel)?; + .map_err(|(_signed_operation, error)| mantle::Error::Channel(error))?; self.mantle_ledger = self.mantle_ledger.update_channels(result.channels); tx_events.extend(events); } - Op::ChannelTransfer(op) => { + SignedOp::ChannelTransfer(signed_operation) => { let channels = self.mantle_ledger.channels(); let utxos = self.cryptarchia_ledger.latest_utxos(); @@ -683,32 +698,38 @@ impl LedgerState { utxos: utxos.clone(), tx_hash: *tx_hash, }; - let (result, events) = op.execute(context).map_err(mantle::Error::Channel)?; + let (result, events) = signed_operation + .execute(context) + .map_err(|(_signed_operation, error)| mantle::Error::Channel(error))?; self.mantle_ledger = self.mantle_ledger.update_channels(result.channels); self.cryptarchia_ledger = self.cryptarchia_ledger.update_utxos(result.utxos); tx_events.extend(events); } - Op::SDPDeclare(op) => { + SignedOp::SDPDeclare(signed_operation) => { let (result, events) = self.mantle_ledger.try_apply_sdp_declaration( - op, + signed_operation, self.cryptarchia_ledger.latest_utxos(), config, )?; self.mantle_ledger = result; tx_events.extend(events); } - Op::SDPActive(op) => { - let (result, events) = self.mantle_ledger.try_apply_sdp_active(op, config)?; + SignedOp::SDPActive(signed_operation) => { + let (result, events) = self + .mantle_ledger + .try_apply_sdp_active(signed_operation, config)?; self.mantle_ledger = result; tx_events.extend(events); } - Op::SDPWithdraw(op) => { - let (result, events) = self.mantle_ledger.try_apply_sdp_withdraw(op, config)?; + SignedOp::SDPWithdraw(signed_operation) => { + let (result, events) = self + .mantle_ledger + .try_apply_sdp_withdraw(signed_operation, config)?; self.mantle_ledger = result; tx_events.extend(events); } - Op::LeaderClaim(op) => { - let (result, events) = op + SignedOp::LeaderClaim(signed_operation) => { + let (result, events) = signed_operation .execute(LeaderClaimExecutionContext { nullifiers: self.mantle_ledger.leaders.nullifiers_cloned(), reward_amount: self.mantle_ledger.leaders.reward_amount(), @@ -716,7 +737,7 @@ impl LedgerState { utxos: self.cryptarchia_ledger.latest_utxos().clone(), tx_hash: *tx_hash, }) - .map_err(mantle::Error::LeaderClaim)?; + .map_err(|(_signed_operation, error)| mantle::Error::LeaderClaim(error))?; self.mantle_ledger .leaders .update_nullifiers(result.nullifiers); @@ -727,19 +748,19 @@ impl LedgerState { .update_rewards(result.claimable_rewards); tx_events.extend(events); } - Op::Transfer(op) => { + SignedOp::Transfer(signed_operation) => { let transfer_balance; let events; (self.cryptarchia_ledger, transfer_balance, events) = self.cryptarchia_ledger - .try_apply_transfer::<_, Profile>(op)?; + .try_apply_transfer::<_, Profile>(signed_operation)?; balance = balance .checked_add(transfer_balance) .ok_or(LedgerError::BalanceOverflow)?; tx_events.extend(events); } - Op::ClaimPowReward(claim_pow_reward) => { - let (result, events) = claim_pow_reward + SignedOp::ClaimPowReward(signed_operation) => { + let (result, events) = signed_operation .execute(ClaimPoWRewardExecutionContext { reward_pool: self.mantle_ledger.pow.reward_pool(), // TODO: check correctness of epoch reward, as it should be from the op @@ -750,7 +771,7 @@ impl LedgerState { utxos: self.cryptarchia_ledger.latest_utxos().clone(), block_slots: self.mantle_ledger.pow.block_slots().clone(), }) - .map_err(mantle::Error::ClaimPow)?; + .map_err(|(_signed_operation, error)| mantle::Error::ClaimPow(error))?; self.mantle_ledger .pow .update_from_claim_execution_result(&result); @@ -768,7 +789,7 @@ impl LedgerState { /// /// Verification is interleaved with execution: each operation is verified /// against the current ledger state (via - /// [`MantleTransaction::verified_ops`]) immediately before it is executed, + /// [`SignedOps::verified_ops`]) immediately before it is executed, /// so an operation may depend on state produced by earlier operations /// in the same transaction. /// @@ -779,15 +800,15 @@ impl LedgerState { /// /// If any operation fails verification or execution, returns a /// [`LedgerError`] describing the failure. - fn try_apply_tx<'tx, Tx, Id, Profile: GasProfile>( + fn try_apply_tx( mut self, config: &Config, - tx: &'tx Tx, + tx: Tx, ) -> Result<(Self, Balance, Vec), LedgerError> where - Tx: PreverifiedMantleTx + 'tx + MantleTxWithProofs, + Tx: PreverifiedMantleTransaction, { - let mut verified_ops = tx.verified_ops(); + let mut verified_ops = tx.into_verified_operations(); let mut balance: Balance = 0; let mut tx_events = Vec::new(); @@ -798,9 +819,14 @@ impl LedgerState { &self.cryptarchia_ledger, config, ); + + // On Error (failed verification), the transaction is considered invalid and + // rejected. let Some(op) = verified_ops.next(&helper).transpose()? else { + // All operations have been processed, exit the loop. break; }; + (self, balance, tx_events) = self.try_apply_op::<_, Profile>( op, config, @@ -824,11 +850,11 @@ mod tests { use lb_core::{ events::DepositNote, mantle::{ - MantleTransaction, Note, OpProof, RawMantleTx, TxGasCalculator as _, + Note, Op, OpProof, SignedOps, gas::MainnetGasProfile, ledger::{Inputs, Outputs, Utxos, VerifiableOperation as _}, ops::{ - OpId as _, + OpId as _, OpRef, SignedOperation, channel::{ ChannelId, MsgId, config::ChannelConfigOp, @@ -844,7 +870,6 @@ mod tests { transactions::{ OpProofs, Ops, hash::TxHashView, - mantle_tx::MantleTx as _, states::{Preverified, Unverified}, }, }, @@ -856,7 +881,9 @@ mod tests { }; use lb_cryptarchia_engine::Epoch; use lb_groth16::{CompressedGroth16Proof, Field as _}; - use lb_key_management_system_keys::keys::{Ed25519Key, Ed25519PublicKey, ZkKey, ZkPublicKey}; + use lb_key_management_system_keys::keys::{ + Ed25519Key, Ed25519PublicKey, ZkKey, ZkPublicKey, ZkSignature, + }; use num_bigint::BigUint; use super::*; @@ -878,17 +905,16 @@ mod tests { inputs: Vec, outputs: Vec, sks: &[ZkKey], - ) -> MantleTransaction { + ) -> SignedOps { let transfer_op = TransferOp::new( Inputs::try_new(inputs).expect("Invalid inputs size"), Outputs::try_new(outputs).expect("Invalid outputs size"), ); - let mantle_tx = RawMantleTx([Op::Transfer(transfer_op)].into()); - let ops_proofs = [OpProof::ZkSig( + let mantle_tx = Ops::from([Op::Transfer(transfer_op)]); + let ops_proofs = OpProofs::from([OpProof::ZkSig( ZkKey::multi_sign(sks, &mantle_tx.hash().to_fr()).unwrap(), - )] - .into(); - MantleTransaction::new(mantle_tx, ops_proofs) + )]); + SignedOps::from_parts(mantle_tx, ops_proofs).unwrap() } pub fn create_test_ledger() -> (Ledger, HeaderId, Utxo) { @@ -950,20 +976,20 @@ mod tests { MultiSequencer(ChannelMultiSigProof), } - fn create_signed_tx(op: Op, signing_key: &Key) -> MantleTransaction { + fn create_signed_tx(op: Op, signing_key: &Key) -> SignedOps { create_multi_signed_tx(vec![op], vec![signing_key]) } fn create_multi_signed_tx( - ops: Vec, + ops_vec: Vec, signing_keys: Vec<&Key>, - ) -> MantleTransaction { - let mantle_tx = RawMantleTx(Ops::new_unchecked(ops.clone())); + ) -> SignedOps { + let ops = Ops::new_unchecked(ops_vec); - let tx_hash = mantle_tx.hash(); + let tx_hash = ops.hash(); let ops_proofs = signing_keys .into_iter() - .zip(ops) + .zip(ops.iter()) .map(|(key, _)| match key { Key::Ed25519(key) => { OpProof::Ed25519Sig(key.sign_payload(tx_hash.as_signing_bytes().as_ref())) @@ -975,9 +1001,9 @@ mod tests { Key::MultiSequencer(proof) => OpProof::ChannelMultiSigProof(proof.clone()), }) .collect::>(); - let ops_proofs = OpProofs::try_from(ops_proofs).expect("operation proofs are bounded"); - - MantleTransaction::new(mantle_tx, ops_proofs) + let ops_proofs = OpProofs::new_unchecked(ops_proofs); + SignedOps::from_parts(ops, ops_proofs) + .unwrap() .preverify() .expect("Test transaction should have valid signatures") } @@ -999,7 +1025,7 @@ mod tests { &Key::Ed25519(signing_key.clone()), ); ledger_state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(config, &tx) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(config, tx) .unwrap() .0 } @@ -1036,11 +1062,17 @@ mod tests { &config.sdp_config.service_rewards_params.blend, ))), }; + let signed_operation = SignedOperation::new( + active_op, + ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ) + .into_state_trusted(); + let block_ledger = ledger.states.get_mut(&id).unwrap(); block_ledger.mantle_ledger = block_ledger .mantle_ledger .clone() - .try_apply_sdp_active(&active_op, &config) + .try_apply_sdp_active(signed_operation, &config) .unwrap() .0; id @@ -1076,7 +1108,11 @@ mod tests { let tx = create_tx(vec![utxo.id()], vec![output_note], &[sk]) .preverify() .unwrap(); - let mantle_tx = tx.mantle_tx().clone(); + let output_utxo = if let OpRef::Transfer(transfer_op) = tx.op_refs().get(0).unwrap() { + transfer_op.outputs.utxo_by_index(0, transfer_op).unwrap() + } else { + panic!("first op must be a transfer") + }; // Create a dummy proof (using same structure as in cryptarchia tests) @@ -1094,7 +1130,7 @@ mod tests { Slot::from(1u64), &proof, &UncleSlots::default(), - std::iter::once(&tx), + std::iter::once(tx), ) .unwrap(); assert!(events.is_empty()); @@ -1105,12 +1141,7 @@ mod tests { assert!(!new_state.latest_utxos().contains(&utxo.id())); // Verify output was created - if let Op::Transfer(transfer_op) = &mantle_tx.ops()[0] { - let output_utxo = transfer_op.outputs.utxo_by_index(0, transfer_op).unwrap(); - assert!(new_state.latest_utxos().contains(&output_utxo.id())); - } else { - panic!("first op must be a transfer") - } + assert!(new_state.latest_utxos().contains(&output_utxo.id())); } #[test] @@ -1128,7 +1159,7 @@ mod tests { }; let tx = create_signed_tx(Op::ChannelInscribe(inscribe_op), &Key::Ed25519(signing_key)); - let result = state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &tx); + let result = state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, tx); assert!(result.is_ok()); let (new_state, _, events) = result.unwrap(); @@ -1158,7 +1189,7 @@ mod tests { transfer_threshold: 1, }; - let config_tx = RawMantleTx([Op::ChannelConfig(config_op.clone())].into()); + let config_tx = Ops::from([Op::ChannelConfig(config_op.clone())]); let config_tx_hash = config_tx.hash(); let config_proof = ChannelMultiSigProof::try_new( [IndexedSignature::new( @@ -1173,7 +1204,7 @@ mod tests { Op::ChannelConfig(config_op), &Key::MultiSequencer(config_proof), ); - let result = state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &tx); + let result = state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, tx); assert!(result.is_ok()); let (new_state, _, events) = result.unwrap(); @@ -1229,7 +1260,8 @@ mod tests { }; let ops = vec![Op::ChannelDeposit(deposit.clone())]; let tx = create_multi_signed_tx(ops, vec![&Key::Zk(sk)]); - let result = ledger_state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &tx); + let tx_hash = tx.hash(); + let result = ledger_state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, tx); let (new_state, balance, events) = result.unwrap(); // The deposited note is consumed and re-created as a channel note under // a new NoteId. @@ -1273,7 +1305,7 @@ mod tests { else { panic!("events should include deposit event") }; - assert_eq!(*event_tx_hash, tx.hash()); + assert_eq!(*event_tx_hash, tx_hash); assert_eq!(*op_id, deposit.op_id()); assert_eq!(*event_channel_id, deposit.channel_id); assert_eq!(*amount, utxo.note.value); @@ -1314,7 +1346,7 @@ mod tests { let deposit_ops = vec![Op::ChannelDeposit(deposit)]; let tx = create_multi_signed_tx(deposit_ops, vec![&Key::Zk(sk)]); ledger_state = ledger_state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &tx) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, tx) .unwrap() .0; @@ -1331,7 +1363,7 @@ mod tests { channel_id, inputs: Inputs::new([deposited]), }; - let withdraw_tx = RawMantleTx([Op::ChannelWithdraw(withdraw)].into()); + let withdraw_tx = Ops::from([Op::ChannelWithdraw(withdraw)]); let withdraw_tx_hash = withdraw_tx.hash(); let withdraw_proof = ChannelMultiSigProof::try_new( [IndexedSignature::new( @@ -1343,12 +1375,12 @@ mod tests { .unwrap(); let signed_tx = create_multi_signed_tx( - withdraw_tx.0.to_vec(), + withdraw_tx.to_vec(), vec![&Key::MultiSequencer(withdraw_proof)], ); let result = - ledger_state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &signed_tx); + ledger_state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, signed_tx); assert!(result.is_ok()); let (new_state, tx_balance, events) = result.unwrap(); @@ -1389,19 +1421,16 @@ mod tests { let deposit_tx = create_multi_signed_tx(vec![Op::ChannelDeposit(deposit)], vec![&Key::Zk(sk)]); ledger_state = ledger_state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &deposit_tx) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, deposit_tx.clone()) .unwrap() .0; // 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 = RawMantleTx( - [Op::ChannelWithdraw(ChannelWithdrawOp { - channel_id, - inputs: Inputs::new([deposited]), - })] - .into(), - ); + let withdraw_tx = Ops::from([Op::ChannelWithdraw(ChannelWithdrawOp { + channel_id, + inputs: Inputs::new([deposited]), + })]); let withdraw_proof = ChannelMultiSigProof::try_new( [IndexedSignature::new( 0, @@ -1411,11 +1440,11 @@ mod tests { ) .unwrap(); let signed_withdraw = create_multi_signed_tx( - withdraw_tx.0.to_vec(), + withdraw_tx.to_vec(), vec![&Key::MultiSequencer(withdraw_proof)], ); ledger_state = ledger_state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &signed_withdraw) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, signed_withdraw) .unwrap() .0; @@ -1423,7 +1452,7 @@ mod tests { // Replaying the signed deposit fails: its input no longer exists. let result = - ledger_state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &deposit_tx); + ledger_state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, deposit_tx); assert!(result.is_err()); } @@ -1453,7 +1482,7 @@ mod tests { let deposit_ops = vec![Op::ChannelDeposit(deposit)]; let tx = create_multi_signed_tx(deposit_ops, vec![&Key::Zk(sk)]); ledger_state = ledger_state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &tx) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, tx) .unwrap() .0; // The deposit re-created the note as a channel note @@ -1470,7 +1499,7 @@ mod tests { inputs: Inputs::new([deposited]), }; let wrong_key = Ed25519Key::from_bytes(&[42; 32]); - let withdraw_tx = RawMantleTx([Op::ChannelWithdraw(withdraw)].into()); + let withdraw_tx = Ops::from([Op::ChannelWithdraw(withdraw)]); let withdraw_tx_hash = withdraw_tx.hash(); let invalid_proof = ChannelMultiSigProof::try_new( [IndexedSignature::new( @@ -1482,13 +1511,13 @@ mod tests { .unwrap(); let signed_tx = create_multi_signed_tx( - withdraw_tx.0.to_vec(), + withdraw_tx.to_vec(), vec![&Key::MultiSequencer(invalid_proof), &Key::EmptyZk], ); let err = ledger_state .clone() - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &signed_tx) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, signed_tx) .unwrap_err(); assert_eq!( err, @@ -1526,7 +1555,7 @@ mod tests { &Key::Ed25519(signing_key.clone()), ); state = state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &first_tx) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, first_tx) .unwrap() .0; @@ -1545,7 +1574,7 @@ mod tests { ); let result = state .clone() - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &second_tx); + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, second_tx); assert!(matches!( result, Err(LedgerError::VerificationError( @@ -1569,7 +1598,7 @@ mod tests { &Key::Ed25519(signing_key), ); let empty_result = - state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &empty_tx); + state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, empty_tx); assert!(matches!( empty_result, Err(LedgerError::VerificationError( @@ -1602,7 +1631,7 @@ mod tests { &Key::Ed25519(signing_key), ); state = state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &first_tx) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, first_tx) .unwrap() .0; @@ -1618,7 +1647,7 @@ mod tests { Op::ChannelInscribe(second_inscribe), &Key::Ed25519(unauthorized_signing_key), ); - let result = state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &second_tx); + let result = state.try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, second_tx); assert!(matches!( result, Err(LedgerError::VerificationError( @@ -1681,7 +1710,7 @@ mod tests { Op::ChannelConfig(config_op), Op::ChannelInscribe(inscribe_op3.clone()), ]; - let config_tx = RawMantleTx(Ops::new_unchecked(ops.clone())); + let config_tx = Ops::new_unchecked(ops.clone()); let config_tx_hash = config_tx.hash(); let config_proof = ChannelMultiSigProof::try_new( [IndexedSignature::new( @@ -1703,7 +1732,7 @@ mod tests { ); let result = state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, &tx) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&test_config, tx) .unwrap() .0; @@ -1854,7 +1883,10 @@ mod tests { let result = ledger .clone() - .try_apply_contents::<_, HeaderId, MainnetGasProfile>(&config, std::iter::once(&tx)); + .try_apply_contents::<_, HeaderId, MainnetGasProfile>( + &config, + std::iter::once(tx.clone()), + ); // The `unwrap` should succeed because the user pays at least the base fee of // 2705 result.unwrap(); @@ -1862,7 +1894,7 @@ mod tests { ledger.cryptarchia_ledger = ledger.cryptarchia_ledger.set_execution_base_fee(10.into()); let err = ledger - .try_apply_contents::<_, HeaderId, MainnetGasProfile>(&config, std::iter::once(&tx)) + .try_apply_contents::<_, HeaderId, MainnetGasProfile>(&config, std::iter::once(tx)) .unwrap_err(); // The transaction should be rejected because the price indicated for execution // doesn't cover the base fee that cost 27 050 @@ -1899,7 +1931,7 @@ mod tests { let result = ledger .clone() - .try_apply_contents::<_, HeaderId, MainnetGasProfile>(&config, std::iter::once(&tx)); + .try_apply_contents::<_, HeaderId, MainnetGasProfile>(&config, std::iter::once(tx)); // The `unwrap` should succeed because the user pays at least the base fee of // 794 let (no_priority_fee_ledger, events) = result.unwrap(); @@ -1917,7 +1949,7 @@ mod tests { .unwrap(); let result = ledger - .try_apply_contents::<_, HeaderId, MainnetGasProfile>(&config, std::iter::once(&tx)); + .try_apply_contents::<_, HeaderId, MainnetGasProfile>(&config, std::iter::once(tx)); // The `unwrap` should succeed because the user pays at least the base fee of // 794 let (priority_fee_ledger, events) = result.unwrap(); @@ -1959,7 +1991,7 @@ mod tests { assert!(storage_gas.into_inner() > 0); let (applied, _) = ledger - .try_apply_contents::<_, HeaderId, MainnetGasProfile>(&config, std::iter::once(&tx)) + .try_apply_contents::<_, HeaderId, MainnetGasProfile>(&config, std::iter::once(tx)) .unwrap(); // Storage gas consumed by the tx should be accumulated in the ledger @@ -1994,8 +2026,13 @@ mod tests { voucher_nullifier: nf.into(), pk: ZkPublicKey::zero(), }; + let signed_operation = SignedOperation::<_, _, StandardMode>::new( + op, + Groth16LeaderClaimProof::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ) + .into_state_trusted(); // Skip `op.validate` in this test to avoid having to generate a valid proof - let (result, _events) = op + let (result, _events) = signed_operation .execute(LeaderClaimExecutionContext { nullifiers: leaders.nullifiers_cloned(), reward_amount: leaders.reward_amount(), @@ -2033,8 +2070,15 @@ mod tests { voucher_nullifier: Fr::ZERO.into(), // nf of the 1st voucher pk: ZkPublicKey::zero(), }; + let signed_operation = SignedOperation::<_, _, StandardMode>::new( + op, + Groth16LeaderClaimProof::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ); + let preverified_signed_operation = signed_operation.into_state_trusted::(); + let executable_signed_operation = preverified_signed_operation.clone().into_state_trusted(); + // Skip `op.validate` in this test to avoid having to generate a valid proof - let (result, _events) = op + let (result, _events) = executable_signed_operation .execute(LeaderClaimExecutionContext { nullifiers: leaders.nullifiers_cloned(), reward_amount: leaders.reward_amount(), @@ -2053,16 +2097,15 @@ mod tests { let tx_hash = TxHash::from([0u8; 32]); let tx_hash_view = TxHashView::from(tx_hash); // Use a dummy proof since duplication is detected before proof verification - let proof = Groth16LeaderClaimProof::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])); - let err = op - .verify( - &proof, - &LeaderClaimVerificationContext { - nullifiers: leaders.nullifiers(), - claimable_vouchers_root: leaders.vouchers_snapshot_root(), - tx_hash_view: &tx_hash_view, - }, - ) + // let proof = + // Groth16LeaderClaimProof::new(CompressedGroth16Proof::from_bytes(&[0u8; + // 128])); + let err = preverified_signed_operation + .verify(&LeaderClaimVerificationContext { + nullifiers: leaders.nullifiers(), + claimable_vouchers_root: leaders.vouchers_snapshot_root(), + tx_hash_view: &tx_hash_view, + }) .unwrap_err(); assert_eq!(err, LeaderClaimError::DuplicatedVoucherNullifier); } @@ -2190,9 +2233,11 @@ mod tests { assert!(difficulty_at(&test_ledger, block_1) > genesis_difficulty); } - fn claim_tx() -> MantleTransaction { - let mantle_tx = RawMantleTx([Op::ClaimPowReward(claim_op())].into()); - MantleTransaction::new(mantle_tx, [OpProof::None(NoOpProof)].into()) + fn claim_tx() -> SignedOps { + let mantle_tx = Ops::from([Op::ClaimPowReward(claim_op())]); + let op_proofs = OpProofs::from([OpProof::None(NoOpProof)]); + SignedOps::from_parts(mantle_tx, op_proofs) + .unwrap() .preverify() .expect("claim op with OpProof::None should pass preverification") } @@ -2220,7 +2265,7 @@ mod tests { assert_eq!(state.mantle_ledger.pow.epoch_reward(), 0); let err = state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&config, &claim_tx()) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&config, claim_tx()) .expect_err("claim should fail validation"); assert!(matches!( @@ -2239,7 +2284,7 @@ mod tests { let (state, config) = pow_ledger_state(1_000); let err = state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&config, &claim_tx()) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&config, claim_tx()) .expect_err("claim should fail validation"); assert!(matches!( @@ -2276,7 +2321,7 @@ mod tests { let epoch_reward = state.mantle_ledger.pow.epoch_reward(); let (state, _balance, events) = state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&config, &claim_tx()) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&config, claim_tx()) .expect("claim should validate and execute"); assert_eq!( @@ -2315,11 +2360,11 @@ mod tests { // during tx-level validation. let (state, config) = claim_accepting_state(); let (state, _, _) = state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&config, &claim_tx()) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&config, claim_tx()) .expect("first claim should succeed"); let err = state - .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&config, &claim_tx()) + .try_apply_tx::<_, HeaderId, MainnetGasProfile>(&config, claim_tx()) .expect_err("second claim should be rejected"); assert!(matches!( @@ -2346,11 +2391,14 @@ mod tests { let pool_before = state.mantle_ledger.pow.reward_pool(); let epoch_reward = state.mantle_ledger.pow.epoch_reward(); let op = claim_op(); + let signed_operation = + SignedOperation::<_, _, StandardMode>::new(op.clone(), NoOpProof) + .into_state_trusted(); let tx_hash = TxHash::from([9u8; 32]); let (state, _balance, events) = state .try_apply_op::( - &Op::ClaimPowReward(op.clone()), + signed_operation.into(), &config, &tx_hash, 0, @@ -2414,14 +2462,29 @@ mod tests { .add_seen_block_slots(claim_op().block_hash, Slot::from(0u64)); let pool_before = state.mantle_ledger.pow.reward_pool(); let epoch_reward = state.mantle_ledger.pow.epoch_reward(); - let op = Op::ClaimPowReward(claim_op()); + let op = claim_op(); + let signed_operation = + SignedOperation::<_, _, StandardMode>::new(op, NoOpProof).into_state_trusted(); + let tx_hash = TxHash::from([9u8; 32]); let (state, _, _) = state - .try_apply_op::(&op, &config, &tx_hash, 0, Vec::new()) + .try_apply_op::( + signed_operation.clone().into(), + &config, + &tx_hash, + 0, + Vec::new(), + ) .expect("first claim should succeed"); let (state, _, _) = state - .try_apply_op::(&op, &config, &tx_hash, 0, Vec::new()) + .try_apply_op::( + signed_operation.into(), + &config, + &tx_hash, + 0, + Vec::new(), + ) .expect("second claim currently also succeeds (no validation)"); assert_eq!( diff --git a/ledger/src/mantle/mod.rs b/ledger/src/mantle/mod.rs index 6f1fa9558..9adfde56b 100644 --- a/ledger/src/mantle/mod.rs +++ b/ledger/src/mantle/mod.rs @@ -9,8 +9,9 @@ use lb_core::{ events::TxEvent, mantle::{ NoteId, Value, - ledger::ExecutableOperation as _, + ledger::verification_mode::{GenesisMode, StandardMode}, ops::{ + SignedOperation, channel::{ config::{ChannelConfigExecutionContext, ChannelConfigOp}, inscribe::{InscriptionExecutionContext, InscriptionOp}, @@ -20,7 +21,7 @@ use lb_core::{ sdp::{SDPActiveOp, SDPDeclareOp, SDPWithdrawOp}, transfer::TransferError, }, - traits::GenesisTx, + transactions::states::{Preverified, Verified}, }, sdp::locked_notes::LockedNotes, }; @@ -76,14 +77,15 @@ impl LedgerState { } pub fn from_genesis_tx( - tx: impl GenesisTx, + signed_operation_inscription: SignedOperation, + signed_operation_declarations: Vec>, config: &Config, utxo_tree: &UtxoTree, epoch_state: &EpochState, ) -> Result<(Self, Vec), Error> { let mut tx_events = Vec::new(); - let (channels, events) = channel::Channels::from_genesis(tx.genesis_inscription())?; + let (channels, events) = channel::Channels::from_genesis(signed_operation_inscription)?; tx_events.extend(events); let (sdp, events) = sdp::SdpLedger::from_genesis( @@ -91,7 +93,7 @@ impl LedgerState { utxo_tree, &channels, epoch_state, - tx.sdp_declarations(), + signed_operation_declarations, )?; tx_events.extend(events); @@ -175,14 +177,15 @@ impl LedgerState { pub fn try_apply_channel_inscription( mut self, - inscription_op: &InscriptionOp, + signed_operation: SignedOperation, block_slot: Slot, ) -> Result<(Self, Vec), Error> { - let (result, events) = inscription_op + let (result, events) = signed_operation .execute(InscriptionExecutionContext { channels: self.channels, block_slot, }) + .map_err(|(_signed_operation, error)| error) .inspect_err( |err| error!(target: LOG_TARGET, %err, "failed to apply channel inscribe message"), )?; @@ -193,7 +196,7 @@ impl LedgerState { pub fn try_apply_channel_config( mut self, - config_op: &ChannelConfigOp, + config_op: SignedOperation, block_slot: Slot, ) -> Result<(Self, Vec), Error> { let (result, events) = config_op @@ -201,6 +204,7 @@ impl LedgerState { channels: self.channels, block_slot, }) + .map_err(|(_signed_operation, error)| error) .inspect_err( |err| error!(target: LOG_TARGET, %err, "failed to apply channel set-keys message"), )?; @@ -211,7 +215,7 @@ impl LedgerState { pub fn try_apply_sdp_declaration( mut self, - sdp_declare_op: &SDPDeclareOp, + sdp_declare_op: SignedOperation, utxo_tree: &UtxoTree, config: &Config, ) -> Result<(Self, Vec), Error> { @@ -227,7 +231,7 @@ impl LedgerState { pub fn try_apply_sdp_active( mut self, - sdp_active_op: &SDPActiveOp, + sdp_active_op: SignedOperation, config: &Config, ) -> Result<(Self, Vec), Error> { let (result, events) = self @@ -242,7 +246,7 @@ impl LedgerState { pub fn try_apply_sdp_withdraw( mut self, - sdp_withdraw_op: &SDPWithdrawOp, + sdp_withdraw_op: SignedOperation, config: &Config, ) -> Result<(Self, Vec), Error> { let (result, events) = self diff --git a/ledger/src/mantle/sdp/mod.rs b/ledger/src/mantle/sdp/mod.rs index 92db2ee4a..270cc1379 100644 --- a/ledger/src/mantle/sdp/mod.rs +++ b/ledger/src/mantle/sdp/mod.rs @@ -9,14 +9,18 @@ use lb_core::{ block::BlockNumber, events::{HeaderEvent, TxEvent}, mantle::{ - NoteId, OpProof, Utxo, Value, + NoteId, Utxo, Value, channel::Channels, - ledger::{ExecutableOperation, VerifiableOperation, verification_mode::GenesisMode}, - ops::sdp::{ - SDPActiveExecutionContext, SDPActiveOp, SDPDeclareExecutionContext, SDPDeclareOp, - SDPWithdrawExecutionContext, SDPWithdrawOp, - declare::SDPDeclareGenesisValidationContext, + ledger::verification_mode::{GenesisMode, StandardMode}, + ops::{ + SignedOperation, + sdp::{ + SDPActiveExecutionContext, SDPActiveOp, SDPDeclareExecutionContext, SDPDeclareOp, + SDPWithdrawExecutionContext, SDPWithdrawOp, + declare::SDPDeclareGenesisValidationContext, + }, }, + transactions::states::{Preverified, Verified}, }, sdp::{ ActivityMetadata, Declaration, DeclarationId, MinStake, Nonce, ProviderId, @@ -300,42 +304,37 @@ impl SdpLedger { } } - pub fn from_genesis<'a>( + pub fn from_genesis( config: &Config, utxo_tree: &UtxoTree, channels: &Channels, epoch_state: &EpochState, - ops: impl Iterator + 'a, + declarations: impl IntoIterator>, ) -> Result<(Self, Vec), Error> { let mut sdp = Self::new(epoch_state.epoch()) .with_blend_service(&config.service_rewards_params.blend, epoch_state); let mut all_events = Vec::new(); - for (op, proof) in ops { - // TODO: remove this match once op/proof pairing is enforced by - // construction (e.g. via `SignedOp`) instead of at this call site. - let OpProof::ZkAndEd25519Sigs(proof) = proof else { - return Err(Error::InvalidProof); + for declaration in declarations { + let service_state = { + let operation = declaration.operation(); + sdp.services + .get(&operation.service_type) + .ok_or(Error::ServiceNotFound(operation.service_type))? }; - let service_state = sdp - .services - .get(&op.service_type) - .ok_or(Error::ServiceNotFound(op.service_type))?; - - >::verify( - op, - proof, - &SDPDeclareGenesisValidationContext { + let verified_declaration = declaration + .into_verified(&SDPDeclareGenesisValidationContext { utxo_tree, channels, locked_notes: &sdp.locked_notes, declarations: service_state.declarations(), min_stake: &config.min_stake, - }, - )?; + }) + .map_err(|(_signed_operation, error)| error)?; - let (result, events) = sdp.try_apply_genesis_sdp_declaration(utxo_tree, op, config)?; + let (result, events) = + sdp.try_apply_genesis_sdp_declaration(utxo_tree, verified_declaration, config)?; sdp = result; all_events.extend(events); } @@ -417,24 +416,25 @@ impl SdpLedger { pub fn try_apply_genesis_sdp_declaration( mut self, utxo_tree: &UtxoTree, - op: &SDPDeclareOp, + declaration: SignedOperation, config: &Config, ) -> Result<(Self, Vec), Error> { - let Some(service_state) = self.services.get_mut(&op.service_type) else { - return Err(Error::ServiceNotFound(op.service_type)); + let operation = declaration.operation(); + + let Some(service_state) = self.services.get_mut(&operation.service_type) else { + return Err(Error::ServiceNotFound(operation.service_type)); }; // Execute SDP Declare - let (result, events) = ::execute( - op, - SDPDeclareExecutionContext { + let (result, events) = declaration + .execute(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, - }, - )?; + }) + .map_err(|(_signed_operation, error)| error)?; self.locked_notes = result.locked_notes; service_state.update_declarations(result.declarations); @@ -444,23 +444,23 @@ impl SdpLedger { pub fn try_apply_sdp_declaration( mut self, utxo_tree: &UtxoTree, - op: &SDPDeclareOp, + signed_operation: SignedOperation, config: &Config, ) -> Result<(Self, Vec), Error> { - let Some(service_state) = self.services.get_mut(&op.service_type) else { - return Err(Error::ServiceNotFound(op.service_type)); + let operation = signed_operation.operation(); + let Some(service_state) = self.services.get_mut(&operation.service_type) else { + return Err(Error::ServiceNotFound(operation.service_type)); }; - let (result, events) = ::execute( - op, - SDPDeclareExecutionContext { + let (result, events) = signed_operation + .execute(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, - }, - )?; + }) + .map_err(|(_signed_operation, error)| error)?; self.locked_notes = result.locked_notes; service_state.update_declarations(result.declarations); @@ -469,46 +469,60 @@ impl SdpLedger { pub fn apply_active_msg( mut self, - op: &SDPActiveOp, + signed_operation: SignedOperation, config: &Config, ) -> Result<(Self, Vec), Error> { - let (service, _) = self.get_service(&op.declaration_id, config)?; + let operation = signed_operation.operation(); + let operation_declaration_id = operation.declaration_id; + let operation_metadata = operation.metadata.clone(); + + let (service, _) = self.get_service(&operation.declaration_id, config)?; let Some(service_state) = self.services.get_mut(&service) else { return Err(Error::ServiceNotFound(service)); }; - let (result, events) = op.execute(SDPActiveExecutionContext { - epoch: self.epoch, - declarations: service_state.declarations_clone(), - })?; + let (result, events) = signed_operation + .execute(SDPActiveExecutionContext { + epoch: self.epoch, + declarations: service_state.declarations_clone(), + }) + .map_err(|(_signed_operation, error)| error)?; let provider_id = result .declarations - .get(&op.declaration_id) + .get(&operation_declaration_id) .expect("the declaration should be in the list after execution") .provider_id; service_state.update_declarations(result.declarations); - service_state.update_rewards(provider_id, &op.metadata, &config.service_rewards_params)?; + service_state.update_rewards( + provider_id, + &operation_metadata, + &config.service_rewards_params, + )?; Ok((self, events)) } pub fn apply_withdrawn_msg( mut self, - op: &SDPWithdrawOp, + signed_operation: SignedOperation, config: &Config, ) -> Result<(Self, Vec), Error> { - let (service, _) = self.get_service(&op.declaration_id, config)?; + let operation = signed_operation.operation(); + + let (service, _) = self.get_service(&operation.declaration_id, config)?; let Some(service_state) = self.services.get_mut(&service) else { return Err(Error::ServiceNotFound(service)); }; - let (result, events) = op.execute(SDPWithdrawExecutionContext { - declarations: service_state.declarations_clone(), - locked_notes: self.locked_notes.clone(), - epoch: self.epoch, - })?; + let (result, events) = signed_operation + .execute(SDPWithdrawExecutionContext { + declarations: service_state.declarations_clone(), + locked_notes: self.locked_notes.clone(), + epoch: self.epoch, + }) + .map_err(|(_signed_operation, error)| error)?; self.locked_notes = result.locked_notes; service_state.update_declarations(result.declarations); @@ -637,11 +651,11 @@ mod tests { use std::{num::NonZeroU64, sync::Arc}; use lb_core::{ - mantle::ledger::Utxos, + mantle::{ledger::Utxos, ops::ZkAndEd25519Proof}, sdp::{Locator, SNAPSHOT_FINALIZATION_DELAY}, }; - use lb_groth16::{AdditiveGroup as _, Fr}; - use lb_key_management_system_keys::keys::{Ed25519Key, ZkKey}; + use lb_groth16::{AdditiveGroup as _, CompressedGroth16Proof, Fr}; + use lb_key_management_system_keys::keys::{Ed25519Key, Ed25519Signature, ZkKey, ZkSignature}; use lb_utils::math::NonNegativeF64; use num_bigint::BigUint; @@ -768,7 +782,7 @@ mod tests { let (_utxo_sk, utxo) = utxo_with_sk(); let signing_key = create_signing_key(); let zk_key = create_zk_key(1); - let declare_op = &SDPDeclareOp { + let declare_op = SDPDeclareOp { service_type: ServiceType::BlendNetwork, locked_note_id: utxo.id(), zk_id: zk_key.to_public_key(), @@ -776,8 +790,14 @@ mod tests { locators: "/ip4/1.1.1.1/udp/0".parse::().unwrap().into(), }; let declaration_id = declare_op.id(); + let proof = ZkAndEd25519Proof { + zk_sig: ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ed25519_sig: Ed25519Signature::zero(), + }; + let signed_operation = SignedOperation::new(declare_op, proof).into_state_trusted(); + let ledger = ledger - .try_apply_sdp_declaration(&utxo_tree(vec![utxo]), declare_op, &config) + .try_apply_sdp_declaration(&utxo_tree(vec![utxo]), signed_operation, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); @@ -821,7 +841,7 @@ mod tests { let (_utxo_sk, utxo) = utxo_with_sk(); let signing_key = create_signing_key(); let zk_key = create_zk_key(1); - let declare_op = &SDPDeclareOp { + let declare_op = SDPDeclareOp { service_type: ServiceType::BlendNetwork, locked_note_id: utxo.id(), zk_id: zk_key.to_public_key(), @@ -829,8 +849,14 @@ mod tests { locators: "/ip4/1.1.1.1/udp/0".parse::().unwrap().into(), }; let declaration_id = declare_op.id(); + let proof = ZkAndEd25519Proof { + zk_sig: ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ed25519_sig: Ed25519Signature::zero(), + }; + let signed_operation = SignedOperation::new(declare_op, proof).into_state_trusted(); + let ledger = ledger - .try_apply_sdp_declaration(&utxo_tree(vec![utxo]), declare_op, &config) + .try_apply_sdp_declaration(&utxo_tree(vec![utxo]), signed_operation, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); @@ -874,7 +900,7 @@ mod tests { let note_id = utxo.id(); let signing_key = create_signing_key(); let zk_key = create_zk_key(1); - let declare_op = &SDPDeclareOp { + let declare_op = SDPDeclareOp { service_type: ServiceType::BlendNetwork, locked_note_id: note_id, zk_id: zk_key.to_public_key(), @@ -882,19 +908,29 @@ mod tests { locators: "/ip4/1.1.1.1/udp/0".parse::().unwrap().into(), }; let declaration_id = declare_op.id(); + let proof = ZkAndEd25519Proof { + zk_sig: ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ed25519_sig: Ed25519Signature::zero(), + }; + let signed_operation_declare = SignedOperation::new(declare_op, proof).into_state_trusted(); + let ledger = ledger - .try_apply_sdp_declaration(&utxo_tree(vec![utxo]), declare_op, &config) + .try_apply_sdp_declaration(&utxo_tree(vec![utxo]), signed_operation_declare, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); // Withdraw at epoch 1: `withdrawn = 1 + SNAPSHOT_FINALIZATION_DELAY = 3`. - let withdraw_op = &SDPWithdrawOp { + let withdraw_op = SDPWithdrawOp { declaration_id, nonce: 1, locked_note_id: note_id, }; + let proof = ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])); + let signed_operation_withdraw = + SignedOperation::new(withdraw_op, proof).into_state_trusted(); + let ledger = ledger - .apply_withdrawn_msg(withdraw_op, &config) + .apply_withdrawn_msg(signed_operation_withdraw, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); let withdraw_at = ledger @@ -957,8 +993,15 @@ mod tests { locators: "/ip4/1.1.1.1/udp/0".parse::().unwrap().into(), }; let declaration_id = declare_op.id(); + let proof = ZkAndEd25519Proof { + zk_sig: ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ed25519_sig: Ed25519Signature::zero(), + }; + let signed_operation_declare = + SignedOperation::new(declare_op.clone(), proof).into_state_trusted(); + ledger = ledger - .try_apply_sdp_declaration(&utxo_tree(vec![utxo]), declare_op, &config) + .try_apply_sdp_declaration(&utxo_tree(vec![utxo]), signed_operation_declare, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); let declarations = ledger @@ -994,8 +1037,11 @@ mod tests { &config.service_rewards_params.blend, ))), }; + let proof = ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])); + let signed_operation_active = SignedOperation::new(active_op, proof).into_state_trusted(); + ledger = ledger - .apply_active_msg(&active_op, &config) + .apply_active_msg(signed_operation_active, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); let declarations = ledger @@ -1054,7 +1100,7 @@ mod tests { let note_id = utxo.id(); let signing_key = create_signing_key(); let zk_key = create_zk_key(1); - let declare_op = &SDPDeclareOp { + let declare_op = SDPDeclareOp { service_type: ServiceType::BlendNetwork, locked_note_id: note_id, zk_id: zk_key.to_public_key(), @@ -1062,8 +1108,14 @@ mod tests { locators: "/ip4/1.1.1.1/udp/0".parse::().unwrap().into(), }; let declaration_id = declare_op.id(); + let proof = ZkAndEd25519Proof { + zk_sig: ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ed25519_sig: Ed25519Signature::zero(), + }; + let signed_operation_declare = SignedOperation::new(declare_op, proof).into_state_trusted(); + ledger = ledger - .try_apply_sdp_declaration(&utxo_tree(vec![utxo]), declare_op, &config) + .try_apply_sdp_declaration(&utxo_tree(vec![utxo]), signed_operation_declare, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); @@ -1092,8 +1144,11 @@ mod tests { &config.service_rewards_params.blend, ))), }; + let proof = ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])); + let signed_operation_active = SignedOperation::new(active_op, proof).into_state_trusted(); + let ledger = ledger - .apply_active_msg(&active_op, &config) + .apply_active_msg(signed_operation_active, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); @@ -1161,13 +1216,19 @@ mod tests { locators: "/ip4/1.1.1.1/udp/0".parse::().unwrap().into(), }; let declaration_id_a = declare_a.id(); + let proof_a = ZkAndEd25519Proof { + zk_sig: ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ed25519_sig: Ed25519Signature::zero(), + }; + let signed_operation_declare_a = + SignedOperation::new(declare_a, proof_a).into_state_trusted(); let epoch0 = dummy_epoch_state(0.into()); let sdp_ledger = dummy_sdp_ledger(0.into(), &config); let utxos = utxo_tree(vec![utxo_a, utxo_b]); let sdp_ledger = sdp_ledger - .try_apply_sdp_declaration(&utxos, &declare_a, &config) + .try_apply_sdp_declaration(&utxos, signed_operation_declare_a, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); @@ -1177,8 +1238,12 @@ mod tests { nonce: 1, locked_note_id: utxo_a.id(), }; + let proof_withdraw = ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])); + let signed_operation_withdraw = + SignedOperation::new(withdraw_op, proof_withdraw).into_state_trusted(); + let (sdp_ledger, _events) = sdp_ledger - .apply_withdrawn_msg(&withdraw_op, &config) + .apply_withdrawn_msg(signed_operation_withdraw, &config) .unwrap(); let withdraw_epoch = sdp_ledger @@ -1211,8 +1276,15 @@ mod tests { provider_id: ProviderId(signing_key.public_key()), locators: "/ip4/2.2.2.2/udp/0".parse::().unwrap().into(), }; + let proof_b = ZkAndEd25519Proof { + zk_sig: ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ed25519_sig: Ed25519Signature::zero(), + }; + let signed_operation_declare_b = + SignedOperation::new(declare_b, proof_b).into_state_trusted(); + sdp_ledger - .try_apply_sdp_declaration(&utxos, &declare_b, &config) + .try_apply_sdp_declaration(&utxos, signed_operation_declare_b, &config) .expect( "Declaration reusing A's provider_id and zk_id must be accepted after A is removed", ); @@ -1231,14 +1303,20 @@ mod tests { let signing_key = create_signing_key(); let zk_key = create_zk_key(1); - let declare_op = &SDPDeclareOp { + let declare_op = SDPDeclareOp { service_type: service_a, locked_note_id: note_id, zk_id: zk_key.to_public_key(), provider_id: ProviderId(signing_key.public_key()), locators: "/ip4/1.1.1.1/udp/0".parse::().unwrap().into(), }; + let declare_locked_note_id = declare_op.locked_note_id; let declaration_id = declare_op.id(); + let proof = ZkAndEd25519Proof { + zk_sig: ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])), + ed25519_sig: Ed25519Signature::zero(), + }; + let signed_operation_declare = SignedOperation::new(declare_op, proof).into_state_trusted(); // Initialize ledger with service config and declare let epoch0 = dummy_epoch_state(0.into()); @@ -1246,7 +1324,7 @@ mod tests { let utxo_tree = utxo_tree(vec![utxo]); let sdp_ledger = sdp_ledger - .try_apply_sdp_declaration(&utxo_tree, declare_op, &config) + .try_apply_sdp_declaration(&utxo_tree, signed_operation_declare, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); @@ -1254,13 +1332,17 @@ mod tests { assert!(sdp_ledger.get_declaration(&declaration_id).is_some()); // Withdraw the declaration - let withdraw_op = &SDPWithdrawOp { + let withdraw_op = SDPWithdrawOp { declaration_id, nonce: 1, locked_note_id: note_id, }; + let proof_withdraw = ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])); + let signed_operation_withdraw = + SignedOperation::new(withdraw_op, proof_withdraw).into_state_trusted(); + let sdp_ledger = sdp_ledger - .apply_withdrawn_msg(withdraw_op, &config) + .apply_withdrawn_msg(signed_operation_withdraw, &config) .map(|(sdp_ledger, _)| sdp_ledger) .unwrap(); @@ -1293,7 +1375,7 @@ mod tests { assert!( sdp_ledger .locked_notes() - .is_locked_for_service(&declare_op.locked_note_id, &ServiceType::BlendNetwork), + .is_locked_for_service(&declare_locked_note_id, &ServiceType::BlendNetwork), "the provider's note must still be locked before the withdrawn epoch is reached" ); @@ -1315,7 +1397,7 @@ mod tests { assert!( !sdp_ledger .locked_notes() - .is_locked_for_service(&declare_op.locked_note_id, &ServiceType::BlendNetwork), + .is_locked_for_service(&declare_locked_note_id, &ServiceType::BlendNetwork), "the provider's note must be unlocked at the withdrawn epoch" ); }