use crate::error::{AdapterError, Result};
use crate::proof::ProofBundle;
use crate::signature::{verify_signatures, Signature, SignatureScheme};
pub fn verify_proof(
bundle: &ProofBundle,
seal_registry: impl Fn(&[u8]) -> bool,
signature_scheme: SignatureScheme,
) -> Result<()> {
bundle
.transition_dag
.validate_structure()
.map_err(|e| AdapterError::Generic(format!("Invalid DAG structure: {}", e)))?;
verify_bundle_signatures(bundle, signature_scheme)?;
if seal_registry(bundle.seal_ref.seal_id.as_ref()) {
return Err(AdapterError::SealReplay(format!(
"Seal {:?} has already been used",
bundle.seal_ref
)));
}
if bundle.inclusion_proof.proof_bytes.is_empty() {
return Err(AdapterError::InclusionProofFailed(
"Empty inclusion proof".to_string(),
));
}
if bundle.finality_proof.confirmations == 0 {
return Err(AdapterError::FinalityNotReached(
"No confirmations yet".to_string(),
));
}
Ok(())
}
fn verify_bundle_signatures(bundle: &ProofBundle, scheme: SignatureScheme) -> Result<()> {
if bundle.signatures.is_empty() {
return Err(AdapterError::SignatureVerificationFailed(
"No signatures in proof bundle".to_string(),
));
}
let mut signatures = Vec::with_capacity(bundle.signatures.len());
for (i, sig_bytes) in bundle.signatures.iter().enumerate() {
if sig_bytes.len() < 4 {
return Err(AdapterError::SignatureVerificationFailed(format!(
"Signature {} too short for header",
i
)));
}
let pk_len =
u32::from_le_bytes([sig_bytes[0], sig_bytes[1], sig_bytes[2], sig_bytes[3]]) as usize;
if sig_bytes.len() < 4 + pk_len {
return Err(AdapterError::SignatureVerificationFailed(format!(
"Signature {} too short for public key",
i
)));
}
let public_key = sig_bytes[4..4 + pk_len].to_vec();
let signature = sig_bytes[4 + pk_len..].to_vec();
let message = bundle.transition_dag.root_commitment.as_bytes().to_vec();
signatures.push(Signature::new(signature, public_key, message));
}
verify_signatures(&signatures, scheme)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dag::{DAGNode, DAGSegment};
use crate::hash::Hash;
use crate::proof::{FinalityProof, InclusionProof};
use crate::seal::{AnchorRef, SealRef};
use crate::signature::SignatureScheme;
fn make_secp256k1_signature_bytes(message: &[u8; 32]) -> Vec<u8> {
use secp256k1::{Message, Secp256k1, SecretKey};
let secp = Secp256k1::new();
let secret_key = SecretKey::new(&mut secp256k1::rand::thread_rng());
let public_key = secp256k1::PublicKey::from_secret_key(&secp, &secret_key);
let msg = Message::from_digest_slice(message).unwrap();
let signature = secp.sign_ecdsa(&msg, &secret_key);
let sig_bytes = signature.serialize_compact();
let pubkey_bytes = public_key.serialize();
let mut encoded = Vec::with_capacity(4 + pubkey_bytes.len() + sig_bytes.len());
encoded.extend_from_slice(&(pubkey_bytes.len() as u32).to_le_bytes());
encoded.extend_from_slice(&pubkey_bytes);
encoded.extend_from_slice(&sig_bytes);
encoded
}
fn make_ed25519_signature_bytes(message: &[u8]) -> Vec<u8> {
use ed25519_dalek::{Signer, SigningKey};
let signing_key = SigningKey::generate(&mut rand::rngs::OsRng);
let verifying_key = signing_key.verifying_key();
let signature = signing_key.sign(message);
let mut encoded = Vec::with_capacity(4 + 32 + 64);
encoded.extend_from_slice(&32u32.to_le_bytes());
encoded.extend_from_slice(&verifying_key.to_bytes());
encoded.extend_from_slice(&signature.to_bytes());
encoded
}
fn test_bundle_with_signatures() -> Result<ProofBundle> {
let message = [0u8; 32];
let signature = make_secp256k1_signature_bytes(&message);
let bundle = ProofBundle::new(
DAGSegment::new(
vec![DAGNode::new(
Hash::new([1u8; 32]),
vec![0x01, 0x02],
vec![signature.clone()],
vec![],
vec![],
)],
Hash::zero(),
),
vec![signature],
SealRef::new(vec![1, 2, 3], Some(42))
.map_err(|e| AdapterError::Generic(e.to_string()))?,
AnchorRef::new(vec![4, 5, 6], 100, vec![])
.map_err(|e| AdapterError::Generic(e.to_string()))?,
InclusionProof::new(vec![0xCD; 32], Hash::new([2u8; 32]), 0)
.map_err(|e| AdapterError::Generic(e.to_string()))?,
FinalityProof::new(vec![], 6, false)
.map_err(|e| AdapterError::Generic(e.to_string()))?,
)
.map_err(|e| AdapterError::Generic(e.to_string()))?;
Ok(bundle)
}
#[test]
fn test_verify_proof_valid() {
let bundle = test_bundle_with_signatures().unwrap();
let seal_registry = |_seal_id: &[u8]| false;
assert!(verify_proof(&bundle, seal_registry, SignatureScheme::Secp256k1).is_ok());
}
#[test]
fn test_verify_proof_seal_replay() {
let bundle = test_bundle_with_signatures().unwrap();
let seal_registry = |seal_id: &[u8]| seal_id == [1, 2, 3];
assert!(verify_proof(&bundle, seal_registry, SignatureScheme::Secp256k1).is_err());
}
#[test]
fn test_verify_proof_no_signatures() {
let mut bundle = test_bundle_with_signatures().unwrap();
bundle.signatures.clear();
let seal_registry = |_seal_id: &[u8]| false;
assert!(verify_proof(&bundle, seal_registry, SignatureScheme::Secp256k1).is_err());
}
#[test]
fn test_verify_proof_no_confirmations() {
let mut bundle = test_bundle_with_signatures().unwrap();
bundle.finality_proof.confirmations = 0;
let seal_registry = |_seal_id: &[u8]| false;
assert!(verify_proof(&bundle, seal_registry, SignatureScheme::Secp256k1).is_err());
}
#[test]
fn test_verify_proof_invalid_signature_format() {
let mut bundle = test_bundle_with_signatures().unwrap();
bundle.signatures[0] = vec![0x00, 0x00]; let seal_registry = |_seal_id: &[u8]| false;
assert!(verify_proof(&bundle, seal_registry, SignatureScheme::Secp256k1).is_err());
}
#[test]
fn test_verify_proof_ed25519_valid_format() {
let message = [0u8; 32];
let signature = make_ed25519_signature_bytes(&message);
let mut bundle = test_bundle_with_signatures().unwrap();
bundle.signatures = vec![signature];
let seal_registry = |_seal_id: &[u8]| false;
assert!(verify_proof(&bundle, seal_registry, SignatureScheme::Ed25519).is_ok());
}
}