avalanche_types/platformvm/txs/
mod.rs

1pub mod add_permissionless_validator;
2pub mod add_subnet_validator;
3pub mod add_validator;
4pub mod create_chain;
5pub mod create_subnet;
6pub mod export;
7pub mod import;
8pub mod status;
9
10use std::cmp::Ordering;
11
12use crate::{
13    codec::{self, serde::hex_0x_bytes::Hex0xBytes},
14    ids::{self, node},
15    key,
16    txs::transferable,
17};
18use serde::{Deserialize, Serialize};
19use serde_with::serde_as;
20
21/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgettx>
22#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)]
23pub struct Tx {
24    #[serde(rename = "unsignedTx")]
25    pub unsigned_tx: UnsignedTx,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub credentials: Option<Vec<key::secp256k1::txs::Credential>>,
28}
29
30/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgettx>
31#[serde_as]
32#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
33pub struct UnsignedTx {
34    #[serde(rename = "networkID")]
35    pub network_id: u32,
36    #[serde(rename = "blockchainID")]
37    pub blockchain_id: ids::Id,
38
39    #[serde(rename = "outputs")]
40    pub transferable_outputs: Option<Vec<transferable::Output>>,
41    #[serde(rename = "inputs")]
42    pub transferable_inputs: Option<Vec<transferable::Input>>,
43
44    #[serde(rename = "owner")]
45    pub output_owners: key::secp256k1::txs::OutputOwners,
46
47    #[serde_as(as = "Option<Hex0xBytes>")]
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub memo: Option<Vec<u8>>,
50}
51
52impl Default for UnsignedTx {
53    fn default() -> Self {
54        Self {
55            network_id: 0,
56            blockchain_id: ids::Id::empty(),
57            transferable_outputs: None,
58            transferable_inputs: None,
59            output_owners: key::secp256k1::txs::OutputOwners::default(),
60            memo: None,
61        }
62    }
63}
64
65/// RUST_LOG=debug cargo test --package avalanche-types --lib -- platformvm::txs::test_json_deserialize --exact --show-output
66#[test]
67fn test_json_deserialize() {
68    let parsed_tx: Tx = serde_json::from_str(
69        "
70    
71    {
72        \"unsignedTx\": {
73            \"networkID\": 1000000,
74            \"blockchainID\": \"11111111111111111111111111111111LpoYY\",
75            \"outputs\": [
76                {
77                    \"assetID\": \"u8aaQ7MxyW32iHuP2xMXgYPrWYAsSbh8RJV9C6p1UeuGvqR3\",
78                    \"fxID\": \"spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ\",
79                    \"output\": {
80                        \"addresses\": [
81                            \"P-custom12szthht8tnl455u4mz3ns3nvvkel8ezvw2n8cx\"
82                        ],
83                        \"amount\": 245952587549460688,
84                        \"locktime\": 0,
85                        \"threshold\": 1
86                    }
87                }
88            ],
89            \"inputs\": [
90                {
91                    \"txID\": \"nN5QsURgEpM8D3e9q8FonS4EE13mnaBDtnQmgSwwUfBZ6FSW1\",
92                    \"outputIndex\": 0,
93                    \"assetID\": \"u8aaQ7MxyW32iHuP2xMXgYPrWYAsSbh8RJV9C6p1UeuGvqR3\",
94                    \"fxID\": \"spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ\",
95                    \"input\": {
96                        \"amount\": 245952587649460688,
97                        \"signatureIndices\": [
98                            0
99                        ]
100                    }
101                }
102            ],
103            \"memo\": \"0x\",
104            \"owner\": {
105                \"addresses\": [
106                    \"P-custom12szthht8tnl455u4mz3ns3nvvkel8ezvw2n8cx\"
107                ],
108                \"locktime\": 0,
109                \"threshold\": 1
110            }
111        },
112        \"credentials\": [
113            {
114                \"signatures\": [
115                    \"0xcb356822dc8990672b5777ec50b57da91baf572240e7d4e9e38f26ec9dbdfd8e376fdc5f30769b842668cd8d81bd71db926dfbe326585137d363566ee500369f01\"
116                ]
117            }
118        ]
119    }
120    
121    ",
122    )
123    .unwrap();
124
125    println!("{:?}", parsed_tx);
126
127    assert_eq!(parsed_tx.unsigned_tx.network_id, 1000000);
128    assert_eq!(
129        parsed_tx.unsigned_tx.transferable_outputs.clone().unwrap()[0]
130            .transfer_output
131            .clone()
132            .unwrap()
133            .amount,
134        245952587549460688
135    );
136    assert_eq!(
137        parsed_tx.unsigned_tx.transferable_outputs.clone().unwrap()[0]
138            .transfer_output
139            .clone()
140            .unwrap()
141            .output_owners
142            .threshold,
143        1
144    );
145}
146
147/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#StakeableLockIn>
148#[derive(Debug, Serialize, Deserialize, Eq, Clone, Default)]
149pub struct StakeableLockIn {
150    pub locktime: u64,
151    pub transfer_input: key::secp256k1::txs::transfer::Input,
152}
153
154impl StakeableLockIn {
155    pub fn type_name() -> String {
156        "platformvm.StakeableLockIn".to_string()
157    }
158
159    pub fn type_id() -> u32 {
160        *(codec::P_TYPES.get(&Self::type_name()).unwrap()) as u32
161    }
162}
163
164impl Ord for StakeableLockIn {
165    fn cmp(&self, other: &StakeableLockIn) -> Ordering {
166        self.locktime
167            .cmp(&(other.locktime)) // returns when "locktime"s are not Equal
168            .then_with(
169                || self.transfer_input.cmp(&other.transfer_input), // if "locktime"s are Equal, compare "transfer_input"
170            )
171    }
172}
173
174impl PartialOrd for StakeableLockIn {
175    fn partial_cmp(&self, other: &StakeableLockIn) -> Option<Ordering> {
176        Some(self.cmp(other))
177    }
178}
179
180impl PartialEq for StakeableLockIn {
181    fn eq(&self, other: &StakeableLockIn) -> bool {
182        self.cmp(other) == Ordering::Equal
183    }
184}
185
186/// RUST_LOG=debug cargo test --package avalanche-types --lib -- platformvm::txs::test_sort_stakeable_lock_ins --exact --show-output
187#[test]
188fn test_sort_stakeable_lock_ins() {
189    let mut ins: Vec<StakeableLockIn> = Vec::new();
190    for i in (0..10).rev() {
191        ins.push(StakeableLockIn {
192            locktime: i as u64,
193            transfer_input: key::secp256k1::txs::transfer::Input {
194                amount: 10,
195                sig_indices: vec![i as u32, 1, 2, 3, 4, 5, 6, 7, 8, 9],
196            },
197        });
198        ins.push(StakeableLockIn {
199            locktime: i as u64,
200            transfer_input: key::secp256k1::txs::transfer::Input {
201                amount: 5,
202                sig_indices: vec![i as u32, 2, 2, 3, 4, 5, 6, 7, 8, 9, 9],
203            },
204        });
205        ins.push(StakeableLockIn {
206            locktime: i as u64,
207            transfer_input: key::secp256k1::txs::transfer::Input {
208                amount: 5,
209                sig_indices: vec![i as u32, 2, 2, 3, 4, 5, 6, 7, 8, 9],
210            },
211        });
212        ins.push(StakeableLockIn {
213            locktime: i as u64,
214            transfer_input: key::secp256k1::txs::transfer::Input {
215                amount: 5,
216                sig_indices: vec![i as u32, 1, 2, 3, 4, 5, 6, 7, 8, 9],
217            },
218        });
219    }
220    assert!(!cmp_manager::is_sorted_and_unique(&ins));
221    ins.sort();
222
223    let mut sorted_ins: Vec<StakeableLockIn> = Vec::new();
224    for i in 0..10 {
225        sorted_ins.push(StakeableLockIn {
226            locktime: i as u64,
227            transfer_input: key::secp256k1::txs::transfer::Input {
228                amount: 5,
229                sig_indices: vec![i as u32, 1, 2, 3, 4, 5, 6, 7, 8, 9],
230            },
231        });
232        sorted_ins.push(StakeableLockIn {
233            locktime: i as u64,
234            transfer_input: key::secp256k1::txs::transfer::Input {
235                amount: 5,
236                sig_indices: vec![i as u32, 2, 2, 3, 4, 5, 6, 7, 8, 9],
237            },
238        });
239        sorted_ins.push(StakeableLockIn {
240            locktime: i as u64,
241            transfer_input: key::secp256k1::txs::transfer::Input {
242                amount: 5,
243                sig_indices: vec![i as u32, 2, 2, 3, 4, 5, 6, 7, 8, 9, 9],
244            },
245        });
246        sorted_ins.push(StakeableLockIn {
247            locktime: i as u64,
248            transfer_input: key::secp256k1::txs::transfer::Input {
249                amount: 10,
250                sig_indices: vec![i as u32, 1, 2, 3, 4, 5, 6, 7, 8, 9],
251            },
252        });
253    }
254    assert!(cmp_manager::is_sorted_and_unique(&sorted_ins));
255    assert_eq!(ins, sorted_ins);
256}
257
258/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#StakeableLockOut>
259#[derive(Debug, Serialize, Deserialize, Eq, Clone, Default)]
260pub struct StakeableLockOut {
261    pub locktime: u64,
262    pub transfer_output: key::secp256k1::txs::transfer::Output,
263}
264
265impl StakeableLockOut {
266    pub fn type_name() -> String {
267        "platformvm.StakeableLockOut".to_string()
268    }
269
270    pub fn type_id() -> u32 {
271        *(codec::P_TYPES.get(&Self::type_name()).unwrap()) as u32
272    }
273}
274
275impl Ord for StakeableLockOut {
276    fn cmp(&self, other: &StakeableLockOut) -> Ordering {
277        self.locktime
278            .cmp(&(other.locktime)) // returns when "locktime"s are not Equal
279            .then_with(
280                || self.transfer_output.cmp(&other.transfer_output), // if "locktime"s are Equal, compare "transfer_output"
281            )
282    }
283}
284
285impl PartialOrd for StakeableLockOut {
286    fn partial_cmp(&self, other: &StakeableLockOut) -> Option<Ordering> {
287        Some(self.cmp(other))
288    }
289}
290
291impl PartialEq for StakeableLockOut {
292    fn eq(&self, other: &StakeableLockOut) -> bool {
293        self.cmp(other) == Ordering::Equal
294    }
295}
296
297/// RUST_LOG=debug cargo test --package avalanche-types --lib -- platformvm::txs::test_sort_stakeable_lock_outs --exact --show-output
298#[test]
299fn test_sort_stakeable_lock_outs() {
300    use crate::ids::short;
301    let mut outs: Vec<StakeableLockOut> = Vec::new();
302    for i in (0..10).rev() {
303        outs.push(StakeableLockOut {
304            locktime: i as u64,
305            transfer_output: key::secp256k1::txs::transfer::Output {
306                amount: (i + 1) as u64,
307                output_owners: key::secp256k1::txs::OutputOwners {
308                    locktime: (i + 1) as u64,
309                    threshold: (i + 1) as u32,
310                    addresses: vec![
311                        short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5]),
312                        short::Id::from_slice(&[i as u8, 2, 2, 3, 4, 5]),
313                    ],
314                },
315            },
316        });
317        outs.push(StakeableLockOut {
318            locktime: i as u64,
319            transfer_output: key::secp256k1::txs::transfer::Output {
320                amount: (i + 1) as u64,
321                output_owners: key::secp256k1::txs::OutputOwners {
322                    locktime: (i + 1) as u64,
323                    threshold: (i + 1) as u32,
324                    addresses: vec![
325                        short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5]),
326                        short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5]),
327                    ],
328                },
329            },
330        });
331        outs.push(StakeableLockOut {
332            locktime: i as u64,
333            transfer_output: key::secp256k1::txs::transfer::Output {
334                amount: (i + 1) as u64,
335                output_owners: key::secp256k1::txs::OutputOwners {
336                    locktime: (i + 1) as u64,
337                    threshold: (i + 1) as u32,
338                    addresses: vec![short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5])],
339                },
340            },
341        });
342        outs.push(StakeableLockOut {
343            locktime: i as u64,
344            transfer_output: key::secp256k1::txs::transfer::Output {
345                amount: (i + 1) as u64,
346                output_owners: key::secp256k1::txs::OutputOwners {
347                    locktime: (i + 1) as u64,
348                    threshold: i as u32,
349                    addresses: vec![short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5])],
350                },
351            },
352        });
353        outs.push(StakeableLockOut {
354            locktime: i as u64,
355            transfer_output: key::secp256k1::txs::transfer::Output {
356                amount: (i + 1) as u64,
357                output_owners: key::secp256k1::txs::OutputOwners {
358                    locktime: i as u64,
359                    threshold: i as u32,
360                    addresses: vec![short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5])],
361                },
362            },
363        });
364        outs.push(StakeableLockOut {
365            locktime: i as u64,
366            transfer_output: key::secp256k1::txs::transfer::Output {
367                amount: i as u64,
368                output_owners: key::secp256k1::txs::OutputOwners {
369                    locktime: i as u64,
370                    threshold: i as u32,
371                    addresses: vec![short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5])],
372                },
373            },
374        });
375    }
376    assert!(!cmp_manager::is_sorted_and_unique(&outs));
377    outs.sort();
378
379    let mut sorted_outs: Vec<StakeableLockOut> = Vec::new();
380    for i in 0..10 {
381        sorted_outs.push(StakeableLockOut {
382            locktime: i as u64,
383            transfer_output: key::secp256k1::txs::transfer::Output {
384                amount: i as u64,
385                output_owners: key::secp256k1::txs::OutputOwners {
386                    locktime: i as u64,
387                    threshold: i as u32,
388                    addresses: vec![short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5])],
389                },
390            },
391        });
392        sorted_outs.push(StakeableLockOut {
393            locktime: i as u64,
394            transfer_output: key::secp256k1::txs::transfer::Output {
395                amount: (i + 1) as u64,
396                output_owners: key::secp256k1::txs::OutputOwners {
397                    locktime: i as u64,
398                    threshold: i as u32,
399                    addresses: vec![short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5])],
400                },
401            },
402        });
403        sorted_outs.push(StakeableLockOut {
404            locktime: i as u64,
405            transfer_output: key::secp256k1::txs::transfer::Output {
406                amount: (i + 1) as u64,
407                output_owners: key::secp256k1::txs::OutputOwners {
408                    locktime: (i + 1) as u64,
409                    threshold: i as u32,
410                    addresses: vec![short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5])],
411                },
412            },
413        });
414        sorted_outs.push(StakeableLockOut {
415            locktime: i as u64,
416            transfer_output: key::secp256k1::txs::transfer::Output {
417                amount: (i + 1) as u64,
418                output_owners: key::secp256k1::txs::OutputOwners {
419                    locktime: (i + 1) as u64,
420                    threshold: (i + 1) as u32,
421                    addresses: vec![short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5])],
422                },
423            },
424        });
425        sorted_outs.push(StakeableLockOut {
426            locktime: i as u64,
427            transfer_output: key::secp256k1::txs::transfer::Output {
428                amount: (i + 1) as u64,
429                output_owners: key::secp256k1::txs::OutputOwners {
430                    locktime: (i + 1) as u64,
431                    threshold: (i + 1) as u32,
432                    addresses: vec![
433                        short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5]),
434                        short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5]),
435                    ],
436                },
437            },
438        });
439        sorted_outs.push(StakeableLockOut {
440            locktime: i as u64,
441            transfer_output: key::secp256k1::txs::transfer::Output {
442                amount: (i + 1) as u64,
443                output_owners: key::secp256k1::txs::OutputOwners {
444                    locktime: (i + 1) as u64,
445                    threshold: (i + 1) as u32,
446                    addresses: vec![
447                        short::Id::from_slice(&[i as u8, 1, 2, 3, 4, 5]),
448                        short::Id::from_slice(&[i as u8, 2, 2, 3, 4, 5]),
449                    ],
450                },
451            },
452        });
453    }
454    assert!(cmp_manager::is_sorted_and_unique(&sorted_outs));
455    assert_eq!(outs, sorted_outs);
456}
457
458/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#Validator>
459/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#AddValidatorArgs>
460/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm/api#Staker>
461#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
462pub struct Validator {
463    pub node_id: node::Id,
464    pub start: u64,
465    pub end: u64,
466    pub weight: u64,
467}
468
469impl Default for Validator {
470    fn default() -> Self {
471        Self {
472            node_id: node::Id::empty(),
473            start: 0,
474            end: 0,
475            weight: 0,
476        }
477    }
478}