Files
zeekayandHanzo Dev 7cc836869f port the Go staker-set and priority tables, and check the BLS proof against Go
TestStakerLess in both directions of each clause, the TestBaseStakers*
set as behaviour of State, every priority predicate against every
priority, and the credential-range table with the three shapes a remote
sender controls.

And a proof of possession Go made now verifies here. Consensus
aggregates signatures under these keys, so the domain the proof is made
under has to be the same on both sides or each accepts proofs the other
refuses. It was only ever a comment saying so; it is now a Go-made
proof in the vector file.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-04 02:35:18 -07:00

841 lines
53 KiB
Rust

// SPDX-License-Identifier: BSD-3-Clause-Eco
//! The bytes, checked against the Go P-Chain.
//!
//! Two implementations of a chain agree about a transaction only if they agree
//! about its bytes, because the bytes are what a signature covers and what an
//! id is the hash of. So the claim "this is the same wire" has to be checked
//! against the other implementation rather than against itself.
//!
//! Every vector below was emitted by the Go P-Chain
//! (`luxfi/node/vms/platformvm`) building the same value with the same fields,
//! and is pasted here verbatim. Each case asserts both directions: the bytes
//! this port writes are byte-identical to Go's, and Go's bytes read back into
//! the same value.
//!
//! Regenerating them is a Go program that calls `txs.New*Tx` and
//! `block.New*Block` and prints hex; it is quoted in LLM.md so a future change
//! to either side can be checked the same way.
use lux_platformvm::block;
use lux_platformvm::components::Utxo;
use lux_platformvm::components::{Credential, Input, Output, Owners, UtxoId};
use lux_platformvm::genesis::{Allocation, Genesis};
use lux_platformvm::ids::{NodeId, ShortId};
use lux_platformvm::security::{Admission, Manager, Mode};
use lux_platformvm::sign;
use lux_platformvm::signer::Signer;
use lux_platformvm::txs::{Envelope, Kind, NetworkValidator, PChainOwner, Tx, Unsigned, Validator};
fn unhex(s: &str) -> Vec<u8> {
(0..s.len() / 2)
.map(|i| u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).unwrap())
.collect()
}
fn hex(b: &[u8]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}
fn id(b: u8) -> [u8; 32] {
[b; 32]
}
fn out(asset: u8, amount: u64, owner: u8) -> Output {
Output {
asset: id(asset),
stake_lock: 0,
amount,
owners: Owners {
locktime: 0,
threshold: 1,
addrs: vec![ShortId([owner; 20])],
},
}
}
fn input(tx: u8, index: u32, amount: u64) -> Input {
Input {
utxo: UtxoId {
tx_id: id(tx),
output_index: index,
},
asset: id(9),
stake_lock: 0,
amount,
sig_indices: vec![0],
}
}
fn base() -> Envelope {
Envelope {
network_id: 1,
blockchain_id: id(3),
outs: vec![out(9, 100, 1)],
ins: vec![input(1, 0, 200)],
memo: Vec::new(),
}
}
fn owners(b: u8) -> Owners {
Owners {
locktime: 0,
threshold: 1,
addrs: vec![ShortId([b; 20])],
}
}
fn validator() -> Validator {
Validator {
node_id: NodeId([5; 20]),
start: 1000,
end: 2000,
weight: 50,
}
}
fn stake() -> Vec<Output> {
vec![out(9, 50, 2)]
}
/// Assert this port writes exactly what Go wrote, and reads exactly what Go
/// meant.
fn same_wire(name: &str, tx: Unsigned, go_hex: &str) {
let want = unhex(go_hex);
let got = tx.to_bytes();
assert_eq!(hex(&got), hex(&want), "{name}: bytes differ from Go's");
let back = Unsigned::parse(&want).unwrap_or_else(|e| panic!("{name}: {e}"));
assert_eq!(back, tx, "{name}: Go's bytes do not read back as the value");
// And an id is the hash of those bytes, so both sides name it the same.
assert_eq!(Tx::new(back, Vec::new()).id(), Tx::new(tx, Vec::new()).id());
}
#[test]
fn a_base_transaction_is_byte_identical_to_go() {
same_wire(
"base",
Unsigned::Base(base()),
"5a41500002000000d80000002501000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000301000000030303030303030303030303030303030303030303030303030303030303030313ffffff0100000053ffffff0100000063ffffff01000000bbffffff010000000000000000000000",
);
}
#[test]
fn a_reward_transaction_is_byte_identical_to_go() {
same_wire(
"reward",
Unsigned::RewardValidator { staker_tx_id: id(7) },
"5a415000020000001000000031000000020707070707070707070707070707070707070707070707070707070707070707",
);
}
#[test]
fn the_legacy_add_validator_transaction_is_byte_identical_to_go() {
same_wire(
"add_validator",
Unsigned::AddValidator {
base: base(),
validator: validator(),
stake: stake(),
rewards_owner: owners(3),
delegation_shares: 20_000,
},
"5a4150000200000050010000f101000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000909090909090909090909090909090909090909090909090909090909090909000000000000000032000000000000000100000000000000000000000000000001000000000000000202020202020202020202020202020202020202000000000303030303030303030303030303030303030303000000000b0100000003030303030303030303030303030303030303030303030303030303030303039bfeffff01000000dbfeffff01000000ebfeffff0100000043ffffff0100000000000000000000000505050505050505050505050505050505050505e803000000000000d00700000000000032000000000000000fffffff010000004fffffff0100000001000000000000000000000053ffffff01000000204e0000",
);
}
#[test]
fn the_legacy_add_delegator_transaction_is_byte_identical_to_go() {
same_wire(
"add_delegator",
Unsigned::AddDelegator {
base: base(),
validator: validator(),
stake: stake(),
rewards_owner: owners(3),
},
"5a4150000200000050010000ed01000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000909090909090909090909090909090909090909090909090909090909090909000000000000000032000000000000000100000000000000000000000000000001000000000000000202020202020202020202020202020202020202000000000303030303030303030303030303030303030303000000000d0100000003030303030303030303030303030303030303030303030303030303030303039bfeffff01000000dbfeffff01000000ebfeffff0100000043ffffff0100000000000000000000000505050505050505050505050505050505050505e803000000000000d00700000000000032000000000000000fffffff010000004fffffff0100000001000000000000000000000053ffffff01000000",
);
}
#[test]
fn a_chain_validator_transaction_is_byte_identical_to_go() {
same_wire(
"add_chain_validator",
Unsigned::AddChainValidator {
base: base(),
validator: validator(),
chain: id(6),
chain_auth: vec![1],
},
"5a41500002000000e00000008101000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c800000000000000000000000100000000000000000000000000000001000000000000000c0100000003030303030303030303030303030303030303030303030303030303030303030bffffff010000004bffffff010000005bffffff01000000b3ffffff0100000000000000000000000505050505050505050505050505050505050505e803000000000000d007000000000000320000000000000006060606060606060606060606060606060606060606060606060606060606065fffffff01000000",
);
}
#[test]
fn a_create_chain_transaction_is_byte_identical_to_go() {
same_wire(
"create_chain",
Unsigned::CreateChain {
base: base(),
chain: id(6),
vm_id: id(8),
name: "a chain".to_string(),
fx_ids: vec![id(1), id(2)],
genesis: b"genesis bytes".to_vec(),
chain_auth: vec![0, 2],
},
"5a4150000200000020010000e101000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c800000000000000000000000100000000000000000000000000000001010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202000000000200000007010000000303030303030303030303030303030303030303030303030303030303030303cbfeffff010000000bffffff010000001bffffff0100000073ffffff01000000000000000000000006060606060606060606060606060606060606060606060606060606060606060808080808080808080808080808080808080808080808080808080808080808200000000700000023ffffff02000000170000000d00000053ffffff020000006120636861696e67656e65736973206279746573",
);
}
#[test]
fn an_import_transaction_is_byte_identical_to_go() {
same_wire(
"import",
Unsigned::Import {
base: base(),
source_chain: id(4),
imported: vec![input(2, 1, 300)],
},
"5a4150000200000040010000bd01000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c8000000000000000000000001000000000000000000000000000000020202020202020202020202020202020202020202020202020202020202020201000000090909090909090909090909090909090909090909090909090909090909090900000000000000002c01000000000000000000000100000000000000000000000000000004010000000303030303030303030303030303030303030303030303030303030303030303abfeffff01000000ebfeffff01000000fbfeffff0100000053ffffff01000000000000000000000004040404040404040404040404040404040404040404040404040404040404042bffffff0100000083ffffff01000000",
);
}
#[test]
fn an_export_transaction_is_byte_identical_to_go() {
same_wire(
"export",
Unsigned::Export {
base: base(),
destination_chain: id(4),
exported: vec![out(9, 40, 1)],
},
"5a4150000200000038010000b501000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c800000000000000000000000100000000000000000000000000000009090909090909090909090909090909090909090909090909090909090909090000000000000000280000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000005010000000303030303030303030303030303030303030303030303030303030303030303b3feffff01000000f3feffff0100000003ffffff010000005bffffff010000000000000000000000040404040404040404040404040404040404040404040404040404040404040433ffffff0100000073ffffff01000000",
);
}
#[test]
fn the_block_kinds_are_byte_identical_to_go() {
let cases: &[(&str, block::Block, &str)] = &[
(
"standard",
block::Block::standard(id(1), 5, 1000, Vec::new()),
"5a4150000200000010000000510000000401010101010101010101010101010101010101010101010101010101010101010500000000000000e80300000000000000000000000000000000000000000000",
),
("commit", block::Block::commit(id(1), 5, 1000), "5a4150000200000010000000410000000201010101010101010101010101010101010101010101010101010101010101010500000000000000e803000000000000"),
("abort", block::Block::abort(id(1), 5, 1000), "5a4150000200000010000000410000000101010101010101010101010101010101010101010101010101010101010101010500000000000000e803000000000000"),
];
for (name, blk, go_hex) in cases {
let want = unhex(go_hex);
assert_eq!(
hex(blk.bytes()),
hex(&want),
"{name}: bytes differ from Go's"
);
// A block's name is the hash of exactly those bytes, so Go and this
// port call the same block by the same name.
let back = block::Block::parse(&want).unwrap();
assert_eq!(back.id(), blk.id(), "{name}: different ids for one block");
assert_eq!(back.kind(), blk.kind());
assert_eq!(back.parent(), id(1));
assert_eq!(back.height(), 5);
assert_eq!(back.timestamp(), 1000);
}
}
#[test]
fn gos_transaction_bytes_are_a_prefix_of_the_signed_form() {
// The property the whole signing scheme rests on, checked against real Go
// bytes rather than against bytes this port made up.
let unsigned = unhex("5a41500002000000d80000002501000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000301000000030303030303030303030303030303030303030303030303030303030303030313ffffff0100000053ffffff0100000063ffffff01000000bbffffff010000000000000000000000");
let tx = Tx::parse(&unsigned).unwrap();
assert_eq!(tx.unsigned_bytes(), &unsigned[..]);
assert_eq!(tx.unsigned.kind(), Kind::Base);
}
#[test]
fn a_signer_that_go_would_write_reads_back() {
// Go stores a signer inline: one tag byte, then the key, then the proof.
// A permissionless validator is the one transaction that carries one, so
// the round trip is checked through a whole transaction rather than in
// isolation.
let sk = blst::min_pk::SecretKey::key_gen(&[4u8; 32], &[]).unwrap();
let tx = Unsigned::AddPermissionlessValidator {
base: base(),
validator: validator(),
chain: lux_platformvm::ids::PRIMARY_NETWORK_ID,
signer: Signer::prove(&sk),
stake: stake(),
validator_rewards_owner: owners(3),
delegator_rewards_owner: owners(4),
delegation_shares: 20_000,
};
let bytes = tx.to_bytes();
assert_eq!(Unsigned::parse(&bytes).unwrap(), tx);
}
#[test]
fn a_remove_chain_validator_transaction_is_byte_identical_to_go() {
same_wire(
"remove_chain_validator",
Unsigned::RemoveChainValidator {
base: base(),
node_id: NodeId([5; 20]),
chain: id(6),
chain_auth: vec![0],
},
"5a41500002000000e00000006901000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000000000000000000090100000003030303030303030303030303030303030303030303030303030303030303030bffffff010000004bffffff010000005bffffff01000000b3ffffff0100000000000000000000000505050505050505050505050505050505050505060606060606060606060606060606060606060606060606060606060606060677ffffff01000000",
);
}
#[test]
fn a_transfer_chain_ownership_transaction_is_byte_identical_to_go() {
same_wire(
"transfer_chain_ownership",
Unsigned::TransferChainOwnership {
base: base(),
chain: id(6),
chain_auth: vec![0],
owner: owners(7),
},
"5a41500002000000f80000008101000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c8000000000000000000000001000000000000000000000000000000000000000000000007070707070707070707070707070707070707070000000008010000000303030303030303030303030303030303030303030303030303030303030303f3feffff0100000033ffffff0100000043ffffff010000009bffffff010000000000000000000000060606060606060606060606060606060606060606060606060606060606060673ffffff0100000001000000000000000000000067ffffff01000000",
);
}
#[test]
fn an_increase_l1_balance_transaction_is_byte_identical_to_go() {
same_wire(
"increase_l1_balance",
Unsigned::IncreaseL1ValidatorBalance {
base: base(),
validation_id: id(13),
balance: 999,
},
"5a41500002000000d80000004d01000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000001201000000030303030303030303030303030303030303030303030303030303030303030313ffffff0100000053ffffff0100000063ffffff01000000bbffffff0100000000000000000000000d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0de703000000000000",
);
}
#[test]
fn a_disable_l1_validator_transaction_is_byte_identical_to_go() {
same_wire(
"disable_l1_validator",
Unsigned::DisableL1Validator {
base: base(),
validation_id: id(13),
auth: vec![0, 1],
},
"5a41500002000000e00000005501000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000000000001000000130100000003030303030303030303030303030303030303030303030303030303030303030bffffff010000004bffffff010000005bffffff01000000b3ffffff0100000000000000000000000d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d8bffffff02000000",
);
}
#[test]
fn a_permissionless_delegator_transaction_is_byte_identical_to_go() {
same_wire(
"add_permissionless_delegator",
Unsigned::AddPermissionlessDelegator {
base: base(),
validator: validator(),
chain: id(6),
stake: stake(),
rewards_owner: owners(3),
},
"5a41500002000000500100000d02000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000909090909090909090909090909090909090909090909090909090909090909000000000000000032000000000000000100000000000000000000000000000001000000000000000202020202020202020202020202020202020202000000000303030303030303030303030303030303030303000000000f0100000003030303030303030303030303030303030303030303030303030303030303039bfeffff01000000dbfeffff01000000ebfeffff0100000043ffffff0100000000000000000000000505050505050505050505050505050505050505e803000000000000d00700000000000032000000000000000606060606060606060606060606060606060606060606060606060606060606effeffff010000002fffffff0100000001000000000000000000000033ffffff01000000",
);
}
/// The transaction this chain exists for: anyone, with a stake and a key.
#[test]
fn a_permissionless_validator_transaction_is_byte_identical_to_go() {
same_wire(
"add_permissionless_validator",
Unsigned::AddPermissionlessValidator {
base: base(),
validator: validator(),
chain: lux_platformvm::ids::PRIMARY_NETWORK_ID,
signer: Signer::ProofOfPossession {
public_key: [11; 48],
proof: [12; 96],
},
stake: stake(),
validator_rewards_owner: owners(3),
delegator_rewards_owner: owners(4),
delegation_shares: 20_000,
},
"5a4150000200000068010000ce02000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000909090909090909090909090909090909090909090909090909090909090909000000000000000032000000000000000100000000000000000000000000000001000000000000000202020202020202020202020202020202020202000000000303030303030303030303030303030303030303000000000404040404040404040404040404040404040404000000000e01000000030303030303030303030303030303030303030303030303030303030303030383feffff01000000c3feffff01000000d3feffff010000002bffffff0100000000000000000000000505050505050505050505050505050505050505e803000000000000d00700000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000010b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c46feffff0100000086feffff010000000100000000000000000000008afeffff010000000100000000000000000000008efeffff01000000204e0000",
);
}
#[test]
fn a_permissionless_validator_with_no_key_is_byte_identical_to_go() {
// The other arm of the signer: 145 bytes of which only the tag is set.
same_wire(
"add_permissionless_validator_empty_signer",
Unsigned::AddPermissionlessValidator {
base: base(),
validator: validator(),
chain: id(6),
signer: Signer::Empty,
stake: stake(),
validator_rewards_owner: owners(3),
delegator_rewards_owner: owners(4),
delegation_shares: 20_000,
},
"5a4150000200000068010000ce02000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000909090909090909090909090909090909090909090909090909090909090909000000000000000032000000000000000100000000000000000000000000000001000000000000000202020202020202020202020202020202020202000000000303030303030303030303030303030303030303000000000404040404040404040404040404040404040404000000000e01000000030303030303030303030303030303030303030303030303030303030303030383feffff01000000c3feffff01000000d3feffff010000002bffffff0100000000000000000000000505050505050505050505050505050505050505e803000000000000d007000000000000320000000000000006060606060606060606060606060606060606060606060606060606060606060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046feffff0100000086feffff010000000100000000000000000000008afeffff010000000100000000000000000000008efeffff01000000204e0000",
);
}
/// Credentials, including an empty one in the middle — the case where the
/// shared signature array's ranges are the only thing keeping the entries
/// apart.
#[test]
fn a_signed_transaction_is_byte_identical_to_go() {
let want = unhex("5a41500002000000d80000002501000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000301000000030303030303030303030303030303030303030303030303030303030303030313ffffff0100000053ffffff0100000063ffffff01000000bbffffff0100000000000000000000005a41500002000000f000000000010000000000000200000002000000000000000200000001000000010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010102020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303000000000020ffffff0300000030ffffff03000000");
let creds = vec![
Credential {
sigs: vec![[1u8; 65], [2u8; 65]],
},
Credential { sigs: vec![] },
Credential {
sigs: vec![[3u8; 65]],
},
];
let tx = Tx::new(Unsigned::Base(base()), creds.clone());
assert_eq!(hex(tx.bytes()), hex(&want), "signed bytes differ from Go's");
let back = Tx::parse(&want).unwrap();
assert_eq!(back.creds, creds);
assert_eq!(back.id(), tx.id());
// The unsigned prefix Go signed is the one this port would sign.
assert_eq!(back.sighash(), tx.sighash());
}
#[test]
fn a_proposal_block_is_byte_identical_to_go() {
let want = unhex("5a41500002000000100000008a0000000301010101010101010101010101010101010101010101010101010101010101010500000000000000e8030000000000000000000000000000000000000000000008000000310000005a415000020000001000000031000000020707070707070707070707070707070707070707070707070707070707070707");
let proposal = Tx::new(
Unsigned::RewardValidator {
staker_tx_id: id(7),
},
Vec::new(),
);
let blk = block::Block::proposal(id(1), 5, 1000, proposal.clone());
assert_eq!(
hex(blk.bytes()),
hex(&want),
"proposal bytes differ from Go's"
);
let back = block::Block::parse(&want).unwrap();
assert_eq!(back.id(), blk.id());
assert_eq!(back.proposal_tx().unwrap().id(), proposal.id());
assert!(back.decision_txs().is_empty());
}
#[test]
fn a_standard_block_carrying_a_transaction_is_byte_identical_to_go() {
let want = unhex("5a41500002000000180000007e02000025020000000000000401010101010101010101010101010101010101010101010101010101010101010500000000000000e803000000000000c7ffffff0100000008000000250200005a41500002000000d80000002501000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000301000000030303030303030303030303030303030303030303030303030303030303030313ffffff0100000053ffffff0100000063ffffff01000000bbffffff0100000000000000000000005a41500002000000f000000000010000000000000200000002000000000000000200000001000000010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010102020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303000000000020ffffff0300000030ffffff03000000");
let tx = Tx::new(
Unsigned::Base(base()),
vec![
Credential {
sigs: vec![[1u8; 65], [2u8; 65]],
},
Credential { sigs: vec![] },
Credential {
sigs: vec![[3u8; 65]],
},
],
);
let blk = block::Block::standard(id(1), 5, 1000, vec![tx.clone()]);
assert_eq!(
hex(blk.bytes()),
hex(&want),
"standard bytes differ from Go's"
);
let back = block::Block::parse(&want).unwrap();
assert_eq!(back.id(), blk.id());
assert_eq!(back.decision_txs().len(), 1);
assert_eq!(back.decision_txs()[0].id(), tx.id());
}
/// The permissionless validator the `add_permissionless_validator` vector
/// carries, reused as the one genesis admits.
fn permissionless_validator() -> Unsigned {
Unsigned::AddPermissionlessValidator {
base: base(),
validator: validator(),
chain: [0; 32],
signer: Signer::ProofOfPossession {
public_key: [11; 48],
proof: [12; 96],
},
stake: stake(),
validator_rewards_owner: owners(3),
delegator_rewards_owner: owners(4),
delegation_shares: 20_000,
}
}
/// The chain the `create_chain` vector makes, reused as the one genesis makes.
fn create_chain() -> Unsigned {
Unsigned::CreateChain {
base: base(),
chain: id(6),
vm_id: id(8),
name: "a chain".to_string(),
fx_ids: vec![id(1), id(2)],
genesis: b"genesis bytes".to_vec(),
chain_auth: vec![0, 2],
}
}
/// The two genesis validators the network vectors carry, in node-id order.
fn genesis_set() -> Vec<NetworkValidator> {
vec![
NetworkValidator {
node_id: vec![5u8; 20],
weight: 100,
balance: 900,
signer: Signer::ProofOfPossession {
public_key: [11u8; 48],
proof: [12u8; 96],
},
remaining_balance_owner: PChainOwner {
threshold: 1,
addresses: vec![ShortId([1; 20])],
},
deactivation_owner: PChainOwner {
threshold: 2,
addresses: vec![ShortId([2; 20]), ShortId([3; 20])],
},
},
NetworkValidator {
node_id: vec![6u8; 20],
weight: 200,
balance: 800,
signer: Signer::ProofOfPossession {
public_key: [13u8; 48],
proof: [14u8; 96],
},
remaining_balance_owner: PChainOwner {
threshold: 1,
addresses: vec![ShortId([4; 20])],
},
deactivation_owner: PChainOwner {
threshold: 0,
addresses: Vec::new(),
},
},
]
}
#[test]
fn a_transform_chain_transaction_is_byte_identical_to_go() {
same_wire(
"transform_chain",
Unsigned::TransformChain {
base: base(),
chain: id(6),
asset_id: id(14),
initial_supply: 1_000_000,
maximum_supply: 2_000_000,
min_consumption_rate: 100_000,
max_consumption_rate: 120_000,
min_validator_stake: 1_000,
max_validator_stake: 500_000,
min_stake_duration: 3600,
max_stake_duration: 86400,
min_delegation_fee: 20_000,
min_delegator_stake: 500,
max_validator_weight_factor: 5,
uptime_requirement: 800_000,
chain_auth: vec![0, 3],
},
"5a41500002000000e0000000be01000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c800000000000000000000000100000000000000000000000000000000000000030000000a0100000003030303030303030303030303030303030303030303030303030303030303030bffffff010000004bffffff010000005bffffff01000000b3ffffff01000000000000000000000006060606060606060606060606060606060606060606060606060606060606060e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e40420f000000000080841e0000000000a086010000000000c0d4010000000000e80300000000000020a1070000000000100e000080510100204e0000f4010000000000000500350c0022ffffff02000000",
);
}
#[test]
fn a_register_l1_validator_transaction_is_byte_identical_to_go() {
same_wire(
"register_l1_validator",
Unsigned::RegisterL1Validator {
base: base(),
balance: 777,
proof_of_possession: [15u8; 96],
message: b"a warp message".to_vec(),
},
"5a41500002000000d8000000a301000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000001001000000030303030303030303030303030303030303030303030303030303030303030313ffffff0100000053ffffff0100000063ffffff01000000bbffffff01000000000000000000000009030000000000000f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f080000000e000000612077617270206d657373616765",
);
}
#[test]
fn a_set_l1_validator_weight_transaction_is_byte_identical_to_go() {
same_wire(
"set_l1_validator_weight",
Unsigned::SetL1ValidatorWeight {
base: base(),
message: b"another warp message".to_vec(),
},
"5a41500002000000d80000004101000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000001101000000030303030303030303030303030303030303030303030303030303030303030313ffffff0100000053ffffff0100000063ffffff01000000bbffffff0100000000000000000000000800000014000000616e6f746865722077617270206d657373616765",
);
}
#[test]
fn a_create_network_transaction_is_byte_identical_to_go() {
same_wire(
"create_network",
Unsigned::CreateNetwork {
base: base(),
parent: [0u8; 32],
owner: owners(7),
security: Mode {
restake_parent: false,
admission: Admission::Open,
threshold: 1000,
manager: Manager::Contract,
},
validators: genesis_set(),
manager_chain_id: id(8),
manager_address: b"manager address".to_vec(),
},
"5a41500002000000c0020000c303000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c8000000000000000000000001000000000000000000000000000000070707070707070707070707070707070707070700000000640000000000000084030000000000000b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0000000014000000010000000000000001000000020000000100000002000000c80000000000000020030000000000000d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e14000000140000000100000003000000010000000000000004000000000000000101010101010101010101010101010101010101020202020202020202020202020202020202020203030303030303030303030303030303030303030404040404040404040404040404040404040404060100000003030303030303030303030303030303030303030303030303030303030303032bfdffff010000006bfdffff010000007bfdffff01000000d3fdffff01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000009ffdffff01000000000101e803000000000000a4fdffff02000000380000002800000014ffffff040000000808080808080808080808080808080808080808080808080808080808080808300000000f000000050505050505050505050505050505050505050506060606060606060606060606060606060606066d616e616765722061646472657373",
);
}
/// The simplest network there is: it restakes its parent, runs no set of its
/// own, and so carries no validators at all.
#[test]
fn a_restaking_network_transaction_is_byte_identical_to_go() {
same_wire(
"create_network_restaking",
Unsigned::CreateNetwork {
base: base(),
parent: id(6),
owner: owners(7),
security: Mode {
restake_parent: true,
admission: Admission::NoOwnSet,
threshold: 0,
manager: Manager::PChain,
},
validators: Vec::new(),
manager_chain_id: [0u8; 32],
manager_address: Vec::new(),
},
"5a41500002000000f0000000bc01000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c800000000000000000000000100000000000000000000000000000007070707070707070707070707070707070707070000000006010000000303030303030303030303030303030303030303030303030303030303030303fbfeffff010000003bffffff010000004bffffff01000000a3ffffff01000000000000000000000006060606060606060606060606060606060606060606060606060606060606060100000000000000000000006fffffff01000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
);
}
#[test]
fn a_convert_network_transaction_is_byte_identical_to_go() {
same_wire(
"convert_network",
Unsigned::ConvertNetwork {
base: base(),
network: id(6),
parent: [0u8; 32],
manager_chain_id: id(8),
manager_address: b"manager address".to_vec(),
validators: genesis_set(),
auth: vec![1, 2],
security: Mode {
restake_parent: false,
admission: Admission::Gated,
threshold: 0,
manager: Manager::Contract,
},
},
"5a41500002000000b0020000c703000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c8000000000000000000000001000000000000000000000000000000640000000000000084030000000000000b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0000000014000000010000000000000001000000020000000100000002000000c80000000000000020030000000000000d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e140000001400000001000000030000000100000000000000040000000000000001010101010101010101010101010101010101010202020202020202020202020202020202020202030303030303030303030303030303030303030304040404040404040404040404040404040404040100000002000000140100000003030303030303030303030303030303030303030303030303030303030303033bfdffff010000007bfdffff010000008bfdffff01000000e3fdffff010000000000000000000000060606060606060606060606060606060606060606060606060606060606060600000000000000000000000000000000000000000000000000000000000000000808080808080808080808080808080808080808080808080808080808080808330000000f00000073fdffff020000003200000028000000e3feffff040000002bffffff0200000000020100000000000000006d616e61676572206164647265737305050505050505050505050505050505050505050606060606060606060606060606060606060606",
);
}
/// The envelope an unspent output travels in, and the second envelope a lock
/// puts around it.
#[test]
fn an_unspent_output_is_byte_identical_to_go() {
let plain = Utxo {
id: UtxoId {
tx_id: [0; 32],
output_index: 0,
},
output: Output {
asset: id(9),
stake_lock: 0,
amount: 100,
owners: owners(1),
},
};
assert_eq!(hex(&plain.wire_bytes()), "000a5a4150000200000010000000a20000000000000000000000000000000000000000000000000000000000000000000000000000000909090909090909090909090909090909090909090909090909090909090909080000004600000001015a4150000200000028000000440000000101010101010101010101010101010101010101000000006400000000000000000000000000000001000000d4ffffff01000000");
assert_eq!(Utxo::parse_wire(&unhex("000a5a4150000200000010000000a20000000000000000000000000000000000000000000000000000000000000000000000000000000909090909090909090909090909090909090909090909090909090909090909080000004600000001015a4150000200000028000000440000000101010101010101010101010101010101010101000000006400000000000000000000000000000001000000d4ffffff01000000")).unwrap(), plain);
let locked = Utxo {
id: UtxoId {
tx_id: [0; 32],
output_index: 1,
},
output: Output {
asset: id(9),
stake_lock: 5000,
amount: 250,
owners: owners(1),
},
};
assert_eq!(hex(&locked.wire_bytes()), "000a5a4150000200000010000000c400000000000000000000000000000000000000000000000000000000000000000000000100000009090909090909090909090909090909090909090909090909090909090909090800000068000000000f5a4150000200000010000000660000008813000000000000080000004600000001015a415000020000002800000044000000010101010101010101010101010101010101010100000000fa00000000000000000000000000000001000000d4ffffff01000000");
assert_eq!(Utxo::parse_wire(&unhex("000a5a4150000200000010000000c400000000000000000000000000000000000000000000000000000000000000000000000100000009090909090909090909090909090909090909090909090909090909090909090800000068000000000f5a4150000200000010000000660000008813000000000000080000004600000001015a415000020000002800000044000000010101010101010101010101010101010101010100000000fa00000000000000000000000000000001000000d4ffffff01000000")).unwrap(), locked);
}
/// The whole first state of a network, byte for byte.
#[test]
fn a_genesis_is_byte_identical_to_go() {
let allocation = |index: u32, amount: u64, stake_lock: u64| Allocation {
utxo: Utxo {
id: UtxoId {
tx_id: [0; 32],
output_index: index,
},
output: Output {
asset: id(9),
stake_lock,
amount,
owners: owners(1),
},
},
message: b"hello".to_vec(),
};
let g = Genesis {
utxos: vec![allocation(0, 100, 0), allocation(1, 250, 5000)],
validators: vec![Tx::new(permissionless_validator(), Vec::new())],
chains: vec![Tx::new(create_chain(), Vec::new())],
timestamp: 1000,
initial_supply: 360_000_000_000_000,
message: "let there be light".to_string(),
};
let want = unhex("5a4150000200000028000000e5060000c9000000eb000000ce02000000000000e101000000000000e80300000000000000801e086b4701003800000012000000d0ffffff020000003a000000b4010000c8ffffff01000000de010000ce020000c0ffffff010000009c040000e10100006c6574207468657265206265206c696768745a4150000200000010000000c900000010000000a4000000ac00000005000000000a5a4150000200000010000000a20000000000000000000000000000000000000000000000000000000000000000000000000000000909090909090909090909090909090909090909090909090909090909090909080000004600000001015a4150000200000028000000440000000101010101010101010101010101010101010101000000006400000000000000000000000000000001000000d4ffffff0100000068656c6c6f5a4150000200000010000000eb00000010000000c6000000ce00000005000000000a5a4150000200000010000000c400000000000000000000000000000000000000000000000000000000000000000000000100000009090909090909090909090909090909090909090909090909090909090909090800000068000000000f5a4150000200000010000000660000008813000000000000080000004600000001015a415000020000002800000044000000010101010101010101010101010101010101010100000000fa00000000000000000000000000000001000000d4ffffff0100000068656c6c6f5a4150000200000068010000ce02000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c80000000000000000000000010000000000000000000000000000000909090909090909090909090909090909090909090909090909090909090909000000000000000032000000000000000100000000000000000000000000000001000000000000000202020202020202020202020202020202020202000000000303030303030303030303030303030303030303000000000404040404040404040404040404040404040404000000000e01000000030303030303030303030303030303030303030303030303030303030303030383feffff01000000c3feffff01000000d3feffff010000002bffffff0100000000000000000000000505050505050505050505050505050505050505e803000000000000d00700000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000010b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c46feffff0100000086feffff010000000100000000000000000000008afeffff010000000100000000000000000000008efeffff01000000204e00005a4150000200000020010000e101000009090909090909090909090909090909090909090909090909090909090909090000000000000000640000000000000001000000000000000000000000000000010000000000000001010101010101010101010101010101010101010000000001010101010101010101010101010101010101010101010101010101010101010000000009090909090909090909090909090909090909090909090909090909090909090000000000000000c800000000000000000000000100000000000000000000000000000001010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202000000000200000007010000000303030303030303030303030303030303030303030303030303030303030303cbfeffff010000000bffffff010000001bffffff0100000073ffffff01000000000000000000000006060606060606060606060606060606060606060606060606060606060606060808080808080808080808080808080808080808080808080808080808080808200000000700000023ffffff02000000170000000d00000053ffffff020000006120636861696e67656e65736973206279746573");
assert_eq!(
hex(&g.to_bytes()),
hex(&want),
"genesis bytes differ from Go's"
);
let back = Genesis::parse(&want).expect("Go's genesis reads back");
assert_eq!(back, g);
// The transactions genesis carries keep the names they were published
// under — that is the whole reason they are stored as their own bytes.
assert_eq!(back.validators[0].id(), g.validators[0].id());
assert_eq!(back.chains[0].id(), g.chains[0].id());
}
/// The address a key spends under, and the address a signature names, both as
/// Go computes them.
///
/// This is the one thing the two implementations must agree about before any
/// spend can be checked: given a key, the same address; given a signature over
/// a hash, the same signer. A different hash chain or a different recovery
/// convention would let each side accept spends the other refuses.
#[test]
fn a_signature_names_the_address_go_names() {
let sighash: [u8; 32] = [7; 32];
let go_signature = unhex("111f20b9521ba1924ecfb91595426246b152cc1187e83f798cbd61f95f2c4cb107da8d209539506429d1ecd4033b2c207b89267f7dd8674421737193cd84f1dc00");
let mut sig = [0u8; 65];
sig.copy_from_slice(&go_signature);
let recovered = sign::recover(&sighash, &sig).expect("Go's signature recovers");
assert_eq!(
hex(&recovered.0),
"fc7250a211deddc70ee5a2738de5f07817351cef"
);
}
/// Go's `TestGoldenAbortBlock`, byte for byte.
///
/// These bytes are the chain's commitment: an id is the hash of them, so any
/// drift in the kind byte's position, a field's order, or the header would be
/// a different chain. The vector is Go's own, pinned at the re-genesis
/// cutover, with a parent of 0x00..0x1f, height 0x1122334455667788 and time
/// 0x0102030405060708.
#[test]
fn the_golden_abort_block_is_byte_identical_to_go() {
let golden: [u8; 65] = [
0x5a, 0x41, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00,
0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
0x1c, 0x1d, 0x1e, 0x1f, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x08, 0x07, 0x06,
0x05, 0x04, 0x03, 0x02, 0x01,
];
let mut parent = [0u8; 32];
for (i, b) in parent.iter_mut().enumerate() {
*b = i as u8;
}
let blk = block::Block::abort(parent, 0x1122_3344_5566_7788, 0x0102_0304_0506_0708);
assert_eq!(hex(blk.bytes()), hex(&golden), "abort block bytes drifted");
// And the id is the hash of exactly those bytes.
let parsed = block::Block::parse(&golden).expect("Go's golden block parses");
assert_eq!(parsed.id(), blk.id());
assert_eq!(parsed.bytes(), golden);
}
/// Go's `TestBlockRoundTripByteStability` and `TestBlockTrailingBytesRejected`:
/// the bytes survive a read unchanged, the id is stable across it, and a
/// buffer with a tail — which wraps the same message and hashes differently —
/// is refused.
#[test]
fn every_block_kinds_bytes_survive_a_read_and_a_tail_is_refused() {
let tx = Tx::new(Unsigned::Base(base()), Vec::new());
let parent = id(0x0a);
let (height, ts) = (42u64, 1_700_000_000u64);
let blocks = [
block::Block::abort(parent, height, ts),
block::Block::commit(parent, height, ts),
block::Block::standard(parent, height, ts, vec![tx.clone()]),
block::Block::proposal(parent, height, ts, tx),
];
for blk in blocks {
let wire = blk.bytes().to_vec();
assert!(!wire.is_empty());
let parsed = block::Block::parse(&wire).expect("a block parses");
assert_eq!(parsed.bytes(), wire, "a read must preserve the bytes");
assert_eq!(parsed.id(), blk.id(), "the name must survive a read");
let again = block::Block::parse(parsed.bytes()).expect("and again");
assert_eq!(again.id(), blk.id(), "the encoding is canonical");
let mut malleable = wire.clone();
malleable.push(0);
assert!(
block::Block::parse(&malleable).is_err(),
"a block with a tail must be refused"
);
}
}
/// A validator's proof that it holds the key it signs with, made by Go.
///
/// Consensus aggregates signatures under these keys. Without the proof,
/// anyone could register the difference between an honest validator's key and
/// one they hold, and produce aggregates that validator never took part in.
/// The proof is a signature under a domain separate from every other signature
/// the network makes — and that domain has to be the same one on both sides,
/// or each accepts proofs the other refuses.
#[test]
fn a_proof_of_possession_go_made_verifies_here() {
let mut public_key = [0u8; 48];
public_key.copy_from_slice(&unhex(
"969e07701831d8f7a168f7059d0392abafbdc9bf363bd7fff182949d2e09a709d9daa5f0e12c124d7c99ad9640bdc1c4",
));
let mut proof = [0u8; 96];
proof.copy_from_slice(&unhex(
"a9894e862fd7d84c5adeb3e469d5da2ef54a18c3711c35662e3ca303e757ffe83aaf0db921689ee15b3334cbf3ddb867044e3003efab812b5e0103b0b6d16839e183b0e395b10079dda28ed084fe6d2995b61636aba68a4b167dd5c7953afde9",
));
let signer = Signer::ProofOfPossession { public_key, proof };
assert_eq!(signer.verify(), Ok(()), "Go's proof must verify here");
assert_eq!(signer.public_key(), Some(public_key));
// And the proof is a proof of THAT key: one byte of it is another key's
// proof, and this refuses it.
let mut wrong = public_key;
wrong[0] ^= 0x01;
assert!(Signer::ProofOfPossession {
public_key: wrong,
proof
}
.verify()
.is_err());
}