1use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
8use serde::{Deserialize, Serialize};
9use sha2::{Digest, Sha256};
10use std::time::{SystemTime, UNIX_EPOCH};
11
12fn pod_message(scid: &str, shard_index: u32, ts_unix_ms: u64, leaf_hash: [u8; 32]) -> Vec<u8> {
16 let mut m = Vec::with_capacity(7 + scid.len() + 4 + 8 + 32);
17 m.extend_from_slice(b"s3p-pod-v1");
18 m.extend_from_slice(scid.as_bytes());
19 m.extend_from_slice(&shard_index.to_le_bytes());
20 m.extend_from_slice(&ts_unix_ms.to_le_bytes());
21 m.extend_from_slice(&leaf_hash);
22 m
23}
24
25#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
26pub struct ProofOfDelivery {
27 pub version: u8, pub scid: String, pub shard_index: u32, pub ts_unix_ms: u64, pub signer_pubkey: [u8; 32], pub sig: Vec<u8>, pub leaf_hash: [u8; 32], }
35
36impl ProofOfDelivery {
37 pub fn sign(
39 sk: &SigningKey,
40 scid: &str,
41 shard_index: u32,
42 leaf_hash: [u8; 32],
43 ts_unix_ms: Option<u64>,
44 ) -> Self {
45 let ts = ts_unix_ms.unwrap_or_else(|| {
46 SystemTime::now()
47 .duration_since(UNIX_EPOCH)
48 .expect("time")
49 .as_millis() as u64
50 });
51 let msg = pod_message(scid, shard_index, ts, leaf_hash);
52 let sig: Signature = sk.sign(&msg);
53 let pk: VerifyingKey = sk.verifying_key();
54 Self {
55 version: 1,
56 scid: scid.to_string(),
57 shard_index,
58 ts_unix_ms: ts,
59 signer_pubkey: pk.to_bytes(),
60 sig: sig.to_bytes().to_vec(),
61 leaf_hash,
62 }
63 }
64
65 pub fn verify(&self) -> bool {
67 if self.sig.len() != 64 {
68 return false;
69 }
70 let pk = match VerifyingKey::from_bytes(&self.signer_pubkey) {
71 Ok(v) => v,
72 Err(_) => return false,
73 };
74 let sig = match Signature::from_slice(&self.sig) {
76 Ok(s) => s,
77 Err(_) => return false,
78 };
79 let msg = pod_message(
80 &self.scid,
81 self.shard_index,
82 self.ts_unix_ms,
83 self.leaf_hash,
84 );
85 pk.verify(&msg, &sig).is_ok()
86 }
87
88 pub fn pod_id(&self) -> [u8; 32] {
90 let bytes = serde_json::to_vec(self).expect("serialize");
91 let mut h = Sha256::new();
92 h.update(&bytes);
93 h.finalize().into()
94 }
95}