xavax_avalanche/pvm/
tx_format_impl.rs

1use std::borrow::Borrow;
2use std::convert::TryInto;
3
4use super::tx_format::*;
5use crate::avm::tx_format::{SECP256K1TransferOutput, BaseTx, TransferableInput, ImportTx, ExportTx, Credential};
6use crate::encoding::cb58::encode_cb58;
7use crate::parser::byte_conversion::*;
8use crate::parser::parser_traits::Parser;
9use crate::primitives::address::Address;
10
11/* ----\\\0111100001100001011101100110000101111000_we_are_one\\\ --- NON-IMPORTANT-NOTE:
12    the parser_traits implementation for the pvm, read the note in parser_traits.rs for more info.
13
14    The reason the PVM transaction format is completely seperate to the AVM tx format is that there
15    are some extra data-types, and some which don't exist. Technically I could merge the two modules, but
16    I find seperating things this way to make things clearer.
17
18    Besides, even though some Txs might exist for both VMs, they usually still have different type_ids, such
19    as Export & Import transactions.
20
21    Those transactions aren't defined twice in the pvm::tx_format module, but instead imported from respective the AVM module.
22
23    I feel like this is the optimal solution, it might add some confusion, the API may change in the future slightly if this
24    design changes in-favor of a better one.
25
26    The platform vm seems to be in a state of development, there is little clarity on what some things
27    are and how they are used, nontheless the parser should do its job correctly unless I fked something
28    up...
29*/
30
31/* _________________________________________________ Outputs _________________________________________________ */
32
33impl Parser for SECP256K1OutputOwnersOutput {
34    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
35        let mut offset: usize = 0;
36
37        self.type_id = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
38        offset += 4;
39
40        self.locktime = extract_u64(raw_payload[offset..=(offset + 7)].borrow());
41        offset += 8;
42
43        self.threshold = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
44        offset += 4;
45
46        let address_num = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
47        offset += 4;
48
49        let mut index = 0;
50        while index < address_num {
51            self.addresses.push(Address{
52                address_bytes: raw_payload[offset..=(offset + 19)].try_into().expect("Slice with incorrect length!"),
53                serialized_address: None,
54            });
55            offset += 20;
56            index += 1;
57        }
58        match offset_to_change {
59            Some(v) => { *v += offset},
60            None => {}
61        }
62
63    }
64    fn to_bytes(&self) -> Vec<u8> {
65        let mut result: Vec<u8> = Vec::new();
66
67        result.extend_from_slice(&self.type_id.to_be_bytes());
68        result.extend_from_slice(&self.locktime.to_be_bytes());
69        result.extend_from_slice(&self.threshold.to_be_bytes());
70
71        result.extend_from_slice(&(self.addresses.len() as u32).to_be_bytes());
72
73        for i in &self.addresses {
74            result.extend_from_slice(&i.address_bytes);
75        }
76
77        result
78    }
79    fn to_cb58(&self) -> String {
80        encode_cb58(&self.to_bytes()[..])
81    }
82}
83
84impl Parser for TransferableOutput {
85    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
86        let mut offset: usize = 0;
87        
88        self.asset_id = raw_payload[offset..=(offset + 31)].try_into().expect("Slice with incorrect length!");
89        offset += 32;
90
91        let output_type: u32 = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
92        match output_type {
93            7=> {
94                let mut o: SECP256K1TransferOutput = SECP256K1TransferOutput::default();
95                o.from_bytes(&raw_payload[offset..], Some(&mut offset));
96                self.output = Outputs::SECP256K1TransferOutput(o);
97            }
98            11 => {
99                let mut o: SECP256K1OutputOwnersOutput = SECP256K1OutputOwnersOutput::default();
100                o.from_bytes(&raw_payload[offset..], Some(&mut offset));
101                self.output = Outputs::SECP256K1OutputOwnersOutput(o);
102
103            }
104            _=> {
105                panic!("Incorrect Type ID!")
106            }
107        }
108        match offset_to_change {
109            Some(v) => { *v += offset},
110            None => {}
111        }
112    }
113    fn to_bytes(&self) -> Vec<u8> {
114        let mut result: Vec<u8> = Vec::new();
115
116        result.extend_from_slice(&self.asset_id);
117
118        match &self.output {
119            Outputs::SECP256K1TransferOutput(x) => {
120                result.extend_from_slice(&x.to_bytes())
121            },
122            Outputs::SECP256K1OutputOwnersOutput(x) => {
123                result.extend_from_slice(&x.to_bytes())
124            },
125        }
126
127        result
128    }
129    fn to_cb58(&self) -> String {
130        encode_cb58(&self.to_bytes()[..])
131    }
132}
133
134/* _________________________________________________ FxID _________________________________________________ */
135
136impl Parser for FxID {
137    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
138        let mut offset: usize = 0;
139        self.fx_id = raw_payload[offset..(offset + 31)].to_vec();
140        offset += 32;
141        match offset_to_change {
142            Some(v) => { *v += offset},
143            None => {}
144        }
145    }
146    fn to_bytes(&self) -> Vec<u8> {
147        let mut result: Vec<u8> = Vec::new();
148        result.extend_from_slice(&self.fx_id);
149        result
150    }
151    fn to_cb58(&self) -> String {
152        encode_cb58(&self.to_bytes()[..])
153    }
154}
155
156
157/* _________________________________________________ Validator _________________________________________________ */
158
159impl Parser for Validator {
160    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
161        let mut offset: usize = 0;
162
163        self.node_id = raw_payload[offset..=(offset + 19)].try_into().expect("Slice with incorrect length!");
164        offset += 20;
165        self.start_time = extract_u64(raw_payload[offset..=(offset + 7)].borrow());
166        offset += 8;
167        self.endtime = extract_u64(raw_payload[offset..=(offset + 7)].borrow());
168        offset += 8;
169        self.weight = extract_u64(raw_payload[offset..=(offset + 7)].borrow());
170        offset += 8;
171
172        match offset_to_change {
173            Some(v) => { *v += offset},
174            None => {}
175        }
176    }
177
178    fn to_bytes(&self) -> Vec<u8> {
179        let mut result: Vec<u8> = Vec::new();
180        result.extend_from_slice(&self.node_id);
181        result.extend_from_slice(&self.start_time.to_be_bytes());
182        result.extend_from_slice(&self.endtime.to_be_bytes());
183        result.extend_from_slice(&self.weight.to_be_bytes());
184        result
185    }
186
187    fn to_cb58(&self) -> String {
188        encode_cb58(&self.to_bytes()[..])
189    }
190}
191
192/* _________________________________________________ Stake _________________________________________________ */
193
194
195impl Parser for Stake {
196    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
197        let mut offset: usize = 0;
198
199        let output_len: u32 = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
200        offset += 4;
201
202        let mut index: usize = 0;
203        while index < output_len as usize{
204            let mut output: TransferableOutput = TransferableOutput::default();
205            output.from_bytes(&raw_payload[offset..], Some(&mut offset));
206            self.locked_outs.push(output);
207            index += 1;
208        }
209
210        match offset_to_change {
211            Some(v) => { *v += offset},
212            None => {}
213        }
214    }
215    fn to_bytes(&self) -> Vec<u8> {
216        let mut result: Vec<u8> = Vec::new();
217        for l_o in &self.locked_outs {
218            result.extend_from_slice(&l_o.to_bytes());
219        }
220        result
221    }
222    fn to_cb58(&self) -> String {
223        encode_cb58(&self.to_bytes()[..])
224    }
225}
226
227/* _________________________________________________ Subnet Auth _________________________________________________ */
228
229impl Parser for SubnetAuth {
230    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
231        let mut offset: usize = 0;
232
233        self.type_id =  extract_u32(raw_payload[offset..=(offset + 3)].borrow());
234        offset += 4;
235
236        let sig_indices_len = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
237        offset += 4;
238
239        let mut index: usize = 0;
240        while index < sig_indices_len as usize{
241            self.sig_indices.push(extract_u32(raw_payload[offset..=(offset + 3)].borrow()));
242            offset += 4;
243            index += 1;
244        }
245
246        match offset_to_change {
247            Some(v) => { *v += offset},
248            None => {}
249        }
250    }
251    fn to_bytes(&self) -> Vec<u8> {
252        let mut result: Vec<u8> = Vec::new();
253        result.extend_from_slice(&self.type_id.to_be_bytes());
254        
255        result.extend_from_slice(&(self.sig_indices.len() as u32).to_be_bytes());
256        for type_id in &self.sig_indices {
257            result.extend_from_slice(&type_id.to_be_bytes());
258        }
259
260        result
261    }
262    fn to_cb58(&self) -> String {
263        encode_cb58(&self.to_bytes()[..])
264    }
265}
266/* _________________________________________________ Unsigned Transactions _________________________________________________ */
267
268impl Parser for AddValidatorTx {
269    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
270        let mut offset: usize = 0;
271
272        let mut base_tx: BaseTx = BaseTx::default();
273        base_tx.from_bytes(raw_payload, Some(&mut offset));
274        self.base_tx = base_tx;
275
276        let mut validator: Validator = Validator::default();
277        validator.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));
278
279        let mut stake: Stake = Stake::default();
280        stake.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));
281
282        let mut rewards_owner: SECP256K1OutputOwnersOutput = SECP256K1OutputOwnersOutput::default();
283        rewards_owner.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));
284
285        self.shares = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
286        offset += 4;
287
288        match offset_to_change {
289            Some(v) => { *v += offset},
290            None => {}
291        }
292    }
293    fn to_bytes(&self) -> Vec<u8> {
294        let mut result: Vec<u8> = Vec::new();
295        result.extend_from_slice(&self.base_tx.to_bytes());
296        result.extend_from_slice(&self.validator.to_bytes());
297        result.extend_from_slice(&self.stake.to_bytes());
298        result.extend_from_slice(&self.rewards_owner.to_bytes());
299        result.extend_from_slice(&self.shares.to_be_bytes());
300        result
301    }
302    fn to_cb58(&self) -> String {
303        encode_cb58(&self.to_bytes()[..])
304    }
305}
306
307impl Parser for AddSubnetValidatorTx {
308    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
309        let mut offset: usize = 0;
310
311        let mut base_tx: BaseTx = BaseTx::default();
312        base_tx.from_bytes(raw_payload, Some(&mut offset));
313        self.base_tx = base_tx;
314
315        let mut validator: Validator = Validator::default();
316        validator.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));
317
318        self.subnet_id = raw_payload[offset..=(offset + 31)].borrow().to_vec();
319        offset += 32;
320
321        let mut subnet_auth: SubnetAuth = SubnetAuth::default();
322        subnet_auth.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));
323
324        self.subnet_auth = subnet_auth;
325
326        match offset_to_change {
327            Some(v) => { *v += offset},
328            None => {}
329        }
330    }
331    fn to_bytes(&self) -> Vec<u8> {
332        let mut result: Vec<u8> = Vec::new();
333        result.extend_from_slice(&self.base_tx.to_bytes());
334        result.extend_from_slice(&self.validator.to_bytes());
335        result.extend_from_slice(&self.subnet_id[..]);
336        result.extend_from_slice(&self.subnet_auth.to_bytes());
337        result
338    }
339    fn to_cb58(&self) -> String {
340        encode_cb58(&self.to_bytes()[..])
341    }
342}
343
344impl Parser for AddDelegatorTx {
345    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
346        let mut offset: usize = 0;
347
348        let mut base_tx: BaseTx = BaseTx::default();
349        base_tx.from_bytes(raw_payload, Some(&mut offset));
350        self.base_tx = base_tx;
351
352        let mut validator: Validator = Validator::default();
353        validator.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));
354
355        let mut stake: Stake = Stake::default();
356        stake.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));
357
358        let mut rewards_owner: SECP256K1OutputOwnersOutput = SECP256K1OutputOwnersOutput::default();
359        rewards_owner.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));
360
361
362        match offset_to_change {
363            Some(v) => { *v += offset},
364            None => {}
365        }
366    }
367    fn to_bytes(&self) -> Vec<u8> {
368        let mut result: Vec<u8> = Vec::new();
369        result.extend_from_slice(&self.base_tx.to_bytes());
370        result.extend_from_slice(&self.validator.to_bytes());
371        result.extend_from_slice(&self.stake.to_bytes());
372        result.extend_from_slice(&self.rewards_owner.to_bytes());
373        result
374    }
375    fn to_cb58(&self) -> String {
376        encode_cb58(&self.to_bytes()[..])
377    }
378}
379impl Parser for CreateChainTx {
380    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
381        let mut offset: usize = 0;
382
383        let mut base_tx: BaseTx = BaseTx::default();
384        base_tx.from_bytes(raw_payload, Some(&mut offset));
385        self.base_tx = base_tx;
386
387        self.subnet_id = raw_payload[offset..=(offset + 31)].to_vec();
388        offset += 32;
389
390        let chain_name_len: u16 = extract_u16(raw_payload[offset..=(offset + 1)].borrow());
391        offset += 2;
392
393        let mut index: u16 = 0;
394        while index < chain_name_len {
395            self.chain_name.push(*raw_payload[offset].borrow());
396            offset += 1;
397            index += 1;
398        }
399
400        self.vm_id = raw_payload[offset..=(offset + 31)].to_vec();
401        offset += 32;
402
403        let fx_ids_len: u32 = extract_u32(raw_payload[offset..=(offset + 1)].borrow());
404        offset += 4;
405
406        let mut index: u32 = 0;
407        while index < fx_ids_len {
408            self.fx_id.push(FxID { fx_id: raw_payload[offset..=(offset + 31)].to_vec() });
409            offset += 32;
410            index += 1;
411        }
412
413        let genesis_data_len: u32 = extract_u32(raw_payload[offset..=(offset + 1)].borrow());
414        offset += 4;
415
416        let mut index: u32 = 0;
417        while index < genesis_data_len {
418            self.chain_name.push(*raw_payload[offset].borrow());
419            offset += 1;
420            index += 1;
421        }
422
423        let mut subnet_auth: SubnetAuth = SubnetAuth::default();
424        subnet_auth.from_bytes(&raw_payload[offset..], Some(&mut offset));
425
426        match offset_to_change {
427            Some(v) => { *v += offset},
428            None => {}
429        }
430    }
431    fn to_bytes(&self) -> Vec<u8> {
432        let mut result: Vec<u8> = Vec::new();
433        result.extend_from_slice(&self.base_tx.to_bytes());
434        result.extend_from_slice(&self.subnet_id);
435
436        result.extend_from_slice(&(self.chain_name.len() as u16).to_be_bytes());
437        result.extend_from_slice(&self.chain_name);
438
439        result.extend_from_slice(&self.vm_id);
440        result.extend_from_slice(&(self.fx_id.len() as u32).to_be_bytes());
441
442        result.extend_from_slice(&(self.genesis_data.len() as u32).to_be_bytes());
443        result.extend_from_slice(&self.genesis_data);
444
445        result.extend_from_slice(&self.subnet_auth.to_bytes());
446        result
447    }
448    fn to_cb58(&self) -> String {
449        encode_cb58(&self.to_bytes()[..])
450    }
451}
452impl Parser for CreateSubnetTx {
453    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
454        let mut offset: usize = 0;
455
456        let mut base_tx: BaseTx = BaseTx::default();
457        base_tx.from_bytes(raw_payload, Some(&mut offset));
458        self.base_tx = base_tx.clone();
459
460
461        let mut rewards_owner: SECP256K1OutputOwnersOutput = SECP256K1OutputOwnersOutput::default();
462        rewards_owner.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));
463        self.rewards_owner = rewards_owner;
464        
465        match offset_to_change {
466            Some(v) => { *v += offset},
467            None => {}
468        }
469    }
470    fn to_bytes(&self) -> Vec<u8> {
471        let mut result: Vec<u8> = Vec::new();
472        result.extend_from_slice(&self.base_tx.to_bytes());
473        result.extend_from_slice(&self.rewards_owner.to_bytes());
474        result
475    }
476    fn to_cb58(&self) -> String {
477        encode_cb58(&self.to_bytes()[..])
478    }
479}
480
481/* _________________________________________________ Stakeable Outs and Ins _________________________________________________ */
482
483impl Parser for StakeableLockIn {
484    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
485        let mut offset: usize = 0;
486
487        self.type_id = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
488        offset += 4;
489        self.locktime = extract_u64(raw_payload[offset..=(offset + 7)].borrow());
490        offset += 8;
491        
492        let mut trans_in: TransferableInput = TransferableInput::default();
493        trans_in.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));  
494        self.transferable_in = trans_in;
495
496        match offset_to_change {
497            Some(v) => { *v += offset},
498            None => {}
499        }
500    }
501    fn to_bytes(&self) -> Vec<u8> {
502        let mut result: Vec<u8> = Vec::new();
503        result.extend_from_slice(&self.type_id.to_be_bytes());
504        result.extend_from_slice(&self.locktime.to_be_bytes());
505        result.extend_from_slice(&self.transferable_in.to_bytes());
506        result
507    }
508    fn to_cb58(&self) -> String {
509        encode_cb58(&self.to_bytes()[..])
510    }
511}
512impl Parser for StakeableLockOut {
513    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
514        let mut offset: usize = 0;
515
516        self.type_id = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
517        offset += 4;
518        self.locktime = extract_u64(raw_payload[offset..=(offset + 7)].borrow());
519        offset += 8;
520        
521        let mut trans_out: TransferableOutput = TransferableOutput::default();
522        trans_out.from_bytes(raw_payload[offset..].borrow(), Some(&mut offset));  
523        self.transferable_out = trans_out;
524
525        match offset_to_change {
526            Some(v) => { *v += offset},
527            None => {}
528        }
529    }
530    fn to_bytes(&self) -> Vec<u8> {
531        let mut result: Vec<u8> = Vec::new();
532        result.extend_from_slice(&self.type_id.to_be_bytes());
533        result.extend_from_slice(&self.locktime.to_be_bytes());
534        result.extend_from_slice(&self.transferable_out.to_bytes());
535        result
536    }
537    fn to_cb58(&self) -> String {
538        encode_cb58(&self.to_bytes()[..])
539    }
540}
541
542/* _________________________________________________ Signed Transaction _________________________________________________ */
543
544impl Parser for SignedTransaction {
545    fn from_bytes(&mut self, raw_payload: &[u8], offset_to_change: Option<&mut usize>) {
546        let mut offset: usize = 0;
547        self.codec_id = extract_u16(raw_payload[offset..=(offset + 1)].borrow());
548        offset += 2;
549
550        let tx_type_id: u32 = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
551        match tx_type_id {
552            0 => {
553                let mut tx: BaseTx = BaseTx::default();
554                tx.from_bytes(&raw_payload[offset..], Some(&mut offset));
555                self.unsigned_tx = Transactions::BaseTx(tx);
556            }
557            12 => {
558                let mut tx: AddValidatorTx = AddValidatorTx::default();
559                tx.from_bytes(&raw_payload[offset..], Some(&mut offset));
560                self.unsigned_tx = Transactions::AddValidatorTx(tx);
561            }
562            13 => {
563                let mut tx: AddSubnetValidatorTx = AddSubnetValidatorTx::default();
564                tx.from_bytes(&raw_payload[offset..], Some(&mut offset));
565                self.unsigned_tx = Transactions::AddSubnetValidatorTx(tx);
566            }
567            14 => {
568                let mut tx: AddDelegatorTx = AddDelegatorTx::default();
569                tx.from_bytes(&raw_payload[offset..], Some(&mut offset));
570                self.unsigned_tx = Transactions::AddDelegatorTx(tx);
571            }
572            15 => {
573                let mut tx: CreateChainTx = CreateChainTx::default();
574                tx.from_bytes(&raw_payload[offset..], Some(&mut offset));
575                self.unsigned_tx = Transactions::CreateChainTx(tx);
576            }
577            16 => {
578                let mut tx: CreateSubnetTx = CreateSubnetTx::default();
579                tx.from_bytes(&raw_payload[offset..], Some(&mut offset));
580                self.unsigned_tx = Transactions::CreateSubnetTx(tx);
581            }
582            17 => {
583                let mut tx: ImportTx = ImportTx::default();
584                tx.from_bytes(&raw_payload[offset..], Some(&mut offset));
585                self.unsigned_tx = Transactions::ImportTx(tx);
586            }
587            18 => {
588                let mut tx: ExportTx = ExportTx::default();
589                tx.from_bytes(&raw_payload[offset..], Some(&mut offset));
590                self.unsigned_tx = Transactions::ExportTx(tx);
591            }
592            _=> {
593                panic!("Incorrect tx_id!")
594            }
595        }
596        
597        let cred_len: u32 = extract_u32(raw_payload[offset..=(offset + 3)].borrow());
598        offset += 4;
599
600        let mut index: u32 = 0;
601        while index < cred_len {
602            let mut c = Credential::default();
603            c.from_bytes(&raw_payload[offset..], Some(&mut offset));
604            self.credentials.push(c);
605            
606            index += 1;
607        }
608        match offset_to_change {
609            Some(v) => { *v += offset},
610            None => {}
611        }
612    }
613    fn to_bytes(&self) -> Vec<u8> {
614        let mut result: Vec<u8> = Vec::new();
615
616        result.extend_from_slice(&self.codec_id.to_be_bytes());
617        match &self.unsigned_tx {
618            Transactions::BaseTx(tx) => {
619                result.extend_from_slice(&tx.to_bytes()[..]);
620            },
621            Transactions::ExportTx(tx) => {
622                result.extend_from_slice(&tx.to_bytes()[..]);
623            },
624            Transactions::ImportTx(tx) => {
625                result.extend_from_slice(&tx.to_bytes()[..]);
626            },
627            Transactions::CreateChainTx(tx) => {
628                result.extend_from_slice(&tx.to_bytes()[..]);
629            },
630            Transactions::AddDelegatorTx(tx) => {
631                result.extend_from_slice(&tx.to_bytes()[..]);
632            },
633            Transactions::AddSubnetValidatorTx(tx) => {
634                result.extend_from_slice(&tx.to_bytes()[..]);
635            },
636            Transactions::AddValidatorTx(tx) => {
637                result.extend_from_slice(&tx.to_bytes()[..]);
638            },
639            Transactions::CreateSubnetTx(tx) => {
640                result.extend_from_slice(&tx.to_bytes()[..]);
641            },
642        }
643
644        result.extend_from_slice(&(self.credentials.len() as u32).to_be_bytes());
645        for i in &self.credentials {
646            result.extend_from_slice(&i.to_bytes());
647        }
648        result
649    }
650    fn to_cb58(&self) -> String {
651        encode_cb58(&self.to_bytes()[..])
652    }
653}
654
655
656// Todo: tests :p
657