1use crate::{
2 codec,
3 errors::{Error, Result},
4 hash, ids, key, platformvm, txs,
5};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
13#[serde(rename_all = "camelCase")]
14pub struct Tx {
15 pub base_tx: txs::Tx,
20 pub validator: platformvm::txs::Validator,
21
22 #[serde(rename = "subnetID")]
25 pub subnet_id: ids::Id,
26 pub signer: Option<key::bls::ProofOfPossession>,
31
32 #[serde(rename = "stake")]
33 pub stake_transferable_outputs: Option<Vec<txs::transferable::Output>>,
34
35 pub validator_rewards_owner: key::secp256k1::txs::OutputOwners,
36 pub delegator_rewards_owner: key::secp256k1::txs::OutputOwners,
37
38 #[serde(rename = "shares")]
39 pub delegation_shares: u32,
40
41 pub creds: Vec<key::secp256k1::txs::Credential>,
43}
44
45impl Default for Tx {
46 fn default() -> Self {
47 Self {
48 base_tx: txs::Tx::default(),
49 validator: platformvm::txs::Validator::default(),
50 subnet_id: ids::Id::empty(), signer: None,
52 stake_transferable_outputs: None,
53 validator_rewards_owner: key::secp256k1::txs::OutputOwners::default(),
54 delegator_rewards_owner: key::secp256k1::txs::OutputOwners::default(),
55 delegation_shares: 0,
56 creds: Vec::new(),
57 }
58 }
59}
60
61impl Tx {
62 pub fn new(base_tx: txs::Tx) -> Self {
63 Self {
64 base_tx,
65 ..Self::default()
66 }
67 }
68
69 pub fn tx_id(&self) -> ids::Id {
73 if self.base_tx.metadata.is_some() {
74 let m = self.base_tx.metadata.clone().unwrap();
75 m.id
76 } else {
77 ids::Id::default()
78 }
79 }
80
81 pub fn type_name() -> String {
82 "platformvm.AddPermissionlessValidatorTx".to_string()
83 }
84
85 pub fn type_id() -> u32 {
86 *(codec::P_TYPES.get(&Self::type_name()).unwrap()) as u32
87 }
88
89 pub async fn sign<T: key::secp256k1::SignOnly + Clone>(
92 &mut self,
93 signers: Vec<Vec<T>>,
94 ) -> Result<()> {
95 let type_id = Self::type_id();
97 let packer = self.base_tx.pack(codec::VERSION, type_id)?;
98
99 let unsigned_tx_bytes = packer.take_bytes();
107 packer.set_bytes(&unsigned_tx_bytes);
108
109 packer.pack_bytes(self.validator.node_id.as_ref())?;
111 packer.pack_u64(self.validator.start)?;
112 packer.pack_u64(self.validator.end)?;
113 packer.pack_u64(self.validator.weight)?;
114
115 packer.pack_bytes(self.subnet_id.as_ref())?;
117
118 if let Some(signer) = &self.signer {
120 let type_id_signer: u32 = 28;
121 packer.pack_u32(type_id_signer)?;
122 packer.pack_bytes(&signer.public_key)?;
123 packer.pack_bytes(&signer.proof_of_possession)?;
124 } else {
125 let type_id_signer: u32 = 27;
127 packer.pack_u32(type_id_signer)?;
128 }
129
130 if let Some(stake_transferable_outputs) = &self.stake_transferable_outputs {
132 packer.pack_u32(stake_transferable_outputs.len() as u32)?;
133
134 for transferable_output in stake_transferable_outputs.iter() {
135 packer.pack_bytes(transferable_output.asset_id.as_ref())?;
141
142 if transferable_output.transfer_output.is_none()
147 && transferable_output.stakeable_lock_out.is_none()
148 {
149 return Err(Error::Other {
150 message: "unexpected Nones in TransferableOutput transfer_output and stakeable_lock_out".to_string(),
151 retryable: false,
152 });
153 }
154 let type_id_transferable_out = {
155 if transferable_output.transfer_output.is_some() {
156 key::secp256k1::txs::transfer::Output::type_id()
157 } else {
158 platformvm::txs::StakeableLockOut::type_id()
159 }
160 };
161 packer.pack_u32(type_id_transferable_out)?;
163
164 match type_id_transferable_out {
165 7 => {
166 let transfer_output = transferable_output.transfer_output.clone().unwrap();
169
170 packer.pack_u64(transfer_output.amount)?;
172
173 packer.pack_u64(transfer_output.output_owners.locktime)?;
179 packer.pack_u32(transfer_output.output_owners.threshold)?;
180 packer.pack_u32(transfer_output.output_owners.addresses.len() as u32)?;
181 for addr in transfer_output.output_owners.addresses.iter() {
182 packer.pack_bytes(addr.as_ref())?;
183 }
184 }
185 22 => {
186 let stakeable_lock_out =
189 transferable_output.stakeable_lock_out.clone().unwrap();
190
191 packer.pack_u64(stakeable_lock_out.locktime)?;
193
194 packer.pack_u32(7)?;
196
197 packer.pack_u64(stakeable_lock_out.transfer_output.amount)?;
206 packer
207 .pack_u64(stakeable_lock_out.transfer_output.output_owners.locktime)?;
208 packer
209 .pack_u32(stakeable_lock_out.transfer_output.output_owners.threshold)?;
210 packer.pack_u32(
211 stakeable_lock_out
212 .transfer_output
213 .output_owners
214 .addresses
215 .len() as u32,
216 )?;
217 for addr in stakeable_lock_out
218 .transfer_output
219 .output_owners
220 .addresses
221 .iter()
222 {
223 packer.pack_bytes(addr.as_ref())?;
224 }
225 }
226 _ => {
227 return Err(Error::Other {
228 message: format!(
229 "unexpected type ID {} for TransferableOutput",
230 type_id_transferable_out
231 ),
232 retryable: false,
233 });
234 }
235 }
236 }
237 } else {
238 packer.pack_u32(0_u32)?;
239 }
240
241 let output_owners_type_id = key::secp256k1::txs::OutputOwners::type_id();
244 packer.pack_u32(output_owners_type_id)?;
245 packer.pack_u64(self.validator_rewards_owner.locktime)?;
246 packer.pack_u32(self.validator_rewards_owner.threshold)?;
247 packer.pack_u32(self.validator_rewards_owner.addresses.len() as u32)?;
248 for addr in self.validator_rewards_owner.addresses.iter() {
249 packer.pack_bytes(addr.as_ref())?;
250 }
251
252 packer.pack_u32(output_owners_type_id)?;
253 packer.pack_u64(self.delegator_rewards_owner.locktime)?;
254 packer.pack_u32(self.delegator_rewards_owner.threshold)?;
255 packer.pack_u32(self.delegator_rewards_owner.addresses.len() as u32)?;
256 for addr in self.delegator_rewards_owner.addresses.iter() {
257 packer.pack_bytes(addr.as_ref())?;
258 }
259
260 packer.pack_u32(self.delegation_shares)?;
262
263 let tx_bytes_with_no_signature = packer.take_bytes();
265 packer.set_bytes(&tx_bytes_with_no_signature);
266
267 let tx_bytes_hash = hash::sha256(&tx_bytes_with_no_signature);
272
273 let creds_len = signers.len() as u32;
275 packer.pack_u32(creds_len)?;
277
278 self.creds = Vec::new();
281 for keys in signers.iter() {
282 let mut sigs: Vec<Vec<u8>> = Vec::new();
283 for k in keys.iter() {
284 let sig = k.sign_digest(&tx_bytes_hash).await?;
285 sigs.push(Vec::from(sig));
286 }
287
288 let cred = key::secp256k1::txs::Credential { signatures: sigs };
289
290 self.creds.push(cred);
292 }
293 if creds_len > 0 {
294 let cred_type_id = key::secp256k1::txs::Credential::type_id();
297 for cred in self.creds.iter() {
298 packer.pack_u32(cred_type_id)?;
300
301 packer.pack_u32(cred.signatures.len() as u32)?;
303 for sig in cred.signatures.iter() {
304 packer.pack_bytes(sig)?;
305 }
306 }
307 }
308 let tx_bytes_with_signatures = packer.take_bytes();
309 let tx_id = hash::sha256(&tx_bytes_with_signatures);
310
311 self.base_tx.metadata = Some(txs::Metadata {
315 id: ids::Id::from_slice(&tx_id),
316 tx_bytes_with_no_signature: tx_bytes_with_no_signature.to_vec(),
317 tx_bytes_with_signatures: tx_bytes_with_signatures.to_vec(),
318 });
319
320 Ok(())
321 }
322}
323
324#[test]
326fn test_add_permissionless_validator_tx_serialization_with_one_signer() {
327 use crate::ids::{node, short};
328 use std::str::FromStr;
329
330 let _ = env_logger::builder()
331 .filter_level(log::LevelFilter::Info)
332 .is_test(true)
333 .try_init();
334
335 macro_rules! ab {
336 ($e:expr) => {
337 tokio_test::block_on($e)
338 };
339 }
340
341 let sk = key::secp256k1::private_key::Key::from_cb58(
342 "24jUJ9vZexUM6expyMcT48LBx27k1m7xpraoV62oSQAHdziao5",
343 )
344 .unwrap();
345
346 let mut tx = Tx {
347 base_tx: txs::Tx {
348 network_id: 1000000,
349 transferable_outputs: Some(vec![txs::transferable::Output {
350 asset_id: ids::Id::from_slice(&<Vec<u8>>::from([
351 0x88, 0xee, 0xc2, 0xe0, 0x99, 0xc6, 0xa5, 0x28, 0xe6, 0x89, 0x61, 0x8e, 0x87, 0x21, 0xe0, 0x4a, 0xe8, 0x5e, 0xa5, 0x74, 0xc7, 0xa1, 0x5a, 0x79, 0x68, 0x64, 0x4d, 0x14, 0xd5, 0x47, 0x80, 0x14, ])),
356 transfer_output: Some(key::secp256k1::txs::transfer::Output {
357 amount: 0x2c6874d687fc000,
358 output_owners: key::secp256k1::txs::OutputOwners {
359 locktime: 0,
360 threshold: 1,
361 addresses: vec![sk.to_public_key().to_short_id().unwrap()],
362 },
363 }),
364 ..txs::transferable::Output::default()
365 }]),
366 transferable_inputs: Some(vec![txs::transferable::Input {
367 utxo_id: txs::utxo::Id {
368 tx_id: ids::Id::from_slice(&<Vec<u8>>::from([
369 0x74, 0x78, 0x49, 0x44,
370 ])),
371 output_index: 2,
372 ..txs::utxo::Id::default()
373 },
374 asset_id: ids::Id::from_slice(&<Vec<u8>>::from([
375 0x88, 0xee, 0xc2, 0xe0, 0x99, 0xc6, 0xa5, 0x28, 0xe6, 0x89, 0x61, 0x8e, 0x87, 0x21, 0xe0, 0x4a, 0xe8, 0x5e, 0xa5, 0x74, 0xc7, 0xa1, 0x5a, 0x79, 0x68, 0x64, 0x4d, 0x14, 0xd5, 0x47, 0x80, 0x14, ])),
380 transfer_input: Some(key::secp256k1::txs::transfer::Input {
381 amount: 5678,
382 sig_indices: vec![0],
383 }),
384 ..txs::transferable::Input::default()
385 }]),
386 ..txs::Tx::default()
387 },
388 validator: platformvm::txs::Validator {
389 node_id: node::Id::from_slice(&<Vec<u8>>::from([
390 0x9c, 0xd7, 0xb3, 0xe4, 0x79, 0x04, 0xf6, 0x7c, 0xc4, 0x8e, 0xb5, 0xb9, 0xaf, 0xdb, 0x03, 0xe6, 0xd1, 0x8a, 0xcf, 0x6c, ])),
393 start: 0x623d7267,
394 end: 0x63c91062,
395 weight: 0x7e7,
396 },
397 subnet_id: ids::Id::from_str("2u5EYNkXMDFNi4pL9eGBt2F5DnXLGriecu7Ctje8jK155FFkPx").unwrap(),
398 signer: Some(key::bls::ProofOfPossession{
399 public_key: hex::decode("0x8f95423f7142d00a48e1014a3de8d28907d420dc33b3052a6dee03a3f2941a393c2351e354704ca66a3fc29870282e15".trim_start_matches("0x")).unwrap(),
400 proof_of_possession: hex::decode("0x86a3ab4c45cfe31cae34c1d06f212434ac71b1be6cfe046c80c162e057614a94a5bc9f1ded1a7029deb0ba4ca7c9b71411e293438691be79c2dbf19d1ca7c3eadb9c756246fc5de5b7b89511c7d7302ae051d9e03d7991138299b5ed6a570a98".trim_start_matches("0x")).unwrap(),
401 ..Default::default()
402 }),
403 stake_transferable_outputs: Some(vec![txs::transferable::Output {
404 asset_id: ids::Id::from_slice(&<Vec<u8>>::from([
405 0x88, 0xee, 0xc2, 0xe0, 0x99, 0xc6, 0xa5, 0x28, 0xe6, 0x89, 0x61, 0x8e, 0x87, 0x21, 0xe0, 0x4a, 0xe8, 0x5e, 0xa5, 0x74, 0xc7, 0xa1, 0x5a, 0x79, 0x68, 0x64, 0x4d, 0x14, 0xd5, 0x47, 0x80, 0x14, ])),
410 stakeable_lock_out: Some(platformvm::txs::StakeableLockOut{
411 locktime:0,
412 transfer_output: key::secp256k1::txs::transfer::Output {
413 amount: 0x7e7,
414 output_owners: key::secp256k1::txs::OutputOwners {
415 locktime: 0,
416 threshold: 1,
417 addresses: vec![short::Id::from_slice(&<Vec<u8>>::from([
418 0xfc,0xed,0xa8,0xf9,0x0f,0xcb,0x5d,0x30,0x61,0x4b, 0x99,0xd7,0x9f,0xc4,0xba,0xa2,0x93,0x07,0x76,0x26, ]))],
421 },
422 },
423 }),
424 ..txs::transferable::Output::default()
425 }]),
426 validator_rewards_owner: key::secp256k1::txs::OutputOwners {
427 locktime: 0,
428 threshold: 1,
429 addresses: vec![short::Id::from_slice(&<Vec<u8>>::from([
430 0xfc,0xed,0xa8,0xf9,0x0f,0xcb,0x5d,0x30,0x61,0x4b, 0x99,0xd7,0x9f,0xc4,0xba,0xa2,0x93,0x07,0x76,0x26, ]))],
433 },
434 delegator_rewards_owner: key::secp256k1::txs::OutputOwners {
435 locktime: 0,
436 threshold: 1,
437 addresses: vec![short::Id::from_slice(&<Vec<u8>>::from([
438 0xfc,0xed,0xa8,0xf9,0x0f,0xcb,0x5d,0x30,0x61,0x4b, 0x99,0xd7,0x9f,0xc4,0xba,0xa2,0x93,0x07,0x76,0x26, ]))],
441 },
442 delegation_shares: 1_000_000,
443 ..Tx::default()
444 };
445
446 let test_key = key::secp256k1::private_key::Key::from_cb58(
447 "PrivateKey-24jUJ9vZexUM6expyMcT48LBx27k1m7xpraoV62oSQAHdziao5",
448 )
449 .expect("failed to load private key");
450 let keys1: Vec<key::secp256k1::private_key::Key> = vec![test_key];
451 let signers: Vec<Vec<key::secp256k1::private_key::Key>> = vec![keys1];
452 ab!(tx.sign(signers)).expect("failed to sign");
453 let tx_metadata = tx.base_tx.metadata.clone().unwrap();
454 let tx_bytes_with_signatures = tx_metadata.tx_bytes_with_signatures;
455 log::info!("tx id {}", tx.tx_id().to_string());
456 assert_eq!(
457 tx.tx_id().to_string(),
458 "22tDNpLuSpTfv8dweokq22KCo8hVTK4o2mgBESg1XQGHJegve5"
459 );
460
461 let expected_signed_bytes: &[u8] = &[
469 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0xee, 0xc2, 0xe0, 0x99, 0xc6, 0xa5, 0x28, 0xe6, 0x89, 0x61, 0x8e, 0x87, 0x21, 0xe0, 0x4a, 0xe8, 0x5e, 0xa5, 0x74, 0xc7, 0xa1, 0x5a, 0x79, 0x68, 0x64, 0x4d, 0x14, 0xd5, 0x47, 0x80, 0x14, 0x00, 0x00, 0x00, 0x07, 0x02, 0xc6, 0x87, 0x4d, 0x68, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xfc, 0xed, 0xa8, 0xf9, 0x0f, 0xcb, 0x5d, 0x30, 0x61, 0x4b, 0x99, 0xd7, 0x9f, 0xc4, 0xba, 0xa2, 0x93, 0x07, 0x76, 0x26, 0x00, 0x00, 0x00, 0x01, 0x74, 0x78, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x88, 0xee, 0xc2, 0xe0, 0x99, 0xc6, 0xa5, 0x28, 0xe6, 0x89, 0x61, 0x8e, 0x87, 0x21, 0xe0, 0x4a, 0xe8, 0x5e, 0xa5, 0x74, 0xc7, 0xa1, 0x5a, 0x79, 0x68, 0x64, 0x4d, 0x14, 0xd5, 0x47, 0x80, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x2e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xd7, 0xb3, 0xe4, 0x79, 0x04, 0xf6, 0x7c, 0xc4, 0x8e, 0xb5, 0xb9, 0xaf, 0xdb, 0x03, 0xe6, 0xd1, 0x8a, 0xcf, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x62, 0x3d, 0x72, 0x67, 0x00, 0x00, 0x00, 0x00, 0x63, 0xc9, 0x10, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe7, 0xf9, 0xef, 0x27, 0x25, 0xf6, 0x61, 0x9b, 0x92, 0x3f, 0x1e, 0x84, 0xbf, 0x34, 0x81, 0xd5,
557 0x3f, 0xd0, 0x7e, 0x2b, 0xa4, 0xbc, 0x49, 0xcc, 0xf5, 0xa6, 0x9e, 0x9a, 0xc7, 0x36, 0x73,
558 0x4e, 0x1a, 0x00, 0x00, 0x00, 0x1c, 0x8f, 0x95, 0x42, 0x3f, 0x71, 0x42, 0xd0, 0x0a, 0x48, 0xe1, 0x01, 0x4a, 0x3d, 0xe8, 0xd2, 0x89, 0x07, 0xd4, 0x20, 0xdc, 0x33, 0xb3, 0x05, 0x2a, 0x6d, 0xee, 0x03, 0xa3, 0xf2, 0x94, 0x1a, 0x39, 0x3c, 0x23, 0x51, 0xe3, 0x54, 0x70, 0x4c, 0xa6, 0x6a, 0x3f, 0xc2, 0x98, 0x70, 0x28, 0x2e, 0x15, 0x86, 0xa3, 0xab, 0x4c, 0x45, 0xcf, 0xe3, 0x1c, 0xae, 0x34, 0xc1, 0xd0, 0x6f, 0x21, 0x24, 0x34, 0xac, 0x71, 0xb1, 0xbe, 0x6c, 0xfe, 0x04, 0x6c, 0x80, 0xc1, 0x62, 0xe0, 0x57, 0x61, 0x4a, 0x94, 0xa5, 0xbc, 0x9f, 0x1d, 0xed, 0x1a, 0x70, 0x29, 0xde, 0xb0, 0xba, 0x4c, 0xa7, 0xc9, 0xb7, 0x14, 0x11, 0xe2, 0x93, 0x43, 0x86, 0x91, 0xbe, 0x79, 0xc2, 0xdb, 0xf1, 0x9d, 0x1c, 0xa7, 0xc3, 0xea, 0xdb, 0x9c, 0x75, 0x62, 0x46, 0xfc, 0x5d, 0xe5, 0xb7, 0xb8, 0x95, 0x11, 0xc7, 0xd7, 0x30, 0x2a, 0xe0, 0x51, 0xd9, 0xe0, 0x3d, 0x79, 0x91, 0x13, 0x82, 0x99, 0xb5, 0xed, 0x6a, 0x57, 0x0a, 0x98, 0x00, 0x00, 0x00, 0x01, 0x88, 0xee, 0xc2, 0xe0, 0x99, 0xc6, 0xa5, 0x28, 0xe6, 0x89, 0x61, 0x8e, 0x87, 0x21, 0xe0, 0x4a, 0xe8, 0x5e, 0xa5, 0x74, 0xc7, 0xa1, 0x5a, 0x79, 0x68, 0x64, 0x4d, 0x14, 0xd5, 0x47, 0x80, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
603 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xfc, 0xed, 0xa8, 0xf9, 0x0f, 0xcb, 0x5d, 0x30, 0x61, 0x4b, 0x99, 0xd7, 0x9f, 0xc4, 0xba, 0xa2, 0x93, 0x07, 0x76, 0x26, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xfc, 0xed, 0xa8, 0xf9, 0x0f, 0xcb, 0x5d, 0x30, 0x61, 0x4b, 0x99, 0xd7, 0x9f, 0xc4, 0xba, 0xa2, 0x93, 0x07, 0x76, 0x26, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xfc, 0xed, 0xa8, 0xf9, 0x0f, 0xcb, 0x5d, 0x30, 0x61, 0x4b, 0x99, 0xd7, 0x9f, 0xc4, 0xba, 0xa2, 0x93, 0x07, 0x76, 0x26, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x13, 0x6a, 0x2d, 0x14, 0x0d, 0x7e, 0xdf, 0xdc, 0x87, 0xa4, 0x13, 0xcd, 0x8f, 0xdf, 0xa6, 0x80, 0xdd, 0x07, 0x69, 0xf3, 0x61, 0xdc, 0x22, 0x7f, 0xe4, 0x84, 0x53, 0x47, 0xec, 0xda, 0xd7, 0x06, 0x93, 0x96, 0x9a, 0x45, 0x35, 0xe2, 0x51, 0x71, 0x94, 0x84, 0xe2, 0xe5, 0x52, 0xb1, 0x53, 0xe7, 0x66, 0xde, 0x74, 0x2b, 0x3c, 0x24, 0x52, 0x66, 0xc9, 0x29, 0x45, 0xe7, 0x98, 0x99, 0xac, 0x00, ];
666 assert!(cmp_manager::eq_vectors(
667 expected_signed_bytes,
668 &tx_bytes_with_signatures
669 ));
670}