Skip to main content

avalanche_types/avm/txs/
vertex.rs

1//! Vertex types used in the Avalanche X-chain.
2use crate::{errors::Result, ids, packer::Packer, txs::raw};
3
4/// Vertex represents a set of transactions for Avalanche X-chain.
5///
6/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/snow/engine/avalanche/vertex#Build>
7#[derive(Debug, Clone, Eq, PartialEq)]
8pub struct Vertex {
9    pub codec_version: u16,
10    pub chain_id: ids::Id,
11    pub height: u64,
12    pub epoch: u32,
13    pub parent_ids: Vec<ids::Id>,
14    pub txs: Vec<Vec<u8>>,
15}
16
17impl Packer {
18    /// Encodes vertex fields with codec version and packer.
19    ///
20    /// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/snow/engine/avalanche/vertex#Build>
21    pub fn pack_vertex(&self, vtx: &mut Vertex) -> Result<()> {
22        // sort "parent_ids"
23        // ref. "ids.SortIDs"
24        vtx.parent_ids.sort();
25
26        // sort "txs" by SHA256 hashes
27        // ref. "SortHashOf"
28        vtx.txs.sort_by(|a, b| {
29            (raw::Data::from_slice(a.as_ref())).cmp(&raw::Data::from_slice(b.as_ref()))
30        });
31
32        self.pack_u16(vtx.codec_version)?;
33        self.pack_bytes(vtx.chain_id.as_ref())?;
34        self.pack_u64(vtx.height)?;
35        self.pack_u32(vtx.epoch)?;
36
37        self.pack_u32(vtx.parent_ids.len() as u32)?;
38        for id in vtx.parent_ids.iter() {
39            self.pack_bytes(id.as_ref())?;
40        }
41
42        self.pack_u32(vtx.txs.len() as u32)?;
43        for tx in vtx.txs.iter() {
44            self.pack_bytes_with_header(tx.as_ref())?;
45        }
46
47        Ok(())
48    }
49
50    /// Unpacks the vertex.
51    ///
52    /// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/snow/engine/avalanche/vertex#Build>
53    pub fn unpack_vertex(&self) -> Result<Vertex> {
54        let codec_version = self.unpack_u16()?;
55
56        let chain_id = self.unpack_bytes(ids::LEN)?;
57        let chain_id = ids::Id::from_slice(chain_id.as_ref());
58
59        let height = self.unpack_u64()?;
60        let epoch = self.unpack_u32()?;
61
62        let parent_ids_size = self.unpack_u32()?;
63        let mut parent_ids: Vec<ids::Id> = Vec::new();
64        for _ in 0..parent_ids_size {
65            let parent_id = self.unpack_bytes(ids::LEN)?;
66            let parent_id = ids::Id::from_slice(parent_id.as_ref());
67            parent_ids.push(parent_id);
68        }
69
70        let txs_size = self.unpack_u32()?;
71        let mut txs: Vec<Vec<u8>> = Vec::new();
72        for _ in 0..txs_size {
73            let tx_size = self.unpack_u32()?;
74            let tx = self.unpack_bytes(tx_size as usize)?;
75            txs.push(tx);
76        }
77
78        Ok(Vertex {
79            codec_version,
80            chain_id,
81            height,
82            epoch,
83            parent_ids,
84            txs,
85        })
86    }
87}
88
89/// RUST_LOG=debug cargo test --package avalanche-types --lib -- avm::txs::vertex::test_pack_and_unpack --exact --show-output
90#[test]
91fn test_pack_and_unpack() {
92    use bytes::BytesMut;
93
94    let mut vtx = Vertex {
95        codec_version: 0_u16,
96        chain_id: ids::Id::from_slice(&<Vec<u8>>::from([
97            0x3d, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
98            0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
99            0xc3, 0x2b, 0xff, 0x1d, 0x6d, 0xec, 0x47, 0x2b, 0x25, 0xcf, //
100            0x59, 0xa7,
101        ])),
102        height: 1234567_u64,
103        epoch: 0,
104
105        // to be sorted
106        parent_ids: vec![
107            ids::Id::from_slice(&<Vec<u8>>::from([
108                0x03, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
109                0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
110                0xc3, 0x2b, 0xff, 0x1d, 0x6d, 0xec, 0x47, 0x2b, 0x25, 0xcf, //
111                0x59, 0xa7,
112            ])),
113            ids::Id::from_slice(&<Vec<u8>>::from([
114                0x02, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
115                0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
116                0xc3, 0x2b, 0xff, 0x1d, 0x6d, 0xec, 0x47, 0x2b, 0x25, 0xcf, //
117                0x59, 0xa7,
118            ])),
119            ids::Id::from_slice(&<Vec<u8>>::from([
120                0x01, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
121                0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
122                0xc3, 0x2b, 0xff, 0x1d, 0x6d, 0xec, 0x47, 0x2b, 0x25, 0xcf, //
123                0x59, 0xa7,
124            ])),
125        ],
126
127        // to be sorted
128        txs: vec![
129            <Vec<u8>>::from([0x01]),
130            <Vec<u8>>::from([0x02]),
131            <Vec<u8>>::from([0x03]),
132        ],
133    };
134
135    let packer = Packer::new(1024, 0);
136    packer.pack_vertex(&mut vtx).unwrap();
137
138    let vtx_sorted = Vertex {
139        codec_version: 0_u16,
140        chain_id: ids::Id::from_slice(&<Vec<u8>>::from([
141            0x3d, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
142            0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
143            0xc3, 0x2b, 0xff, 0x1d, 0x6d, 0xec, 0x47, 0x2b, 0x25, 0xcf, //
144            0x59, 0xa7,
145        ])),
146        height: 1234567_u64,
147        epoch: 0,
148
149        // sorted
150        parent_ids: vec![
151            ids::Id::from_slice(&<Vec<u8>>::from([
152                0x01, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
153                0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
154                0xc3, 0x2b, 0xff, 0x1d, 0x6d, 0xec, 0x47, 0x2b, 0x25, 0xcf, //
155                0x59, 0xa7,
156            ])),
157            ids::Id::from_slice(&<Vec<u8>>::from([
158                0x02, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
159                0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
160                0xc3, 0x2b, 0xff, 0x1d, 0x6d, 0xec, 0x47, 0x2b, 0x25, 0xcf, //
161                0x59, 0xa7,
162            ])),
163            ids::Id::from_slice(&<Vec<u8>>::from([
164                0x03, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
165                0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
166                0xc3, 0x2b, 0xff, 0x1d, 0x6d, 0xec, 0x47, 0x2b, 0x25, 0xcf, //
167                0x59, 0xa7,
168            ])),
169        ],
170
171        // sorted
172        txs: vec![
173            <Vec<u8>>::from([0x03]),
174            <Vec<u8>>::from([0x01]),
175            <Vec<u8>>::from([0x02]),
176        ],
177    };
178    assert!(vtx == vtx_sorted);
179
180    let b = packer.take_bytes();
181    let b = BytesMut::from(&b[..]);
182
183    let packer = Packer::new(0, 0);
184    packer.set_bytes(&b);
185
186    let vtx_unpacked = packer.unpack_vertex().unwrap();
187    assert!(vtx == vtx_unpacked);
188}