mirror of
https://github.com/logos-co/nomos-node.git
synced 2026-08-27 17:41:11 +00:00
refactor(txhash): Simplify Mantle Transaction (2/2) (#2670)
This commit is contained in:
@@ -46,16 +46,13 @@ const SIZES: &[usize] = &[
|
||||
// Helper fn to create an inscription `MantleTx`, no ledger inputs ot outputs.
|
||||
fn make_inscription_tx(payload_size: usize) -> MantleTx {
|
||||
let signing_key = Ed25519Key::from_bytes(&[1; 32]);
|
||||
MantleTx {
|
||||
ops: vec![Op::ChannelInscribe(InscriptionOp {
|
||||
channel_id: ChannelId::from([0xAA; 32]),
|
||||
inscription: vec![0xAB; payload_size],
|
||||
parent: MsgId::from([0xBB; 32]),
|
||||
signer: signing_key.public_key(),
|
||||
})],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
}
|
||||
vec![Op::ChannelInscribe(InscriptionOp {
|
||||
channel_id: ChannelId::from([0xAA; 32]),
|
||||
inscription: vec![0xAB; payload_size],
|
||||
parent: MsgId::from([0xBB; 32]),
|
||||
signer: signing_key.public_key(),
|
||||
})]
|
||||
.into()
|
||||
}
|
||||
|
||||
// Helper fn to create a `SignedMantleTx`.
|
||||
|
||||
@@ -6,8 +6,7 @@ use crate::{
|
||||
block::Block,
|
||||
header::Header,
|
||||
mantle::{
|
||||
MantleTx, Note, Op, OpProof, SignedMantleTx,
|
||||
gas::GasPrice,
|
||||
Note, Op, OpProof, SignedMantleTx,
|
||||
genesis_tx::{self, GenesisTx},
|
||||
ledger::{Inputs, Outputs},
|
||||
ops::{channel::inscribe::InscriptionOp, sdp::SDPDeclareOp, transfer::TransferOp},
|
||||
@@ -1095,11 +1094,7 @@ impl GenesisBlockBuilder<WithAll> {
|
||||
.collect();
|
||||
let n = ops.len();
|
||||
let signed_tx = SignedMantleTx::new_unverified(
|
||||
MantleTx {
|
||||
ops,
|
||||
execution_gas_price: GasPrice::new(0),
|
||||
storage_gas_price: GasPrice::new(0),
|
||||
},
|
||||
ops.into(),
|
||||
vec![OpProof::Ed25519Sig(Ed25519Signature::zero()); n],
|
||||
);
|
||||
Ok(GenesisBlock::genesis(GenesisTx::from_tx(signed_tx)?))
|
||||
@@ -1202,11 +1197,7 @@ mod tests {
|
||||
ops.extend(extra_ops);
|
||||
let n = ops.len();
|
||||
SignedMantleTx::new_unverified(
|
||||
MantleTx {
|
||||
ops,
|
||||
execution_gas_price: GasPrice::new(0),
|
||||
storage_gas_price: GasPrice::new(0),
|
||||
},
|
||||
ops.into(),
|
||||
vec![OpProof::Ed25519Sig(Ed25519Signature::from_bytes(&[0u8; 64])); n],
|
||||
)
|
||||
}
|
||||
@@ -1512,7 +1503,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let tx = block.transactions().next().unwrap();
|
||||
let ops = &tx.mantle_tx().ops;
|
||||
let ops = tx.mantle_tx().ops();
|
||||
assert!(matches!(ops[0], Op::Transfer(_)));
|
||||
assert!(matches!(ops[1], Op::ChannelInscribe(_)));
|
||||
assert!(matches!(ops[2], Op::SDPDeclare(_)));
|
||||
|
||||
@@ -323,13 +323,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn create_tx(count: usize) -> Vec<MantleTx> {
|
||||
iter::repeat_with(|| MantleTx {
|
||||
ops: vec![],
|
||||
execution_gas_price: 0.into(),
|
||||
storage_gas_price: 0.into(),
|
||||
})
|
||||
.take(count)
|
||||
.collect()
|
||||
iter::repeat_with(|| MantleTx(vec![])).take(count).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::mantle::{
|
||||
ops::channel::{
|
||||
ChannelId, ChannelKeyIndex, Ed25519PublicKey as PublicKey, MsgId, inscribe::InscriptionOp,
|
||||
},
|
||||
tx::MantleTxGasContext,
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
|
||||
@@ -57,17 +56,6 @@ pub struct Channels {
|
||||
pub channels: rpds::HashTrieMapSync<ChannelId, ChannelState>,
|
||||
}
|
||||
|
||||
impl From<&Channels> for MantleTxGasContext {
|
||||
fn from(value: &Channels) -> Self {
|
||||
let withdraw_thresholds = value
|
||||
.channels
|
||||
.iter()
|
||||
.map(|(channel_id, channel)| (*channel_id, channel.withdraw_threshold))
|
||||
.collect();
|
||||
Self::new(withdraw_thresholds)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ChannelState {
|
||||
pub tip: MsgId,
|
||||
@@ -141,6 +129,7 @@ mod tests {
|
||||
deposit::{DepositExecutionContext, DepositOp},
|
||||
withdraw::{ChannelWithdrawOp, WithdrawExecutionContext},
|
||||
},
|
||||
tx::{GasPrices, MantleTxGasContext},
|
||||
},
|
||||
sdp::locked_notes::LockedNotes,
|
||||
};
|
||||
@@ -217,7 +206,7 @@ mod tests {
|
||||
),
|
||||
};
|
||||
|
||||
let gas_context = MantleTxGasContext::from(&channels);
|
||||
let gas_context = MantleTxGasContext::from_channels(&channels, GasPrices::new(0, 0));
|
||||
|
||||
assert_eq!(gas_context.withdraw_threshold(&first_id), Some(1));
|
||||
assert_eq!(gas_context.withdraw_threshold(&second_id), Some(2));
|
||||
|
||||
+70
-157
@@ -57,7 +57,7 @@ const LOCATOR_BYTES_SIZE_LIMIT: usize = 329usize;
|
||||
pub fn decode_signed_mantle_tx(input: &[u8]) -> IResult<&[u8], SignedMantleTx> {
|
||||
// SignedMantleTx = MantleTx OpsProofs
|
||||
let (input, mantle_tx) = decode_mantle_tx(input)?;
|
||||
let (input, ops_proofs) = decode_ops_proofs(input, &mantle_tx.ops)?;
|
||||
let (input, ops_proofs) = decode_ops_proofs(input, mantle_tx.ops())?;
|
||||
|
||||
let signed_tx = SignedMantleTx::new(mantle_tx, ops_proofs)
|
||||
.map_err(|_| nom::Err::Error(Error::new(input, ErrorKind::Verify)))?;
|
||||
@@ -68,17 +68,8 @@ pub fn decode_signed_mantle_tx(input: &[u8]) -> IResult<&[u8], SignedMantleTx> {
|
||||
pub fn decode_mantle_tx(input: &[u8]) -> IResult<&[u8], MantleTx> {
|
||||
// MantleTx = Ops ExecutionGasPrice StorageGasPrice
|
||||
let (input, ops) = decode_ops(input)?;
|
||||
let (input, execution_gas_price) = decode_uint64(input)?;
|
||||
let (input, storage_gas_price) = decode_uint64(input)?;
|
||||
|
||||
Ok((
|
||||
input,
|
||||
MantleTx {
|
||||
ops,
|
||||
execution_gas_price: execution_gas_price.into(),
|
||||
storage_gas_price: storage_gas_price.into(),
|
||||
},
|
||||
))
|
||||
Ok((input, ops.into()))
|
||||
}
|
||||
|
||||
// ==============================================================================
|
||||
@@ -900,18 +891,14 @@ fn encode_ops_proofs(proofs: &[OpProof], ops: &[Op]) -> Vec<u8> {
|
||||
/// Encode top-level transactions
|
||||
#[must_use]
|
||||
pub fn encode_mantle_tx(tx: &MantleTx) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend(encode_ops(&tx.ops));
|
||||
bytes.extend(encode_uint64(tx.execution_gas_price.into_inner()));
|
||||
bytes.extend(encode_uint64(tx.storage_gas_price.into_inner()));
|
||||
bytes
|
||||
encode_ops(tx.ops())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn encode_signed_mantle_tx(tx: &SignedMantleTx) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend(encode_mantle_tx(&tx.mantle_tx));
|
||||
bytes.extend(encode_ops_proofs(&tx.ops_proofs, &tx.mantle_tx.ops));
|
||||
bytes.extend(encode_ops_proofs(&tx.ops_proofs, tx.mantle_tx.ops()));
|
||||
bytes
|
||||
}
|
||||
|
||||
@@ -919,7 +906,7 @@ pub(crate) fn predict_signed_mantle_tx_size(tx: &MantleTx, context: &MantleTxGas
|
||||
let mantle_tx_size = encode_mantle_tx(tx).len();
|
||||
|
||||
let ops_proofs_size = tx
|
||||
.ops
|
||||
.ops()
|
||||
.iter()
|
||||
.map(|op| match op {
|
||||
// Ed25519SigProof = Ed25519Signature
|
||||
@@ -959,7 +946,10 @@ mod tests {
|
||||
use num_bigint::BigUint;
|
||||
|
||||
use super::*;
|
||||
use crate::{mantle::Transaction as _, sdp::blend::ActivityProof};
|
||||
use crate::{
|
||||
mantle::{Transaction as _, tx::GasPrices},
|
||||
sdp::blend::ActivityProof,
|
||||
};
|
||||
|
||||
fn dbg_test_vector(actual: &str, expected: &str) {
|
||||
println!("{:32} {:32}", "actual", "expected");
|
||||
@@ -1030,11 +1020,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_decode_signed_mantle_tx_empty() {
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![]);
|
||||
|
||||
let signed_tx = SignedMantleTx {
|
||||
mantle_tx,
|
||||
@@ -1045,10 +1031,7 @@ mod tests {
|
||||
clippy::string_add,
|
||||
reason = "Recommended String::push_str does not support chaining"
|
||||
)]
|
||||
let test_vector = String::new()
|
||||
+ "00" // OpCount=0u8
|
||||
+ "6400000000000000" // ExecutionGasPrice
|
||||
+ "3200000000000000"; // StorageGasPrice
|
||||
let test_vector = String::new() + "00"; // OpCount=0u8
|
||||
|
||||
// ENCODING
|
||||
let encoded = hex::encode(encode_signed_mantle_tx(&signed_tx));
|
||||
@@ -1067,16 +1050,12 @@ mod tests {
|
||||
#[test]
|
||||
fn test_decode_signed_mantle_tx_with_inscribe() {
|
||||
let signing_key = Ed25519Key::from_bytes(&[4u8; 32]);
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::ChannelInscribe(InscriptionOp {
|
||||
channel_id: ChannelId::from([0xAA; 32]),
|
||||
inscription: b"hello".to_vec(),
|
||||
parent: MsgId::from([0xBB; 32]),
|
||||
signer: signing_key.public_key(),
|
||||
})],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::ChannelInscribe(InscriptionOp {
|
||||
channel_id: ChannelId::from([0xAA; 32]),
|
||||
inscription: b"hello".to_vec(),
|
||||
parent: MsgId::from([0xBB; 32]),
|
||||
signer: signing_key.public_key(),
|
||||
})]);
|
||||
|
||||
let txhash = mantle_tx.hash();
|
||||
let inscribe_sig =
|
||||
@@ -1095,10 +1074,8 @@ mod tests {
|
||||
+ "68656c6c6f" // Inscription
|
||||
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" // Parent (32Byte)
|
||||
+ "ca93ac1705187071d67b83c7ff0efe8108e8ec4530575d7726879333dbdabe7c" // Signer (32Byte)
|
||||
+ "6400000000000000" // ExecutionGasPrice
|
||||
+ "3200000000000000" // StorageGasPrice
|
||||
+ "a53621dc1a5d7be2d8f8771139df91f961a60e546e9cdeff499d0c28ae10165c" // Signature (64Byte)
|
||||
+ "7dd0e314f2ba839dd0bff3a567be06f3aafdeddddd9de00e5117ab1584cd3201";
|
||||
+ "4ec789fc67b7f7bfba02f8cc7f3f671a107225faefbe60ca0b8e9e7e8e43e8db" // Signature (64Byte)
|
||||
+ "835075aed539fac37e0fdc03acc2aba873e43eef8a835476c4c6bdaaba866901";
|
||||
|
||||
// ENCODING
|
||||
let encoded = hex::encode(encode_signed_mantle_tx(&signed_tx));
|
||||
@@ -1116,22 +1093,18 @@ 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: vec![
|
||||
Op::ChannelInscribe(InscriptionOp {
|
||||
channel_id: ChannelId::from([0x11; 32]),
|
||||
inscription: b"first".to_vec(),
|
||||
parent: MsgId::from([0x00; 32]),
|
||||
signer: signing_key.public_key(),
|
||||
}),
|
||||
Op::ChannelSetKeys(SetKeysOp {
|
||||
channel: ChannelId::from([0x22; 32]),
|
||||
keys: vec![signing_key.public_key()],
|
||||
}),
|
||||
],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![
|
||||
Op::ChannelInscribe(InscriptionOp {
|
||||
channel_id: ChannelId::from([0x11; 32]),
|
||||
inscription: b"first".to_vec(),
|
||||
parent: MsgId::from([0x00; 32]),
|
||||
signer: signing_key.public_key(),
|
||||
}),
|
||||
Op::ChannelSetKeys(SetKeysOp {
|
||||
channel: ChannelId::from([0x22; 32]),
|
||||
keys: vec![signing_key.public_key()],
|
||||
}),
|
||||
]);
|
||||
|
||||
let txhash = mantle_tx.hash();
|
||||
let sig = signing_key.sign_payload(&txhash.as_signing_bytes());
|
||||
@@ -1173,11 +1146,7 @@ mod tests {
|
||||
signer: signing_key.public_key(),
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::ChannelInscribe(inscribe_op)],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::ChannelInscribe(inscribe_op)]);
|
||||
|
||||
let txhash = mantle_tx.hash();
|
||||
let op_sig = signing_key.sign_payload(&txhash.as_signing_bytes());
|
||||
@@ -1186,7 +1155,7 @@ mod tests {
|
||||
|
||||
let encoded = encode_signed_mantle_tx(&signed_tx);
|
||||
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size =
|
||||
predict_signed_mantle_tx_size(&signed_tx.mantle_tx, &gas_context);
|
||||
assert_eq!(
|
||||
@@ -1220,11 +1189,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_encode_decode_roundtrip_empty_tx() {
|
||||
// Create an empty MantleTx
|
||||
let original_tx = MantleTx {
|
||||
ops: vec![],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let original_tx = MantleTx(vec![]);
|
||||
|
||||
// Encode
|
||||
let encoded = encode_mantle_tx(&original_tx);
|
||||
@@ -1247,11 +1212,7 @@ mod tests {
|
||||
let note_id = NoteId(BigUint::from(123u64).into());
|
||||
let transfer_op = TransferOp::new(Inputs::new(vec![note_id]), Outputs::new(vec![note]));
|
||||
|
||||
let original_tx = MantleTx {
|
||||
ops: vec![Op::Transfer(transfer_op)],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let original_tx = MantleTx(vec![Op::Transfer(transfer_op)]);
|
||||
|
||||
// Encode
|
||||
let encoded = encode_mantle_tx(&original_tx);
|
||||
@@ -1267,11 +1228,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_encode_decode_roundtrip_signed_tx() {
|
||||
// Create a simple SignedMantleTx
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![]);
|
||||
let original_tx = SignedMantleTx::new(mantle_tx, vec![]).unwrap();
|
||||
|
||||
// Encode
|
||||
@@ -1288,14 +1245,10 @@ mod tests {
|
||||
#[test]
|
||||
fn test_predict_signed_mantle_tx_size_empty_tx() {
|
||||
// Create an empty MantleTx
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![]);
|
||||
|
||||
// Predict size
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size = predict_signed_mantle_tx_size(&mantle_tx, &gas_context);
|
||||
|
||||
// Create a signed tx and encode it to get actual size
|
||||
@@ -1316,14 +1269,10 @@ mod tests {
|
||||
signer: signing_key.public_key(),
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::ChannelInscribe(inscribe_op)],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::ChannelInscribe(inscribe_op)]);
|
||||
|
||||
// Predict size
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size = predict_signed_mantle_tx_size(&mantle_tx, &gas_context);
|
||||
|
||||
// Create a signed tx and encode it to get actual size
|
||||
@@ -1351,14 +1300,10 @@ mod tests {
|
||||
],
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::ChannelSetKeys(set_keys_op)],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::ChannelSetKeys(set_keys_op)]);
|
||||
|
||||
// Predict size
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size = predict_signed_mantle_tx_size(&mantle_tx, &gas_context);
|
||||
|
||||
// Create a signed tx and encode it to get actual size
|
||||
@@ -1397,14 +1342,10 @@ mod tests {
|
||||
locked_note_id: locked_note.id(),
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::SDPDeclare(sdp_declare_op)],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::SDPDeclare(sdp_declare_op)]);
|
||||
|
||||
// Predict size
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size = predict_signed_mantle_tx_size(&mantle_tx, &gas_context);
|
||||
|
||||
// Create a signed tx and encode it to get actual size
|
||||
@@ -1433,16 +1374,12 @@ mod tests {
|
||||
locked_note_id,
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::SDPWithdraw(sdp_withdraw_op)],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::SDPWithdraw(sdp_withdraw_op)]);
|
||||
|
||||
let txhash = mantle_tx.hash();
|
||||
|
||||
// Predict size
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size = predict_signed_mantle_tx_size(&mantle_tx, &gas_context);
|
||||
|
||||
// Create a signed tx and encode it to get actual size
|
||||
@@ -1479,13 +1416,9 @@ mod tests {
|
||||
metadata,
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::SDPActive(sdp_active_op)],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::SDPActive(sdp_active_op)]);
|
||||
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size = predict_signed_mantle_tx_size(&mantle_tx, &gas_context);
|
||||
|
||||
let txhash = mantle_tx.hash();
|
||||
@@ -1534,18 +1467,14 @@ mod tests {
|
||||
metadata: ActivityMetadata::Blend(Box::new(blend_proof)),
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![
|
||||
Op::ChannelInscribe(inscribe_op),
|
||||
Op::ChannelSetKeys(set_keys_op),
|
||||
Op::SDPActive(sdp_active_op),
|
||||
],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![
|
||||
Op::ChannelInscribe(inscribe_op),
|
||||
Op::ChannelSetKeys(set_keys_op),
|
||||
Op::SDPActive(sdp_active_op),
|
||||
]);
|
||||
|
||||
// Predict size
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size = predict_signed_mantle_tx_size(&mantle_tx, &gas_context);
|
||||
|
||||
let txhash = mantle_tx.hash();
|
||||
@@ -1585,14 +1514,10 @@ mod tests {
|
||||
Outputs::new(vec![note1, note2]),
|
||||
);
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::Transfer(transfer_op)],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::Transfer(transfer_op)]);
|
||||
|
||||
// Predict size
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size = predict_signed_mantle_tx_size(&mantle_tx, &gas_context);
|
||||
|
||||
// Create a signed tx and encode it to get actual size
|
||||
@@ -1646,19 +1571,15 @@ mod tests {
|
||||
.id(),
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![
|
||||
Op::ChannelInscribe(inscribe_op),
|
||||
Op::ChannelSetKeys(set_keys_op),
|
||||
Op::SDPDeclare(sdp_declare_op),
|
||||
Op::Transfer(transfer_op),
|
||||
],
|
||||
execution_gas_price: 150.into(),
|
||||
storage_gas_price: 75.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![
|
||||
Op::ChannelInscribe(inscribe_op),
|
||||
Op::ChannelSetKeys(set_keys_op),
|
||||
Op::SDPDeclare(sdp_declare_op),
|
||||
Op::Transfer(transfer_op),
|
||||
]);
|
||||
|
||||
// Predict size
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size = predict_signed_mantle_tx_size(&mantle_tx, &gas_context);
|
||||
|
||||
// Create a signed tx and encode it to get actual size
|
||||
@@ -1693,13 +1614,9 @@ mod tests {
|
||||
pk: ZkPublicKey::from(BigUint::from(0u64)),
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::LeaderClaim(leader_claim_op.clone())],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::LeaderClaim(leader_claim_op.clone())]);
|
||||
|
||||
let empty_gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let empty_gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
let predicted_size = predict_signed_mantle_tx_size(&mantle_tx, &empty_gas_context);
|
||||
|
||||
let poc_proof = Groth16LeaderClaimProof::new(
|
||||
@@ -1773,15 +1690,11 @@ mod tests {
|
||||
let note2 = Note::new(2000, pk2);
|
||||
|
||||
let signing_key = Ed25519Key::from_bytes(&[21u8; 32]);
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::ChannelWithdraw(ChannelWithdrawOp {
|
||||
channel_id: ChannelId::from([0xAB; 32]),
|
||||
outputs: Outputs::new(vec![note1, note2]),
|
||||
withdraw_nonce: 0,
|
||||
})],
|
||||
execution_gas_price: 100.into(),
|
||||
storage_gas_price: 50.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::ChannelWithdraw(ChannelWithdrawOp {
|
||||
channel_id: ChannelId::from([0xAB; 32]),
|
||||
outputs: Outputs::new(vec![note1, note2]),
|
||||
withdraw_nonce: 0,
|
||||
})]);
|
||||
let tx_hash = mantle_tx.hash();
|
||||
let proof = ChannelWithdrawProof::new(vec![WithdrawSignature::new(
|
||||
0,
|
||||
|
||||
+13
-2
@@ -1,4 +1,7 @@
|
||||
use std::fmt::{self, Display};
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
ops::Add,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -29,7 +32,7 @@ impl From<Value> for Gas {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub struct GasPrice(Value);
|
||||
|
||||
impl GasPrice {
|
||||
@@ -44,6 +47,14 @@ impl GasPrice {
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for GasPrice {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value> for GasPrice {
|
||||
fn from(value: Value) -> Self {
|
||||
Self(value)
|
||||
|
||||
@@ -66,17 +66,9 @@ impl GenesisTx {
|
||||
pub fn from_tx(signed_mantle_tx: SignedMantleTx) -> Result<Self, Error> {
|
||||
let mantle_tx = &signed_mantle_tx.mantle_tx;
|
||||
|
||||
// Genesis transactions must have execution gas price and storage gas price
|
||||
// matching the expected genesis values
|
||||
if mantle_tx.execution_gas_price != GENESIS_EXECUTION_GAS_PRICE
|
||||
|| mantle_tx.storage_gas_price != GENESIS_STORAGE_GAS_PRICE
|
||||
{
|
||||
return Err(Error::InvalidGenesisGasPrice);
|
||||
}
|
||||
|
||||
// Genesis transactions must contain exactly one transfer as the first op,
|
||||
// one inscription as the second op, and then may contain other SDP declarations
|
||||
let cryptarchia_parameter = match mantle_tx.ops.as_slice() {
|
||||
let cryptarchia_parameter = match mantle_tx.ops().as_slice() {
|
||||
[
|
||||
Op::Transfer(transfer),
|
||||
Op::ChannelInscribe(inscription),
|
||||
@@ -174,7 +166,7 @@ impl GasCalculator for GenesisTx {
|
||||
impl crate::mantle::GenesisTx for GenesisTx {
|
||||
fn genesis_inscription(&self) -> &InscriptionOp {
|
||||
// Safe to unwrap because we validated this in from_tx
|
||||
match &self.mantle_tx().ops[1] {
|
||||
match &self.mantle_tx().ops()[1] {
|
||||
Op::ChannelInscribe(op) => op,
|
||||
_ => unreachable!("GenesisTx always has a valid inscription as second op"),
|
||||
}
|
||||
@@ -182,7 +174,7 @@ impl crate::mantle::GenesisTx for GenesisTx {
|
||||
|
||||
fn genesis_transfer(&self) -> &TransferOp {
|
||||
// Safe to unwrap because we validated this in from_tx
|
||||
match &self.mantle_tx().ops[0] {
|
||||
match &self.mantle_tx().ops()[0] {
|
||||
Op::Transfer(op) => op,
|
||||
_ => unreachable!("GenesisTx always has a valid transfer as first op"),
|
||||
}
|
||||
@@ -194,7 +186,7 @@ impl crate::mantle::GenesisTx for GenesisTx {
|
||||
|
||||
fn sdp_declarations(&self) -> impl Iterator<Item = (&SDPDeclareOp, &OpProof)> {
|
||||
self.mantle_tx()
|
||||
.ops
|
||||
.ops()
|
||||
.iter()
|
||||
.zip(self.tx.ops_proofs.iter())
|
||||
.filter_map(|(op, proof)| {
|
||||
@@ -354,11 +346,7 @@ mod tests {
|
||||
);
|
||||
let mut new_ops = vec![Op::Transfer(transfer_op)];
|
||||
new_ops.append(&mut ops);
|
||||
let mantle_tx = MantleTx {
|
||||
ops: new_ops,
|
||||
execution_gas_price: GENESIS_EXECUTION_GAS_PRICE,
|
||||
storage_gas_price: GENESIS_STORAGE_GAS_PRICE,
|
||||
};
|
||||
let mantle_tx = MantleTx(new_ops);
|
||||
let mut new_op_proofs = vec![OpProof::ZkSig(
|
||||
ZkKey::multi_sign(&[], &mantle_tx.hash().to_fr()).unwrap(),
|
||||
)];
|
||||
@@ -534,45 +522,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_genesis_fees() {
|
||||
// Should succeed with execution_gas_price=GENESIS_EXECUTION_GAS_PRICE
|
||||
// and storage_gas_price=GENESIS_STORAGE_GAS_PRICE
|
||||
let mut signed_mantle_tx = create_tx(
|
||||
vec![Op::ChannelInscribe(inscription_op(
|
||||
ChannelId::from([0; 32]),
|
||||
&cryptarchia_param(),
|
||||
MsgId::root(),
|
||||
Ed25519PublicKey::from_bytes(&[0; 32]).unwrap(),
|
||||
))],
|
||||
vec![OpProof::Ed25519Sig(Ed25519Signature::from_bytes(
|
||||
&[0u8; 64],
|
||||
))],
|
||||
);
|
||||
assert!(GenesisTx::from_tx(signed_mantle_tx.clone()).is_ok());
|
||||
|
||||
// Test with wrong execution gas price
|
||||
signed_mantle_tx.mantle_tx.execution_gas_price =
|
||||
(GENESIS_EXECUTION_GAS_PRICE.into_inner() + 1).into();
|
||||
let result = GenesisTx::from_tx(signed_mantle_tx.clone());
|
||||
assert_eq!(result, Err(Error::InvalidGenesisGasPrice));
|
||||
|
||||
// Test with wrong storage gas price
|
||||
signed_mantle_tx.mantle_tx.storage_gas_price =
|
||||
(GENESIS_STORAGE_GAS_PRICE.into_inner() + 1).into();
|
||||
signed_mantle_tx.mantle_tx.execution_gas_price = 0.into();
|
||||
let result = GenesisTx::from_tx(signed_mantle_tx.clone());
|
||||
assert_eq!(result, Err(Error::InvalidGenesisGasPrice));
|
||||
|
||||
// Test with wrong storage/execution gas prices
|
||||
signed_mantle_tx.mantle_tx.storage_gas_price =
|
||||
(GENESIS_STORAGE_GAS_PRICE.into_inner() + 1).into();
|
||||
signed_mantle_tx.mantle_tx.execution_gas_price =
|
||||
(GENESIS_EXECUTION_GAS_PRICE.into_inner() + 1).into();
|
||||
let result = GenesisTx::from_tx(signed_mantle_tx);
|
||||
assert_eq!(result, Err(Error::InvalidGenesisGasPrice));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_genesis_tx_serde() {
|
||||
// Create a genesis transaction with inscription (no signature proof required)
|
||||
|
||||
+38
-12
@@ -49,16 +49,29 @@ pub trait Transaction {
|
||||
}
|
||||
|
||||
pub trait AuthenticatedMantleTx: Transaction<Hash = TxHash> + GasCalculator + StorageSize {
|
||||
type Context;
|
||||
/// Returns the underlying `MantleTx` that this transaction represents.
|
||||
fn mantle_tx(&self) -> &MantleTx;
|
||||
|
||||
fn ops_with_proof(&self) -> impl Iterator<Item = (&Op, &OpProof)>;
|
||||
|
||||
// Gas Cost functions with context already handled
|
||||
fn total_gas_cost<Constants: GasConstants>(&self) -> Result<GasCost, GasOverflow>;
|
||||
fn storage_gas_cost(&self) -> Result<GasCost, GasOverflow>;
|
||||
fn execution_gas_consumption<Constants: GasConstants>(&self) -> Result<Gas, GasOverflow>;
|
||||
fn storage_gas_consumption(&self) -> Result<Gas, GasOverflow>;
|
||||
fn total_gas_cost<Constants: GasConstants>(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<GasCost, GasOverflow>;
|
||||
fn storage_gas_cost(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<GasCost, GasOverflow>;
|
||||
fn execution_gas_consumption<Constants: GasConstants>(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<Gas, GasOverflow>;
|
||||
fn storage_gas_consumption(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<Gas, GasOverflow>;
|
||||
|
||||
fn verify_ops_proofs_with_helper(
|
||||
&self,
|
||||
@@ -92,6 +105,7 @@ impl<T: StorageSize> StorageSize for &T {
|
||||
}
|
||||
|
||||
impl<T: AuthenticatedMantleTx> AuthenticatedMantleTx for &T {
|
||||
type Context = <T as AuthenticatedMantleTx>::Context;
|
||||
fn mantle_tx(&self) -> &MantleTx {
|
||||
T::mantle_tx(self)
|
||||
}
|
||||
@@ -100,20 +114,32 @@ impl<T: AuthenticatedMantleTx> AuthenticatedMantleTx for &T {
|
||||
T::ops_with_proof(self)
|
||||
}
|
||||
|
||||
fn total_gas_cost<Constants: GasConstants>(&self) -> Result<GasCost, GasOverflow> {
|
||||
<T as AuthenticatedMantleTx>::total_gas_cost::<Constants>(self)
|
||||
fn total_gas_cost<Constants: GasConstants>(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<GasCost, GasOverflow> {
|
||||
<T as AuthenticatedMantleTx>::total_gas_cost::<Constants>(self, context)
|
||||
}
|
||||
|
||||
fn storage_gas_cost(&self) -> Result<GasCost, GasOverflow> {
|
||||
<T as AuthenticatedMantleTx>::storage_gas_cost(self)
|
||||
fn storage_gas_cost(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<GasCost, GasOverflow> {
|
||||
<T as AuthenticatedMantleTx>::storage_gas_cost(self, context)
|
||||
}
|
||||
|
||||
fn execution_gas_consumption<Constants: GasConstants>(&self) -> Result<Gas, GasOverflow> {
|
||||
<T as AuthenticatedMantleTx>::execution_gas_consumption::<Constants>(self)
|
||||
fn execution_gas_consumption<Constants: GasConstants>(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<Gas, GasOverflow> {
|
||||
<T as AuthenticatedMantleTx>::execution_gas_consumption::<Constants>(self, context)
|
||||
}
|
||||
|
||||
fn storage_gas_consumption(&self) -> Result<Gas, GasOverflow> {
|
||||
<T as AuthenticatedMantleTx>::storage_gas_consumption(self)
|
||||
fn storage_gas_consumption(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<Gas, GasOverflow> {
|
||||
<T as AuthenticatedMantleTx>::storage_gas_consumption(self, context)
|
||||
}
|
||||
|
||||
fn verify_ops_proofs_with_helper(
|
||||
|
||||
+101
-58
@@ -13,8 +13,10 @@ use crate::{
|
||||
crypto::{Digest as _, Hash, Hasher},
|
||||
mantle::{
|
||||
AuthenticatedMantleTx, StorageSize, Transaction, TransactionHasher, Value,
|
||||
channel::Channels,
|
||||
encoding::{decode_mantle_tx, encode_mantle_tx, encode_signed_mantle_tx},
|
||||
gas::{Gas, GasCalculator, GasConstants, GasCost, GasOverflow, GasPrice},
|
||||
genesis_tx::{GENESIS_EXECUTION_GAS_PRICE, GENESIS_STORAGE_GAS_PRICE},
|
||||
ops::{
|
||||
Op, OpProof,
|
||||
channel::{ChannelId, ChannelKeyIndex, withdraw::ChannelWithdrawOp},
|
||||
@@ -80,8 +82,6 @@ impl TxHash {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct MantleTxDeSerImpl {
|
||||
pub ops: Vec<Op>,
|
||||
pub execution_gas_price: GasPrice,
|
||||
pub storage_gas_price: GasPrice,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -93,13 +93,43 @@ pub struct MantleTxContext {
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct MantleTxGasContext {
|
||||
withdraw_thresholds: HashMap<ChannelId, ChannelKeyIndex>,
|
||||
gas_prices: GasPrices,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct GasPrices {
|
||||
pub execution_base_gas_price: GasPrice,
|
||||
pub storage_gas_price: GasPrice,
|
||||
}
|
||||
|
||||
impl GasPrices {
|
||||
#[must_use]
|
||||
pub fn new(execution: u64, storage: u64) -> Self {
|
||||
Self {
|
||||
execution_base_gas_price: execution.into(),
|
||||
storage_gas_price: storage.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GasPrices {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
execution_base_gas_price: GENESIS_EXECUTION_GAS_PRICE,
|
||||
storage_gas_price: GENESIS_STORAGE_GAS_PRICE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MantleTxGasContext {
|
||||
#[must_use]
|
||||
pub const fn new(withdraw_thresholds: HashMap<ChannelId, ChannelKeyIndex>) -> Self {
|
||||
pub const fn new(
|
||||
withdraw_thresholds: HashMap<ChannelId, ChannelKeyIndex>,
|
||||
gas_prices: GasPrices,
|
||||
) -> Self {
|
||||
Self {
|
||||
withdraw_thresholds,
|
||||
gas_prices,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,44 +137,41 @@ impl MantleTxGasContext {
|
||||
pub fn withdraw_threshold(&self, channel_id: &ChannelId) -> Option<ChannelKeyIndex> {
|
||||
self.withdraw_thresholds.get(channel_id).copied()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_channels(value: &Channels, base_prices: GasPrices) -> Self {
|
||||
let withdraw_thresholds = value
|
||||
.channels
|
||||
.iter()
|
||||
.map(|(channel_id, channel)| (*channel_id, channel.withdraw_threshold))
|
||||
.collect();
|
||||
Self::new(withdraw_thresholds, base_prices)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_gas_prices(&self) -> GasPrices {
|
||||
self.gas_prices.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct MantleTx {
|
||||
pub ops: Vec<Op>,
|
||||
pub execution_gas_price: GasPrice,
|
||||
pub storage_gas_price: GasPrice,
|
||||
}
|
||||
pub struct MantleTx(pub Vec<Op>);
|
||||
|
||||
impl From<MantleTxDeSerImpl> for MantleTx {
|
||||
fn from(
|
||||
MantleTxDeSerImpl {
|
||||
ops,
|
||||
execution_gas_price,
|
||||
storage_gas_price,
|
||||
}: MantleTxDeSerImpl,
|
||||
) -> Self {
|
||||
Self {
|
||||
ops,
|
||||
execution_gas_price,
|
||||
storage_gas_price,
|
||||
}
|
||||
fn from(MantleTxDeSerImpl { ops }: MantleTxDeSerImpl) -> Self {
|
||||
Self(ops)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MantleTx> for MantleTxDeSerImpl {
|
||||
fn from(
|
||||
MantleTx {
|
||||
ops,
|
||||
execution_gas_price,
|
||||
storage_gas_price,
|
||||
}: MantleTx,
|
||||
) -> Self {
|
||||
Self {
|
||||
ops,
|
||||
execution_gas_price,
|
||||
storage_gas_price,
|
||||
}
|
||||
fn from(MantleTx(ops): MantleTx) -> Self {
|
||||
Self { ops }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoIterator<Item = Op>> From<T> for MantleTx {
|
||||
fn from(ops: T) -> Self {
|
||||
Self(ops.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +214,8 @@ impl GasCalculator for MantleTx {
|
||||
context: &Self::Context,
|
||||
) -> Result<GasCost, GasOverflow> {
|
||||
let execution_gas = self.execution_gas_consumption::<Constants>(context);
|
||||
let execution_gas_cost = GasCost::calculate(execution_gas?, self.execution_gas_price)?;
|
||||
let execution_gas_cost =
|
||||
GasCost::calculate(execution_gas?, context.gas_prices.execution_base_gas_price)?;
|
||||
let storage_gas_cost = self.storage_gas_cost(context)?;
|
||||
|
||||
execution_gas_cost.checked_add(storage_gas_cost)
|
||||
@@ -196,7 +224,7 @@ impl GasCalculator for MantleTx {
|
||||
fn storage_gas_cost(&self, context: &Self::Context) -> Result<GasCost, GasOverflow> {
|
||||
GasCost::calculate(
|
||||
self.storage_gas_consumption(context)?,
|
||||
self.storage_gas_price,
|
||||
context.gas_prices.storage_gas_price,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -204,7 +232,7 @@ impl GasCalculator for MantleTx {
|
||||
&self,
|
||||
_context: &Self::Context,
|
||||
) -> Result<Gas, GasOverflow> {
|
||||
self.ops
|
||||
self.ops()
|
||||
.iter()
|
||||
.map(Op::execution_gas::<Constants>)
|
||||
.try_fold(Gas::from(0), Gas::checked_add)
|
||||
@@ -224,13 +252,18 @@ impl MantleTx {
|
||||
#[must_use]
|
||||
pub fn transfers(&self) -> Vec<TransferOp> {
|
||||
let mut transfers: Vec<TransferOp> = vec![];
|
||||
for op in self.ops.clone() {
|
||||
for op in self.ops().clone() {
|
||||
if let Op::Transfer(transfer_op) = op {
|
||||
transfers.push(transfer_op);
|
||||
}
|
||||
}
|
||||
transfers
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn ops(&self) -> &Vec<Op> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
static MANTLE_TXHASH_V1_BYTES: LazyLock<Vec<u8>> = LazyLock::new(|| b"MANTLE_TXHASH_V1".to_vec());
|
||||
@@ -360,9 +393,9 @@ impl SignedMantleTx {
|
||||
// TODO: might drop proofs after verification
|
||||
fn verify_ops_proofs(&self) -> Result<(), VerificationError> {
|
||||
// Check that we have the same number of proofs as ops
|
||||
if self.mantle_tx.ops.len() != self.ops_proofs.len() {
|
||||
if self.mantle_tx.ops().len() != self.ops_proofs.len() {
|
||||
return Err(VerificationError::ProofCountMismatch {
|
||||
ops_count: self.mantle_tx.ops.len(),
|
||||
ops_count: self.mantle_tx.ops().len(),
|
||||
proofs_count: self.ops_proofs.len(),
|
||||
});
|
||||
}
|
||||
@@ -372,7 +405,7 @@ impl SignedMantleTx {
|
||||
|
||||
for (idx, (op, proof)) in self
|
||||
.mantle_tx
|
||||
.ops
|
||||
.ops()
|
||||
.iter()
|
||||
.zip(self.ops_proofs.iter())
|
||||
.enumerate()
|
||||
@@ -421,7 +454,7 @@ impl SignedMantleTx {
|
||||
|
||||
for (idx, (op, proof)) in self
|
||||
.mantle_tx
|
||||
.ops
|
||||
.ops()
|
||||
.iter()
|
||||
.zip(self.ops_proofs.iter())
|
||||
.enumerate()
|
||||
@@ -515,28 +548,42 @@ impl Transaction for SignedMantleTx {
|
||||
}
|
||||
|
||||
impl AuthenticatedMantleTx for SignedMantleTx {
|
||||
type Context = GasPrices;
|
||||
|
||||
fn mantle_tx(&self) -> &MantleTx {
|
||||
&self.mantle_tx
|
||||
}
|
||||
|
||||
fn ops_with_proof(&self) -> impl Iterator<Item = (&Op, &OpProof)> {
|
||||
self.mantle_tx.ops.iter().zip(self.ops_proofs.iter())
|
||||
self.mantle_tx.ops().iter().zip(self.ops_proofs.iter())
|
||||
}
|
||||
|
||||
fn total_gas_cost<Constants: GasConstants>(&self) -> Result<GasCost, GasOverflow> {
|
||||
GasCalculator::total_gas_cost::<Constants>(&self, &())
|
||||
fn total_gas_cost<Constants: GasConstants>(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<GasCost, GasOverflow> {
|
||||
GasCalculator::total_gas_cost::<Constants>(&self, &context)
|
||||
}
|
||||
|
||||
fn storage_gas_cost(&self) -> Result<GasCost, GasOverflow> {
|
||||
GasCalculator::storage_gas_cost(&self, &())
|
||||
fn storage_gas_cost(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<GasCost, GasOverflow> {
|
||||
GasCalculator::storage_gas_cost(&self, &context)
|
||||
}
|
||||
|
||||
fn execution_gas_consumption<Constants: GasConstants>(&self) -> Result<Gas, GasOverflow> {
|
||||
GasCalculator::execution_gas_consumption::<Constants>(&self, &())
|
||||
fn execution_gas_consumption<Constants: GasConstants>(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<Gas, GasOverflow> {
|
||||
GasCalculator::execution_gas_consumption::<Constants>(&self, &context)
|
||||
}
|
||||
|
||||
fn storage_gas_consumption(&self) -> Result<Gas, GasOverflow> {
|
||||
GasCalculator::storage_gas_consumption(&self, &())
|
||||
fn storage_gas_consumption(
|
||||
&self,
|
||||
context: <Self as AuthenticatedMantleTx>::Context,
|
||||
) -> Result<Gas, GasOverflow> {
|
||||
GasCalculator::storage_gas_consumption(&self, &context)
|
||||
}
|
||||
|
||||
fn verify_ops_proofs_with_helper(
|
||||
@@ -548,7 +595,7 @@ impl AuthenticatedMantleTx for SignedMantleTx {
|
||||
}
|
||||
|
||||
impl GasCalculator for SignedMantleTx {
|
||||
type Context = ();
|
||||
type Context = GasPrices;
|
||||
|
||||
fn total_gas_cost<Constants: GasConstants>(
|
||||
&self,
|
||||
@@ -556,7 +603,7 @@ impl GasCalculator for SignedMantleTx {
|
||||
) -> Result<GasCost, GasOverflow> {
|
||||
let execution_gas = GasCalculator::execution_gas_consumption::<Constants>(&self, context)?;
|
||||
let execution_gas_cost =
|
||||
GasCost::calculate(execution_gas, self.mantle_tx.execution_gas_price)?;
|
||||
GasCost::calculate(execution_gas, context.execution_base_gas_price)?;
|
||||
let storage_gas_cost = GasCalculator::storage_gas_cost(self, context)?;
|
||||
|
||||
execution_gas_cost.checked_add(storage_gas_cost)
|
||||
@@ -564,7 +611,7 @@ impl GasCalculator for SignedMantleTx {
|
||||
|
||||
fn storage_gas_cost(&self, context: &Self::Context) -> Result<GasCost, GasOverflow> {
|
||||
let storage_gas = GasCalculator::storage_gas_consumption(&self, context)?;
|
||||
GasCost::calculate(storage_gas, self.mantle_tx.storage_gas_price)
|
||||
GasCost::calculate(storage_gas, context.storage_gas_price)
|
||||
}
|
||||
|
||||
fn execution_gas_consumption<Constants: GasConstants>(
|
||||
@@ -572,7 +619,7 @@ impl GasCalculator for SignedMantleTx {
|
||||
_context: &Self::Context,
|
||||
) -> Result<Gas, GasOverflow> {
|
||||
self.mantle_tx
|
||||
.ops
|
||||
.ops()
|
||||
.iter()
|
||||
.map(Op::execution_gas::<Constants>)
|
||||
.try_fold(Gas::from(0), Gas::checked_add)
|
||||
@@ -617,11 +664,7 @@ mod tests {
|
||||
};
|
||||
|
||||
fn create_test_mantle_tx(ops: Vec<Op>) -> MantleTx {
|
||||
MantleTx {
|
||||
ops,
|
||||
execution_gas_price: 1.into(),
|
||||
storage_gas_price: 1.into(),
|
||||
}
|
||||
ops.into()
|
||||
}
|
||||
|
||||
fn create_test_inscribe_op(signing_key: &Ed25519Key) -> InscriptionOp {
|
||||
|
||||
@@ -6,10 +6,10 @@ use super::{GasCalculator as _, GasConstants, MantleTx, Note, Op, Utxo};
|
||||
use crate::{
|
||||
mantle::{
|
||||
NoteId,
|
||||
gas::{GasCost, GasOverflow, GasPrice},
|
||||
gas::{GasCost, GasOverflow},
|
||||
ledger::{Inputs, Outputs},
|
||||
ops::{channel::withdraw::ChannelWithdrawOp, transfer::TransferOp},
|
||||
tx::MantleTxContext,
|
||||
tx::{GasPrices, MantleTxContext},
|
||||
},
|
||||
proofs::channel_withdraw_proof::ChannelWithdrawProof,
|
||||
};
|
||||
@@ -29,11 +29,7 @@ impl MantleTxBuilder {
|
||||
#[must_use]
|
||||
pub fn new(context: MantleTxContext) -> Self {
|
||||
Self {
|
||||
mantle_tx: MantleTx {
|
||||
ops: vec![],
|
||||
execution_gas_price: 0.into(),
|
||||
storage_gas_price: 0.into(),
|
||||
},
|
||||
mantle_tx: vec![].into(),
|
||||
ledger_inputs: vec![],
|
||||
pending_transfer: TransferOp::new(Inputs::new(vec![]), Outputs::new(vec![])),
|
||||
channel_withdraw_proofs: HashMap::new(),
|
||||
@@ -41,6 +37,11 @@ impl MantleTxBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_gas_prices(&self) -> GasPrices {
|
||||
self.context.gas_context.get_gas_prices()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn push_op(self, op: Op) -> Self {
|
||||
self.extend_ops([op])
|
||||
@@ -48,14 +49,14 @@ impl MantleTxBuilder {
|
||||
|
||||
#[must_use]
|
||||
pub fn extend_ops(mut self, ops: impl IntoIterator<Item = Op>) -> Self {
|
||||
self.mantle_tx.ops.extend(ops);
|
||||
self.mantle_tx.0.extend(ops);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn push_channel_withdraw(self, op: ChannelWithdrawOp, proof: ChannelWithdrawProof) -> Self {
|
||||
let mut builder = self.push_op(Op::ChannelWithdraw(op));
|
||||
let index = builder.mantle_tx.ops.len() - 1;
|
||||
let index = builder.mantle_tx.ops().len() - 1;
|
||||
builder.channel_withdraw_proofs.insert(index, proof);
|
||||
builder
|
||||
}
|
||||
@@ -85,18 +86,6 @@ impl MantleTxBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn set_execution_gas_price(mut self, price: GasPrice) -> Self {
|
||||
self.mantle_tx.execution_gas_price = price;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn set_storage_gas_price(mut self, price: GasPrice) -> Self {
|
||||
self.mantle_tx.storage_gas_price = price;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn return_change<G: GasConstants>(
|
||||
self,
|
||||
change_pk: ZkPublicKey,
|
||||
@@ -174,7 +163,7 @@ impl MantleTxBuilder {
|
||||
/// build.
|
||||
pub fn consumed_or_locked_notes(&self) -> impl Iterator<Item = NoteId> {
|
||||
self.mantle_tx
|
||||
.ops
|
||||
.ops()
|
||||
.iter()
|
||||
.flat_map(|op| {
|
||||
let inputs: &[NoteId] = match op {
|
||||
@@ -204,7 +193,7 @@ impl MantleTxBuilder {
|
||||
|
||||
#[must_use]
|
||||
pub fn build(mut self) -> MantleTx {
|
||||
self.mantle_tx.ops.push(Op::Transfer(self.pending_transfer));
|
||||
self.mantle_tx.0.push(Op::Transfer(self.pending_transfer));
|
||||
self.mantle_tx
|
||||
}
|
||||
}
|
||||
@@ -286,7 +275,7 @@ mod tests {
|
||||
|
||||
// Init a tx builder
|
||||
let context = MantleTxContext {
|
||||
gas_context: MantleTxGasContext::new([(op.channel_id, 1)].into()),
|
||||
gas_context: MantleTxGasContext::new([(op.channel_id, 1)].into(), GasPrices::new(0, 0)),
|
||||
leader_reward_amount: 30,
|
||||
};
|
||||
let builder = MantleTxBuilder::new(context).push_op(Op::ChannelWithdraw(op));
|
||||
@@ -354,7 +343,7 @@ mod tests {
|
||||
// Init a tx builder for sending 30 to the recipient
|
||||
let channel_id = ChannelId::from([0; 32]);
|
||||
let context = MantleTxContext {
|
||||
gas_context: MantleTxGasContext::new([(channel_id, 1)].into()),
|
||||
gas_context: MantleTxGasContext::new([(channel_id, 1)].into(), GasPrices::new(0, 0)),
|
||||
leader_reward_amount: 30,
|
||||
};
|
||||
let withdraw_note = Note {
|
||||
|
||||
@@ -662,6 +662,7 @@ pub mod tests {
|
||||
gas::MainnetGasConstants,
|
||||
ledger::{Inputs, Outputs},
|
||||
ops::leader_claim::VoucherCm,
|
||||
tx::GasPrices,
|
||||
},
|
||||
sdp::ServiceParameters,
|
||||
};
|
||||
@@ -738,6 +739,15 @@ pub mod tests {
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[must_use]
|
||||
pub fn set_storage_price(self, new_storage_price: GasPrice) -> Self {
|
||||
Self {
|
||||
storage_gas_price: new_storage_price,
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_ledger(
|
||||
@@ -1256,11 +1266,7 @@ pub mod tests {
|
||||
.collect::<Vec<_>>();
|
||||
let inputs = inputs.iter().map(|(_, utxo)| utxo.id()).collect::<Vec<_>>();
|
||||
let transfer_op = TransferOp::new(Inputs::new(inputs), Outputs::new(outputs));
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::Transfer(transfer_op.clone())],
|
||||
execution_gas_price: GENESIS_EXECUTION_GAS_PRICE,
|
||||
storage_gas_price: GENESIS_STORAGE_GAS_PRICE,
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::Transfer(transfer_op.clone())]);
|
||||
let transfer_sig = ZkKey::multi_sign(&sks, &mantle_tx.hash().to_fr()).unwrap();
|
||||
(
|
||||
SignedMantleTx {
|
||||
@@ -1292,7 +1298,8 @@ pub mod tests {
|
||||
vec![output_note],
|
||||
);
|
||||
|
||||
let _fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx);
|
||||
let _fees =
|
||||
AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx, GasPrices::new(0, 0));
|
||||
let result = ledger_state.try_apply_transfer::<(), MainnetGasConstants>(
|
||||
&locked_notes,
|
||||
&transfer_op,
|
||||
@@ -1323,7 +1330,8 @@ pub mod tests {
|
||||
let (tx, transfer_op, transfer_sig) =
|
||||
create_tx_with_transfer(&[(¬e_sk, &input_utxo)], vec![output_note1, output_note2]);
|
||||
|
||||
let _fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx);
|
||||
let _fees =
|
||||
AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx, GasPrices::new(0, 0));
|
||||
let (new_state, balance) = ledger_state
|
||||
.try_apply_transfer::<(), MainnetGasConstants>(
|
||||
&locked_notes,
|
||||
@@ -1359,7 +1367,8 @@ pub mod tests {
|
||||
vec![],
|
||||
);
|
||||
let locked_notes = LockedNotes::new();
|
||||
let _fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx);
|
||||
let _fees =
|
||||
AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx, GasPrices::new(0, 0));
|
||||
let (final_state, final_balance) = new_state
|
||||
.try_apply_transfer::<(), MainnetGasConstants>(
|
||||
&locked_notes,
|
||||
@@ -1487,7 +1496,8 @@ pub mod tests {
|
||||
let (tx, transfer_op, transfer_sig) =
|
||||
create_tx_with_transfer(&[(&input_sk, &input_utxo)], vec![]);
|
||||
|
||||
let _fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx);
|
||||
let _fees =
|
||||
AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx, GasPrices::new(0, 0));
|
||||
let result = ledger_state.try_apply_transfer::<(), MainnetGasConstants>(
|
||||
&locked_notes,
|
||||
&transfer_op,
|
||||
|
||||
+90
-137
@@ -6,7 +6,7 @@ mod config;
|
||||
pub mod cryptarchia;
|
||||
pub mod mantle;
|
||||
|
||||
use std::{cmp::Ordering, collections::HashMap, hash::Hash};
|
||||
use std::{collections::HashMap, hash::Hash};
|
||||
|
||||
pub use config::Config;
|
||||
use cryptarchia::LedgerState as CryptarchiaLedger;
|
||||
@@ -24,7 +24,7 @@ use lb_core::{
|
||||
},
|
||||
leader_claim::{LeaderClaimExecutionContext, LeaderClaimValidationContext},
|
||||
},
|
||||
tx::MantleTxContext,
|
||||
tx::{GasPrices, MantleTxContext, MantleTxGasContext},
|
||||
},
|
||||
proofs::leader_proof,
|
||||
sdp::{Declaration, DeclarationId, ProviderId, ProviderInfo, ServiceType, SessionNumber},
|
||||
@@ -143,7 +143,7 @@ where
|
||||
parent_id: Id,
|
||||
slot: Slot,
|
||||
proof: &LeaderProof,
|
||||
txs: impl Iterator<Item = impl AuthenticatedMantleTx>,
|
||||
txs: impl Iterator<Item = impl AuthenticatedMantleTx<Context = GasPrices>>,
|
||||
) -> Result<(Id, LedgerState), LedgerError<Id>>
|
||||
where
|
||||
LeaderProof: leader_proof::LeaderProof,
|
||||
@@ -218,7 +218,7 @@ impl LedgerState {
|
||||
self,
|
||||
slot: Slot,
|
||||
proof: &LeaderProof,
|
||||
txs: impl Iterator<Item = impl AuthenticatedMantleTx>,
|
||||
txs: impl Iterator<Item = impl AuthenticatedMantleTx<Context = GasPrices>>,
|
||||
config: &Config,
|
||||
) -> Result<Self, LedgerError<Id>>
|
||||
where
|
||||
@@ -265,6 +265,14 @@ impl LedgerState {
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_gas_prices(&self) -> GasPrices {
|
||||
GasPrices {
|
||||
execution_base_gas_price: *self.cryptarchia_ledger.execution_base_fee(),
|
||||
storage_gas_price: *self.cryptarchia_ledger.storage_gas_price(),
|
||||
}
|
||||
}
|
||||
|
||||
/// total estimated stake and on the average of fees consumed per block over
|
||||
/// the last `BLOCK_REWARD_WINDOW_SIZE` blocks. See the block rewards
|
||||
/// specification: <https://www.notion.so/nomos-tech/v1-1-Block-Rewards-Specification-326261aa09df80579edddaf092057b3d>
|
||||
@@ -337,7 +345,7 @@ impl LedgerState {
|
||||
pub fn try_apply_contents<Id, Constants: GasConstants>(
|
||||
mut self,
|
||||
config: &Config,
|
||||
txs: impl Iterator<Item = impl AuthenticatedMantleTx>,
|
||||
txs: impl Iterator<Item = impl AuthenticatedMantleTx<Context = GasPrices>>,
|
||||
) -> Result<Self, LedgerError<Id>> {
|
||||
let mut total_block_execution_gas: Gas = 0.into();
|
||||
let mut total_fee_burned: GasCost = 0.into();
|
||||
@@ -346,44 +354,46 @@ impl LedgerState {
|
||||
let balance;
|
||||
(self, balance) = self.try_apply_tx::<_, Constants>(config, &tx)?;
|
||||
|
||||
let gas_prices = GasPrices {
|
||||
execution_base_gas_price: *self.cryptarchia_ledger.execution_base_fee(),
|
||||
storage_gas_price: *self.cryptarchia_ledger.storage_gas_price(),
|
||||
};
|
||||
// Check the transaction is balanced
|
||||
let total_gas_cost = AuthenticatedMantleTx::total_gas_cost::<Constants>(&tx)?;
|
||||
let total_gas_cost =
|
||||
AuthenticatedMantleTx::total_gas_cost::<Constants>(&tx, gas_prices.clone())?;
|
||||
tracing::debug!(
|
||||
balance,
|
||||
total_gas_cost = total_gas_cost.into_inner(),
|
||||
storage_gas_price = ?tx.mantle_tx().storage_gas_price,
|
||||
execution_gas_price = ?tx.mantle_tx().execution_gas_price,
|
||||
storage_gas_price = ?self.cryptarchia_ledger.storage_gas_price(),
|
||||
execution_gas_price = ?self.cryptarchia_ledger.execution_base_fee(),
|
||||
"tx balance check"
|
||||
);
|
||||
match balance.cmp(&Balance::from(total_gas_cost.into_inner())) {
|
||||
Ordering::Less => return Err(LedgerError::InsufficientBalance),
|
||||
Ordering::Greater => return Err(LedgerError::UnbalancedTransaction),
|
||||
Ordering::Equal => {} // OK!
|
||||
|
||||
// Check that the transaction at least pays for the base execution fee and
|
||||
// storage
|
||||
if balance < Balance::from(total_gas_cost.into_inner()) {
|
||||
return Err(LedgerError::InsufficientBalance);
|
||||
}
|
||||
|
||||
// Update the total of fee burned and tipped in the block
|
||||
let tx_fee_burned = GasCost::calculate(
|
||||
AuthenticatedMantleTx::execution_gas_consumption::<Constants>(&tx)?,
|
||||
*self.cryptarchia_ledger.execution_base_fee(),
|
||||
AuthenticatedMantleTx::execution_gas_consumption::<Constants>(
|
||||
&tx,
|
||||
gas_prices.clone(),
|
||||
)?,
|
||||
gas_prices.execution_base_gas_price,
|
||||
)?
|
||||
.checked_add(AuthenticatedMantleTx::storage_gas_cost(&tx)?)?;
|
||||
.checked_add(AuthenticatedMantleTx::storage_gas_cost(
|
||||
&tx,
|
||||
gas_prices.clone(),
|
||||
)?)?;
|
||||
|
||||
// Check that the transaction at least pays for the base fee
|
||||
if balance < Balance::from(tx_fee_burned.into_inner()) {
|
||||
return Err(LedgerError::InsufficientExecutionFee);
|
||||
}
|
||||
|
||||
// Check that the transaction pays the correct storage fees
|
||||
// TODO: remove the storage price from the Mantle Transaction and wallet should
|
||||
// pull the price from ledger to get the fees to pay
|
||||
if tx.mantle_tx().storage_gas_price != *self.cryptarchia_ledger.storage_gas_price() {
|
||||
return Err(LedgerError::InvalidStoragePrice);
|
||||
}
|
||||
let tx_fee_tip = GasCost::from(balance as Value).checked_sub(tx_fee_burned)?;
|
||||
total_fee_burned = total_fee_burned.checked_add(tx_fee_burned)?;
|
||||
total_fee_tip = total_fee_tip.checked_add(tx_fee_tip)?;
|
||||
total_block_execution_gas = total_block_execution_gas
|
||||
.checked_add(AuthenticatedMantleTx::execution_gas_consumption::<Constants>(&tx)?)?;
|
||||
total_block_execution_gas = total_block_execution_gas.checked_add(
|
||||
AuthenticatedMantleTx::execution_gas_consumption::<Constants>(&tx, gas_prices)?,
|
||||
)?;
|
||||
|
||||
// Check that the block is not exceeding the Gas limit
|
||||
if total_block_execution_gas > EXECUTION_GAS_LIMIT {
|
||||
@@ -495,7 +505,10 @@ impl LedgerState {
|
||||
#[must_use]
|
||||
pub fn tx_context(&self) -> MantleTxContext {
|
||||
MantleTxContext {
|
||||
gas_context: self.mantle_ledger().channels().into(),
|
||||
gas_context: MantleTxGasContext::from_channels(
|
||||
self.mantle_ledger().channels(),
|
||||
self.get_gas_prices(),
|
||||
),
|
||||
leader_reward_amount: self.mantle_ledger().leader_reward_amount(),
|
||||
}
|
||||
}
|
||||
@@ -689,8 +702,7 @@ mod tests {
|
||||
use lb_core::{
|
||||
mantle::{
|
||||
MantleTx, Note, SignedMantleTx, Transaction as _,
|
||||
gas::{GasPrice, MainnetGasConstants},
|
||||
genesis_tx::{GENESIS_EXECUTION_GAS_PRICE, GENESIS_STORAGE_GAS_PRICE},
|
||||
gas::MainnetGasConstants,
|
||||
ledger::{Inputs, Outputs},
|
||||
ops::{
|
||||
channel::{
|
||||
@@ -714,19 +726,9 @@ mod tests {
|
||||
|
||||
type HeaderId = [u8; 32];
|
||||
|
||||
fn create_tx(
|
||||
inputs: Vec<NoteId>,
|
||||
outputs: Vec<Note>,
|
||||
sks: &[ZkKey],
|
||||
execution_price: GasPrice,
|
||||
storage_price: GasPrice,
|
||||
) -> SignedMantleTx {
|
||||
fn create_tx(inputs: Vec<NoteId>, outputs: Vec<Note>, sks: &[ZkKey]) -> SignedMantleTx {
|
||||
let transfer_op = TransferOp::new(Inputs::new(inputs), Outputs::new(outputs));
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::Transfer(transfer_op)],
|
||||
execution_gas_price: execution_price,
|
||||
storage_gas_price: storage_price,
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::Transfer(transfer_op)]);
|
||||
SignedMantleTx {
|
||||
ops_proofs: vec![OpProof::ZkSig(
|
||||
ZkKey::multi_sign(sks, &mantle_tx.hash().to_fr()).unwrap(),
|
||||
@@ -749,6 +751,17 @@ mod tests {
|
||||
(signing_key, verifying_key)
|
||||
}
|
||||
|
||||
fn update_ledger_prices(ledger_state: &mut LedgerState, new_execution: u64, new_storage: u64) {
|
||||
ledger_state.cryptarchia_ledger = ledger_state
|
||||
.cryptarchia_ledger
|
||||
.clone()
|
||||
.set_storage_price(new_storage.into());
|
||||
ledger_state.cryptarchia_ledger = ledger_state
|
||||
.cryptarchia_ledger
|
||||
.clone()
|
||||
.set_execution_base_fee(new_execution.into());
|
||||
}
|
||||
|
||||
enum Key {
|
||||
Ed25519(Ed25519Key),
|
||||
Zk(ZkKey),
|
||||
@@ -761,11 +774,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn create_multi_signed_tx(ops: Vec<Op>, signing_keys: Vec<&Key>) -> SignedMantleTx {
|
||||
let mantle_tx = MantleTx {
|
||||
ops: ops.clone(),
|
||||
execution_gas_price: 0.into(),
|
||||
storage_gas_price: 0.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(ops.clone());
|
||||
|
||||
let tx_hash = mantle_tx.hash();
|
||||
let ops_proofs = signing_keys
|
||||
@@ -830,18 +839,12 @@ mod tests {
|
||||
vec![utxo.id()],
|
||||
vec![output_note],
|
||||
std::slice::from_ref(&sk),
|
||||
GENESIS_EXECUTION_GAS_PRICE,
|
||||
GENESIS_STORAGE_GAS_PRICE,
|
||||
);
|
||||
let fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx).unwrap();
|
||||
let fees =
|
||||
AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx, GasPrices::default())
|
||||
.unwrap();
|
||||
output_note.value = utxo.note.value - fees.into_inner();
|
||||
let tx = create_tx(
|
||||
vec![utxo.id()],
|
||||
vec![output_note],
|
||||
&[sk],
|
||||
GENESIS_EXECUTION_GAS_PRICE,
|
||||
GENESIS_STORAGE_GAS_PRICE,
|
||||
);
|
||||
let tx = create_tx(vec![utxo.id()], vec![output_note], &[sk]);
|
||||
|
||||
// Create a dummy proof (using same structure as in cryptarchia tests)
|
||||
|
||||
@@ -868,7 +871,7 @@ mod tests {
|
||||
assert!(!new_state.latest_utxos().contains(&utxo.id()));
|
||||
|
||||
// Verify output was created
|
||||
if let Op::Transfer(transfer_op) = &tx.mantle_tx.ops[0] {
|
||||
if let Op::Transfer(transfer_op) = &tx.mantle_tx.ops()[0] {
|
||||
let output_utxo = transfer_op.outputs.utxo_by_index(0, transfer_op).unwrap();
|
||||
assert!(new_state.latest_utxos().contains(&output_utxo.id()));
|
||||
} else {
|
||||
@@ -1043,11 +1046,7 @@ mod tests {
|
||||
outputs: Outputs::new(vec![withdraw_note]),
|
||||
withdraw_nonce: 0,
|
||||
};
|
||||
let withdraw_tx = MantleTx {
|
||||
ops: vec![Op::ChannelWithdraw(withdraw.clone())],
|
||||
execution_gas_price: 0.into(),
|
||||
storage_gas_price: 0.into(),
|
||||
};
|
||||
let withdraw_tx = MantleTx(vec![Op::ChannelWithdraw(withdraw.clone())]);
|
||||
let withdraw_tx_hash = withdraw_tx.hash();
|
||||
let withdraw_proof = ChannelWithdrawProof::new(vec![WithdrawSignature::new(
|
||||
0,
|
||||
@@ -1055,8 +1054,7 @@ mod tests {
|
||||
)])
|
||||
.unwrap();
|
||||
|
||||
let signed_tx =
|
||||
create_multi_signed_tx(withdraw_tx.ops, vec![&Key::Withdraw(withdraw_proof)]);
|
||||
let signed_tx = create_multi_signed_tx(withdraw_tx.0, vec![&Key::Withdraw(withdraw_proof)]);
|
||||
|
||||
let result =
|
||||
ledger_state.try_apply_tx::<HeaderId, MainnetGasConstants>(&test_config, signed_tx);
|
||||
@@ -1132,11 +1130,7 @@ mod tests {
|
||||
withdraw_nonce: 0,
|
||||
};
|
||||
let wrong_key = Ed25519Key::from_bytes(&[42; 32]);
|
||||
let withdraw_tx = MantleTx {
|
||||
ops: vec![Op::ChannelWithdraw(withdraw.clone())],
|
||||
execution_gas_price: 0.into(),
|
||||
storage_gas_price: 0.into(),
|
||||
};
|
||||
let withdraw_tx = MantleTx(vec![Op::ChannelWithdraw(withdraw.clone())]);
|
||||
let withdraw_tx_hash = withdraw_tx.hash();
|
||||
let invalid_proof = ChannelWithdrawProof::new(vec![WithdrawSignature::new(
|
||||
0,
|
||||
@@ -1145,7 +1139,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let signed_tx = create_multi_signed_tx(
|
||||
withdraw_tx.ops,
|
||||
withdraw_tx.0,
|
||||
vec![&Key::Withdraw(invalid_proof), &Key::EmptyZk],
|
||||
);
|
||||
|
||||
@@ -1421,42 +1415,13 @@ mod tests {
|
||||
// This test has been disabled pending API updates
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_storage_price_rejection() {
|
||||
let utxo = utxo();
|
||||
let config = config();
|
||||
let ledger = LedgerState::from_utxos([utxo], &config);
|
||||
|
||||
let mut output_note = Note::new(1, ZkPublicKey::new(BigUint::from(1u8).into()));
|
||||
let sk = ZkKey::from(BigUint::from(0u8));
|
||||
let tx = create_tx(
|
||||
vec![utxo.id()],
|
||||
vec![output_note],
|
||||
std::slice::from_ref(&sk),
|
||||
GENESIS_EXECUTION_GAS_PRICE,
|
||||
(GENESIS_STORAGE_GAS_PRICE.into_inner() + 1).into(), // wrong storage gas price
|
||||
);
|
||||
let fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx).unwrap();
|
||||
output_note.value = utxo.note.value - fees.into_inner();
|
||||
let tx = create_tx(
|
||||
vec![utxo.id()],
|
||||
vec![output_note],
|
||||
&[sk],
|
||||
GENESIS_EXECUTION_GAS_PRICE,
|
||||
(GENESIS_STORAGE_GAS_PRICE.into_inner() + 1).into(), // wrong storage gas price
|
||||
);
|
||||
|
||||
let result = ledger
|
||||
.try_apply_contents::<HeaderId, MainnetGasConstants>(&config, std::iter::once(&tx));
|
||||
assert_eq!(result, Err(LedgerError::InvalidStoragePrice));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "TODO: enable once we determine non-zero genesis execution gas price"]
|
||||
fn test_base_fee_rejection() {
|
||||
fn test_fee_rejection() {
|
||||
let utxo = utxo();
|
||||
let config = config();
|
||||
let mut ledger = LedgerState::from_utxos([utxo], &config);
|
||||
update_ledger_prices(&mut ledger, 1, 1);
|
||||
|
||||
let mut output_note = Note::new(1, ZkPublicKey::new(BigUint::from(0u8).into()));
|
||||
let sk = ZkKey::from(BigUint::from(0u8));
|
||||
@@ -1464,19 +1429,15 @@ mod tests {
|
||||
vec![utxo.id()],
|
||||
vec![output_note],
|
||||
std::slice::from_ref(&sk),
|
||||
1.into(),
|
||||
1.into(),
|
||||
);
|
||||
// Pays 2925 fees = 2705 execution base fee + 0 execution tip + 220 storage
|
||||
let fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx).unwrap();
|
||||
let fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(
|
||||
&tx,
|
||||
ledger.get_gas_prices(),
|
||||
)
|
||||
.unwrap();
|
||||
output_note.value = utxo.note.value - fees.into_inner();
|
||||
let tx = create_tx(
|
||||
vec![utxo.id()],
|
||||
vec![output_note],
|
||||
&[sk],
|
||||
1.into(),
|
||||
1.into(),
|
||||
);
|
||||
let tx = create_tx(vec![utxo.id()], vec![output_note], &[sk]);
|
||||
|
||||
let result = ledger
|
||||
.clone()
|
||||
@@ -1490,7 +1451,7 @@ mod tests {
|
||||
.try_apply_contents::<HeaderId, MainnetGasConstants>(&config, std::iter::once(&tx));
|
||||
// The transaction should be rejected because the price indicated for execution
|
||||
// doesn't cover the base fee that cost 27 050
|
||||
assert_eq!(result, Err(LedgerError::InsufficientExecutionFee));
|
||||
assert_eq!(result, Err(LedgerError::InsufficientBalance));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1498,7 +1459,7 @@ mod tests {
|
||||
fn test_priority_fees_go_to_leader() {
|
||||
let utxo = utxo();
|
||||
let config = config();
|
||||
let ledger = LedgerState::from_utxos([utxo], &config);
|
||||
let mut ledger = LedgerState::from_utxos([utxo], &config);
|
||||
|
||||
let mut output_note = Note::new(1, ZkPublicKey::new(BigUint::from(0u8).into()));
|
||||
let sk = ZkKey::from(BigUint::from(0u8));
|
||||
@@ -1506,48 +1467,40 @@ mod tests {
|
||||
vec![utxo.id()],
|
||||
vec![output_note],
|
||||
std::slice::from_ref(&sk),
|
||||
1.into(),
|
||||
1.into(),
|
||||
);
|
||||
// The tx ays 2925 fees = 2705 execution base fee + 0 execution tip + 220
|
||||
update_ledger_prices(&mut ledger, 1, 1);
|
||||
// The tx pays 794 fees = 590 execution base fee + 0 execution tip + 204
|
||||
// storage
|
||||
let fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx).unwrap();
|
||||
let fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(
|
||||
&tx,
|
||||
ledger.get_gas_prices(),
|
||||
)
|
||||
.unwrap();
|
||||
output_note.value = utxo.note.value - fees.into_inner();
|
||||
let tx = create_tx(
|
||||
vec![utxo.id()],
|
||||
vec![output_note],
|
||||
std::slice::from_ref(&sk),
|
||||
1.into(),
|
||||
1.into(),
|
||||
);
|
||||
|
||||
let result = ledger
|
||||
.clone()
|
||||
.try_apply_contents::<HeaderId, MainnetGasConstants>(&config, std::iter::once(&tx));
|
||||
// The unwrap should succeed because the user pays at least the base fee of 2705
|
||||
// The unwrap should succeed because the user pays at least the base fee of 794
|
||||
let no_priority_fee_ledger = result.unwrap();
|
||||
|
||||
// The tx ays 1794 fees = 590 execution base fee + 1000 execution tip + 204
|
||||
// storage
|
||||
output_note.value = utxo.note.value - fees.into_inner() - 1000;
|
||||
let tx = create_tx(
|
||||
vec![utxo.id()],
|
||||
vec![output_note],
|
||||
std::slice::from_ref(&sk),
|
||||
2.into(),
|
||||
1.into(),
|
||||
);
|
||||
// The tx ays 5630 fees = 2705 execution base fee + 2705 execution tip + 220
|
||||
// storage
|
||||
let fees = AuthenticatedMantleTx::total_gas_cost::<MainnetGasConstants>(&tx).unwrap();
|
||||
output_note.value = utxo.note.value - fees.into_inner();
|
||||
let tx = create_tx(
|
||||
vec![utxo.id()],
|
||||
vec![output_note],
|
||||
&[sk],
|
||||
2.into(),
|
||||
1.into(),
|
||||
);
|
||||
|
||||
let result = ledger
|
||||
.try_apply_contents::<HeaderId, MainnetGasConstants>(&config, std::iter::once(&tx));
|
||||
// The unwrap should succeed because the user pays at least the base fee of 2705
|
||||
// The unwrap should succeed because the user pays at least the base fee of 794
|
||||
let priority_fee_ledger = result.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
@@ -1555,7 +1508,7 @@ mod tests {
|
||||
.mantle_ledger
|
||||
.leaders
|
||||
.get_pending_rewards()
|
||||
+ 2705,
|
||||
+ 1000,
|
||||
priority_fee_ledger
|
||||
.mantle_ledger
|
||||
.leaders
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use lb_core::mantle::{MantleTx, Op, OpProof, SignedMantleTx, TxHash, gas::GasPrice};
|
||||
use lb_core::mantle::{MantleTx, Op, OpProof, SignedMantleTx, TxHash};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -6,9 +6,8 @@ use serde::Serialize;
|
||||
pub struct ApiTransactionSerializer {
|
||||
#[serde(getter = "<MantleTx as lb_core::mantle::Transaction>::hash")]
|
||||
hash: TxHash,
|
||||
#[serde(getter = "MantleTx::ops")]
|
||||
ops: Vec<Op>,
|
||||
execution_gas_price: GasPrice,
|
||||
storage_gas_price: GasPrice,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
||||
@@ -57,33 +57,31 @@ cryptarchia:
|
||||
transactions:
|
||||
- mantle_tx:
|
||||
ops:
|
||||
- opcode: 0
|
||||
payload:
|
||||
inputs: []
|
||||
outputs:
|
||||
- value: 1
|
||||
pk: d204000000000000000000000000000000000000000000000000000000000000
|
||||
- value: 100
|
||||
pk: '2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26'
|
||||
- value: 1
|
||||
pk: ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717
|
||||
- opcode: 17
|
||||
payload:
|
||||
channel_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
# chain_id_len=12 (u64_le), chain_id=logos-devnet (utf-8),
|
||||
# genesis_time=2026-01-10T07:47:56Z (u64_le), epoch_nonce=[0u8; 32]
|
||||
inscription: '0c000000000000006c6f676f732d6465766e65742c046269000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
parent: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
signer: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
- opcode: 32
|
||||
payload:
|
||||
service_type: BN
|
||||
locators: []
|
||||
provider_id: '86c8519f00178e9eb1fe5f4247e4bed77d4c9f6da2fb10e8a1fdd7ba6bc79fa0'
|
||||
zk_id: '64249c75c2cb813578b75d05b215fc95f67cea5862fff047228183f22e63460e'
|
||||
locked_note_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
execution_gas_price: 0
|
||||
storage_gas_price: 0
|
||||
- opcode: 0
|
||||
payload:
|
||||
inputs: []
|
||||
outputs:
|
||||
- value: 1
|
||||
pk: d204000000000000000000000000000000000000000000000000000000000000
|
||||
- value: 100
|
||||
pk: '2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26'
|
||||
- value: 1
|
||||
pk: ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717
|
||||
- opcode: 17
|
||||
payload:
|
||||
channel_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
# chain_id_len=12 (u64_le), chain_id=logos-devnet (utf-8),
|
||||
# genesis_time=2026-01-10T07:47:56Z (u64_le), epoch_nonce=[0u8; 32]
|
||||
inscription: '0c000000000000006c6f676f732d6465766e65742c046269000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
parent: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
signer: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
- opcode: 32
|
||||
payload:
|
||||
service_type: BN
|
||||
locators: []
|
||||
provider_id: '86c8519f00178e9eb1fe5f4247e4bed77d4c9f6da2fb10e8a1fdd7ba6bc79fa0'
|
||||
zk_id: '64249c75c2cb813578b75d05b215fc95f67cea5862fff047228183f22e63460e'
|
||||
locked_note_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
ops_proofs:
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
|
||||
@@ -10,34 +10,32 @@ header:
|
||||
voucher_cm: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
signature: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
transactions:
|
||||
- mantle_tx:
|
||||
ops:
|
||||
- opcode: 0
|
||||
payload:
|
||||
inputs: []
|
||||
outputs:
|
||||
- value: 1
|
||||
pk: d204000000000000000000000000000000000000000000000000000000000000
|
||||
- value: 100
|
||||
pk: '2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26'
|
||||
- value: 1
|
||||
pk: ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717
|
||||
- opcode: 17
|
||||
payload:
|
||||
channel_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
inscription: '67656e65736973'
|
||||
parent: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
signer: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
- opcode: 32
|
||||
payload:
|
||||
service_type: BN
|
||||
locators: []
|
||||
provider_id: '86c8519f00178e9eb1fe5f4247e4bed77d4c9f6da2fb10e8a1fdd7ba6bc79fa0'
|
||||
zk_id: '64249c75c2cb813578b75d05b215fc95f67cea5862fff047228183f22e63460e'
|
||||
locked_note_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
execution_gas_price: 0
|
||||
storage_gas_price: 0
|
||||
ops_proofs:
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- mantle_tx:
|
||||
ops:
|
||||
- opcode: 0
|
||||
payload:
|
||||
inputs: [ ]
|
||||
outputs:
|
||||
- value: 1
|
||||
pk: d204000000000000000000000000000000000000000000000000000000000000
|
||||
- value: 100
|
||||
pk: '2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26'
|
||||
- value: 1
|
||||
pk: ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717
|
||||
- opcode: 17
|
||||
payload:
|
||||
channel_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
inscription: '67656e65736973'
|
||||
parent: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
signer: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
- opcode: 32
|
||||
payload:
|
||||
service_type: BN
|
||||
locators: [ ]
|
||||
provider_id: '86c8519f00178e9eb1fe5f4247e4bed77d4c9f6da2fb10e8a1fdd7ba6bc79fa0'
|
||||
zk_id: '64249c75c2cb813578b75d05b215fc95f67cea5862fff047228183f22e63460e'
|
||||
locked_note_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
ops_proofs:
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
|
||||
@@ -52,39 +52,37 @@ cryptarchia:
|
||||
voucher_cm: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
signature: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
transactions:
|
||||
- mantle_tx:
|
||||
ops:
|
||||
- opcode: 0
|
||||
payload:
|
||||
inputs: []
|
||||
outputs:
|
||||
- value: 1
|
||||
pk: d204000000000000000000000000000000000000000000000000000000000000
|
||||
- value: 100
|
||||
pk: '2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26'
|
||||
- value: 1
|
||||
pk: ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717
|
||||
- opcode: 17
|
||||
payload:
|
||||
channel_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
# chain_id_len=12 (u64_le), chain_id=logos-devnet (utf-8),
|
||||
# genesis_time=2026-01-10T07:47:56Z (u64_le), epoch_nonce=[0u8; 32]
|
||||
inscription: '0c000000000000006c6f676f732d6465766e65742c046269000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
parent: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
signer: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
- opcode: 32
|
||||
payload:
|
||||
service_type: BN
|
||||
locators: []
|
||||
provider_id: '86c8519f00178e9eb1fe5f4247e4bed77d4c9f6da2fb10e8a1fdd7ba6bc79fa0'
|
||||
zk_id: '64249c75c2cb813578b75d05b215fc95f67cea5862fff047228183f22e63460e'
|
||||
locked_note_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
execution_gas_price: 0
|
||||
storage_gas_price: 0
|
||||
ops_proofs:
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- mantle_tx:
|
||||
ops:
|
||||
- opcode: 0
|
||||
payload:
|
||||
inputs: [ ]
|
||||
outputs:
|
||||
- value: 1
|
||||
pk: d204000000000000000000000000000000000000000000000000000000000000
|
||||
- value: 100
|
||||
pk: '2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26'
|
||||
- value: 1
|
||||
pk: ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717
|
||||
- opcode: 17
|
||||
payload:
|
||||
channel_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
# chain_id_len=12 (u64_le), chain_id=logos-devnet (utf-8),
|
||||
# genesis_time=2026-01-10T07:47:56Z (u64_le), epoch_nonce=[0u8; 32]
|
||||
inscription: '0c000000000000006c6f676f732d6465766e65742c046269000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
parent: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
signer: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
- opcode: 32
|
||||
payload:
|
||||
service_type: BN
|
||||
locators: [ ]
|
||||
provider_id: '86c8519f00178e9eb1fe5f4247e4bed77d4c9f6da2fb10e8a1fdd7ba6bc79fa0'
|
||||
zk_id: '64249c75c2cb813578b75d05b215fc95f67cea5862fff047228183f22e63460e'
|
||||
locked_note_id: '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
ops_proofs:
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
time:
|
||||
slot_duration: '1.0'
|
||||
mempool:
|
||||
|
||||
@@ -29,7 +29,7 @@ use lb_chain_broadcast_service::{
|
||||
use lb_core::{
|
||||
block::{Block, genesis::GenesisBlock},
|
||||
header::HeaderId,
|
||||
mantle::{AuthenticatedMantleTx, Transaction, TxHash, gas::MainnetGasConstants},
|
||||
mantle::{AuthenticatedMantleTx, Transaction, TxHash, gas::MainnetGasConstants, tx::GasPrices},
|
||||
sdp::{Declaration, DeclarationId, ProviderId, ProviderInfo, ServiceType},
|
||||
};
|
||||
use lb_cryptarchia_engine::{Branch, PrunedBlocks, ReorgedBlocks};
|
||||
@@ -297,7 +297,7 @@ impl Cryptarchia {
|
||||
current_slot: Slot,
|
||||
) -> Result<(PrunedBlocks<HeaderId>, ReorgedBlocks<HeaderId>), Error>
|
||||
where
|
||||
Tx: AuthenticatedMantleTx,
|
||||
Tx: AuthenticatedMantleTx<Context = GasPrices>,
|
||||
{
|
||||
let header = block.header();
|
||||
let id = header.id();
|
||||
@@ -512,7 +512,7 @@ impl<Tx, Storage, TimeBackend, RuntimeServiceId> ServiceCore<RuntimeServiceId>
|
||||
for CryptarchiaConsensus<Tx, Storage, TimeBackend, RuntimeServiceId>
|
||||
where
|
||||
Tx: Transaction<Hash = TxHash>
|
||||
+ AuthenticatedMantleTx
|
||||
+ AuthenticatedMantleTx<Context = GasPrices>
|
||||
+ Debug
|
||||
+ Clone
|
||||
+ Eq
|
||||
@@ -720,7 +720,7 @@ impl<Tx, Storage, TimeBackend, RuntimeServiceId>
|
||||
CryptarchiaConsensus<Tx, Storage, TimeBackend, RuntimeServiceId>
|
||||
where
|
||||
Tx: Transaction<Hash = TxHash>
|
||||
+ AuthenticatedMantleTx
|
||||
+ AuthenticatedMantleTx<Context = GasPrices>
|
||||
+ Debug
|
||||
+ Clone
|
||||
+ Eq
|
||||
|
||||
@@ -253,7 +253,7 @@ mod tests {
|
||||
|
||||
use lb_core::mantle::{
|
||||
ops::channel::{ChannelId, ChannelKeyIndex},
|
||||
tx::MantleTxGasContext,
|
||||
tx::{GasPrices, MantleTxGasContext},
|
||||
};
|
||||
use overwatch::services::state::{NoOperator, NoState};
|
||||
use tokio::sync::mpsc;
|
||||
@@ -294,6 +294,7 @@ mod tests {
|
||||
let expected_block_id = HeaderId::from([7u8; 32]);
|
||||
let expected_channel_id = ChannelId::from([9u8; 32]);
|
||||
let expected_threshold: ChannelKeyIndex = 2;
|
||||
let expected_gas_prices = GasPrices::new(3, 7);
|
||||
|
||||
let (msg_sender, mut msg_receiver) = mpsc::channel(1);
|
||||
tokio::spawn(async move {
|
||||
@@ -303,6 +304,7 @@ mod tests {
|
||||
let context = MantleTxContext {
|
||||
gas_context: MantleTxGasContext::new(
|
||||
std::iter::once((expected_channel_id, expected_threshold)).collect(),
|
||||
expected_gas_prices,
|
||||
),
|
||||
leader_reward_amount: 0,
|
||||
};
|
||||
|
||||
@@ -748,7 +748,7 @@ where
|
||||
let tx_hash = mantle_tx.hash();
|
||||
|
||||
let mut ops_proofs = Vec::new();
|
||||
for (i, op) in mantle_tx.ops.iter().enumerate() {
|
||||
for (i, op) in mantle_tx.ops().iter().enumerate() {
|
||||
let proof = match op {
|
||||
Op::ChannelInscribe(inscribe_op) => {
|
||||
Self::sign_inscription(tx_hash, inscribe_op, kms).await?
|
||||
|
||||
@@ -90,7 +90,7 @@ fn extract_l2_blocks(
|
||||
.flat_map(|tx| {
|
||||
let tx_hash = tx.mantle_tx.hash();
|
||||
tx.mantle_tx
|
||||
.ops
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|op| match op {
|
||||
Op::ChannelInscribe(InscriptionOp {
|
||||
|
||||
@@ -165,11 +165,7 @@ impl Sequencer {
|
||||
signer: verifying_key,
|
||||
};
|
||||
|
||||
let inscribe_tx = MantleTx {
|
||||
ops: vec![Op::ChannelInscribe(inscribe_op)],
|
||||
storage_gas_price: 0.into(),
|
||||
execution_gas_price: 0.into(),
|
||||
};
|
||||
let inscribe_tx = MantleTx(vec![Op::ChannelInscribe(inscribe_op)]);
|
||||
|
||||
let tx_hash = inscribe_tx.hash();
|
||||
let signature_bytes = self
|
||||
@@ -206,7 +202,7 @@ impl Sequencer {
|
||||
block_id: HeaderId,
|
||||
) -> bool {
|
||||
for tx in &block.transactions {
|
||||
for op in &tx.mantle_tx.ops {
|
||||
for op in tx.mantle_tx.ops() {
|
||||
if let Op::ChannelInscribe(inscribe) = op {
|
||||
tracing::debug!(
|
||||
"Found inscription: channel={}, parent={}",
|
||||
@@ -273,7 +269,7 @@ impl Sequencer {
|
||||
fn get_expected_inscription(tx: &SignedMantleTx) -> &InscriptionOp {
|
||||
let expected_op = tx
|
||||
.mantle_tx
|
||||
.ops
|
||||
.ops()
|
||||
.first()
|
||||
.expect("transaction should have at least one op");
|
||||
|
||||
@@ -513,7 +509,7 @@ impl Sequencer {
|
||||
let parent = self.get_last_msg_id().await?;
|
||||
let tx = self.create_inscribe_tx(inscription_data, parent);
|
||||
|
||||
let new_msg_id = match tx.mantle_tx.ops.first() {
|
||||
let new_msg_id = match tx.mantle_tx.ops().first() {
|
||||
Some(Op::ChannelInscribe(inscribe)) => inscribe.id(),
|
||||
_ => panic!("Expected ChannelInscribe op"),
|
||||
};
|
||||
|
||||
@@ -2,12 +2,13 @@ use std::collections::HashMap;
|
||||
|
||||
use lb_core::mantle::{
|
||||
OpProof, TxHash,
|
||||
gas::GasPrice,
|
||||
genesis_tx::GENESIS_STORAGE_GAS_PRICE,
|
||||
ops::{
|
||||
Op,
|
||||
channel::{ChannelId, MsgId, inscribe::InscriptionOp},
|
||||
},
|
||||
tx::{MantleTxContext, MantleTxGasContext},
|
||||
tx::{GasPrices, MantleTxContext, MantleTxGasContext},
|
||||
tx_builder::MantleTxBuilder,
|
||||
};
|
||||
use lb_key_management_system_service::keys::{Ed25519Key, Ed25519Signature};
|
||||
@@ -19,19 +20,22 @@ pub fn build_inscription_tx_builder(
|
||||
parent: Option<MsgId>,
|
||||
) -> MantleTxBuilder {
|
||||
let tx_context = MantleTxContext {
|
||||
gas_context: MantleTxGasContext::new(HashMap::new()),
|
||||
gas_context: MantleTxGasContext::new(
|
||||
HashMap::new(),
|
||||
GasPrices {
|
||||
execution_base_gas_price: GasPrice::new(0),
|
||||
storage_gas_price: GENESIS_STORAGE_GAS_PRICE,
|
||||
},
|
||||
),
|
||||
leader_reward_amount: 0,
|
||||
};
|
||||
|
||||
MantleTxBuilder::new(tx_context)
|
||||
.push_op(Op::ChannelInscribe(InscriptionOp {
|
||||
channel_id,
|
||||
inscription,
|
||||
parent: parent.unwrap_or_else(MsgId::root),
|
||||
signer: signing_key.public_key(),
|
||||
}))
|
||||
.set_storage_gas_price(GENESIS_STORAGE_GAS_PRICE)
|
||||
.set_execution_gas_price(0.into())
|
||||
MantleTxBuilder::new(tx_context).push_op(Op::ChannelInscribe(InscriptionOp {
|
||||
channel_id,
|
||||
inscription,
|
||||
parent: parent.unwrap_or_else(MsgId::root),
|
||||
signer: signing_key.public_key(),
|
||||
}))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@@ -38,11 +38,7 @@ pub fn create_channel_inscribe_tx(
|
||||
signer: verifying_key,
|
||||
};
|
||||
|
||||
let inscribe_tx = MantleTx {
|
||||
ops: vec![Op::ChannelInscribe(inscribe_op)],
|
||||
storage_gas_price: 0.into(),
|
||||
execution_gas_price: 0.into(),
|
||||
};
|
||||
let inscribe_tx = MantleTx(vec![Op::ChannelInscribe(inscribe_op)]);
|
||||
|
||||
let tx_hash = inscribe_tx.hash();
|
||||
let signature_bytes = signing_key
|
||||
@@ -67,11 +63,7 @@ pub fn create_channel_set_keys_tx(
|
||||
keys,
|
||||
};
|
||||
|
||||
let set_keys_tx = MantleTx {
|
||||
ops: vec![Op::ChannelSetKeys(set_keys_op)],
|
||||
storage_gas_price: 0.into(),
|
||||
execution_gas_price: 0.into(),
|
||||
};
|
||||
let set_keys_tx = MantleTx(vec![Op::ChannelSetKeys(set_keys_op)]);
|
||||
|
||||
let tx_hash = set_keys_tx.hash();
|
||||
let signature_bytes = signing_key
|
||||
@@ -107,11 +99,7 @@ pub fn create_sdp_declare_tx(
|
||||
locked_note_id,
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::SDPDeclare(declaration.clone())],
|
||||
execution_gas_price: 0.into(),
|
||||
storage_gas_price: 0.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::SDPDeclare(declaration.clone())]);
|
||||
|
||||
let tx_hash = mantle_tx.hash();
|
||||
|
||||
@@ -139,11 +127,7 @@ pub fn create_sdp_active_tx(
|
||||
zk_sk: &ZkKey,
|
||||
note_sk: &ZkKey,
|
||||
) -> SignedMantleTx {
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::SDPActive(active.clone())],
|
||||
execution_gas_price: 0.into(),
|
||||
storage_gas_price: 0.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::SDPActive(active.clone())]);
|
||||
|
||||
let tx_hash = mantle_tx.hash();
|
||||
let zk_sig = prove_zk_signature(&tx_hash, &[note_sk.clone(), zk_sk.clone()]);
|
||||
@@ -160,11 +144,7 @@ pub fn create_sdp_withdraw_tx(
|
||||
zk_sk: &ZkKey,
|
||||
note_sk: &ZkKey,
|
||||
) -> SignedMantleTx {
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::SDPWithdraw(withdraw)],
|
||||
execution_gas_price: 0.into(),
|
||||
storage_gas_price: 0.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::SDPWithdraw(withdraw)]);
|
||||
|
||||
let tx_hash = mantle_tx.hash();
|
||||
let zk_sig = prove_zk_signature(&tx_hash, &[note_sk.clone(), zk_sk.clone()]);
|
||||
@@ -193,11 +173,7 @@ pub fn create_inscription_transaction_with_id(
|
||||
signer,
|
||||
};
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::ChannelInscribe(inscription_op)],
|
||||
storage_gas_price: 0.into(),
|
||||
execution_gas_price: 0.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::ChannelInscribe(inscription_op)]);
|
||||
|
||||
let tx_hash = mantle_tx.hash();
|
||||
let signature = signing_key.sign_payload(&tx_hash.as_signing_bytes());
|
||||
|
||||
@@ -147,7 +147,7 @@ fn with_transfer_input_chunks(
|
||||
/// Reads the builder-authored pending transfer outputs so caller-side chunking
|
||||
/// can preserve them on the final transfer chunk.
|
||||
fn pending_transfer_output_sum(tx_builder: &MantleTxBuilder) -> u128 {
|
||||
match tx_builder.clone().build().ops.pop() {
|
||||
match tx_builder.clone().build().0.pop() {
|
||||
Some(Op::Transfer(transfer)) => transfer
|
||||
.outputs
|
||||
.iter()
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::{collections::HashSet, time::Duration};
|
||||
use lb_common_http_client::ApiBlock;
|
||||
use lb_core::mantle::{
|
||||
MantleTx, Note, Op, OpProof, SignedMantleTx, Transaction as _, TxHash,
|
||||
genesis_tx::GENESIS_STORAGE_GAS_PRICE,
|
||||
ledger::{Inputs, Outputs},
|
||||
ops::transfer::TransferOp,
|
||||
};
|
||||
@@ -184,11 +183,7 @@ fn create_invalid_transaction() -> SignedMantleTx {
|
||||
let output_note = Note::new(1000, ZkPublicKey::new(1u8.into()));
|
||||
let transfer_op = TransferOp::new(Inputs::new(vec![]), Outputs::new(vec![output_note]));
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::Transfer(transfer_op)],
|
||||
storage_gas_price: GENESIS_STORAGE_GAS_PRICE,
|
||||
execution_gas_price: 0.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::Transfer(transfer_op)]);
|
||||
|
||||
let transfer_proof = ZkKey::multi_sign(&[], &mantle_tx.hash().to_fr())
|
||||
.expect("invalid transfer proof should still be constructible");
|
||||
|
||||
@@ -15,7 +15,7 @@ use lb_core::{
|
||||
TxHash, Utxo,
|
||||
gas::MainnetGasConstants,
|
||||
ops::Op,
|
||||
tx::{MantleTxContext, MantleTxGasContext},
|
||||
tx::{GasPrices, MantleTxContext, MantleTxGasContext},
|
||||
tx_builder::MantleTxBuilder,
|
||||
},
|
||||
};
|
||||
@@ -237,7 +237,8 @@ pub(crate) async fn prepare_user_wallet_built_transaction_submission(
|
||||
|
||||
let mantle_tx = funded_builder.clone().build();
|
||||
let tx_hash = mantle_tx.hash();
|
||||
let transfer_proofs = build_transfer_proofs(step, &mantle_tx.ops, &tx_hash, &transfer_signers)?;
|
||||
let transfer_proofs =
|
||||
build_transfer_proofs(step, mantle_tx.ops(), &tx_hash, &transfer_signers)?;
|
||||
|
||||
Ok(PreparedUserWalletSubmission {
|
||||
wallet,
|
||||
@@ -302,6 +303,7 @@ pub(crate) async fn submit_prepared_user_wallet_transaction(
|
||||
newly_encumbered_fee,
|
||||
} = prepared;
|
||||
let sender_wallet_name = wallet.wallet_name.as_str();
|
||||
let gas_prices = funded_builder.get_gas_prices();
|
||||
|
||||
let mantle_tx = funded_builder.build();
|
||||
extra_op_proofs.extend(transfer_proofs);
|
||||
@@ -337,7 +339,7 @@ pub(crate) async fn submit_prepared_user_wallet_transaction(
|
||||
world.record_tracked_spent_fee(
|
||||
sender_wallet_name,
|
||||
signed_tx
|
||||
.total_gas_cost::<MainnetGasConstants>()
|
||||
.total_gas_cost::<MainnetGasConstants>(gas_prices)
|
||||
.map_err(|e| StepError::LogicalError {
|
||||
message: format!("Step `{step}` error: failed to compute gas cost: {e}"),
|
||||
})
|
||||
@@ -422,7 +424,7 @@ fn funding_inputs_from_transfers(
|
||||
step: &str,
|
||||
) -> Result<Vec<Utxo>, StepError> {
|
||||
mantle_tx
|
||||
.ops
|
||||
.ops()
|
||||
.iter()
|
||||
.filter_map(|op| match op {
|
||||
Op::Transfer(transfer_op) => Some(transfer_op),
|
||||
@@ -796,11 +798,13 @@ fn log_wallet_balance(
|
||||
|
||||
fn base_user_wallet_transaction(receivers: &[(ZkPublicKey, u64)]) -> MantleTxBuilder {
|
||||
let empty_context = MantleTxContext {
|
||||
gas_context: MantleTxGasContext::new(HashMap::new()),
|
||||
gas_context: MantleTxGasContext::new(
|
||||
HashMap::new(),
|
||||
GasPrices::new(0, DEFAULT_STORAGE_GAS_PRICE),
|
||||
),
|
||||
..MantleTxContext::default()
|
||||
};
|
||||
let mut tx_builder =
|
||||
MantleTxBuilder::new(empty_context).set_storage_gas_price(DEFAULT_STORAGE_GAS_PRICE.into());
|
||||
let mut tx_builder = MantleTxBuilder::new(empty_context);
|
||||
|
||||
for (receiver_pk, value) in receivers {
|
||||
tx_builder = tx_builder.add_ledger_output(Note::new(*value, *receiver_pk));
|
||||
|
||||
@@ -8,7 +8,9 @@ use std::{
|
||||
use lb_core::{
|
||||
mantle::{
|
||||
GenesisTx as _, MantleTx, NoteId, OpProof, SignedMantleTx, Transaction as _, Utxo,
|
||||
genesis_tx::GENESIS_STORAGE_GAS_PRICE, ops::Op, tx::MantleTxGasContext,
|
||||
genesis_tx::GENESIS_STORAGE_GAS_PRICE,
|
||||
ops::Op,
|
||||
tx::{GasPrices, MantleTxGasContext},
|
||||
tx_builder::MantleTxBuilder,
|
||||
},
|
||||
sdp::{Declaration, DeclarationMessage, Locator, ServiceType, WithdrawMessage},
|
||||
@@ -464,15 +466,18 @@ async fn fund_sdp_transaction(
|
||||
let funding_public_key = funding_secret_key.to_public_key();
|
||||
let funding_utxos = current_utxos_for_public_key(node, genesis_utxos, funding_public_key).await;
|
||||
|
||||
let empty_context = MantleTxGasContext::new(HashMap::new());
|
||||
let empty_context = MantleTxGasContext::new(
|
||||
HashMap::new(),
|
||||
GasPrices {
|
||||
execution_base_gas_price: 0.into(),
|
||||
storage_gas_price: GENESIS_STORAGE_GAS_PRICE,
|
||||
},
|
||||
);
|
||||
let tx_context = lb_core::mantle::tx::MantleTxContext {
|
||||
gas_context: empty_context,
|
||||
leader_reward_amount: 0,
|
||||
};
|
||||
let tx_builder = MantleTxBuilder::new(tx_context)
|
||||
.push_op(extra_op)
|
||||
.set_storage_gas_price(GENESIS_STORAGE_GAS_PRICE)
|
||||
.set_execution_gas_price(0.into());
|
||||
let tx_builder = MantleTxBuilder::new(tx_context).push_op(extra_op);
|
||||
|
||||
let funded_builder =
|
||||
fund_transfer_builder_from_utxos(funding_utxos, &tx_builder, funding_public_key)
|
||||
|
||||
@@ -9,7 +9,6 @@ use std::{
|
||||
use async_trait::async_trait;
|
||||
use lb_core::mantle::{
|
||||
MantleTx, SignedMantleTx, Transaction as _,
|
||||
genesis_tx::GENESIS_STORAGE_GAS_PRICE,
|
||||
ops::{
|
||||
Op, OpProof,
|
||||
channel::{ChannelId, MsgId, inscribe::InscriptionOp},
|
||||
@@ -389,11 +388,7 @@ fn build_inscription_transaction(
|
||||
};
|
||||
let msg_id = op.id();
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![Op::ChannelInscribe(op)],
|
||||
storage_gas_price: GENESIS_STORAGE_GAS_PRICE,
|
||||
execution_gas_price: 0.into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![Op::ChannelInscribe(op)]);
|
||||
let tx_hash = mantle_tx.hash();
|
||||
|
||||
let ed25519_signature = channel
|
||||
|
||||
@@ -11,9 +11,8 @@ use async_trait::async_trait;
|
||||
use lb_core::mantle::{
|
||||
GasCalculator as _, GenesisTx as _, Note, OpProof, SignedMantleTx, Transaction as _, Utxo,
|
||||
gas::MainnetGasConstants,
|
||||
genesis_tx::GENESIS_STORAGE_GAS_PRICE,
|
||||
ops::OpId as _,
|
||||
tx::{MantleTxContext, MantleTxGasContext},
|
||||
tx::{GasPrices, MantleTxContext, MantleTxGasContext},
|
||||
tx_builder::MantleTxBuilder,
|
||||
};
|
||||
use lb_key_management_system_service::keys::{ZkKey, ZkPublicKey};
|
||||
@@ -193,7 +192,7 @@ impl<'a, E: LbcScenarioEnv> Submission<'a, E> {
|
||||
}
|
||||
|
||||
async fn execute(mut self) -> Result<(), DynError> {
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new());
|
||||
let gas_context = MantleTxGasContext::new(HashMap::new(), GasPrices::new(0, 0));
|
||||
while let Some(input) = self.plan.pop_front() {
|
||||
submit_wallet_transaction(self.ctx, &input, gas_context.clone()).await?;
|
||||
if !self.interval.is_zero() {
|
||||
@@ -285,8 +284,6 @@ fn build_wallet_transaction(
|
||||
};
|
||||
|
||||
let provisional_tx = MantleTxBuilder::new(tx_context.clone())
|
||||
.set_execution_gas_price(0.into())
|
||||
.set_storage_gas_price(GENESIS_STORAGE_GAS_PRICE)
|
||||
.add_ledger_input(input.utxo)
|
||||
.add_ledger_output(Note::new(input.utxo.note.value, receiver))
|
||||
.build();
|
||||
@@ -302,8 +299,6 @@ fn build_wallet_transaction(
|
||||
})?;
|
||||
|
||||
let tx = MantleTxBuilder::new(tx_context)
|
||||
.set_execution_gas_price(0.into())
|
||||
.set_storage_gas_price(GENESIS_STORAGE_GAS_PRICE)
|
||||
.add_ledger_input(input.utxo)
|
||||
.add_ledger_output(Note::new(output_value, receiver))
|
||||
.build();
|
||||
|
||||
@@ -5,7 +5,7 @@ use lb_core::{
|
||||
block::genesis::{GenesisBlock, GenesisBlockBuilder},
|
||||
mantle::{
|
||||
CryptarchiaParameter, MantleTx, Note, NoteId, OpProof, Utxo,
|
||||
genesis_tx::{GENESIS_EXECUTION_GAS_PRICE, GENESIS_STORAGE_GAS_PRICE, GenesisTx},
|
||||
genesis_tx::GenesisTx,
|
||||
ops::{
|
||||
Op, OpId as _,
|
||||
channel::{ChannelId, Ed25519PublicKey, MsgId, inscribe::InscriptionOp},
|
||||
@@ -290,11 +290,7 @@ pub fn create_genesis_block_with_declarations(
|
||||
ops.push(Op::SDPDeclare(declaration));
|
||||
}
|
||||
|
||||
let mantle_tx = MantleTx {
|
||||
ops,
|
||||
execution_gas_price: GENESIS_EXECUTION_GAS_PRICE,
|
||||
storage_gas_price: GENESIS_STORAGE_GAS_PRICE,
|
||||
};
|
||||
let mantle_tx = MantleTx(ops);
|
||||
|
||||
let mantle_tx_hash = mantle_tx.hash();
|
||||
let mut ops_proofs = vec![
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct GeneralSdpConfig {
|
||||
pub fn create_sdp_configs(genesis_tx: &GenesisTx, count: usize) -> Vec<GeneralSdpConfig> {
|
||||
let mut configs = genesis_tx
|
||||
.mantle_tx()
|
||||
.ops
|
||||
.ops()
|
||||
.iter()
|
||||
.filter_map(|op| match op {
|
||||
Op::SDPDeclare(decl) => Some(GeneralSdpConfig {
|
||||
|
||||
+40
-31
@@ -58,7 +58,7 @@ impl WalletBlock {
|
||||
let mut unlocked_notes = HashSet::new();
|
||||
|
||||
for auth_tx in block.transactions() {
|
||||
for op in &auth_tx.mantle_tx().ops {
|
||||
for op in auth_tx.mantle_tx().ops() {
|
||||
match op {
|
||||
Op::ChannelDeposit(deposit) => {
|
||||
spent_notes.extend(deposit.inputs.iter().copied());
|
||||
@@ -578,10 +578,11 @@ mod tests {
|
||||
crypto::{Hash, ZkDigest as _},
|
||||
mantle::{
|
||||
Note,
|
||||
channel::Channels,
|
||||
gas::MainnetGasConstants as Gas,
|
||||
ledger::{Inputs, Outputs},
|
||||
ops::channel::{ChannelId, MsgId, inscribe::InscriptionOp},
|
||||
tx::MantleTxContext,
|
||||
tx::{GasPrices, MantleTxContext, MantleTxGasContext},
|
||||
},
|
||||
sdp::{MinStake, ServiceParameters, ServiceType},
|
||||
};
|
||||
@@ -822,9 +823,13 @@ mod tests {
|
||||
// Lock `utxo1` deliberately to ensure that `fund_tx` excludes locked notes
|
||||
wallet_state.locked_notes = wallet_state.locked_notes.insert(utxo1.id());
|
||||
|
||||
let tx_builder = MantleTxBuilder::new(ledger_state.tx_context())
|
||||
.set_execution_gas_price(1.into())
|
||||
.set_storage_gas_price(1.into());
|
||||
let tx_builder = MantleTxBuilder::new(MantleTxContext {
|
||||
gas_context: MantleTxGasContext::from_channels(
|
||||
&Channels::default(),
|
||||
GasPrices::new(1, 1),
|
||||
),
|
||||
leader_reward_amount: 0,
|
||||
});
|
||||
|
||||
// Fund the transaction
|
||||
let funded_tx_builder = wallet_state
|
||||
@@ -832,22 +837,22 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
810,
|
||||
794,
|
||||
funded_tx_builder.gas_cost::<Gas>().unwrap().into_inner()
|
||||
);
|
||||
assert_eq!(810, funded_tx_builder.net_balance());
|
||||
assert_eq!(794, funded_tx_builder.net_balance());
|
||||
assert_eq!(0, funded_tx_builder.funding_delta::<Gas>().unwrap());
|
||||
|
||||
let funded_tx = funded_tx_builder.build();
|
||||
|
||||
if let Op::Transfer(transfer_op) = &funded_tx.ops[funded_tx.ops.len() - 1] {
|
||||
if let Op::Transfer(transfer_op) = &funded_tx.ops()[funded_tx.ops().len() - 1] {
|
||||
// ensure alices utxo was used to pay the fee
|
||||
assert_eq!(transfer_op.inputs, Inputs::new(vec![utxo2.id()]));
|
||||
// ensure change was returned to alice
|
||||
assert_eq!(
|
||||
transfer_op.outputs,
|
||||
Outputs::new(vec![Note {
|
||||
value: 4190,
|
||||
value: 4206,
|
||||
pk: alice,
|
||||
}])
|
||||
);
|
||||
@@ -869,11 +874,17 @@ mod tests {
|
||||
&ledger_config(),
|
||||
);
|
||||
|
||||
let builder_context = MantleTxContext {
|
||||
gas_context: MantleTxGasContext::from_channels(
|
||||
ledger_state.mantle_ledger().channels(),
|
||||
GasPrices::new(1, 1),
|
||||
),
|
||||
leader_reward_amount: ledger_state.mantle_ledger().leader_reward_amount(),
|
||||
};
|
||||
|
||||
let wallet_state =
|
||||
WalletState::from_ledger(&HashMap::from_iter([(alice, 1)]), &ledger_state);
|
||||
let mut tx_builder = MantleTxBuilder::new(ledger_state.tx_context())
|
||||
.set_execution_gas_price(1.into())
|
||||
.set_storage_gas_price(1.into());
|
||||
let mut tx_builder = MantleTxBuilder::new(builder_context);
|
||||
|
||||
// Add a costly inscription
|
||||
let signing_key = Ed25519Key::from_bytes(&[1; 32]);
|
||||
@@ -903,9 +914,7 @@ mod tests {
|
||||
let wallet_state =
|
||||
WalletState::from_ledger(&HashMap::from_iter([(alice, 1)]), &ledger_state);
|
||||
|
||||
let tx_builder = MantleTxBuilder::new(ledger_state.tx_context())
|
||||
.set_execution_gas_price(1.into())
|
||||
.set_storage_gas_price(1.into());
|
||||
let tx_builder = MantleTxBuilder::new(ledger_state.tx_context());
|
||||
|
||||
// Fund the transaction
|
||||
let fund_attempt = wallet_state.fund_tx::<Gas>(&tx_builder, alice, [alice]);
|
||||
@@ -927,9 +936,7 @@ mod tests {
|
||||
// Lock `utxo` deliberately to ensure that `fund_tx` excludes locked notes
|
||||
wallet_state.locked_notes = wallet_state.locked_notes.insert(utxo.id());
|
||||
|
||||
let tx_builder = MantleTxBuilder::new(ledger_state.tx_context())
|
||||
.set_execution_gas_price(1.into())
|
||||
.set_storage_gas_price(1.into());
|
||||
let tx_builder = MantleTxBuilder::new(ledger_state.tx_context());
|
||||
|
||||
// Fund the transaction
|
||||
let fund_attempt = wallet_state.fund_tx::<Gas>(&tx_builder, alice, [alice]);
|
||||
@@ -952,9 +959,7 @@ mod tests {
|
||||
let wallet_state =
|
||||
WalletState::from_ledger(&HashMap::from_iter([(alice, 1), (bob, 2)]), &ledger_state);
|
||||
|
||||
let tx_builder = MantleTxBuilder::new(ledger_state.tx_context())
|
||||
.set_execution_gas_price(1.into())
|
||||
.set_storage_gas_price(1.into());
|
||||
let tx_builder = MantleTxBuilder::new(ledger_state.tx_context());
|
||||
|
||||
// Attempt to fund the transaction with Alice's notes.
|
||||
let fund_attempt = wallet_state.fund_tx::<Gas>(&tx_builder, alice, [alice]);
|
||||
@@ -974,13 +979,17 @@ mod tests {
|
||||
fn test_fund_tx_unfundable_region() {
|
||||
let alice = pk(1);
|
||||
|
||||
let tx_builder = MantleTxBuilder::new(MantleTxContext::default())
|
||||
.set_execution_gas_price(1.into())
|
||||
.set_storage_gas_price(1.into());
|
||||
let tx_builder = MantleTxBuilder::new(MantleTxContext {
|
||||
gas_context: MantleTxGasContext::from_channels(
|
||||
&Channels::default(),
|
||||
GasPrices::new(1, 1),
|
||||
),
|
||||
leader_reward_amount: 0,
|
||||
});
|
||||
|
||||
// Determine gas cost without change note
|
||||
assert_eq!(
|
||||
770,
|
||||
754,
|
||||
tx_builder
|
||||
.clone()
|
||||
.add_ledger_input(Utxo::new(tx_hash(0), 0, Note::new(0, pk(0))))
|
||||
@@ -994,7 +1003,7 @@ mod tests {
|
||||
let wallet_state = WalletState::from_ledger(
|
||||
&HashMap::from_iter([(alice, 1)]),
|
||||
&LedgerState::from_utxos(
|
||||
[Utxo::new(tx_hash(0), 0, Note::new(770, alice))],
|
||||
[Utxo::new(tx_hash(0), 0, Note::new(754, alice))],
|
||||
&ledger_config(),
|
||||
),
|
||||
);
|
||||
@@ -1006,7 +1015,7 @@ mod tests {
|
||||
|
||||
// verify that no change output was used.
|
||||
if let Op::Transfer(transfer_op) =
|
||||
&funded_tx_wo_change.ops[funded_tx_wo_change.ops.len() - 1]
|
||||
&funded_tx_wo_change.ops()[funded_tx_wo_change.ops().len() - 1]
|
||||
{
|
||||
assert_eq!(transfer_op.outputs, Outputs::new(vec![]));
|
||||
} else {
|
||||
@@ -1015,7 +1024,7 @@ mod tests {
|
||||
|
||||
// Determine gas cost with change note
|
||||
assert_eq!(
|
||||
810,
|
||||
794,
|
||||
tx_builder
|
||||
.clone()
|
||||
.add_ledger_input(Utxo::new(tx_hash(0), 0, Note::new(0, pk(0))))
|
||||
@@ -1025,7 +1034,7 @@ mod tests {
|
||||
.into_inner()
|
||||
);
|
||||
|
||||
for value in 771..=810 {
|
||||
for value in 755..=794 {
|
||||
// this region of note values will fail to fund the tx.
|
||||
// We can fund the tx if the note value is exactly the gas cost without change
|
||||
// note
|
||||
@@ -1049,7 +1058,7 @@ mod tests {
|
||||
let wallet_state = WalletState::from_ledger(
|
||||
&HashMap::from_iter([(alice, 1)]),
|
||||
&LedgerState::from_utxos(
|
||||
[Utxo::new(tx_hash(0), 0, Note::new(811, alice))],
|
||||
[Utxo::new(tx_hash(0), 0, Note::new(795, alice))],
|
||||
&ledger_config(),
|
||||
),
|
||||
);
|
||||
@@ -1061,7 +1070,7 @@ mod tests {
|
||||
|
||||
// verify that indeed a change output was used.
|
||||
if let Op::Transfer(transfer_op) =
|
||||
&funded_tx_wo_change.ops[funded_tx_wo_change.ops.len() - 1]
|
||||
&funded_tx_wo_change.ops()[funded_tx_wo_change.ops().len() - 1]
|
||||
{
|
||||
assert_eq!(transfer_op.outputs, Outputs::new(vec![Note::new(1, alice)]));
|
||||
} else {
|
||||
|
||||
@@ -101,7 +101,7 @@ impl Node for NodeHttpClient {
|
||||
Ok(Box::pin(stream::iter(
|
||||
transactions
|
||||
.into_iter()
|
||||
.flat_map(|tx| tx.mantle_tx.ops)
|
||||
.flat_map(|tx| tx.mantle_tx.0)
|
||||
.filter_map(move |op| op_to_zone_message(&op, channel_id)),
|
||||
)))
|
||||
}
|
||||
@@ -127,7 +127,7 @@ impl Node for NodeHttpClient {
|
||||
block
|
||||
.transactions
|
||||
.into_iter()
|
||||
.flat_map(|tx| tx.mantle_tx.ops)
|
||||
.flat_map(|tx| tx.mantle_tx.0)
|
||||
.filter_map(move |op| op_to_zone_message(&op, channel_id))
|
||||
.map(move |msg| (msg, slot))
|
||||
},
|
||||
|
||||
+12
-24
@@ -459,7 +459,7 @@ where
|
||||
// Filter by `channel_id` — a checkpoint can in principle carry
|
||||
// txs for other channels if the caller reused it.
|
||||
let mut is_inscription = false;
|
||||
for op in &tx.mantle_tx.ops {
|
||||
for op in tx.mantle_tx.ops() {
|
||||
if let Op::ChannelInscribe(inscribe) = op
|
||||
&& inscribe.channel_id == channel_id
|
||||
{
|
||||
@@ -1315,7 +1315,7 @@ fn enqueue_resubmit<Node>(
|
||||
for (id, tx) in &pending {
|
||||
let payloads: Vec<String> = tx
|
||||
.mantle_tx
|
||||
.ops
|
||||
.ops()
|
||||
.iter()
|
||||
.filter_map(|op| {
|
||||
if let Op::ChannelInscribe(ins) = op {
|
||||
@@ -1355,7 +1355,7 @@ fn extract_inscriptions(txs: &[SignedMantleTx], channel_id: ChannelId) -> Vec<In
|
||||
let items: Vec<InscriptionInfo> = txs
|
||||
.iter()
|
||||
.flat_map(|tx| {
|
||||
tx.mantle_tx.ops.iter().filter_map(|op| {
|
||||
tx.mantle_tx.ops().iter().filter_map(|op| {
|
||||
if let Op::ChannelInscribe(inscribe) = op
|
||||
&& inscribe.channel_id == channel_id
|
||||
{
|
||||
@@ -1398,7 +1398,7 @@ fn extract_inscriptions(txs: &[SignedMantleTx], channel_id: ChannelId) -> Vec<In
|
||||
}
|
||||
|
||||
fn matches_channel(tx: &SignedMantleTx, channel_id: ChannelId) -> bool {
|
||||
tx.mantle_tx.ops.iter().any(|op| match op {
|
||||
tx.mantle_tx.ops().iter().any(|op| match op {
|
||||
Op::ChannelInscribe(inscribe) => inscribe.channel_id == channel_id,
|
||||
Op::ChannelSetKeys(set_keys) => set_keys.channel == channel_id,
|
||||
_ => false,
|
||||
@@ -1422,11 +1422,7 @@ fn create_inscribe_tx(
|
||||
let msg_id = inscribe_op.id();
|
||||
|
||||
// TODO: set realistic gas prices and fund tx
|
||||
let inscribe_tx = MantleTx {
|
||||
ops: vec![Op::ChannelInscribe(inscribe_op)],
|
||||
storage_gas_price: 0.into(),
|
||||
execution_gas_price: 0.into(),
|
||||
};
|
||||
let inscribe_tx = MantleTx(vec![Op::ChannelInscribe(inscribe_op)]);
|
||||
|
||||
let tx_hash = inscribe_tx.hash();
|
||||
let signature = sign_tx(tx_hash, signing_key);
|
||||
@@ -1449,12 +1445,8 @@ fn create_set_keys_tx(
|
||||
keys,
|
||||
};
|
||||
|
||||
// TODO: set realistic gas prices and fund tx
|
||||
let set_keys_tx = MantleTx {
|
||||
ops: vec![Op::ChannelSetKeys(set_keys_op)],
|
||||
storage_gas_price: 0.into(),
|
||||
execution_gas_price: 0.into(),
|
||||
};
|
||||
// TODO: fund tx
|
||||
let set_keys_tx = MantleTx(vec![Op::ChannelSetKeys(set_keys_op)]);
|
||||
|
||||
let tx_hash = set_keys_tx.hash();
|
||||
let signature = sign_tx(tx_hash, signing_key);
|
||||
@@ -1481,12 +1473,8 @@ fn prepare_tx(
|
||||
let msg_id = inscription_op.id();
|
||||
ops.push(Op::ChannelInscribe(inscription_op));
|
||||
|
||||
// TODO: set realistic gas prices and fund tx
|
||||
let tx = MantleTx {
|
||||
ops,
|
||||
storage_gas_price: 0.into(),
|
||||
execution_gas_price: 0.into(),
|
||||
};
|
||||
// TODO: fund tx
|
||||
let tx = MantleTx(ops);
|
||||
|
||||
let inscription_sig = sign_tx(tx.hash(), signing_key);
|
||||
|
||||
@@ -1562,9 +1550,9 @@ mod tests {
|
||||
_ = sequencer.next_event() => {}
|
||||
}
|
||||
};
|
||||
assert_eq!(tx.ops.len(), 2);
|
||||
assert_eq!(&tx.ops[0], &Op::ChannelDeposit(deposit_op));
|
||||
assert!(matches!(&tx.ops[1], &Op::ChannelInscribe(_)));
|
||||
assert_eq!(tx.ops().len(), 2);
|
||||
assert_eq!(&tx.ops()[0], &Op::ChannelDeposit(deposit_op));
|
||||
assert!(matches!(&tx.ops()[1], &Op::ChannelInscribe(_)));
|
||||
|
||||
// Sign the `MantleTx`
|
||||
let signed_tx = SignedMantleTx::new(
|
||||
|
||||
+10
-6
@@ -583,7 +583,10 @@ impl TxState {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use lb_core::mantle::{MantleTx, Transaction as _};
|
||||
use lb_core::mantle::{
|
||||
MantleTx, Op::ChannelInscribe, Transaction as _, ops::channel::inscribe::InscriptionOp,
|
||||
};
|
||||
use lb_key_management_system_service::keys::Ed25519PublicKey;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -594,11 +597,12 @@ mod tests {
|
||||
}
|
||||
|
||||
fn make_dummy_tx(data: u8) -> SignedMantleTx {
|
||||
let mantle_tx = MantleTx {
|
||||
ops: vec![],
|
||||
storage_gas_price: 0.into(),
|
||||
execution_gas_price: u64::from(data).into(),
|
||||
};
|
||||
let mantle_tx = MantleTx(vec![ChannelInscribe(InscriptionOp {
|
||||
channel_id: [0u8; 32].into(),
|
||||
inscription: vec![data],
|
||||
parent: [0u8; 32].into(),
|
||||
signer: Ed25519PublicKey::from_bytes(&[0u8; 32]).unwrap(),
|
||||
})]);
|
||||
SignedMantleTx {
|
||||
ops_proofs: vec![],
|
||||
mantle_tx,
|
||||
|
||||
Reference in New Issue
Block a user