mirror of
https://github.com/logos-co/nomos-node.git
synced 2026-08-31 03:21:15 +00:00
chore: migrate proofs to NomEncode and NomDecode (#3078)
This commit is contained in:
Generated
+1
@@ -4452,6 +4452,7 @@ dependencies = [
|
||||
"logos-blockchain-poseidon2",
|
||||
"logos-blockchain-utils",
|
||||
"logos-blockchain-utxotree",
|
||||
"logos-blockchain-zksign",
|
||||
"multiaddr",
|
||||
"nom 8.0.0",
|
||||
"num-bigint",
|
||||
|
||||
@@ -32,6 +32,7 @@ lb-pol = { workspace = true }
|
||||
lb-poseidon2 = { workspace = true }
|
||||
lb-utils = { workspace = true }
|
||||
lb-utxotree = { workspace = true }
|
||||
lb-zksign = { workspace = true }
|
||||
multiaddr = { workspace = true }
|
||||
nom = { features = ["alloc"], workspace = true }
|
||||
num-bigint = { workspace = true }
|
||||
|
||||
+36
-129
@@ -1,14 +1,12 @@
|
||||
use lb_groth16::{CompressedGroth16Proof, Fr, fr_from_bytes};
|
||||
use lb_key_management_system_keys::keys::{Ed25519Signature, ZkSignature};
|
||||
use lb_groth16::{COMPRESSED_PROOF_SIZE, Fr, fr_from_bytes};
|
||||
use lb_key_management_system_keys::keys::{ED25519_SIGNATURE_SIZE, Ed25519Signature, ZkSignature};
|
||||
use lb_utils::bounded_vec::UpperBoundedVec;
|
||||
use nom::{
|
||||
IResult, Parser as _,
|
||||
bytes::complete::take,
|
||||
combinator::{map, map_res},
|
||||
error::{Error, ErrorKind},
|
||||
multi::length_count,
|
||||
number::complete::{le_u16, le_u64},
|
||||
sequence::pair,
|
||||
number::complete::le_u64,
|
||||
};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
@@ -83,12 +81,12 @@ fn decode_ops_proofs<'a>(input: &'a [u8], ops: &[Op]) -> IResult<&'a [u8], Vec<O
|
||||
fn decode_op_proof<'a>(input: &'a [u8], op: &Op) -> IResult<&'a [u8], OpProof> {
|
||||
match op {
|
||||
// Ed25519SigProof = Ed25519Signature
|
||||
Op::ChannelInscribe(_) => map(decode_ed25519_signature, OpProof::Ed25519Sig).parse(input),
|
||||
Op::ChannelInscribe(_) => map(Ed25519Signature::decode, OpProof::Ed25519Sig).parse(input),
|
||||
|
||||
// ZkAndEd25519SigsProof = ZkSignature Ed25519Signature
|
||||
Op::SDPDeclare(_) => {
|
||||
let (input, zk_sig) = decode_zk_signature(input)?;
|
||||
let (input, ed25519_sig) = decode_ed25519_signature(input)?;
|
||||
let (input, zk_sig) = ZkSignature::decode(input)?;
|
||||
let (input, ed25519_sig) = Ed25519Signature::decode(input)?;
|
||||
Ok((
|
||||
input,
|
||||
OpProof::ZkAndEd25519Sigs {
|
||||
@@ -100,21 +98,16 @@ fn decode_op_proof<'a>(input: &'a [u8], op: &Op) -> IResult<&'a [u8], OpProof> {
|
||||
|
||||
// ZkSigProof = ZkSignature
|
||||
Op::SDPWithdraw(_) | Op::SDPActive(_) | Op::Transfer(_) | Op::ChannelDeposit(_) => {
|
||||
map(decode_zk_signature, OpProof::ZkSig).parse(input)
|
||||
map(ZkSignature::decode, OpProof::ZkSig).parse(input)
|
||||
}
|
||||
|
||||
// ProofOfClaimProof = Groth16
|
||||
Op::LeaderClaim(_) => map(decode_groth16, |proof| {
|
||||
OpProof::PoC(Groth16LeaderClaimProof::new(proof))
|
||||
})
|
||||
.parse(input),
|
||||
Op::LeaderClaim(_) => map(Groth16LeaderClaimProof::decode, OpProof::PoC).parse(input),
|
||||
|
||||
// ChannelMultiSigProof — also used by ChannelConfig (threshold sigs)
|
||||
Op::ChannelWithdraw(_) | Op::ChannelConfig(_) => map(
|
||||
decode_channel_multi_sig_proof,
|
||||
OpProof::ChannelMultiSigProof,
|
||||
)
|
||||
.parse(input),
|
||||
Op::ChannelWithdraw(_) | Op::ChannelConfig(_) => {
|
||||
map(ChannelMultiSigProof::decode, OpProof::ChannelMultiSigProof).parse(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,53 +115,9 @@ fn decode_op_proof<'a>(input: &'a [u8], op: &Op) -> IResult<&'a [u8], OpProof> {
|
||||
// Cryptographic Primitive Decoders
|
||||
// ==============================================================================
|
||||
|
||||
fn decode_zk_signature(input: &[u8]) -> IResult<&[u8], ZkSignature> {
|
||||
// ZkSignature = Groth16
|
||||
map(decode_groth16, ZkSignature::new).parse(input)
|
||||
}
|
||||
|
||||
const GROTH16_BYTES: usize = 128;
|
||||
fn decode_groth16(input: &[u8]) -> IResult<&[u8], CompressedGroth16Proof> {
|
||||
// Groth16 = 128BYTE
|
||||
map(
|
||||
decode_array::<GROTH16_BYTES>,
|
||||
|proof: [u8; GROTH16_BYTES]| CompressedGroth16Proof::from_bytes(&proof),
|
||||
)
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
const ED25519_SIG_BYTES: usize = 64;
|
||||
fn decode_ed25519_signature(input: &[u8]) -> IResult<&[u8], Ed25519Signature> {
|
||||
// Ed25519Signature = 64BYTE
|
||||
map(
|
||||
decode_array::<ED25519_SIG_BYTES>,
|
||||
|bytes: [u8; ED25519_SIG_BYTES]| Ed25519Signature::from_bytes(&bytes),
|
||||
)
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
const fn calculate_channel_multi_sig_proof_byte_size(threshold: ChannelKeyIndex) -> usize {
|
||||
// Encoding: u16 signature count + N * (Ed25519 sig + u16 key index)
|
||||
2 + (threshold as usize) * (ED25519_SIG_BYTES + 2)
|
||||
}
|
||||
|
||||
fn decode_channel_multi_sig_proof(input: &[u8]) -> IResult<&[u8], ChannelMultiSigProof> {
|
||||
// ChannelMultiSigProof = SignatureCount *WithdrawSignature
|
||||
// WithdrawSignature = Ed25519Signature Index
|
||||
let (input, signatures) = length_count(
|
||||
map(decode_uint16, |n: ChannelKeyIndex| n as usize),
|
||||
pair(decode_ed25519_signature, decode_uint16),
|
||||
)
|
||||
.parse(input)?;
|
||||
|
||||
let signatures: Vec<IndexedSignature> = signatures
|
||||
.into_iter()
|
||||
.map(|(signature, index)| IndexedSignature::from((index, signature)))
|
||||
.collect();
|
||||
|
||||
ChannelMultiSigProof::new(signatures)
|
||||
.map(|proof| (input, proof))
|
||||
.map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::Verify)))
|
||||
2 + (threshold as usize) * (ED25519_SIGNATURE_SIZE + 2)
|
||||
}
|
||||
|
||||
pub(crate) fn decode_field_element(input: &[u8]) -> IResult<&[u8], Fr> {
|
||||
@@ -182,14 +131,6 @@ pub(crate) fn decode_field_element(input: &[u8]) -> IResult<&[u8], Fr> {
|
||||
// ==============================================================================
|
||||
// Primitive Decoders
|
||||
// ==============================================================================
|
||||
fn decode_array<const N: usize>(input: &[u8]) -> IResult<&[u8], [u8; N]> {
|
||||
map(take(N), |bytes: &[u8]| {
|
||||
let mut arr = [0u8; N];
|
||||
arr.copy_from_slice(bytes);
|
||||
arr
|
||||
})
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
pub(crate) fn decode_utf8_string(input: &[u8], len: usize) -> IResult<&[u8], String> {
|
||||
map_res(take(len), |bytes: &[u8]| {
|
||||
@@ -200,11 +141,6 @@ pub(crate) fn decode_utf8_string(input: &[u8], len: usize) -> IResult<&[u8], Str
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
fn decode_uint16(input: &[u8]) -> IResult<&[u8], u16> {
|
||||
// UINT16 = 2BYTE
|
||||
le_u16(input)
|
||||
}
|
||||
|
||||
pub(crate) fn decode_uint64(input: &[u8]) -> IResult<&[u8], u64> {
|
||||
// UINT64 = 8BYTE
|
||||
le_u64(input)
|
||||
@@ -230,14 +166,10 @@ use lb_groth16::fr_to_bytes;
|
||||
|
||||
use crate::{
|
||||
mantle::{Utxo, ops::channel::ChannelKeyIndex, tx::MantleTxGasContext},
|
||||
proofs::channel_multi_sig_proof::{ChannelMultiSigProof, IndexedSignature},
|
||||
proofs::channel_multi_sig_proof::ChannelMultiSigProof,
|
||||
};
|
||||
|
||||
/// Encode primitives
|
||||
fn encode_uint16(value: u16) -> Vec<u8> {
|
||||
value.to_le_bytes().to_vec()
|
||||
}
|
||||
|
||||
pub(crate) fn encode_uint64(value: u64) -> Vec<u8> {
|
||||
value.to_le_bytes().to_vec()
|
||||
}
|
||||
@@ -258,36 +190,6 @@ pub(crate) fn encode_field_element(fr: &Fr) -> Vec<u8> {
|
||||
fr_to_bytes(fr).to_vec()
|
||||
}
|
||||
|
||||
/// Encode cryptographic primitives
|
||||
fn encode_ed25519_signature(sig: &Ed25519Signature) -> Vec<u8> {
|
||||
sig.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
fn encode_zk_signature(sig: &ZkSignature) -> Vec<u8> {
|
||||
// ZkSignature wraps ZkSignProof which is CompressedGroth16Proof
|
||||
encode_groth16_proof(sig.as_proof())
|
||||
}
|
||||
|
||||
fn encode_poc(poc: &Groth16LeaderClaimProof) -> Vec<u8> {
|
||||
// Groth16LeaderClaimProof wraps PocProof which is CompressedGroth16Proof
|
||||
encode_groth16_proof(poc.proof())
|
||||
}
|
||||
|
||||
fn encode_groth16_proof(proof: &CompressedGroth16Proof) -> Vec<u8> {
|
||||
proof.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
fn encode_channel_multi_sig_proof(proof: &ChannelMultiSigProof) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend(encode_uint16(proof.signatures().len() as ChannelKeyIndex));
|
||||
bytes.extend(proof.signatures().iter().flat_map(|signature| {
|
||||
encode_ed25519_signature(&signature.signature)
|
||||
.into_iter()
|
||||
.chain(encode_uint16(signature.channel_key_index))
|
||||
}));
|
||||
bytes
|
||||
}
|
||||
|
||||
// Check if proofs correspond to ops
|
||||
#[must_use]
|
||||
pub const fn proof_matches(proof: &OpProof, op: &Op) -> bool {
|
||||
@@ -311,18 +213,18 @@ pub const fn proof_matches(proof: &OpProof, op: &Op) -> bool {
|
||||
fn encode_op_proof(proof: &OpProof, op: &Op) -> Vec<u8> {
|
||||
if proof_matches(proof, op) {
|
||||
match proof {
|
||||
OpProof::Ed25519Sig(sig) => encode_ed25519_signature(sig),
|
||||
OpProof::ChannelMultiSigProof(proof) => encode_channel_multi_sig_proof(proof),
|
||||
OpProof::Ed25519Sig(sig) => sig.encode(),
|
||||
OpProof::ChannelMultiSigProof(proof) => proof.encode(),
|
||||
OpProof::ZkAndEd25519Sigs {
|
||||
zk_sig,
|
||||
ed25519_sig,
|
||||
} => {
|
||||
let mut bytes = encode_zk_signature(zk_sig);
|
||||
bytes.extend(encode_ed25519_signature(ed25519_sig));
|
||||
let mut bytes = zk_sig.encode();
|
||||
bytes.extend(ed25519_sig.encode());
|
||||
bytes
|
||||
}
|
||||
OpProof::ZkSig(sig) => encode_zk_signature(sig),
|
||||
OpProof::PoC(poc) => encode_poc(poc),
|
||||
OpProof::ZkSig(sig) => sig.encode(),
|
||||
OpProof::PoC(poc) => poc.encode(),
|
||||
}
|
||||
} else {
|
||||
panic!("Mismatch between proof type and operation type");
|
||||
@@ -359,7 +261,7 @@ pub(crate) fn predict_signed_mantle_tx_size(tx: &MantleTx, context: &MantleTxGas
|
||||
.iter()
|
||||
.map(|op| match op {
|
||||
// Ed25519SigProof = Ed25519Signature
|
||||
Op::ChannelInscribe(_) => ED25519_SIG_BYTES,
|
||||
Op::ChannelInscribe(_) => ED25519_SIGNATURE_SIZE,
|
||||
|
||||
// ChannelMultiSigProof — for an existing channel, threshold sigs;
|
||||
// for a new channel (just-in-time created here), no sigs required.
|
||||
@@ -373,11 +275,11 @@ pub(crate) fn predict_signed_mantle_tx_size(tx: &MantleTx, context: &MantleTxGas
|
||||
}
|
||||
|
||||
// ZkAndEd25519SigsProof = ZkSignature Ed25519Signature
|
||||
Op::SDPDeclare(_) => GROTH16_BYTES + ED25519_SIG_BYTES,
|
||||
Op::SDPDeclare(_) => COMPRESSED_PROOF_SIZE + ED25519_SIGNATURE_SIZE,
|
||||
|
||||
// ZkSigProof = ZkSignature = ProofOfClaimProof = Groth16
|
||||
Op::SDPWithdraw(_) | Op::SDPActive(_) | Op::LeaderClaim(_) | Op::Transfer(_) => {
|
||||
GROTH16_BYTES
|
||||
COMPRESSED_PROOF_SIZE
|
||||
}
|
||||
|
||||
// ChannelMultiSigProof
|
||||
@@ -401,6 +303,7 @@ mod tests {
|
||||
use std::{collections::HashMap, panic};
|
||||
|
||||
use ark_ff::AdditiveGroup as _;
|
||||
use lb_groth16::CompressedGroth16Proof;
|
||||
use lb_key_management_system_keys::keys::{Ed25519Key, ZkKey, ZkPublicKey};
|
||||
use lb_utils::bounded_vec::BoundedError;
|
||||
use multiaddr::Multiaddr;
|
||||
@@ -424,6 +327,7 @@ mod tests {
|
||||
},
|
||||
tx::GasPrices,
|
||||
},
|
||||
proofs::channel_multi_sig_proof::IndexedSignature,
|
||||
sdp::{
|
||||
ActivityMetadata, DeclarationId, Locator, MAX_LOCATOR_BYTE_SIZE, ProviderId,
|
||||
ServiceType, blend::ActivityProof,
|
||||
@@ -596,7 +500,7 @@ mod tests {
|
||||
|
||||
// ChannelConfig creates the channel just-in-time, so no signatures are
|
||||
// required for validation — empty proof is well-formed.
|
||||
let config_proof = ChannelMultiSigProof::new(vec![]).unwrap();
|
||||
let config_proof = ChannelMultiSigProof::try_new([].into()).unwrap();
|
||||
|
||||
// Encode and decode roundtrip test (no hardcoded test vector since signatures
|
||||
// are deterministic)
|
||||
@@ -810,7 +714,7 @@ mod tests {
|
||||
|
||||
// Create a signed tx and encode it to get actual size. New channel
|
||||
// → empty proof (no signatures required for just-in-time create).
|
||||
let config_proof = ChannelMultiSigProof::new(vec![]).unwrap();
|
||||
let config_proof = ChannelMultiSigProof::try_new([].into()).unwrap();
|
||||
let signed_tx =
|
||||
SignedMantleTx::new(mantle_tx, vec![OpProof::ChannelMultiSigProof(config_proof)])
|
||||
.unwrap();
|
||||
@@ -998,7 +902,7 @@ mod tests {
|
||||
let op_sig = signing_key.sign_payload(&txhash.as_signing_bytes());
|
||||
// Create a signed tx and encode it to get actual size. ChannelConfig
|
||||
// creates the channel here, so its proof has no signatures.
|
||||
let config_proof = ChannelMultiSigProof::new(vec![]).unwrap();
|
||||
let config_proof = ChannelMultiSigProof::try_new([].into()).unwrap();
|
||||
let signed_tx = SignedMantleTx::new(
|
||||
mantle_tx,
|
||||
vec![
|
||||
@@ -1111,7 +1015,7 @@ mod tests {
|
||||
// creates the channel here, so its proof has no signatures.
|
||||
let txhash = mantle_tx.hash();
|
||||
let op_ed25519_sig = signing_key1.sign_payload(&txhash.as_signing_bytes());
|
||||
let config_proof = ChannelMultiSigProof::new(vec![]).unwrap();
|
||||
let config_proof = ChannelMultiSigProof::try_new([].into()).unwrap();
|
||||
let signed_tx = SignedMantleTx::new(
|
||||
mantle_tx,
|
||||
vec![
|
||||
@@ -1176,7 +1080,7 @@ mod tests {
|
||||
let op = Op::LeaderClaim(leader_claim_op);
|
||||
|
||||
let encoded = encode_op_proof(&OpProof::PoC(poc_proof), &op);
|
||||
assert_eq!(encoded.len(), GROTH16_BYTES);
|
||||
assert_eq!(encoded.len(), COMPRESSED_PROOF_SIZE);
|
||||
|
||||
let (remaining, decoded) = decode_op_proof(&encoded, &op).unwrap();
|
||||
assert!(remaining.is_empty());
|
||||
@@ -1220,10 +1124,13 @@ mod tests {
|
||||
},
|
||||
)]));
|
||||
let tx_hash = mantle_tx.hash();
|
||||
let proof = ChannelMultiSigProof::new(vec![IndexedSignature::new(
|
||||
0,
|
||||
signing_key.sign_payload(tx_hash.as_signing_bytes().as_ref()),
|
||||
)])
|
||||
let proof = ChannelMultiSigProof::try_new(
|
||||
[IndexedSignature::new(
|
||||
0,
|
||||
signing_key.sign_payload(tx_hash.as_signing_bytes().as_ref()),
|
||||
)]
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let signed_tx =
|
||||
SignedMantleTx::new(mantle_tx, vec![OpProof::ChannelMultiSigProof(proof)]).unwrap();
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
use lb_core_macros::nom_wire_fixtures;
|
||||
use lb_groth16::Fr;
|
||||
use lb_key_management_system_keys::keys::{Ed25519PublicKey, ZkPublicKey};
|
||||
use lb_key_management_system_keys::keys::{
|
||||
Ed25519PublicKey, Ed25519Signature, ZkPublicKey, ZkSignature,
|
||||
};
|
||||
use lb_zksign::ZkSignProof;
|
||||
|
||||
nom_wire_fixtures!(
|
||||
Ed25519PublicKey,
|
||||
Self::from_bytes(&[1u8; _]).unwrap() => "0101010101010101010101010101010101010101010101010101010101010101"
|
||||
);
|
||||
|
||||
nom_wire_fixtures!(
|
||||
ZkPublicKey,
|
||||
Fr::from(1u64).into() => "0100000000000000000000000000000000000000000000000000000000000000"
|
||||
);
|
||||
|
||||
nom_wire_fixtures!(Ed25519Signature, Self::from_bytes(&[1u8; _]) => "01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101");
|
||||
|
||||
nom_wire_fixtures!(
|
||||
ZkSignature,
|
||||
Self::new(ZkSignProof::from_bytes(&[1u8; _])) => "0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"
|
||||
);
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
use lb_core_macros::nom_wire_fixtures;
|
||||
use lb_key_management_system_keys::keys::Ed25519PublicKey;
|
||||
use lb_key_management_system_keys::keys::{Ed25519PublicKey, Ed25519Signature};
|
||||
|
||||
use crate::mantle::{
|
||||
channel::{SlotTimeframe, SlotTimeout},
|
||||
ledger::{Inputs, Outputs},
|
||||
ops::{
|
||||
Op,
|
||||
channel::{
|
||||
ChannelId, MsgId,
|
||||
config::ChannelConfigOp,
|
||||
deposit::{DepositOp, Metadata},
|
||||
inscribe::InscriptionOp,
|
||||
withdraw::ChannelWithdrawOp,
|
||||
use crate::{
|
||||
mantle::{
|
||||
channel::{SlotTimeframe, SlotTimeout},
|
||||
ledger::{Inputs, Outputs},
|
||||
ops::{
|
||||
Op,
|
||||
channel::{
|
||||
ChannelId, MsgId,
|
||||
config::ChannelConfigOp,
|
||||
deposit::{DepositOp, Metadata},
|
||||
inscribe::InscriptionOp,
|
||||
withdraw::ChannelWithdrawOp,
|
||||
},
|
||||
},
|
||||
},
|
||||
proofs::channel_multi_sig_proof::{ChannelMultiSigProof, IndexedSignature},
|
||||
};
|
||||
|
||||
nom_wire_fixtures!(ChannelId, ChannelId::from([0u8; 32]) => "0000000000000000000000000000000000000000000000000000000000000000");
|
||||
@@ -67,3 +70,16 @@ nom_wire_fixtures!(
|
||||
.value
|
||||
) => "1100000000000000000000000000000000000000000000000000000000000000000700000067656e6573697300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
);
|
||||
|
||||
nom_wire_fixtures!(
|
||||
IndexedSignature,
|
||||
Self {
|
||||
channel_key_index: 1,
|
||||
signature: Ed25519Signature::from_bytes(&[0u8; _])
|
||||
} => "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100"
|
||||
);
|
||||
|
||||
nom_wire_fixtures!(ChannelMultiSigProof,
|
||||
Self::try_new([].into()).unwrap() => "0000",
|
||||
Self::try_new([IndexedSignature::new(0, Ed25519Signature::from_bytes(&[0u8; _]))].into()).unwrap() => "0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
use lb_core_macros::nom_wire_fixtures;
|
||||
use lb_groth16::{AdditiveGroup as _, Field as _, Fr};
|
||||
use lb_poc::PoCProof;
|
||||
|
||||
use crate::mantle::ops::leader_claim::{LeaderClaimOp, RewardsRoot, VoucherNullifier};
|
||||
use crate::{
|
||||
mantle::ops::leader_claim::{LeaderClaimOp, RewardsRoot, VoucherNullifier},
|
||||
proofs::leader_claim_proof::Groth16LeaderClaimProof,
|
||||
};
|
||||
|
||||
nom_wire_fixtures!(
|
||||
RewardsRoot,
|
||||
@@ -21,3 +25,8 @@ nom_wire_fixtures!(
|
||||
pk: Fr::ZERO.into()
|
||||
} => "000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
);
|
||||
|
||||
nom_wire_fixtures!(
|
||||
Groth16LeaderClaimProof,
|
||||
Self::new(PoCProof::from_bytes(&[1u8; _])) => "0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"
|
||||
);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use lb_groth16::Fr;
|
||||
use lb_key_management_system_keys::keys::{Ed25519PublicKey, ZkPublicKey};
|
||||
use lb_key_management_system_keys::keys::{
|
||||
Ed25519PublicKey, Ed25519Signature, ZkPublicKey, ZkSignature,
|
||||
};
|
||||
use lb_zksign::ZkSignProof;
|
||||
use nom::{
|
||||
IResult,
|
||||
error::{Error, ErrorKind},
|
||||
@@ -37,3 +40,29 @@ impl NomDecode for ZkPublicKey {
|
||||
Ok((bytes, Self::new(inner)))
|
||||
}
|
||||
}
|
||||
|
||||
impl NomEncode for Ed25519Signature {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
self.to_bytes().encode()
|
||||
}
|
||||
}
|
||||
|
||||
impl NomDecode for Ed25519Signature {
|
||||
fn decode(bytes: &[u8]) -> IResult<&[u8], Self> {
|
||||
let (remaining_bytes, inner) = <[u8; _]>::decode(bytes)?;
|
||||
Ok((remaining_bytes, Self::from_bytes(&inner)))
|
||||
}
|
||||
}
|
||||
|
||||
impl NomEncode for ZkSignature {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
self.as_proof().to_bytes().encode()
|
||||
}
|
||||
}
|
||||
|
||||
impl NomDecode for ZkSignature {
|
||||
fn decode(bytes: &[u8]) -> IResult<&[u8], Self> {
|
||||
let (remaining_bytes, inner) = <[u8; _]>::decode(bytes)?;
|
||||
Ok((remaining_bytes, Self::new(ZkSignProof::from_bytes(&inner))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,7 +700,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
mantle::{Note, ledger::Outputs, ops::channel::inscribe::InscriptionOp},
|
||||
proofs::channel_multi_sig_proof::IndexedSignature,
|
||||
proofs::channel_multi_sig_proof::{IndexedSignature, IndexedSignatures},
|
||||
};
|
||||
|
||||
fn create_test_mantle_tx(ops: Vec<Op>) -> MantleTx {
|
||||
@@ -771,7 +771,7 @@ mod tests {
|
||||
withdraw_nonce: 0,
|
||||
})]);
|
||||
let tx_hash = mantle_tx.hash();
|
||||
let signatures = signing_keys
|
||||
let signatures: IndexedSignatures = signing_keys
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, key)| {
|
||||
@@ -780,8 +780,10 @@ mod tests {
|
||||
key.sign_payload(tx_hash.as_signing_bytes().as_ref()),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let proof = ChannelMultiSigProof::new(signatures).unwrap();
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let proof = ChannelMultiSigProof::try_new(signatures).unwrap();
|
||||
SignedMantleTx::new(mantle_tx, vec![OpProof::ChannelMultiSigProof(proof)]).unwrap()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use lb_core_macros::NomCodec;
|
||||
use lb_key_management_system_keys::keys::Ed25519Signature;
|
||||
use lb_utils::bounded_vec::UpperBoundedVec;
|
||||
use nom::{
|
||||
Err,
|
||||
error::{Error as NomError, ErrorKind},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::mantle::ops::channel::ChannelKeyIndex;
|
||||
use crate::mantle::{
|
||||
nom::{NomDecode, NomEncode},
|
||||
ops::channel::ChannelKeyIndex,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, NomCodec)]
|
||||
pub struct IndexedSignature {
|
||||
pub signature: Ed25519Signature,
|
||||
pub channel_key_index: ChannelKeyIndex, /* Using ChannelKeyIndex ensures indices are
|
||||
* bounded, and MAX provides an upper limit for the
|
||||
* number of unique signatures (one per index) */
|
||||
pub signature: Ed25519Signature,
|
||||
}
|
||||
|
||||
impl IndexedSignature {
|
||||
#[must_use]
|
||||
pub const fn new(channel_key_index: ChannelKeyIndex, signature: Ed25519Signature) -> Self {
|
||||
Self {
|
||||
channel_key_index,
|
||||
signature,
|
||||
channel_key_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +61,9 @@ pub enum Error {
|
||||
TooManySignatures { actual: usize, maximum: usize },
|
||||
}
|
||||
|
||||
pub const MAX_SIGNATURES: usize = u16::MAX as usize;
|
||||
pub type IndexedSignatures = UpperBoundedVec<IndexedSignature, MAX_SIGNATURES>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
// Serde goes through `ChannelMultiSigProofRepr` via `try_from`/`into`: `Deserialize`
|
||||
// routes through `new`, so the well-formedness invariant (strictly-increasing
|
||||
@@ -65,7 +77,22 @@ pub enum Error {
|
||||
pub struct ChannelMultiSigProof {
|
||||
// Invariant: signature indices are strictly increasing (hence ordered and
|
||||
// unique), as required by the spec.
|
||||
signatures: Vec<IndexedSignature>,
|
||||
signatures: IndexedSignatures,
|
||||
}
|
||||
|
||||
impl NomEncode for ChannelMultiSigProof {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
self.signatures.encode()
|
||||
}
|
||||
}
|
||||
|
||||
impl NomDecode for ChannelMultiSigProof {
|
||||
fn decode(bytes: &[u8]) -> nom::IResult<&[u8], Self> {
|
||||
let (remaining_bytes, inner) = IndexedSignatures::decode(bytes)?;
|
||||
let proof = Self::try_new(inner)
|
||||
.map_err(|_| Err::Error(NomError::new(bytes, ErrorKind::MapRes)))?;
|
||||
Ok((remaining_bytes, proof))
|
||||
}
|
||||
}
|
||||
|
||||
/// Serde wire representation of [`ChannelMultiSigProof`] — a struct with a
|
||||
@@ -74,14 +101,14 @@ pub struct ChannelMultiSigProof {
|
||||
/// preserving the `{ "signatures": [..] }` JSON shape.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct ChannelMultiSigProofRepr {
|
||||
signatures: Vec<IndexedSignature>,
|
||||
signatures: IndexedSignatures,
|
||||
}
|
||||
|
||||
impl TryFrom<ChannelMultiSigProofRepr> for ChannelMultiSigProof {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(repr: ChannelMultiSigProofRepr) -> Result<Self, Self::Error> {
|
||||
Self::new(repr.signatures)
|
||||
Self::try_new(repr.signatures)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,20 +121,19 @@ impl From<ChannelMultiSigProof> for ChannelMultiSigProofRepr {
|
||||
}
|
||||
|
||||
impl ChannelMultiSigProof {
|
||||
pub fn new(signatures: Vec<IndexedSignature>) -> Result<Self, Error> {
|
||||
Self::validate_well_formedness(&signatures)?;
|
||||
pub fn try_new(signatures: IndexedSignatures) -> Result<Self, Error> {
|
||||
let signatures = Self::validate_well_formedness(signatures)?;
|
||||
Ok(Self { signatures })
|
||||
}
|
||||
|
||||
/// Validates that the proof is structurally well-formed: signature indices
|
||||
/// must be strictly increasing (so they are ordered and unique, per the
|
||||
/// `CHANNEL_CONFIG` / `CHANNEL_WITHDRAW` spec), and the count must not
|
||||
/// exceed `ChannelKeyIndex::MAX`.
|
||||
/// `CHANNEL_CONFIG` / `CHANNEL_WITHDRAW` spec).
|
||||
///
|
||||
/// This validates structural correctness only. Cryptographic validity
|
||||
/// (signature verification, threshold requirements, index-to-key
|
||||
/// correspondence) must be checked separately.
|
||||
fn validate_well_formedness(signatures: &[IndexedSignature]) -> Result<(), Error> {
|
||||
fn validate_well_formedness(signatures: IndexedSignatures) -> Result<IndexedSignatures, Error> {
|
||||
if signatures
|
||||
.windows(2)
|
||||
.any(|w| w[0].channel_key_index >= w[1].channel_key_index)
|
||||
@@ -116,27 +142,12 @@ impl ChannelMultiSigProof {
|
||||
signatures.iter().map(|s| s.channel_key_index).collect(),
|
||||
));
|
||||
}
|
||||
let max_signatures_allowed = usize::from(ChannelKeyIndex::MAX) + 1;
|
||||
if signatures.len() > max_signatures_allowed {
|
||||
return Err(Error::TooManySignatures {
|
||||
actual: signatures.len(),
|
||||
maximum: max_signatures_allowed,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
Ok(signatures)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn signatures(&self) -> &Vec<IndexedSignature> {
|
||||
&self.signatures
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<IndexedSignature>> for ChannelMultiSigProof {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Vec<IndexedSignature>) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
pub fn signatures(&self) -> &[IndexedSignature] {
|
||||
self.signatures.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,12 +162,12 @@ mod tests {
|
||||
#[test]
|
||||
fn rejects_repeated_index() {
|
||||
// Same index twice (distinct sigs): not strictly increasing, so rejected.
|
||||
let signatures = vec![
|
||||
let signatures = [
|
||||
IndexedSignature::new(0, sig(1)),
|
||||
IndexedSignature::new(0, sig(2)),
|
||||
];
|
||||
assert!(matches!(
|
||||
ChannelMultiSigProof::new(signatures),
|
||||
ChannelMultiSigProof::try_new(signatures.into()),
|
||||
Err(Error::IndicesNotStrictlyIncreasing(_))
|
||||
));
|
||||
}
|
||||
@@ -165,23 +176,23 @@ mod tests {
|
||||
fn rejects_unsorted_indices() {
|
||||
// Unique but not strictly increasing (descending): rejected (we no longer
|
||||
// silently sort — the spec asserts monotonic order).
|
||||
let signatures = vec![
|
||||
let signatures = [
|
||||
IndexedSignature::new(1, sig(1)),
|
||||
IndexedSignature::new(0, sig(2)),
|
||||
];
|
||||
assert!(matches!(
|
||||
ChannelMultiSigProof::new(signatures),
|
||||
ChannelMultiSigProof::try_new(signatures.into()),
|
||||
Err(Error::IndicesNotStrictlyIncreasing(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accepts_strictly_increasing_indices() {
|
||||
let signatures = vec![
|
||||
let signatures = [
|
||||
IndexedSignature::new(0, sig(1)),
|
||||
IndexedSignature::new(1, sig(2)),
|
||||
];
|
||||
let proof = ChannelMultiSigProof::new(signatures)
|
||||
let proof = ChannelMultiSigProof::try_new(signatures.into())
|
||||
.expect("strictly-increasing indices are well-formed");
|
||||
assert_eq!(proof.signatures().len(), 2);
|
||||
}
|
||||
@@ -195,7 +206,7 @@ mod tests {
|
||||
fn deserialize_rejects_non_monotonic_indices() {
|
||||
// Two distinct signatures sharing index 0 — not strictly increasing, so
|
||||
// `new` (and now `Deserialize`) must reject it.
|
||||
let raw = vec![
|
||||
let raw = [
|
||||
IndexedSignature::new(0, sig(1)),
|
||||
IndexedSignature::new(0, sig(2)),
|
||||
];
|
||||
@@ -210,10 +221,13 @@ mod tests {
|
||||
|
||||
// A well-formed proof still round-trips, and keeps the `{ "signatures": [..] }`
|
||||
// JSON shape.
|
||||
let ok = ChannelMultiSigProof::new(vec![
|
||||
IndexedSignature::new(0, sig(1)),
|
||||
IndexedSignature::new(1, sig(2)),
|
||||
])
|
||||
let ok = ChannelMultiSigProof::try_new(
|
||||
[
|
||||
IndexedSignature::new(0, sig(1)),
|
||||
IndexedSignature::new(1, sig(2)),
|
||||
]
|
||||
.into(),
|
||||
)
|
||||
.expect("distinct indices are well-formed");
|
||||
let serialized = serde_json::to_string(&ok).expect("serialize proof");
|
||||
assert!(
|
||||
@@ -224,4 +238,41 @@ mod tests {
|
||||
serde_json::from_str(&serialized).expect("well-formed proof round-trips");
|
||||
assert_eq!(round_tripped, ok);
|
||||
}
|
||||
|
||||
/// The nom decoder must uphold the same well-formedness invariant as `new`:
|
||||
/// a wire-encoded vector with a repeated index is not strictly increasing,
|
||||
/// so `decode` must fail (the `try_new` inside `decode` maps to a nom
|
||||
/// error).
|
||||
#[test]
|
||||
fn decode_rejects_repeated_index() {
|
||||
// Encode a raw vector (bypassing `ChannelMultiSigProof`) with the same
|
||||
// index twice, then decode it back through the proof's nom path.
|
||||
let raw: IndexedSignatures = [
|
||||
IndexedSignature::new(0, sig(1)),
|
||||
IndexedSignature::new(0, sig(2)),
|
||||
]
|
||||
.into();
|
||||
let bytes = raw.encode();
|
||||
assert!(
|
||||
ChannelMultiSigProof::decode(&bytes).is_err(),
|
||||
"decoding a proof with a repeated index must fail"
|
||||
);
|
||||
}
|
||||
|
||||
/// Companion to the above: a wire-encoded vector whose indices are unique
|
||||
/// but out of order (descending) is also not strictly increasing, so the
|
||||
/// nom decoder must reject it.
|
||||
#[test]
|
||||
fn decode_rejects_out_of_order_indices() {
|
||||
let raw: IndexedSignatures = [
|
||||
IndexedSignature::new(1, sig(1)),
|
||||
IndexedSignature::new(0, sig(2)),
|
||||
]
|
||||
.into();
|
||||
let bytes = raw.encode();
|
||||
assert!(
|
||||
ChannelMultiSigProof::decode(&bytes).is_err(),
|
||||
"decoding a proof with out-of-order indices must fail"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,13 @@ use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use tracing::error;
|
||||
|
||||
use crate::{mantle::ops::leader_claim::VoucherSecret, proofs::merkle::mmr_path_to_witness};
|
||||
use crate::{
|
||||
mantle::{
|
||||
nom::{NomDecode, NomEncode},
|
||||
ops::leader_claim::VoucherSecret,
|
||||
},
|
||||
proofs::merkle::mmr_path_to_witness,
|
||||
};
|
||||
|
||||
const LOG_TARGET: &str = proofs::LEADER_CLAIM;
|
||||
|
||||
@@ -15,6 +21,24 @@ pub struct Groth16LeaderClaimProof {
|
||||
proof: lb_poc::PoCProof,
|
||||
}
|
||||
|
||||
impl NomEncode for Groth16LeaderClaimProof {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
self.proof.to_bytes().encode()
|
||||
}
|
||||
}
|
||||
|
||||
impl NomDecode for Groth16LeaderClaimProof {
|
||||
fn decode(bytes: &[u8]) -> nom::IResult<&[u8], Self> {
|
||||
let (remaining_bytes, inner) = <[u8; _]>::decode(bytes)?;
|
||||
Ok((
|
||||
remaining_bytes,
|
||||
Self {
|
||||
proof: lb_poc::PoCProof::from_bytes(&inner),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("Proof of claim failed: {0}")]
|
||||
|
||||
@@ -214,7 +214,7 @@ pub(crate) fn run_config_combine(args: ConfigCombineArgs) -> RunResult<()> {
|
||||
signature: sig.signature,
|
||||
});
|
||||
}
|
||||
let proof = ChannelMultiSigProof::new(
|
||||
let proof = ChannelMultiSigProof::try_new(
|
||||
signature_entries
|
||||
.iter()
|
||||
.map(|sig| {
|
||||
@@ -222,7 +222,8 @@ pub(crate) fn run_config_combine(args: ConfigCombineArgs) -> RunResult<()> {
|
||||
decode_hex_bincode::<Ed25519Signature>(&sig.signature)
|
||||
.map(|signature| IndexedSignature::new(sig.signer_key_index, signature))
|
||||
})
|
||||
.collect::<RunResult<Vec<_>>>()?,
|
||||
.collect::<RunResult<Vec<_>>>()?
|
||||
.try_into()?,
|
||||
)?;
|
||||
if proof.signatures().len() != intent.required_threshold as usize {
|
||||
return Err(format!(
|
||||
|
||||
@@ -172,7 +172,7 @@ pub(crate) fn run_withdraw_combine(args: WithdrawCombineArgs) -> RunResult<()> {
|
||||
signature: sig.signature,
|
||||
});
|
||||
}
|
||||
let proof = ChannelMultiSigProof::new(
|
||||
let proof = ChannelMultiSigProof::try_new(
|
||||
signature_entries
|
||||
.iter()
|
||||
.map(|sig| {
|
||||
@@ -180,7 +180,8 @@ pub(crate) fn run_withdraw_combine(args: WithdrawCombineArgs) -> RunResult<()> {
|
||||
decode_hex_bincode::<Ed25519Signature>(&sig.signature)
|
||||
.map(|signature| IndexedSignature::new(sig.signer_key_index, signature))
|
||||
})
|
||||
.collect::<RunResult<Vec<_>>>()?,
|
||||
.collect::<RunResult<Vec<_>>>()?
|
||||
.try_into()?,
|
||||
)?;
|
||||
if proof.signatures().len() < intent.required_threshold as usize {
|
||||
return Err(format!(
|
||||
|
||||
+28
-16
@@ -1055,10 +1055,13 @@ mod tests {
|
||||
|
||||
let config_tx = MantleTx([Op::ChannelConfig(config_op.clone())].into());
|
||||
let config_tx_hash = config_tx.hash();
|
||||
let config_proof = ChannelMultiSigProof::new(vec![IndexedSignature::new(
|
||||
0,
|
||||
signing_key.sign_payload(config_tx_hash.as_signing_bytes().as_ref()),
|
||||
)])
|
||||
let config_proof = ChannelMultiSigProof::try_new(
|
||||
[IndexedSignature::new(
|
||||
0,
|
||||
signing_key.sign_payload(config_tx_hash.as_signing_bytes().as_ref()),
|
||||
)]
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tx = create_signed_tx(
|
||||
@@ -1221,10 +1224,13 @@ mod tests {
|
||||
};
|
||||
let withdraw_tx = MantleTx([Op::ChannelWithdraw(withdraw.clone())].into());
|
||||
let withdraw_tx_hash = withdraw_tx.hash();
|
||||
let withdraw_proof = ChannelMultiSigProof::new(vec![IndexedSignature::new(
|
||||
0,
|
||||
signing_key.sign_payload(withdraw_tx_hash.as_signing_bytes().as_ref()),
|
||||
)])
|
||||
let withdraw_proof = ChannelMultiSigProof::try_new(
|
||||
[IndexedSignature::new(
|
||||
0,
|
||||
signing_key.sign_payload(withdraw_tx_hash.as_signing_bytes().as_ref()),
|
||||
)]
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let signed_tx = create_multi_signed_tx(
|
||||
@@ -1311,10 +1317,13 @@ mod tests {
|
||||
let wrong_key = Ed25519Key::from_bytes(&[42; 32]);
|
||||
let withdraw_tx = MantleTx([Op::ChannelWithdraw(withdraw.clone())].into());
|
||||
let withdraw_tx_hash = withdraw_tx.hash();
|
||||
let invalid_proof = ChannelMultiSigProof::new(vec![IndexedSignature::new(
|
||||
0,
|
||||
wrong_key.sign_payload(withdraw_tx_hash.as_signing_bytes().as_ref()),
|
||||
)])
|
||||
let invalid_proof = ChannelMultiSigProof::try_new(
|
||||
[IndexedSignature::new(
|
||||
0,
|
||||
wrong_key.sign_payload(withdraw_tx_hash.as_signing_bytes().as_ref()),
|
||||
)]
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let signed_tx = create_multi_signed_tx(
|
||||
@@ -1528,10 +1537,13 @@ mod tests {
|
||||
];
|
||||
let config_tx = MantleTx(Ops::new_unchecked(ops.clone()));
|
||||
let config_tx_hash = config_tx.hash();
|
||||
let config_proof = ChannelMultiSigProof::new(vec![IndexedSignature::new(
|
||||
0,
|
||||
sk1.sign_payload(config_tx_hash.as_signing_bytes().as_ref()),
|
||||
)])
|
||||
let config_proof = ChannelMultiSigProof::try_new(
|
||||
[IndexedSignature::new(
|
||||
0,
|
||||
sk1.sign_payload(config_tx_hash.as_signing_bytes().as_ref()),
|
||||
)]
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tx = create_multi_signed_tx(
|
||||
|
||||
@@ -1588,7 +1588,7 @@ pub async fn submit_zone_withdraw(
|
||||
})?;
|
||||
|
||||
let withdraw_proof =
|
||||
match ChannelMultiSigProof::new(vec![IndexedSignature::new(0, withdraw_sig)]) {
|
||||
match ChannelMultiSigProof::try_new([IndexedSignature::new(0, withdraw_sig)].into()) {
|
||||
Ok(proof) => proof,
|
||||
Err(error) => {
|
||||
return Err(ZoneTestError::SubmitWithdraw {
|
||||
|
||||
@@ -403,10 +403,13 @@ fn signed_channel_withdraw(
|
||||
) -> SignedMantleTx {
|
||||
let mantle_tx = MantleTx([Op::ChannelWithdraw(withdraw)].into());
|
||||
let tx_hash = mantle_tx.hash();
|
||||
let withdraw_proof = ChannelMultiSigProof::new(vec![IndexedSignature::new(
|
||||
0,
|
||||
signing_key.sign_payload(tx_hash.as_signing_bytes().as_ref()),
|
||||
)])
|
||||
let withdraw_proof = ChannelMultiSigProof::try_new(
|
||||
[IndexedSignature::new(
|
||||
0,
|
||||
signing_key.sign_payload(tx_hash.as_signing_bytes().as_ref()),
|
||||
)]
|
||||
.into(),
|
||||
)
|
||||
.expect("withdraw proof should be valid");
|
||||
|
||||
SignedMantleTx::new(
|
||||
|
||||
@@ -281,6 +281,8 @@ pub type UpperBoundedVec<T, const MAX: usize> = BoundedVec<T, 0, MAX>;
|
||||
pub type LowerBoundedVec<T, const MIN: usize> = BoundedVec<T, MIN, { usize::MAX }>;
|
||||
// `[1, MAX]` elements.
|
||||
pub type NonEmptyBoundedVec<T, const MAX: usize> = BoundedVec<T, 1, MAX>;
|
||||
// `[0, usize::MAX]` elements.
|
||||
pub type MaxBoundedVec<T> = UpperBoundedVec<T, { usize::MAX }>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
mod curve;
|
||||
mod from_json_error;
|
||||
mod proof;
|
||||
pub use proof::{CompressSize, CompressedProof};
|
||||
pub use proof::{COMPRESSED_PROOF_SIZE, CompressSize, CompressedProof};
|
||||
mod protocol;
|
||||
mod public_input;
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ pub trait CompressSize: Pairing {
|
||||
type G2CompressedSize: ArrayLength;
|
||||
}
|
||||
|
||||
pub const COMPRESSED_PROOF_SIZE: usize = 128;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct CompressedProof<E: CompressSize> {
|
||||
pub pi_a: GenericArray<u8, E::G1CompressedSize>,
|
||||
@@ -67,8 +69,8 @@ impl<E: CompressSize> CompressedProof<E> {
|
||||
impl CompressedProof<Bn254> {
|
||||
/// Total size = G1 + G2 + G1 at the type level.
|
||||
#[must_use]
|
||||
pub fn to_bytes(&self) -> [u8; 128] {
|
||||
let mut bytes = [0u8; 128];
|
||||
pub fn to_bytes(&self) -> [u8; COMPRESSED_PROOF_SIZE] {
|
||||
let mut bytes = [0u8; COMPRESSED_PROOF_SIZE];
|
||||
let g1 = <Bn254 as CompressSize>::G1CompressedSize::to_usize();
|
||||
let g2 = <Bn254 as CompressSize>::G2CompressedSize::to_usize();
|
||||
|
||||
@@ -81,7 +83,7 @@ impl CompressedProof<Bn254> {
|
||||
|
||||
/// Type-level length bound: accepts exactly G1 + G2 + G1 bytes.
|
||||
#[must_use]
|
||||
pub fn from_bytes(bytes: &[u8; 128]) -> Self {
|
||||
pub fn from_bytes(bytes: &[u8; COMPRESSED_PROOF_SIZE]) -> Self {
|
||||
let g1 = <Bn254 as CompressSize>::G1CompressedSize::to_usize();
|
||||
let g2 = <Bn254 as CompressSize>::G2CompressedSize::to_usize();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ pub(super) fn build_atomic_withdraw_ops_proofs(
|
||||
own_sig: Ed25519Signature,
|
||||
) -> Result<Vec<OpProof>, Error> {
|
||||
let withdraw_proof =
|
||||
ChannelMultiSigProof::new(vec![IndexedSignature::new(own_key_index, own_sig)])
|
||||
ChannelMultiSigProof::try_new([IndexedSignature::new(own_key_index, own_sig)].into())
|
||||
.map_err(|e| Error::Network(format!("multi-sig proof assembly failed: {e:?}")))?;
|
||||
let mut ops_proofs = Vec::with_capacity(tx.ops().len());
|
||||
for op in tx.ops() {
|
||||
@@ -125,8 +125,10 @@ pub(super) fn create_channel_config_tx(
|
||||
key.sign_payload(tx_hash.as_signing_bytes().as_ref()),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let proof = ChannelMultiSigProof::new(signatures).unwrap();
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let proof = ChannelMultiSigProof::try_new(signatures).unwrap();
|
||||
|
||||
SignedMantleTx {
|
||||
ops_proofs: vec![OpProof::ChannelMultiSigProof(proof)],
|
||||
|
||||
Reference in New Issue
Block a user