secret_toolkit_notification/
structs.rs

1use cosmwasm_std::{Addr, Api, Binary, Env, StdError, StdResult, Uint64};
2use minicbor::Encoder;
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::{cbor_to_std_error, encrypt_notification_data, get_seed, notification_id};
7
8#[derive(Serialize, Debug, Deserialize, Clone)]
9#[cfg_attr(test, derive(Eq, PartialEq))]
10pub struct Notification<T: DirectChannel> {
11    /// Recipient address of the notification
12    pub notification_for: Addr,
13    /// Typed notification data
14    pub data: T,
15}
16
17pub trait DirectChannel {
18    const CHANNEL_ID: &'static str;
19    const CDDL_SCHEMA: &'static str;
20    const ELEMENTS: u64;
21    const PAYLOAD_SIZE: usize;
22
23    fn channel_id(&self) -> String {
24        Self::CHANNEL_ID.to_string()
25    }
26
27    fn cddl_schema(&self) -> String {
28        Self::CDDL_SCHEMA.to_string()
29    }
30
31    fn to_cbor(&self, api: &dyn Api) -> StdResult<Vec<u8>> {
32        // dynamically allocate output buffer
33        let mut buffer = vec![0u8; Self::PAYLOAD_SIZE];
34
35        // create CBOR encoder
36        let mut encoder = Encoder::new(&mut buffer[..]);
37
38        // encode number of elements
39        encoder.array(Self::ELEMENTS).map_err(cbor_to_std_error)?;
40
41        // encode CBOR data
42        self.encode_cbor(api, &mut encoder)?;
43
44        // return buffer (already right-padded with zero bytes)
45        Ok(buffer)
46    }
47
48    /// CBOR encodes notification data into the encoder
49    fn encode_cbor(&self, api: &dyn Api, encoder: &mut Encoder<&mut [u8]>) -> StdResult<()>;
50}
51
52impl<T: DirectChannel> Notification<T> {
53    pub fn new(notification_for: Addr, data: T) -> Self {
54        Notification {
55            notification_for,
56            data,
57        }
58    }
59
60    pub fn to_txhash_notification(
61        &self,
62        api: &dyn Api,
63        env: &Env,
64        secret: &[u8],
65        block_size: Option<usize>,
66    ) -> StdResult<TxHashNotification> {
67        // extract and normalize tx hash
68        let tx_hash = env
69            .transaction
70            .clone()
71            .ok_or(StdError::generic_err("no tx hash found"))?
72            .hash
73            .to_ascii_uppercase();
74
75        // canonicalize notification recipient address
76        let notification_for_raw = api.addr_canonicalize(self.notification_for.as_str())?;
77
78        // derive recipient's notification seed
79        let seed = get_seed(&notification_for_raw, secret)?;
80
81        // derive notification id
82        let id = notification_id(&seed, self.data.channel_id().as_str(), &tx_hash)?;
83
84        // use CBOR to encode the data
85        let cbor_data = self.data.to_cbor(api)?;
86
87        // encrypt the receiver message
88        let encrypted_data = encrypt_notification_data(
89            &env.block.height,
90            &tx_hash,
91            &seed,
92            self.data.channel_id().as_str(),
93            cbor_data,
94            block_size,
95        )?;
96
97        // enstruct
98        Ok(TxHashNotification { id, encrypted_data })
99    }
100}
101
102#[derive(Serialize, Debug, Deserialize, Clone)]
103#[cfg_attr(test, derive(Eq, PartialEq))]
104pub struct TxHashNotification {
105    pub id: Binary,
106    pub encrypted_data: Binary,
107}
108
109impl TxHashNotification {
110    pub fn id_plaintext(&self) -> String {
111        format!("snip52:{}", self.id.to_base64())
112    }
113
114    pub fn data_plaintext(&self) -> String {
115        self.encrypted_data.to_base64()
116    }
117}
118
119// types for channel info response
120
121#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
122pub struct ChannelInfoData {
123    /// same as query input
124    pub channel: String,
125    /// "counter", "txhash", "bloom"
126    pub mode: String,
127
128    /// txhash / bloom fields only
129    /// if txhash argument was given, this will be its computed Notification ID
130    pub answer_id: Option<Binary>,
131
132    /// bloom fields only
133    /// bloom filter parameters
134    pub parameters: Option<BloomParameters>,
135    /// bloom filter data
136    pub data: Option<Descriptor>,
137
138    /// counter fields only
139    /// current counter value
140    pub counter: Option<Uint64>,
141    /// the next Notification ID
142    pub next_id: Option<Binary>,
143
144    /// counter / txhash field only
145    /// optional CDDL schema definition string for the CBOR-encoded notification data
146    pub cddl: Option<String>,
147}
148
149#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
150pub struct BloomParameters {
151    pub m: u32,
152    pub k: u32,
153    pub h: String,
154}
155
156#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
157pub struct Descriptor {
158    pub r#type: String,
159    pub version: String,
160    pub packet_size: u32,
161    pub data: StructDescriptor,
162}
163
164#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
165pub struct StructDescriptor {
166    pub r#type: String,
167    pub label: String,
168    pub members: Vec<FlatDescriptor>,
169}
170
171#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
172pub struct FlatDescriptor {
173    pub r#type: String,
174    pub label: String,
175    pub description: Option<String>,
176}
177
178pub trait GroupChannel<D: DirectChannel> {
179    const CHANNEL_ID: &'static str;
180    const BLOOM_N: usize;
181    const BLOOM_M: u32;
182    const BLOOM_K: u32;
183    const PACKET_SIZE: usize;
184
185    const BLOOM_M_LOG2: u32 = Self::BLOOM_M.ilog2();
186
187    fn build_packet(&self, api: &dyn Api, data: &D) -> StdResult<Vec<u8>>;
188
189    fn notifications(&self) -> &Vec<Notification<D>>;
190}