feat(operation): Split trait (#3234)

This commit is contained in:
Álex
2026-08-03 10:51:18 +00:00
committed by GitHub
parent e2a1c3b7ef
commit 1a2aedae12
67 changed files with 639 additions and 620 deletions
+2 -2
View File
@@ -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])
+6 -6
View File
@@ -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())
});
+4 -4
View File
@@ -5,10 +5,10 @@ mod tests {
use crate::{
block::{Block, BlockTransactions, tests::create_proof},
mantle::MantleTx,
mantle::RawMantleTx,
};
fn make_empty_block() -> Block<MantleTx> {
fn make_empty_block() -> Block<RawMantleTx> {
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<MantleTx> =
let restored: Block<RawMantleTx> =
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<MantleTx> =
let restored: Block<RawMantleTx> =
bincode::deserialize(&bytes).expect("bincode deserialization should succeed");
assert_eq!(block.header().id(), restored.header().id());
assert_eq!(block.signature(), restored.signature());
+4 -4
View File
@@ -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<WithAll> {
})
.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<Op>) -> GenesisTx {
+18 -15
View File
@@ -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<MantleTx> {
iter::repeat_with(|| MantleTx(Ops::new_unchecked(vec![])))
fn create_tx(count: usize) -> Vec<RawMantleTx> {
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::<MantleTx>::empty();
let transactions = BlockTransactions::<RawMantleTx>::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<MantleTx> = Block::create(
let _valid_block: Block<RawMantleTx> = 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<MantleTx> = Block::create(
let _valid_block: Block<RawMantleTx> = 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::<MantleTx>::try_from(create_tx(MAX_BLOCK_TRANSACTIONS + 1));
BlockTransactions::<RawMantleTx>::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::<MantleTx>::try_from(create_tx(MAX_BLOCK_TRANSACTIONS)).unwrap(),
BlockTransactions::<RawMantleTx>::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::<MantleTx>::empty(),
BlockTransactions::<RawMantleTx>::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<MantleTx> = Block::create(
let _valid_block: Block<RawMantleTx> = 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::<MantleTx>::MIN, 0);
assert_eq!(BlockTransactions::<MantleTx>::MAX, MAX_BLOCK_TRANSACTIONS);
assert_eq!(BlockTransactions::<RawMantleTx>::MIN, 0);
assert_eq!(
BlockTransactions::<RawMantleTx>::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::<MantleTx>::empty();
let txs = BlockTransactions::<RawMantleTx>::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::<MantleTx>::empty(),
BlockTransactions::<RawMantleTx>::empty(),
&key,
)
.expect("valid non-genesis block");
@@ -729,7 +732,7 @@ mod tests {
let err = Block::reconstruct(
genesis_header,
BlockTransactions::<MantleTx>::empty(),
BlockTransactions::<RawMantleTx>::empty(),
genesis_signature,
)
.expect_err("genesis slot must be rejected by reconstruct path");
+6 -6
View File
@@ -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<MantleTx> = vec![];
let empty: Vec<RawMantleTx> = 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<MantleTx> = txs_with_names.iter().map(|(_, tx)| tx.clone()).collect();
let txs: Vec<RawMantleTx> = txs_with_names.iter().map(|(_, tx)| tx.clone()).collect();
println!("================================================================");
println!(
"vector 2 : one transaction per op kind ({} transactions)",
+3 -3
View File
@@ -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<TxEvent>), 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]
+2 -2
View File
@@ -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"
);
+33 -18
View File
@@ -39,29 +39,44 @@ pub type BoundedUtxos = UpperBoundedVec<Utxo, MAX_TRANSACTION_INPUTS>;
pub type BoundedInputs = UpperBoundedVec<NoteId, MAX_TRANSACTION_INPUTS>;
pub type BoundedOutputs = UpperBoundedVec<Note, MAX_TRANSACTION_OUTPUTS>;
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<VerificationContext> {
type PreverificationContext<'a>
where
Self: 'a;
type ExecutionContext<'a>
where
Self: 'a;
pub trait VerifiableOperation<Mode: verification_mode::VerificationMode> {
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<TxEvent>), Self::Error>;
}
fn verify(&self, context: &VerificationContext) -> Result<(), Self::VerificationError>;
fn execute(
&self,
context: Self::ExecutionContext<'_>,
) -> Result<(Self::ExecutionContext<'_>, Vec<TxEvent>), Self::ExecutionError>;
pub trait Operation<Mode: verification_mode::VerificationMode>:
VerifiableOperation<Mode> + ExecutableOperation
{
}
impl<T: VerifiableOperation<Mode> + ExecutableOperation, Mode: verification_mode::VerificationMode>
Operation<Mode> for T
{
}
pub type Utxos = UtxoTree<NoteId, Utxo, ZkHasher>;
+1 -1
View File
@@ -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;
+35 -39
View File
@@ -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<ChannelTransferValidationContext<'_>> for ChannelTransferOp {
type PreverificationContext<'a>
= ()
where
Self: 'a;
type ExecutionContext<'a>
= ChannelTransferExecutionContext
where
Self: 'a;
type VerificationError = Error;
type ExecutionError = Error;
impl VerifiableOperation<verification_mode::StandardMode> 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<ChannelTransferValidationContext<'_>> 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<ChannelTransferValidationContext<'_>> 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<ChannelTransferValidationContext<'_>> 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<TxEvent>), Self::ExecutionError> {
mut context: Self::Context<'a>,
) -> Result<(Self::Context<'a>, Vec<TxEvent>), 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()))
}
}
+23 -28
View File
@@ -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<ChannelConfigValidationContext<'_>> for ChannelConfigOp {
type PreverificationContext<'a>
= ()
where
Self: 'a;
type ExecutionContext<'a>
= ChannelConfigExecutionContext
where
Self: 'a;
type VerificationError = Error;
type ExecutionError = Error;
impl VerifiableOperation<verification_mode::StandardMode> 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<ChannelConfigValidationContext<'_>> 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<ChannelConfigValidationContext<'_>> 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<ChannelConfigValidationContext<'_>> 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<TxEvent>), Self::ExecutionError> {
mut context: Self::Context<'a>,
) -> Result<(Self::Context<'a>, Vec<TxEvent>), 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()))
}
}
+33 -32
View File
@@ -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<DepositValidationContext<'_>> for DepositOp {
type PreverificationContext<'a>
= ()
where
Self: 'a;
type ExecutionContext<'a>
= DepositExecutionContext
where
Self: 'a;
type VerificationError = Error;
type ExecutionError = Error;
impl VerifiableOperation<verification_mode::StandardMode> 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<TxEvent>), Self::ExecutionError> {
mut context: Self::Context<'a>,
) -> Result<(Self::Context<'a>, Vec<TxEvent>), 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<DepositValidationContext<'_>> for DepositOp {
))
.collect();
Ok((ctx, events))
Ok((context, events))
}
}
+26 -29
View File
@@ -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<InscriptionValidationContext<'_>> 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<verification_mode::StandardMode> 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<InscriptionValidationContext<'_>> 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<InscriptionValidationContext<'_>> 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<InscriptionValidationContext<'_>> 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<TxEvent>), Self::ExecutionError> {
mut context: Self::Context<'a>,
) -> Result<(Self::Context<'a>, Vec<TxEvent>), 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<InscriptionValidationContext<'_>> 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<InscriptionValidationContext<'_>> 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()))
}
}
+28 -32
View File
@@ -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<WithdrawValidationContext<'_>> for ChannelWithdrawOp {
type PreverificationContext<'a>
= ()
where
Self: 'a;
type ExecutionContext<'a>
= WithdrawExecutionContext
where
Self: 'a;
type VerificationError = Error;
type ExecutionError = Error;
impl VerifiableOperation<verification_mode::StandardMode> 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<WithdrawValidationContext<'_>> 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<WithdrawValidationContext<'_>> 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<WithdrawValidationContext<'_>> 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<TxEvent>), Self::ExecutionError> {
mut context: Self::Context<'a>,
) -> Result<(Self::Context<'a>, Vec<TxEvent>), 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()))
}
}
+34 -39
View File
@@ -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<LeaderClaimVerificationContext<'_>> 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<verification_mode::StandardMode> 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<LeaderClaimVerificationContext<'_>> 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<TxEvent>), Self::ExecutionError> {
mut context: Self::Context<'a>,
) -> Result<(Self::Context<'a>, Vec<TxEvent>), 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 {
+4 -4
View File
@@ -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())),
);
}
}
+26 -27
View File
@@ -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<SDPActiveValidationContext<'_>> for SDPActiveOp {
type PreverificationContext<'a>
= ()
where
Self: 'a;
type ExecutionContext<'a>
= SDPActiveExecutionContext
where
Self: 'a;
type VerificationError = SdpError;
type ExecutionError = SdpError;
impl VerifiableOperation<verification_mode::StandardMode> 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<SDPActiveValidationContext<'_>> 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<TxEvent>), Self::ExecutionError> {
let mut declaration = ctx
mut context: Self::Context<'a>,
) -> Result<(Self::Context<'a>, Vec<TxEvent>), 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<SDPActiveValidationContext<'_>> for SDPActiveOp {
"updated declaration with active message"
);
Ok((ctx, Vec::new()))
Ok((context, Vec::new()))
}
}
+44 -67
View File
@@ -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<TxEvent>), SdpError>;
}
@@ -70,22 +72,22 @@ impl SDPDeclareValidationExt for SDPDeclareOp {
fn execute(
&self,
mut ctx: SDPDeclareExecutionContext,
mut context: SDPDeclareExecutionContext,
) -> Result<(SDPDeclareExecutionContext, Vec<TxEvent>), 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<SDPDeclareVerificationContext<'_>> 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<verification_mode::StandardMode> 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<SDPDeclareVerificationContext<'_>> 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<SDPDeclareVerificationContext<'_>> 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<TxEvent>), Self::ExecutionError> {
SDPDeclareValidationExt::execute(self, ctx)
}
}
impl Operation<SDPDeclareGenesisValidationContext<'_>> 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<verification_mode::GenesisMode> 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<SDPDeclareGenesisValidationContext<'_>> 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<TxEvent>), Self::ExecutionError> {
SDPDeclareValidationExt::execute(self, ctx)
context: Self::Context<'a>,
) -> Result<(Self::Context<'a>, Vec<TxEvent>), Self::Error> {
SDPDeclareValidationExt::execute(self, context)
}
}
+24 -29
View File
@@ -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<SDPWithdrawValidationContext<'_>> for SDPWithdrawOp {
type PreverificationContext<'a>
= ()
where
Self: 'a;
type ExecutionContext<'a>
= SDPWithdrawExecutionContext
where
Self: 'a;
type VerificationError = SdpError;
type ExecutionError = SdpError;
impl VerifiableOperation<verification_mode::StandardMode> 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<SDPWithdrawValidationContext<'_>> 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<SDPWithdrawValidationContext<'_>> 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<SDPWithdrawValidationContext<'_>> 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<TxEvent>), Self::ExecutionError> {
let mut declaration = ctx
mut context: Self::Context<'a>,
) -> Result<(Self::Context<'a>, Vec<TxEvent>), Self::Error> {
let mut declaration = context
.declarations
.get(&self.declaration_id)
.expect("The operation should have been validated");
@@ -122,9 +117,9 @@ impl Operation<SDPWithdrawValidationContext<'_>> 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<SDPWithdrawValidationContext<'_>> for SDPWithdrawOp {
"updated declaration with withdraw message"
);
Ok((ctx, Vec::new()))
Ok((context, Vec::new()))
}
}
+25 -24
View File
@@ -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<TransferValidationContext<'_>> for TransferOp {
type PreverificationContext<'a>
= ()
where
Self: 'a;
type ExecutionContext<'a>
= Utxos
where
Self: 'a;
type VerificationError = TransferError;
type ExecutionError = TransferError;
impl VerifiableOperation<verification_mode::StandardMode> 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<TransferValidationContext<'_>> 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<TxEvent>), Self::ExecutionError> {
mut utxos: Self::Context<'a>,
) -> Result<(Self::Context<'a>, Vec<TxEvent>), Self::Error> {
// Remove inputs from the ledger
utxos = self.inputs.execute(utxos)?;
// Add outputs from the ledger
+3 -3
View File
@@ -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<Hash = TxHash> {
fn genesis_inscription(&self) -> &InscriptionOp;
fn cryptarchia_parameter(&self) -> CryptarchiaParameter;
fn sdp_declarations(&self) -> impl Iterator<Item = (&SDPDeclareOp, &OpProof)>;
fn mantle_tx(&self) -> &MantleTx;
fn mantle_tx(&self) -> &RawMantleTx;
}
impl<T: GenesisTx> GenesisTx for &T {
@@ -32,7 +32,7 @@ impl<T: GenesisTx> GenesisTx for &T {
T::sdp_declarations(self)
}
fn mantle_tx(&self) -> &MantleTx {
fn mantle_tx(&self) -> &RawMantleTx {
T::mantle_tx(self)
}
}
+4 -3
View File
@@ -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<Hash = TxHash> + 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<Hash = TxHash> + GasCalculator + StorageS
}
impl<T: MantleTxWithProofs> MantleTxWithProofs for &T {
fn mantle_tx(&self) -> &MantleTx {
fn mantle_tx(&self) -> &RawMantleTx {
T::mantle_tx(self)
}
+7 -7
View File
@@ -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<G: GasConstants>(
&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<MantleTx, TxBuilderError> {
pub fn build(mut self) -> Result<RawMantleTx, TxBuilderError> {
if !self.pending_transfer.is_empty() {
self.mantle_tx
.0
+23 -23
View File
@@ -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<Unverified>), 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<State: VerificationState>(tx: &SignedMantleTx<Sta
/// Attaching more proofs than predicted is allowed, but if the tx is funded
/// based on the predicted size, it may end up paying insufficient fees.
#[must_use]
pub fn minimum_signed_mantle_tx_size(tx: &MantleTx, context: &MantleTxGasContext) -> 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([
+7 -3
View File
@@ -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(),
+21 -16
View File
@@ -23,9 +23,9 @@ use crate::{
static MANTLE_TX_HASH_V1_BYTES: LazyLock<Vec<u8>> = 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<Self> = |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<Constants: GasConstants>(
.checked_mul(Value::from(multiplier))
}
impl<State: VerificationState> From<SignedMantleTx<State>> for MantleTx {
impl<State: VerificationState> From<SignedMantleTx<State>> for RawMantleTx {
fn from(signed_tx: SignedMantleTx<State>) -> Self {
signed_tx.mantle_tx
}
}
#[derive(Serialize, Deserialize)]
struct MantleTxSerde {
struct RawMantleTxSerde {
pub ops: Ops,
}
impl From<MantleTxSerde> for MantleTx {
fn from(MantleTxSerde { ops }: MantleTxSerde) -> Self {
impl From<RawMantleTxSerde> for RawMantleTx {
fn from(RawMantleTxSerde { ops }: RawMantleTxSerde) -> Self {
Self(ops)
}
}
impl From<MantleTx> for MantleTxSerde {
fn from(MantleTx(ops): MantleTx) -> Self {
impl From<RawMantleTx> for RawMantleTxSerde {
fn from(RawMantleTx(ops): RawMantleTx) -> Self {
Self { ops }
}
}
impl Serialize for MantleTx {
impl Serialize for RawMantleTx {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
<MantleTxSerde as Deserialize>::deserialize(deserializer).map(Into::into)
<RawMantleTxSerde as Deserialize>::deserialize(deserializer).map(Into::into)
} else {
let bytes: Vec<u8> = <Vec<u8>>::deserialize(deserializer)?;
Self::decode(&bytes)
@@ -247,3 +248,7 @@ impl MantleTxGasContext {
self.gas_prices.clone()
}
}
pub trait MantleTx {
fn ops(&self) -> &Ops;
}
+1 -1
View File
@@ -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;
@@ -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<State: VerificationState> {
pub(crate) mantle_tx: MantleTx,
pub(crate) mantle_tx: RawMantleTx,
// TODO: make this more efficient
ops_proofs: OpsProofs,
state: PhantomData<State>,
@@ -70,7 +71,7 @@ impl<State: VerificationState> SignedMantleTx<State> {
}
#[must_use]
pub const fn mantle_tx(&self) -> &MantleTx {
pub const fn mantle_tx(&self) -> &RawMantleTx {
&self.mantle_tx
}
@@ -80,14 +81,14 @@ impl<State: VerificationState> SignedMantleTx<State> {
}
#[must_use]
pub fn into_parts(self) -> (MantleTx, OpsProofs) {
pub fn into_parts(self) -> (RawMantleTx, OpsProofs) {
(self.mantle_tx, self.ops_proofs)
}
}
impl SignedMantleTx<Unverified> {
#[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<Unverified> {
}
}
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<Unverified> {
tx_hash_view,
proof_ed25519,
};
<SDPDeclareOp as Operation<SDPDeclareVerificationContext>>::preverify(op, &context)
<SDPDeclareOp as VerifiableOperation<StandardMode>>::preverify(op, &context)
.map_err(VerificationError::SDPVerificationError)
}
(Op::SDPWithdraw(op), OpProof::ZkSig(_proof)) => op
@@ -225,7 +226,7 @@ impl SignedMantleTx<Preverified> {
/// 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<Preverified> {
declarations: helper.get_declarations_by_service(op.service_type)?,
min_stake: helper.get_min_stake(),
};
op.verify(&context)
<SDPDeclareOp as VerifiableOperation<StandardMode>>::verify(op, &context)
.map_err(VerificationError::SDPVerificationError)
}
(Op::SDPWithdraw(op), OpProof::ZkSig(proof)) => {
@@ -391,7 +392,7 @@ impl<State: VerificationState> Hashable for SignedMantleTx<State> {
}
impl<State: VerificationState> MantleTxWithProofs for SignedMantleTx<State> {
fn mantle_tx(&self) -> &MantleTx {
fn mantle_tx(&self) -> &RawMantleTx {
&self.mantle_tx
}
@@ -472,7 +473,7 @@ impl PreverifiedMantleTx for SignedMantleTx<Preverified> {
#[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<State: VerificationState> Serialize for SignedMantleTx<State> {
#[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<Op>) -> MantleTx {
MantleTx(Ops::new_unchecked(ops))
pub fn create_test_mantle_tx(ops: Vec<Op>) -> RawMantleTx {
RawMantleTx(Ops::new_unchecked(ops))
}
#[must_use]
+4 -1
View File
@@ -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> {
+1 -1
View File
@@ -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,
+4 -4
View File
@@ -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<MantleTx> = vec![];
let txs: Vec<RawMantleTx> = vec![];
let result = calculate_block_root(&txs);
let expected = [0u8; 32];
@@ -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
@@ -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()
@@ -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},
};
@@ -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))]),
@@ -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<Vec<Utxo>>
}
/// Decode a hex-encoded mantle transaction and reject trailing bytes.
pub fn decode_mantle_tx_hex(value: &str) -> RunResult<MantleTx> {
pub fn decode_mantle_tx_hex(value: &str) -> RunResult<RawMantleTx> {
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());
}
+4 -4
View File
@@ -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)
+11 -10
View File
@@ -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<Op>,
signing_keys: Vec<&Key>,
) -> SignedMantleTx<Preverified> {
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(
+1 -1
View File
@@ -8,7 +8,7 @@ use lb_core::{
events::TxEvent,
mantle::{
NoteId, Value,
ledger::Operation as _,
ledger::ExecutableOperation as _,
ops::{
channel::{
config::{ChannelConfigExecutionContext, ChannelConfigOp},
+26 -22
View File
@@ -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.
<SDPDeclareOp as VerifiableOperation<GenesisMode>>::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) =
<SDPDeclareOp as Operation<SDPDeclareGenesisValidationContext>>::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) = <SDPDeclareOp as ExecutableOperation>::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) = <SDPDeclareOp as Operation<SDPDeclareVerificationContext>>::execute(
let (result, events) = <SDPDeclareOp as ExecutableOperation>::execute(
op,
SDPDeclareExecutionContext {
utxo_tree: utxo_tree.clone(),
+2 -2
View File
@@ -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).
@@ -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 = "<MantleTx as lb_core::mantle::traits::Hashable>::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<Hash = TxHash>,
{
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<State>> for ApiSignedTransaction<'tx> {
fn from(value: &'tx SignedMantleTx<State>) -> Self {
Self {
mantle_tx: value.mantle_tx(),
mantle_tx: value.mantle_tx().into(),
ops_proofs: value.ops_proofs(),
}
}
@@ -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<MantleTx> = Block::try_from(bytes).unwrap();
let block: Block<RawMantleTx> = Block::try_from(bytes).unwrap();
(
false,
format!("Available(first_block={:?})", block.header().id()),
+2 -1
View File
@@ -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},
+1
View File
@@ -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},
},
};
@@ -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<Unverified> {
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(),
@@ -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;
@@ -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;
@@ -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<NoteId, Utxo>,
) -> Result<Vec<Utxo>, WalletTransactionError> {
mantle_tx
@@ -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<OpsProofs, WalletTransactionError> {
let tx_hash = tx.hash();
@@ -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<Unverified> {
}
fn build_signed_transfer(transfer_op: TransferOp) -> SignedMantleTx<Unverified> {
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");
@@ -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<ZkPublicKey>,
) -> Result<ZkSignature, ZoneTestError> {
let request_url =
+1 -1
View File
@@ -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};
@@ -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
+2 -2
View File
@@ -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([
+5 -1
View File
@@ -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,
};
+5 -3
View File
@@ -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,
)
}
+1 -1
View File
@@ -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.
+5 -5
View File
@@ -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());
+3 -2
View File
@@ -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<State: VerificationState>(
#[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<Unverified> {
let mantle_tx = MantleTx(
let mantle_tx = RawMantleTx(
[Op::ChannelInscribe(InscriptionOp {
channel_id: [0u8; 32].into(),
inscription: Inscription::new_unchecked(vec![seed]),
+5 -5
View File
@@ -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<Ed25519Signature, Error> {
pub async fn sign_tx(&self, tx: &RawMantleTx) -> Result<Ed25519Signature, Error> {
let (response_tx, response_rx) = oneshot::channel();
self.send(ActorRequest::SignTx {
tx: tx.clone(),
+8 -7
View File
@@ -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<Ed25519Signature, Error> {
pub fn sign_tx(&mut self, tx: &RawMantleTx) -> Result<Ed25519Signature, Error> {
self.sequencer.do_sign_tx(tx)
}
+3 -3
View File
@@ -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<Unverified> {
let mantle_tx = MantleTx(
let mantle_tx = RawMantleTx(
[ChannelInscribe(InscriptionOp {
channel_id: [0u8; 32].into(),
inscription: [data].into(),
+11 -7
View File
@@ -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: &Node,
funding: Option<&FundingConfig>,
ops: Vec<Op>,
) -> Result<(MantleTx, Option<OpProof>), Error>
) -> Result<(RawMantleTx, Option<OpProof>), 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<OpProof>,
) -> Result<OpsProofs, Error> {
@@ -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);
+10 -5
View File
@@ -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<Result<(MantleTx, MsgId, Ed25519Signature), Error>>,
response_tx: oneshot::Sender<Result<(RawMantleTx, MsgId, Ed25519Signature), Error>>,
},
SignTx {
tx: MantleTx,
tx: RawMantleTx,
response_tx: oneshot::Sender<Result<Ed25519Signature, Error>>,
},
}
@@ -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<Ed25519Signature, Error> {
pub(super) fn do_sign_tx(&self, tx: &RawMantleTx) -> Result<Ed25519Signature, Error> {
self.ensure_ready()?;
Ok(build_sign_tx(tx.hash(), &self.signing_key))
}
+2 -2
View File
@@ -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<Op>) -> SignedMantleTx<Unverified> {
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]),