mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
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]>
841 lines
53 KiB
Rust
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());
|
|
}
|