Files
zeekay 76bd2a49d5 chains/rust/xvm: name the host's seam and the host's id, rather than a copy
The chain restated `lux-rs/node`'s `src/vm.rs` in a `host.rs` of its own and
minted a 32-byte `Id` newtype beside the one the finality rule already uses.
Both are gone.

`host` is now `pub use lux_node::vm`, so `impl host::Vm for Xvm` implements the
trait the node actually holds its chains behind. A restatement compiles against
a shape the node never sees, and keeps compiling on the day the node changes a
method.

`Id` is `lux_consensus::finality::Id` — the same `[u8; 32]` the P-Chain port
already names, so a node can hold both in one chain map. The operations that
were methods on the newtype are free functions in `ids`, and `is_empty` is one
of them deliberately: an array derefs to a slice, whose own `is_empty` answers
about the length and would be a silently wrong answer to "is this the empty id".

The wire is untouched: the twelve Go-generated golden vectors and the
execution-root KAT pass unchanged.
2026-09-04 18:48:15 -07:00

444 lines
31 KiB
Rust

// SPDX-License-Identifier: BSD-3-Clause-Eco
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
//! Byte-for-byte against Go.
//!
//! Every other test in this crate asks whether the chain agrees with itself: a
//! round trip proves the reader can undo the writer, and nothing more. These
//! ask the only question that matters between two implementations — whether the
//! bytes are the SAME bytes.
//!
//! Each vector below was printed by the Go X-chain (`luxfi/node/vms/xvm` at
//! `github.com/luxfi/zap v1.2.7`, `github.com/luxfi/utxo v0.5.10`) from the
//! structure named in the test, and pasted here unchanged. A transaction's id
//! is the SHA-256 of its signed bytes, so if these ever diverge the two chains
//! are not the same chain — a transaction one node accepted would have a
//! different name on the other.
//!
//! HOW TO REGENERATE. Build the same structures against the Go packages and
//! print `hex.EncodeToString` of: `txs.UnsignedBytes(t)`, `tx.Bytes()`,
//! `tx.ID()`, each fx primitive's `Bytes()`, and `utxo.WireBytes()`.
use lux_xvm::fx::secp256k1::{Credential, MintOutput, TransferInput, TransferOutput};
use lux_xvm::fx::{FxIn, Input, Owners, State};
use lux_xvm::ids::{self, ShortId};
use lux_xvm::txs::{BaseTx, Tx, Unsigned};
use lux_xvm::utxo::{Asset, BaseTxFields, TransferableInput, TransferableOutput, Utxo, UtxoId};
fn hex(b: &[u8]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}
/// The transaction the Go vectors were printed from: network 10, chain 5, one
/// output of 12345 to one address, one input of 12346 spending output 7 of
/// transaction 2, and a four-byte memo.
fn the_transaction() -> Tx {
Tx::new(Unsigned::Base(BaseTx {
base: BaseTxFields {
network_id: 10,
blockchain_id: ids::prefixed(&[5]),
outs: vec![TransferableOutput {
asset: Asset {
id: ids::prefixed(&[1]),
},
out: State::Transfer(TransferOutput {
amt: 12345,
owners: Owners {
locktime: 0,
threshold: 1,
addrs: vec![ShortId::prefixed_bytes(&[0xAA, 0xBB])],
},
}),
}],
ins: vec![TransferableInput {
utxo_id: UtxoId::new(ids::prefixed(&[2]), 7),
asset: Asset {
id: ids::prefixed(&[1]),
},
input: FxIn::Transfer(TransferInput {
amt: 12346,
input: Input {
sig_indices: vec![0, 2],
},
}),
}],
memo: b"memo".to_vec(),
},
}))
}
/// What every signature in this chain is computed over.
const GO_BASETX_UNSIGNED: &str = "5a41500002000000100000006e0100000100000000000000080000004e01000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f";
/// The signed envelope with no credentials attached — the unsigned body plus an
/// empty credential run.
const GO_BASETX_SIGNED_NOCREDS: &str = "000e5a415000020000001000000092010000140000006e0100000000000000000000000000005a41500002000000100000006e0100000100000000000000080000004e01000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f";
/// The id Go gives that transaction.
const GO_BASETX_ID: &str = "76feb0c9414e3a4ecff34210c9a4aaadbde9274c90deacd2b1bcbdd8f14c0731";
#[test]
fn the_unsigned_bytes_are_the_bytes_go_signs() {
let tx = the_transaction();
assert_eq!(hex(tx.unsigned_bytes()), GO_BASETX_UNSIGNED);
}
#[test]
fn the_signed_envelope_is_the_envelope_go_puts_on_the_wire() {
let tx = the_transaction();
assert_eq!(hex(tx.bytes()), GO_BASETX_SIGNED_NOCREDS);
}
#[test]
fn a_transaction_has_the_same_name_in_both_languages() {
// The id is the hash of the signed bytes, so this follows from the vector
// above — and it is asserted separately because it is the thing that would
// actually break: two nodes calling one transaction by two names.
let tx = the_transaction();
assert_eq!(hex(&tx.id()), GO_BASETX_ID);
}
#[test]
fn a_transaction_go_encoded_reads_back_here_as_the_same_transaction() {
let raw: Vec<u8> = (0..GO_BASETX_SIGNED_NOCREDS.len())
.step_by(2)
.map(|i| u8::from_str_radix(&GO_BASETX_SIGNED_NOCREDS[i..i + 2], 16).unwrap())
.collect();
let parsed = Tx::parse(&raw).unwrap();
assert_eq!(hex(&parsed.id()), GO_BASETX_ID);
let base = parsed.unsigned.base();
assert_eq!(base.network_id, 10);
assert_eq!(base.blockchain_id, ids::prefixed(&[5]));
assert_eq!(base.memo, b"memo");
assert_eq!(base.outs.len(), 1);
assert_eq!(base.outs[0].out.amount(), 12345);
assert_eq!(base.ins.len(), 1);
assert_eq!(base.ins[0].utxo_id.output_index, 7);
// And re-encoding it gives back the very bytes it came from.
assert_eq!(hex(parsed.bytes()), GO_BASETX_SIGNED_NOCREDS);
}
#[test]
fn every_fx_primitive_envelope_is_the_envelope_go_writes() {
let out = TransferOutput {
amt: 7,
owners: Owners {
locktime: 3,
threshold: 1,
addrs: vec![ShortId::prefixed_bytes(&[9])],
},
};
assert_eq!(
hex(&out.bytes()),
"01015a4150000200000028000000440000000900000000000000000000000000000000000000000000000700000000000000030000000000000001000000d4ffffff01000000"
);
let in_ = TransferInput {
amt: 7,
input: Input {
sig_indices: vec![1],
},
};
assert_eq!(
hex(&in_.bytes()),
"01025a41500002000000180000002800000001000000000000000700000000000000f0ffffff01000000"
);
let mut sig = [0u8; 65];
sig[0] = 1;
sig[1] = 2;
sig[2] = 3;
let cred = Credential { sigs: vec![sig] };
assert_eq!(
hex(&cred.bytes()),
"01065a41500002000000580000006c00000001020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b4ffffff410000000000000000000000"
);
let mint = MintOutput {
owners: Owners {
locktime: 0,
threshold: 1,
addrs: vec![ShortId::prefixed_bytes(&[9])],
},
};
assert_eq!(
hex(&mint.bytes()),
"01035a41500002000000280000003c000000090000000000000000000000000000000000000000000000000000000000000001000000dcffffff01000000"
);
}
#[test]
fn an_unspent_output_crosses_between_chains_as_the_bytes_go_writes() {
let utxo = Utxo {
utxo_id: UtxoId::new(ids::prefixed(&[3]), 1),
asset: Asset {
id: ids::prefixed(&[1]),
},
out: State::Transfer(TransferOutput {
amt: 7,
owners: Owners {
locktime: 3,
threshold: 1,
addrs: vec![ShortId::prefixed_bytes(&[9])],
},
}),
};
assert_eq!(
hex(&utxo.wire_bytes()),
"000a5a4150000200000010000000a20000000300000000000000000000000000000000000000000000000000000000000000010000000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a4150000200000028000000440000000900000000000000000000000000000000000000000000000700000000000000030000000000000001000000d4ffffff01000000"
);
}
// ---------------------------------------------------------------------------
// The other four transactions, a block, and every fx primitive envelope.
// Same generator, same structures — see the module comment.
// ---------------------------------------------------------------------------
use lux_xvm::block::Block;
use lux_xvm::fx::{nft, property, Op};
use lux_xvm::txs::{CreateAssetTx, ExportTx, ImportTx, InitialState, Operation, OperationTx};
/// The envelope every one of the four shares.
fn base_fields() -> BaseTxFields {
BaseTxFields {
network_id: 10,
blockchain_id: ids::prefixed(&[5]),
outs: vec![TransferableOutput {
asset: Asset {
id: ids::prefixed(&[1]),
},
out: State::Transfer(TransferOutput {
amt: 12345,
owners: Owners::new(1, vec![ShortId::prefixed_bytes(&[0xAA, 0xBB])]),
}),
}],
ins: vec![TransferableInput {
utxo_id: UtxoId::new(ids::prefixed(&[2]), 7),
asset: Asset {
id: ids::prefixed(&[1]),
},
input: FxIn::Transfer(TransferInput {
amt: 12346,
input: Input {
sig_indices: vec![0, 2],
},
}),
}],
memo: b"memo".to_vec(),
}
}
fn owner9() -> Owners {
Owners::new(1, vec![ShortId::prefixed_bytes(&[9])])
}
/// Assert one transaction's three vectors at once: what is signed, what goes on
/// the wire, and what it is called.
fn check(u: Unsigned, unsigned: &str, signed: &str, id: &str) {
let tx = Tx::new(u);
assert_eq!(hex(tx.unsigned_bytes()), unsigned, "unsigned bytes");
assert_eq!(hex(tx.bytes()), signed, "signed bytes");
assert_eq!(hex(&tx.id()), id, "id");
// And what Go wrote reads back here as the same transaction.
let raw: Vec<u8> = (0..signed.len())
.step_by(2)
.map(|i| u8::from_str_radix(&signed[i..i + 2], 16).unwrap())
.collect();
let parsed = Tx::parse(&raw).unwrap();
assert_eq!(hex(parsed.bytes()), signed);
assert_eq!(parsed.unsigned, tx.unsigned);
}
#[test]
fn a_create_asset_is_the_bytes_go_writes() {
check(
Unsigned::CreateAsset(CreateAssetTx {
base: base_fields(),
name: "Test Asset".into(),
symbol: "TST".into(),
denomination: 8,
states: vec![InitialState {
fx_index: 0,
outs: vec![State::Mint(MintOutput { owners: owner9() })],
}],
}),
"5a4150000200000018000000110200006a0000000000000002000000000000002c0000004e010000720100000a000000740100000300000008000000d4ffffff01000000630100006a00000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f546573742041737365745453545a41500002000000180000006a0000003e0000000000000000000000f4ffffff01000000080000003e00000001035a41500002000000280000003c000000090000000000000000000000000000000000000000000000000000000000000001000000dcffffff01000000",
"000e5a41500002000000100000003502000014000000110200000000000000000000000000005a4150000200000018000000110200006a0000000000000002000000000000002c0000004e010000720100000a000000740100000300000008000000d4ffffff01000000630100006a00000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f546573742041737365745453545a41500002000000180000006a0000003e0000000000000000000000f4ffffff01000000080000003e00000001035a41500002000000280000003c000000090000000000000000000000000000000000000000000000000000000000000001000000dcffffff01000000",
"cd718316970d18f0b30826bf7b215f2b7d4a9f0cf90384252bd74ee841396f72",
);
}
#[test]
fn an_operation_transaction_is_the_bytes_go_writes() {
check(
Unsigned::Operation(OperationTx {
base: base_fields(),
ops: vec![Operation {
asset: Asset { id: ids::prefixed(&[1]) },
utxo_ids: vec![UtxoId::new(ids::prefixed(&[4]), 2)],
op: Op::PropertyBurn(property::BurnOperation {
input: Input { sig_indices: vec![0] },
}),
}],
}),
"5a4150000200000018000000100200008a000000000000000300000000000000180000004e010000e8ffffff01000000560100008a00000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f5a41500002000000380000008a000000040000000000000000000000000000000000000000000000000000000000000002000000000000000100000000000000000000000000000000000000000000000000000000000000b8ffffff01000000080000002200000009165a4150000200000018000000200000000000000000000000f8ffffff01000000",
"000e5a41500002000000100000003402000014000000100200000000000000000000000000005a4150000200000018000000100200008a000000000000000300000000000000180000004e010000e8ffffff01000000560100008a00000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f5a41500002000000380000008a000000040000000000000000000000000000000000000000000000000000000000000002000000000000000100000000000000000000000000000000000000000000000000000000000000b8ffffff01000000080000002200000009165a4150000200000018000000200000000000000000000000f8ffffff01000000",
"76c21c1c719205e4bdf2a83b8a3975c464d231406f36b647c92589c810baef0e",
);
}
#[test]
fn an_import_is_the_bytes_go_writes() {
check(
Unsigned::Import(ImportTx {
base: base_fields(),
source_chain: ids::prefixed(&[6]),
imported_ins: vec![TransferableInput {
utxo_id: UtxoId::new(ids::prefixed(&[8]), 3),
asset: Asset { id: ids::prefixed(&[1]) },
input: FxIn::Transfer(TransferInput {
amt: 50,
input: Input { sig_indices: vec![0] },
}),
}],
}),
"5a4150000200000090000000160200000800000000000000000000000000000000000000000000000000000000000000030000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000000000003200000000000000f0ffffff01000000000088ffffff000000000400000000000000300000004e0100000600000000000000000000000000000000000000000000000000000000000000c8ffffff0100000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f",
"000e5a41500002000000100000003a02000014000000160200000000000000000000000000005a4150000200000090000000160200000800000000000000000000000000000000000000000000000000000000000000030000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000000000003200000000000000f0ffffff01000000000088ffffff000000000400000000000000300000004e0100000600000000000000000000000000000000000000000000000000000000000000c8ffffff0100000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f",
"4c29b16ccaef5d364db2a414ea9f0a87febea057f0b5c67973d3fe418b623350",
);
}
#[test]
fn an_export_is_the_bytes_go_writes() {
check(
Unsigned::Export(ExportTx {
base: base_fields(),
destination_chain: ids::prefixed(&[6]),
exported_outs: vec![TransferableOutput {
asset: Asset { id: ids::prefixed(&[1]) },
out: State::Transfer(TransferOutput { amt: 50, owners: owner9() }),
}],
}),
"5a41500002000000880000000e0200000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a4150000200000028000000440000000900000000000000000000000000000000000000000000003200000000000000000000000000000001000000d4ffffff01000000000090ffffff000000000500000000000000300000004e0100000600000000000000000000000000000000000000000000000000000000000000c8ffffff0100000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f",
"000e5a415000020000001000000032020000140000000e0200000000000000000000000000005a41500002000000880000000e0200000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a4150000200000028000000440000000900000000000000000000000000000000000000000000003200000000000000000000000000000001000000d4ffffff01000000000090ffffff000000000500000000000000300000004e0100000600000000000000000000000000000000000000000000000000000000000000c8ffffff0100000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f",
"9002973879e88f10407576114f6ad69db07013fc297d3c1149f35efbfb49c8bd",
);
}
#[test]
fn a_block_is_the_bytes_go_writes_and_has_the_name_go_gives_it() {
let tx = Tx::new(Unsigned::Export(ExportTx {
base: base_fields(),
destination_chain: ids::prefixed(&[6]),
exported_outs: vec![TransferableOutput {
asset: Asset {
id: ids::prefixed(&[1]),
},
out: State::Transfer(TransferOutput {
amt: 50,
owners: owner9(),
}),
}],
}));
let blk = Block::new(
ids::prefixed(&[0xB1]),
42,
123_456,
ids::prefixed(&[0xAA]),
vec![tx],
)
.unwrap();
assert_eq!(hex(blk.bytes()), "5a4150000200000018000000ac0200003402000000000000b1000000000000000000000000000000000000000000000000000000000000002a0000000000000040e2010000000000aa00000000000000000000000000000000000000000000000000000000000000a8ffffff010000000800000034020000000e5a415000020000001000000032020000140000000e0200000000000000000000000000005a41500002000000880000000e0200000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a4150000200000028000000440000000900000000000000000000000000000000000000000000003200000000000000000000000000000001000000d4ffffff01000000000090ffffff000000000500000000000000300000004e0100000600000000000000000000000000000000000000000000000000000000000000c8ffffff0100000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f");
assert_eq!(
hex(&blk.id()),
"ddae8ce1976a006d7f45117f1cc0acacbc7f2232a85bc6d3545ad8c5dd07223b"
);
// And Go's own block bytes parse back here to the same block.
let raw: Vec<u8> = (0.."5a4150000200000018000000ac0200003402000000000000b1000000000000000000000000000000000000000000000000000000000000002a0000000000000040e2010000000000aa00000000000000000000000000000000000000000000000000000000000000a8ffffff010000000800000034020000000e5a415000020000001000000032020000140000000e0200000000000000000000000000005a41500002000000880000000e0200000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a4150000200000028000000440000000900000000000000000000000000000000000000000000003200000000000000000000000000000001000000d4ffffff01000000000090ffffff000000000500000000000000300000004e0100000600000000000000000000000000000000000000000000000000000000000000c8ffffff0100000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f".len())
.step_by(2)
.map(|i| u8::from_str_radix(&"5a4150000200000018000000ac0200003402000000000000b1000000000000000000000000000000000000000000000000000000000000002a0000000000000040e2010000000000aa00000000000000000000000000000000000000000000000000000000000000a8ffffff010000000800000034020000000e5a415000020000001000000032020000140000000e0200000000000000000000000000005a41500002000000880000000e0200000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a4150000200000028000000440000000900000000000000000000000000000000000000000000003200000000000000000000000000000001000000d4ffffff01000000000090ffffff000000000500000000000000300000004e0100000600000000000000000000000000000000000000000000000000000000000000c8ffffff0100000000125a41500002000000080100004c0100000100000000000000000000000000000000000000000000000000000000000000080000004600000001015a415000020000002800000044000000aabb000000000000000000000000000000000000000000003930000000000000000000000000000001000000d4ffffff0100000000000200000000000000000000000000000000000000000000000000000000000000070000000100000000000000000000000000000000000000000000000000000000000000080000002a00000001025a41500002000000180000002800000000000000020000003a30000000000000f0ffffff02000000000018ffffff0000000080ffffff000000000a000000000000000500000000000000000000000000000000000000000000000000000000000000c8ffffff01000000c8ffffff0100000008000000040000006d656d6f"[i..i + 2], 16).unwrap())
.collect();
let parsed = Block::parse(&raw).unwrap();
assert_eq!(parsed, blk);
}
#[test]
fn every_nft_and_property_envelope_is_the_envelope_go_writes() {
assert_eq!(
hex(&nft::MintOutput { group_id: 3, owners: owner9() }.bytes()),
"08105a41500002000000280000004000000009000000000000000000000000000000000000000000000003000000000000000000000001000000d8ffffff01000000"
);
let nto = nft::TransferOutput {
group_id: 3,
payload: vec![0xde, 0xad],
owners: owner9(),
};
assert_eq!(hex(&nto.bytes()), "08115a41500002000000280000004a00000009000000000000000000000000000000000000000000000003000000000000000000000001000000d8ffffff010000000800000002000000dead");
assert_eq!(
hex(&nft::MintOperation {
mint_input: Input { sig_indices: vec![0] },
group_id: 3,
payload: vec![0xde, 0xad],
outputs: vec![owner9()],
}
.bytes()),
"08135a41500002000000180000007c0000000000000000000000f8ffffff01000000030000001800000002000000010000000e0000003e00000000000000dead00095a41500002000000280000003c000000090000000000000000000000000000000000000000000000000000000000000001000000dcffffff01000000"
);
assert_eq!(
hex(&nft::TransferOperation {
input: Input { sig_indices: vec![0] },
output: nto,
}
.bytes()),
"08145a4150000200000018000000740000000000000000000000f8ffffff01000000080000004c00000008115a41500002000000280000004a00000009000000000000000000000000000000000000000000000003000000000000000000000001000000d8ffffff010000000800000002000000dead"
);
let pmo = property::MintOutput { owners: owner9() };
let poo = property::OwnedOutput { owners: owner9() };
assert_eq!(hex(&pmo.bytes()), "09035a41500002000000280000003c000000090000000000000000000000000000000000000000000000000000000000000001000000dcffffff01000000");
assert_eq!(hex(&poo.bytes()), "09155a41500002000000280000003c000000090000000000000000000000000000000000000000000000000000000000000001000000dcffffff01000000");
assert_eq!(
hex(&property::MintOperation {
mint_input: Input { sig_indices: vec![0] },
mint_output: pmo,
owned_output: poo,
}
.bytes()),
"09055a4150000200000018000000ac0000000000000000000000f8ffffff01000000100000003e000000460000003e00000009035a41500002000000280000003c000000090000000000000000000000000000000000000000000000000000000000000001000000dcffffff0100000009155a41500002000000280000003c000000090000000000000000000000000000000000000000000000000000000000000001000000dcffffff01000000"
);
assert_eq!(
hex(&property::BurnOperation {
input: Input {
sig_indices: vec![0]
}
}
.bytes()),
"09165a4150000200000018000000200000000000000000000000f8ffffff01000000"
);
assert_eq!(
hex(&lux_xvm::fx::secp256k1::MintOperation {
mint_input: Input { sig_indices: vec![0] },
mint_output: MintOutput { owners: owner9() },
transfer_output: TransferOutput { amt: 7, owners: owner9() },
}
.bytes()),
"01055a4150000200000018000000b40000000000000000000000f8ffffff01000000100000003e000000460000004600000001035a41500002000000280000003c000000090000000000000000000000000000000000000000000000000000000000000001000000dcffffff0100000001015a4150000200000028000000440000000900000000000000000000000000000000000000000000000700000000000000000000000000000001000000d4ffffff01000000"
);
// A credential's family byte is the whole difference between the three.
let mut sig = [0u8; 65];
sig[0] = 1;
sig[1] = 2;
sig[2] = 3;
let cred = Credential { sigs: vec![sig] };
assert_eq!(
hex(&cred.bytes_for(lux_xvm::wire::TypeKind::Nft)),
"08065a41500002000000580000006c00000001020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b4ffffff410000000000000000000000"
);
assert_eq!(
hex(&cred.bytes_for(lux_xvm::wire::TypeKind::Property)),
"09065a41500002000000580000006c00000001020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b4ffffff410000000000000000000000"
);
}