amadeus_node/consensus/doms/
attestation.rs1use crate::Context;
2use crate::node::protocol;
3use crate::node::protocol::{Handle, Typename};
4use crate::utils::bls12_381 as bls;
5use crate::utils::bls12_381::Error as BlsError;
6use crate::utils::{Hash, PublicKey, Signature};
7use amadeus_utils::constants::DST_ATT;
8use amadeus_utils::vecpak::{Term, VecpakExt, decode, encode};
9use std::fmt::Debug;
10use std::net::Ipv4Addr;
11use tracing::instrument;
12
13#[derive(Debug, thiserror::Error)]
14pub enum Error {
15 #[error("wrong type: {0}")]
16 WrongType(&'static str),
17 #[error("missing field: {0}")]
18 Missing(&'static str),
19 #[error("attestation is not vecpak")]
20 AttestationNotVecpak,
21 #[error("invalid length: {0}")]
22 InvalidLength(&'static str),
23 #[error(transparent)]
24 Bls(#[from] BlsError),
25}
26
27#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
28pub struct EventAttestation {
29 pub attestations: Vec<Attestation>,
30}
31
32#[derive(Clone, serde::Serialize, serde::Deserialize)]
33pub struct Attestation {
34 pub entry_hash: Hash,
35 pub mutations_hash: Hash,
36 pub signer: PublicKey,
37 pub signature: Signature,
38}
39
40impl Debug for Attestation {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 f.debug_struct("Attestation")
43 .field("entry_hash", &bs58::encode(self.entry_hash).into_string())
44 .field("mutations_hash", &bs58::encode(self.mutations_hash).into_string())
45 .field("signer", &bs58::encode(self.signer).into_string())
46 .finish()
47 }
48}
49
50impl Typename for EventAttestation {
51 fn typename(&self) -> &'static str {
52 Self::TYPENAME
53 }
54}
55
56#[async_trait::async_trait]
57impl Handle for EventAttestation {
58 #[instrument(skip(self, _ctx), name = "EventAttestation::handle", err)]
59 async fn handle(&self, _ctx: &Context, _src: Ipv4Addr) -> Result<Vec<protocol::Instruction>, protocol::Error> {
60 Ok(vec![protocol::Instruction::Noop { why: "event_attestation handling not implemented".to_string() }])
61 }
62}
63
64impl EventAttestation {
65 pub const TYPENAME: &'static str = "event_attestation";
66
67 pub fn new(attestations: Vec<Attestation>) -> Self {
68 Self { attestations }
69 }
70}
71
72impl Attestation {
73 #[instrument(skip(map), name = "Attestation::from_vecpak_map", err)]
75 pub fn from_vecpak_map(map: &amadeus_utils::vecpak::PropListMap) -> Result<Self, Error> {
76 let entry_hash_v = map.get_binary::<Vec<u8>>(b"entry_hash").ok_or(Error::Missing("entry_hash"))?;
77 let mutations_hash_v = map.get_binary::<Vec<u8>>(b"mutations_hash").ok_or(Error::Missing("mutations_hash"))?;
78 let signer_v = map.get_binary::<Vec<u8>>(b"signer").ok_or(Error::Missing("signer"))?;
79 let signature_v = map.get_binary::<Vec<u8>>(b"signature").ok_or(Error::Missing("signature"))?;
80
81 Ok(Attestation {
82 entry_hash: entry_hash_v.try_into().map_err(|_| Error::InvalidLength("entry_hash"))?,
83 mutations_hash: mutations_hash_v.try_into().map_err(|_| Error::InvalidLength("mutations_hash"))?,
84 signer: signer_v.try_into().map_err(|_| Error::InvalidLength("signer"))?,
85 signature: signature_v.try_into().map_err(|_| Error::InvalidLength("signature"))?,
86 })
87 }
88
89 #[instrument(skip(self), name = "Attestation::validate", err)]
91 pub fn validate(&self) -> Result<(), Error> {
92 let mut to_sign = [0u8; 64];
93 to_sign[..32].copy_from_slice(self.entry_hash.as_ref());
94 to_sign[32..].copy_from_slice(self.mutations_hash.as_ref());
95 bls::verify(&self.signer, &self.signature, &to_sign, DST_ATT)?;
96 Ok(())
97 }
98
99 pub fn validate_vs_trainers<TPk>(&self, trainers: &[TPk]) -> Result<(), Error>
102 where
103 TPk: AsRef<[u8]>,
104 {
105 let is_allowed = trainers.iter().any(|pk| pk.as_ref() == self.signer.as_ref() as &[u8]);
106 if !is_allowed {
107 return Err(Error::WrongType("signer_not_trainer"));
108 }
109 self.validate()
110 }
111
112 pub fn sign_with(
115 pk_g1_48: &[u8],
116 trainer_sk: &[u8],
117 entry_hash: &Hash,
118 mutations_hash: &Hash,
119 ) -> Result<Self, Error> {
120 let mut msg = [0u8; 64];
121 msg[..32].copy_from_slice(entry_hash.as_ref());
122 msg[32..].copy_from_slice(mutations_hash.as_ref());
123 let signature = bls::sign(trainer_sk, &msg, DST_ATT)?;
124 let signer: PublicKey = pk_g1_48.try_into().map_err(|_| Error::InvalidLength("signer"))?;
125 let signature: Signature = signature.as_slice().try_into().map_err(|_| Error::InvalidLength("signature"))?;
126 Ok(Self { entry_hash: *entry_hash, mutations_hash: *mutations_hash, signer, signature })
127 }
128
129 pub fn to_vecpak_bin(&self) -> Vec<u8> {
130 encode(self.to_vecpak_term())
131 }
132
133 pub fn from_vecpak_bin(data: &[u8]) -> Option<Self> {
134 let term = decode(data).ok()?.get_proplist_map()?;
135 Self::from_vecpak_map(&term).ok()
136 }
137
138 pub fn to_vecpak_term(&self) -> Term {
139 Term::PropList(vec![
140 (Term::Binary(b"entry_hash".to_vec()), Term::Binary(self.entry_hash.to_vec())),
141 (Term::Binary(b"mutations_hash".to_vec()), Term::Binary(self.mutations_hash.to_vec())),
142 (Term::Binary(b"signer".to_vec()), Term::Binary(self.signer.to_vec())),
143 (Term::Binary(b"signature".to_vec()), Term::Binary(self.signature.to_vec())),
144 ])
145 }
146}