use dig_clvm::{NetworkConstants, DIG_MAINNET, DIG_TESTNET};
use hex_literal::hex;
use sha2::{Digest, Sha256};
const CANONICAL_MAINNET_GENESIS: [u8; 32] =
hex!("0af981862a4df51f51ec59c312315d959931d917c375730b89b9e2b0854d1abf");
const CANONICAL_TESTNET_GENESIS: [u8; 32] =
hex!("088c18d6b7859d885dc2f03166e862c958f74b63b6353c3df71d103b9b806c3b");
const AGG_SIG_OPCODES: [(&str, u8); 6] = [
("agg_sig_parent", 43),
("agg_sig_puzzle", 44),
("agg_sig_amount", 45),
("agg_sig_puzzle_amount", 46),
("agg_sig_parent_amount", 47),
("agg_sig_parent_puzzle", 48),
];
fn derive_agg_sig_domain(genesis: &[u8; 32], opcode: u8) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(genesis);
hasher.update([opcode]);
hasher.finalize().into()
}
fn actual_agg_sig_domains(constants: &NetworkConstants) -> [[u8; 32]; 6] {
let consensus = constants.consensus();
[
consensus.agg_sig_parent_additional_data.to_bytes(),
consensus.agg_sig_puzzle_additional_data.to_bytes(),
consensus.agg_sig_amount_additional_data.to_bytes(),
consensus.agg_sig_puzzle_amount_additional_data.to_bytes(),
consensus.agg_sig_parent_amount_additional_data.to_bytes(),
consensus.agg_sig_parent_puzzle_additional_data.to_bytes(),
]
}
fn assert_network_matches_genesis(
network: &str,
constants: &NetworkConstants,
expected_genesis: &[u8; 32],
) {
assert_eq!(
constants.genesis_challenge().to_bytes(),
*expected_genesis,
"{network} genesis_challenge must be the canonical value {}, got {}",
hex::encode(expected_genesis),
hex::encode(constants.genesis_challenge()),
);
assert_eq!(
constants.agg_sig_me_additional_data().to_bytes(),
*expected_genesis,
"{network} agg_sig_me_additional_data must equal the genesis challenge",
);
let actual = actual_agg_sig_domains(constants);
for (index, (name, opcode)) in AGG_SIG_OPCODES.iter().enumerate() {
let expected = derive_agg_sig_domain(expected_genesis, *opcode);
assert_eq!(
actual[index],
expected,
"{network} {name}_additional_data must be sha256(canonical_genesis || {opcode}) = {}, got {}",
hex::encode(expected),
hex::encode(actual[index]),
);
}
}
#[test]
fn con_008_mainnet_genesis_and_agg_sig_domains_are_canonical() {
assert_network_matches_genesis("DIG_MAINNET", &DIG_MAINNET, &CANONICAL_MAINNET_GENESIS);
}
#[test]
fn con_008_testnet_genesis_and_agg_sig_domains_are_canonical() {
assert_network_matches_genesis("DIG_TESTNET", &DIG_TESTNET, &CANONICAL_TESTNET_GENESIS);
}
#[test]
fn con_008_mainnet_and_testnet_genesis_challenges_differ() {
assert_ne!(
DIG_MAINNET.genesis_challenge(),
DIG_TESTNET.genesis_challenge(),
"mainnet and testnet genesis challenges must differ",
);
}
#[test]
fn con_008_no_genesis_challenge_is_a_placeholder() {
for (network, genesis) in [
("DIG_MAINNET", DIG_MAINNET.genesis_challenge()),
("DIG_TESTNET", DIG_TESTNET.genesis_challenge()),
] {
let nonzero_bytes = genesis.iter().filter(|byte| **byte != 0).count();
assert!(
nonzero_bytes >= 16,
"{network} genesis_challenge {} looks like a placeholder, not a real \
header hash ({nonzero_bytes} of 32 bytes non-zero)",
hex::encode(genesis),
);
}
}