mirror of
https://github.com/logos-co/nomos-node.git
synced 2026-08-27 09:31:10 +00:00
feat(operation): Split trait (#3234)
This commit is contained in:
@@ -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])
|
||||
|
||||
@@ -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())
|
||||
});
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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)",
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(
|
||||
|
||||
@@ -8,7 +8,7 @@ use lb_core::{
|
||||
events::TxEvent,
|
||||
mantle::{
|
||||
NoteId, Value,
|
||||
ledger::Operation as _,
|
||||
ledger::ExecutableOperation as _,
|
||||
ops::{
|
||||
channel::{
|
||||
config::{ChannelConfigExecutionContext, ChannelConfigOp},
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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]),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 +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);
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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]),
|
||||
|
||||
Reference in New Issue
Block a user