chik_consensus/
messages.rs

1use crate::condition_sanitizers::sanitize_hash;
2use crate::sanitize_int::{sanitize_uint, SanitizedUint};
3use crate::validation_error::{first, rest, ErrorCode, ValidationErr};
4use chik_protocol::Bytes32;
5use klvmr::{Allocator, NodePtr};
6use std::sync::Arc;
7
8// these are mode flags used as the first argument to SEND_MESSAGE and
9// RECEIVE_MESSAGE. They indicate which properties of the sender and receiver we
10// commit to, that must match. The mode flags for the sender are shifted left 3 bits.
11pub const PARENT: u8 = 0b100;
12pub const PUZZLE: u8 = 0b010;
13pub const AMOUNT: u8 = 0b001;
14pub const PUZZLEAMOUNT: u8 = 0b011;
15pub const PARENTAMOUNT: u8 = 0b101;
16pub const PARENTPUZZLE: u8 = 0b110;
17pub const COINID: u8 = 0b111;
18
19#[derive(Debug)]
20pub enum SpendId {
21    OwnedCoinId(Arc<Bytes32>),
22    CoinId(NodePtr),
23    Parent(NodePtr),
24    Puzzle(NodePtr),
25    Amount(u64),
26    PuzzleAmount(NodePtr, u64),
27    ParentAmount(NodePtr, u64),
28    ParentPuzzle(NodePtr, NodePtr),
29    None,
30}
31
32impl SpendId {
33    // args is an in-out parameter. It's updated to point to then next argument
34    pub fn parse(a: &Allocator, args: &mut NodePtr, mode: u8) -> Result<SpendId, ValidationErr> {
35        // we have a special case for when all three mode flags are set. That means
36        // we're committing to parent, puzzle and amount. In this case you just
37        // specify the coin ID
38        if mode == COINID {
39            let coinid = sanitize_hash(a, first(a, *args)?, 32, ErrorCode::InvalidCoinId)?;
40            *args = rest(a, *args)?;
41            return Ok(Self::CoinId(coinid));
42        }
43
44        let parent = if (mode & PARENT) != 0 {
45            let parent = sanitize_hash(a, first(a, *args)?, 32, ErrorCode::InvalidParentId)?;
46            *args = rest(a, *args)?;
47            parent
48        } else {
49            NodePtr::NIL
50        };
51
52        let puzzle = if (mode & PUZZLE) != 0 {
53            let puzzle = sanitize_hash(a, first(a, *args)?, 32, ErrorCode::InvalidPuzzleHash)?;
54            *args = rest(a, *args)?;
55            puzzle
56        } else {
57            NodePtr::NIL
58        };
59
60        let amount = if (mode & AMOUNT) != 0 {
61            let amount = match sanitize_uint(a, first(a, *args)?, 8, ErrorCode::InvalidCoinAmount)?
62            {
63                SanitizedUint::PositiveOverflow => {
64                    return Err(ValidationErr(*args, ErrorCode::CoinAmountExceedsMaximum));
65                }
66                SanitizedUint::NegativeOverflow => {
67                    return Err(ValidationErr(*args, ErrorCode::CoinAmountNegative));
68                }
69                SanitizedUint::Ok(amount) => amount,
70            };
71            *args = rest(a, *args)?;
72            amount
73        } else {
74            0
75        };
76
77        match mode {
78            PARENT => Ok(Self::Parent(parent)),
79            PUZZLE => Ok(Self::Puzzle(puzzle)),
80            AMOUNT => Ok(Self::Amount(amount)),
81            PARENTPUZZLE => Ok(Self::ParentPuzzle(parent, puzzle)),
82            PARENTAMOUNT => Ok(Self::ParentAmount(parent, amount)),
83            PUZZLEAMOUNT => Ok(Self::PuzzleAmount(puzzle, amount)),
84            0 => Ok(Self::None),
85            _ => Err(ValidationErr(*args, ErrorCode::InvalidMessageMode)),
86        }
87    }
88
89    pub fn from_self(
90        mode: u8,
91        parent: NodePtr,
92        puzzle: NodePtr,
93        amount: u64,
94        coin_id: &Arc<Bytes32>,
95    ) -> Result<SpendId, ValidationErr> {
96        if mode == COINID {
97            return Ok(Self::OwnedCoinId(coin_id.clone()));
98        }
99
100        match mode {
101            PARENT => Ok(Self::Parent(parent)),
102            PUZZLE => Ok(Self::Puzzle(puzzle)),
103            AMOUNT => Ok(Self::Amount(amount)),
104            PARENTPUZZLE => Ok(Self::ParentPuzzle(parent, puzzle)),
105            PARENTAMOUNT => Ok(Self::ParentAmount(parent, amount)),
106            PUZZLEAMOUNT => Ok(Self::PuzzleAmount(puzzle, amount)),
107            0 => Ok(Self::None),
108            _ => Err(ValidationErr(NodePtr::NIL, ErrorCode::InvalidMessageMode)),
109        }
110    }
111
112    pub fn make_key(&self, out: &mut Vec<u8>, a: &Allocator) {
113        match self {
114            Self::OwnedCoinId(coinid) => {
115                out.push(COINID);
116                out.extend_from_slice(coinid);
117            }
118            Self::CoinId(coinid) => {
119                out.push(COINID);
120                out.extend_from_slice(a.atom(*coinid).as_ref());
121            }
122            Self::Parent(parent) => {
123                out.push(PARENT);
124                out.extend_from_slice(a.atom(*parent).as_ref());
125            }
126            Self::Puzzle(puzzle) => {
127                out.push(PUZZLE);
128                out.extend_from_slice(a.atom(*puzzle).as_ref());
129            }
130            Self::Amount(amount) => {
131                out.push(AMOUNT);
132                out.extend_from_slice(&amount.to_be_bytes());
133            }
134            Self::PuzzleAmount(puzzle, amount) => {
135                out.push(PUZZLEAMOUNT);
136                out.extend_from_slice(a.atom(*puzzle).as_ref());
137                out.extend_from_slice(&amount.to_be_bytes());
138            }
139            Self::ParentAmount(parent, amount) => {
140                out.push(PARENTAMOUNT);
141                out.extend_from_slice(a.atom(*parent).as_ref());
142                out.extend_from_slice(&amount.to_be_bytes());
143            }
144            Self::ParentPuzzle(parent, puzzle) => {
145                out.push(PARENTPUZZLE);
146                out.extend_from_slice(a.atom(*parent).as_ref());
147                out.extend_from_slice(a.atom(*puzzle).as_ref());
148            }
149            Self::None => {
150                out.push(0);
151            }
152        }
153    }
154}
155
156pub struct Message {
157    pub src: SpendId,
158    pub dst: SpendId,
159    pub msg: NodePtr,
160    pub counter: i8,
161}
162
163impl Message {
164    pub fn make_key(&self, a: &Allocator) -> Vec<u8> {
165        let mut key = Vec::<u8>::with_capacity((1 + 32 + 32) * 2 + 32);
166        self.src.make_key(&mut key, a);
167        self.dst.make_key(&mut key, a);
168        key.extend_from_slice(a.atom(self.msg).as_ref());
169        key
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176    use hex_literal::hex;
177    use rstest::rstest;
178
179    const BUF0: [u8; 32] = hex!("0000000000000000000000000000000000000000000000000000000000000000");
180    const BUF1: [u8; 32] = hex!("0101010101010101010101010101010101010101010101010101010101010101");
181    const BUF2: [u8; 32] = hex!("0202020202020202020202020202020202020202020202020202020202020202");
182
183    #[rstest]
184    #[case(0b000, "00")]
185    #[case(0b001, "010000000000000539")]
186    #[case(
187        0b010,
188        "020101010101010101010101010101010101010101010101010101010101010101"
189    )]
190    #[case(
191        0b100,
192        "040000000000000000000000000000000000000000000000000000000000000000"
193    )]
194    #[case(0b110, "0600000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101")]
195    #[case(
196        0b111,
197        "070202020202020202020202020202020202020202020202020202020202020202"
198    )]
199    #[case(
200        0b011,
201        "0301010101010101010101010101010101010101010101010101010101010101010000000000000539"
202    )]
203    #[case(
204        0b101,
205        "0500000000000000000000000000000000000000000000000000000000000000000000000000000539"
206    )]
207    fn test_from_self(#[case] mode: u8, #[case] expected: &str) {
208        let mut a = Allocator::new();
209        let parent = a.new_atom(&BUF0).unwrap();
210        let puzzle = a.new_atom(&BUF1).unwrap();
211        let coin_id = Arc::<Bytes32>::new(Bytes32::new(BUF2));
212        let src = SpendId::from_self(mode, parent, puzzle, 1337, &coin_id).unwrap();
213
214        let mut key = Vec::<u8>::new();
215        src.make_key(&mut key, &a);
216        assert_eq!(key, hex::decode(expected).unwrap());
217    }
218
219    #[rstest]
220    #[case(0b000, "00")]
221    #[case(0b001, "010000000000000539")]
222    #[case(
223        0b010,
224        "020101010101010101010101010101010101010101010101010101010101010101"
225    )]
226    #[case(
227        0b100,
228        "040000000000000000000000000000000000000000000000000000000000000000"
229    )]
230    #[case(0b110, "0600000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101")]
231    #[case(
232        0b111,
233        "070202020202020202020202020202020202020202020202020202020202020202"
234    )]
235    #[case(
236        0b011,
237        "0301010101010101010101010101010101010101010101010101010101010101010000000000000539"
238    )]
239    #[case(
240        0b101,
241        "0500000000000000000000000000000000000000000000000000000000000000000000000000000539"
242    )]
243    fn test_parse(#[case] mode: u8, #[case] expected: &str) {
244        let mut a = Allocator::new();
245        let mut args = NodePtr::NIL;
246        if mode == COINID {
247            let value = a.new_atom(&BUF2).unwrap();
248            args = a.new_pair(value, args).unwrap();
249        } else {
250            if (mode & AMOUNT) != 0 {
251                let value = a.new_small_number(1337).unwrap();
252                args = a.new_pair(value, args).unwrap();
253            }
254            if (mode & PUZZLE) != 0 {
255                let value = a.new_atom(&BUF1).unwrap();
256                args = a.new_pair(value, args).unwrap();
257            }
258            if (mode & PARENT) != 0 {
259                let value = a.new_atom(&BUF0).unwrap();
260                args = a.new_pair(value, args).unwrap();
261            }
262        }
263        let src = SpendId::parse(&a, &mut args, mode).unwrap();
264        // ensure we parsed all arguments
265        assert!(a.atom_eq(args, NodePtr::NIL));
266
267        let mut key = Vec::<u8>::new();
268        src.make_key(&mut key, &a);
269        assert_eq!(key, hex::decode(expected).unwrap());
270    }
271}