trn_pact/types/
base.rs

1// Copyright 2019 Centrality Investments Limited
2// This file is part of Pact.
3//
4// Licensed under the Apache License v2.0;
5// you may not use this file except in compliance with the License.
6// Unless required by applicable law or agreed to in writing, software
7// distributed under the License is distributed on an "AS IS" BASIS,
8// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9// See the License for the specific language governing permissions and
10// limitations under the License.
11
12// You should have received a copy of the Apache License v2.0
13// along with Pact. If not, see:
14//   <https://futureverse.com/licenses/apachev2.txt>
15
16//!
17//! Types in the pact interpreter aka "PactType"s
18//!
19use alloc::vec::Vec;
20use bit_reverse::ParallelReverse;
21
22/// A string-like type
23#[cfg_attr(feature = "std", derive(Debug))]
24#[derive(PartialEq, PartialOrd, Clone)]
25pub struct StringLike(pub Vec<u8>);
26
27/// A numeric type
28#[cfg_attr(feature = "std", derive(Debug))]
29#[derive(PartialEq, PartialOrd, Clone)]
30pub struct Numeric(pub u64);
31
32/// Over-arching pact type system
33#[cfg_attr(feature = "std", derive(Debug))]
34#[derive(Clone, PartialEq)]
35pub enum PactType {
36    StringLike(StringLike),
37    Numeric(Numeric),
38    List(Vec<PactType>),
39}
40
41impl PactType {
42    /// Encode the PactType into `buf`c
43    pub fn encode(&self, buf: &mut Vec<u8>) {
44        match self {
45            PactType::StringLike(s) => {
46                buf.push(0);
47                buf.push((s.0.len() as u8).swap_bits());
48                buf.extend(s.0.iter());
49            }
50            PactType::Numeric(n) => {
51                buf.push(1.swap_bits());
52                // only supporting 64-bit numeric here.
53                buf.push(8.swap_bits());
54                for b in n.0.to_le_bytes().iter() {
55                    buf.push(b.swap_bits())
56                }
57            }
58            PactType::List(l) => {
59                let mut buf_elements: Vec<u8> = Vec::<u8>::default();
60                for element in l {
61                    match element {
62                        PactType::StringLike(_) => element.encode(&mut buf_elements),
63                        PactType::Numeric(_) => element.encode(&mut buf_elements),
64                        _ => {} // element not supported
65                    }
66                }
67
68                buf.push(2.swap_bits());
69                buf.push((buf_elements.len() as u8).swap_bits());
70                buf.append(&mut buf_elements);
71
72                //panic!("todo");
73            }
74        };
75    }
76    /// Decode a pact type from the given buffer
77    /// Returns (decoded type, bytes read) or error on failure
78    pub fn decode(buf: Vec<u8>) -> Result<(Self, usize), &'static str> {
79        // Check type header bytes
80        match buf.len() {
81            0 => return Err("missing type ID byte"),
82            1 => return Err("missing type length byte"),
83            _ => (),
84        };
85
86        // 1 byte type ID + 1 byte length gives 2 offset
87        let mut read_offset = 2_usize;
88
89        // Read length byte
90        let data_length = buf[1].swap_bits() as usize;
91        if data_length > buf[read_offset..].len() {
92            return Err("type length > buffer length");
93        }
94
95        // Read type ID byte
96        match buf[0].swap_bits() {
97            0 => {
98                let read_length = read_offset + data_length;
99                let s = PactType::StringLike(StringLike(buf[read_offset..read_length].to_vec()));
100                Ok((s, read_length))
101            }
102            1 => {
103                let data_length = buf[1].swap_bits() as usize;
104                if data_length != 8 {
105                    return Err("implementation only supports 64-bit numerics");
106                }
107
108                let n = PactType::Numeric(Numeric(u64::from_le_bytes([
109                    buf[2].swap_bits(),
110                    buf[3].swap_bits(),
111                    buf[4].swap_bits(),
112                    buf[5].swap_bits(),
113                    buf[6].swap_bits(),
114                    buf[7].swap_bits(),
115                    buf[8].swap_bits(),
116                    buf[9].swap_bits(),
117                ])));
118                Ok((n, 10usize))
119            }
120            2 => {
121                let mut values: Vec<PactType> = Vec::<PactType>::default();
122                let mut remaining_length = data_length;
123
124                while remaining_length > 0 {
125                    let (new_value, offset) = Self::decode(buf[read_offset..].to_vec())?;
126                    read_offset = read_offset + offset;
127                    remaining_length = remaining_length
128                        .checked_sub(offset)
129                        .ok_or("list length overflow")?;
130                    values.push(new_value);
131                }
132                Ok((PactType::List(values), read_offset))
133            }
134            _ => Err("unsupported type ID"),
135        }
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn it_encodes_string_like() {
145        let s = PactType::StringLike(StringLike(b"hello world".to_vec()));
146        let buf: &mut Vec<u8> = &mut Vec::new();
147        s.encode(buf);
148        assert_eq!(buf[0], 0);
149        assert_eq!(buf[1].swap_bits(), 11);
150        assert_eq!(&buf[2..], "hello world".as_bytes());
151    }
152
153    #[test]
154    fn it_encodes_numeric() {
155        let n = PactType::Numeric(Numeric(123));
156        let buf: &mut Vec<u8> = &mut Vec::new();
157        n.encode(buf);
158
159        let mut expected: Vec<u8> = vec![1, 8, 123, 0, 0, 0, 0, 0, 0, 0];
160        expected = expected.into_iter().map(|b| b.swap_bits()).collect(); // convert to LE bit orders
161        assert_eq!(buf, &expected);
162    }
163
164    #[test]
165    fn it_encodes_string_list() {
166        let l = PactType::List(vec![
167            PactType::StringLike(StringLike(b"we're no".to_vec())),
168            PactType::StringLike(StringLike(b"strangers".to_vec())),
169            PactType::StringLike(StringLike(b"to love".to_vec())),
170        ]);
171        let buf: &mut Vec<u8> = &mut Vec::new();
172        l.encode(buf);
173
174        assert_eq!(buf[0].swap_bits(), 2);
175        assert_eq!(buf[1].swap_bits(), 30);
176        assert_eq!(buf[2].swap_bits(), 0);
177        assert_eq!(buf[3].swap_bits(), 8);
178        assert_eq!(&buf[4..12], b"we're no");
179        assert_eq!(buf[12].swap_bits(), 0);
180        assert_eq!(buf[13].swap_bits(), 9);
181        assert_eq!(&buf[14..23], b"strangers");
182        assert_eq!(buf[23].swap_bits(), 0);
183        assert_eq!(buf[24].swap_bits(), 7);
184        assert_eq!(&buf[25..32], b"to love");
185    }
186
187    #[test]
188    fn it_encodes_numeric_list() {
189        let l = PactType::List(vec![
190            PactType::Numeric(Numeric(0x0123456789abcdef)),
191            PactType::Numeric(Numeric(0xfedcba9876543210)),
192        ]);
193        let buf: &mut Vec<u8> = &mut Vec::new();
194        l.encode(buf);
195
196        let list_header: Vec<u8> = vec![2, 20];
197        let item_0: Vec<u8> = vec![1, 8, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01];
198        let item_1: Vec<u8> = vec![1, 8, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe];
199        let mut expected: Vec<u8> = [list_header, item_0, item_1].concat();
200        expected = expected.into_iter().map(|b| b.swap_bits()).collect(); // convert to LE bit orders
201        assert_eq!(buf, &expected);
202    }
203
204    #[test]
205    fn it_decodes_string_like() {
206        let mut buf = vec![0, 11];
207        buf = buf.into_iter().map(|b| b.swap_bits()).collect(); // convert to LE bit orders
208        buf.extend("hello world".as_bytes());
209        let (string_type, bytes_read) = PactType::decode(buf).expect("it decodes");
210
211        assert_eq!(
212            string_type,
213            PactType::StringLike(StringLike(b"hello world".to_vec())),
214        );
215
216        assert_eq!(bytes_read, 13usize,);
217    }
218
219    #[test]
220    fn it_decodes_numeric() {
221        let mut encoded: Vec<u8> = vec![1, 8, 123, 0, 0, 0, 0, 0, 0, 0];
222        encoded = encoded.into_iter().map(|b| b.swap_bits()).collect(); // convert to LE bit orders
223        let (numeric_type, bytes_read) = PactType::decode(encoded).expect("it decodes");
224
225        assert_eq!(numeric_type, PactType::Numeric(Numeric(123)));
226        assert_eq!(10usize, bytes_read,);
227    }
228
229    #[test]
230    fn it_decodes_string_lists() {
231        let list_header: Vec<u8> = vec![2, 35].into_iter().map(|b| b.swap_bits()).collect();
232        let str0_header: Vec<u8> = vec![0, 8].into_iter().map(|b| b.swap_bits()).collect();
233        let str1_header: Vec<u8> = vec![0, 9].into_iter().map(|b| b.swap_bits()).collect();
234        let str2_header: Vec<u8> = vec![0, 6].into_iter().map(|b| b.swap_bits()).collect();
235        let str3_header: Vec<u8> = vec![0, 4].into_iter().map(|b| b.swap_bits()).collect();
236
237        let buf: Vec<u8> = [
238            list_header,
239            str0_header,
240            b"you know".to_vec(),
241            str1_header,
242            b"the rules".to_vec(),
243            str2_header,
244            b"and so".to_vec(),
245            str3_header,
246            b"do I".to_vec(),
247        ]
248        .concat();
249
250        let (list_type, bytes_read) = PactType::decode(buf).expect("it decodes");
251
252        let expected = PactType::List(vec![
253            PactType::StringLike(StringLike(b"you know".to_vec())),
254            PactType::StringLike(StringLike(b"the rules".to_vec())),
255            PactType::StringLike(StringLike(b"and so".to_vec())),
256            PactType::StringLike(StringLike(b"do I".to_vec())),
257        ]);
258
259        assert_eq!(list_type, expected,);
260
261        assert_eq!(bytes_read, 37usize);
262    }
263
264    #[test]
265    fn it_decodes_numeric_lists() {
266        let list_header: Vec<u8> = vec![2, 20];
267        let num_header: Vec<u8> = vec![1, 8];
268
269        let buf: Vec<u8> = [
270            list_header,
271            num_header.clone(),
272            vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef],
273            num_header,
274            vec![0xfe, 0xed, 0xfe, 0xed, 0xfe, 0xed, 0xfe, 0xed],
275        ]
276        .concat()
277        .into_iter()
278        .map(|b| b.swap_bits())
279        .collect();
280
281        let (list_type, bytes_read) = PactType::decode(buf).expect("it decodes");
282
283        let expected = PactType::List(vec![
284            PactType::Numeric(Numeric(0xefcd_ab89_6745_2301)),
285            PactType::Numeric(Numeric(0xedfe_edfe_edfe_edfe)),
286        ]);
287
288        assert_eq!(list_type, expected,);
289
290        assert_eq!(bytes_read, 22usize);
291    }
292
293    #[test]
294    fn it_fails_decode_list_with_bad_length() {
295        let list_header: Vec<u8> = vec![2, 20];
296        let buf: Vec<u8> = [
297            list_header,
298            vec![1, 8],
299            vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef],
300        ]
301        .concat()
302        .into_iter()
303        .map(|b| b.swap_bits())
304        .collect();
305
306        assert_eq!(PactType::decode(buf), Err("type length > buffer length"));
307
308        let list_header: Vec<u8> = vec![2, 5];
309        let buf: Vec<u8> = [
310            list_header,
311            vec![1, 8],
312            vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef],
313        ]
314        .concat()
315        .into_iter()
316        .map(|b| b.swap_bits())
317        .collect();
318
319        assert_eq!(PactType::decode(buf), Err("list length overflow"));
320    }
321
322    #[test]
323    fn it_fails_with_missing_type_id() {
324        assert_eq!(PactType::decode(vec![]), Err("missing type ID byte"));
325    }
326
327    #[test]
328    fn it_fails_with_missing_type_length() {
329        assert_eq!(
330            PactType::decode([0].to_vec()),
331            Err("missing type length byte")
332        );
333    }
334
335    #[test]
336    #[should_panic(expected = "type length > buffer length")]
337    fn it_fails_with_short_string_like() {
338        PactType::decode([0, 11].to_vec()).unwrap();
339    }
340
341    #[test]
342    #[should_panic(expected = "implementation only supports 64-bit numerics")]
343    fn it_fails_with_u128_numeric() {
344        PactType::decode(
345            [
346                1.swap_bits(),
347                16.swap_bits(),
348                0,
349                0,
350                0,
351                0,
352                0,
353                0,
354                0,
355                0,
356                0,
357                0,
358                0,
359                0,
360                0,
361                0,
362                0,
363                0,
364            ]
365            .to_vec(),
366        )
367        .unwrap();
368    }
369}