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#[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#[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#[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#[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)) .then_with(
169 || self.transfer_input.cmp(&other.transfer_input), )
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#[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#[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)) .then_with(
280 || self.transfer_output.cmp(&other.transfer_output), )
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#[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#[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}