use crate::{
block::{genesis::regtest_genesis_block, Block, MAX_BLOCK_BYTES},
parameters::Network,
serialization::{
CompactSizeMessage, SerializationError, ZcashDeserialize, ZcashDeserializeInto,
ZcashSerialize,
},
work::equihash::{Error, Solution, REGTEST_SOLUTION_SIZE, SOLUTION_SIZE},
};
use super::super::*;
const EQUIHASH_SOLUTION_BLOCK_OFFSET: usize = equihash::Solution::INPUT_LENGTH + 32;
const BLOCK_HEADER_LENGTH: usize = EQUIHASH_SOLUTION_BLOCK_OFFSET + 3 + equihash::SOLUTION_SIZE;
#[test]
fn equihash_solution_test_vectors() {
let _init_guard = zakura_test::init();
for block in zakura_test::vectors::BLOCKS.iter() {
let solution_bytes = &block[EQUIHASH_SOLUTION_BLOCK_OFFSET..BLOCK_HEADER_LENGTH];
let solution = solution_bytes
.zcash_deserialize_into::<equihash::Solution>()
.expect("Test vector EquihashSolution should deserialize");
let mut data = Vec::new();
solution
.zcash_serialize(&mut data)
.expect("Test vector EquihashSolution should serialize");
assert_eq!(solution_bytes.len(), data.len());
assert_eq!(solution_bytes, data.as_slice());
}
}
#[test]
fn equihash_solution_test_vectors_are_valid() -> color_eyre::eyre::Result<()> {
let _init_guard = zakura_test::init();
for block in zakura_test::vectors::BLOCKS.iter() {
let block =
Block::zcash_deserialize(&block[..]).expect("block test vector should deserialize");
block
.header
.solution
.check(&block.header, &Network::Mainnet)?;
}
Ok(())
}
static EQUIHASH_SIZE_TESTS: &[usize] = &[
0,
1,
SOLUTION_SIZE - 1,
SOLUTION_SIZE,
SOLUTION_SIZE + 1,
(MAX_BLOCK_BYTES - 1) as usize,
MAX_BLOCK_BYTES as usize,
];
#[test]
fn equihash_solution_size_field() {
let _init_guard = zakura_test::init();
for size in EQUIHASH_SIZE_TESTS.iter().copied() {
let mut data = Vec::new();
let size: CompactSizeMessage = size
.try_into()
.expect("test size fits in MAX_PROTOCOL_MESSAGE_LEN");
size.zcash_serialize(&mut data)
.expect("CompactSize should serialize");
data.resize(data.len() + SOLUTION_SIZE, 0);
let result = Solution::zcash_deserialize(data.as_slice());
if size == SOLUTION_SIZE.try_into().unwrap() {
result.expect("Correct size field in EquihashSolution should deserialize");
} else {
result.expect_err("Wrong size field in EquihashSolution should fail on deserialize");
}
}
}
#[test]
fn equihash_solution_rejects_oversize_compactsize_before_allocating() {
let _init_guard = zakura_test::init();
let mut data = Vec::new();
let oversize: CompactSizeMessage = (SOLUTION_SIZE + 1)
.try_into()
.expect("fits in MAX_PROTOCOL_MESSAGE_LEN");
oversize
.zcash_serialize(&mut data)
.expect("CompactSize should serialize");
let err = Solution::zcash_deserialize(data.as_slice())
.expect_err("oversize equihash CompactSize must fail to deserialize");
assert!(
matches!(
err,
SerializationError::Parse("incorrect equihash solution size"),
),
"expected size-rejection Parse error, got: {err:?}",
);
}
#[test]
fn regtest_solution_is_rejected_off_regtest() {
let _init_guard = zakura_test::init();
let block = Block::zcash_deserialize(zakura_test::vectors::BLOCKS[0])
.expect("block test vector should deserialize");
let mut header = *block.header;
header.solution = Solution::Regtest([0; REGTEST_SOLUTION_SIZE]);
for network in [Network::Mainnet, Network::new_default_testnet()] {
assert!(
matches!(
header.solution.check(&header, &network),
Err(Error::InvalidSolutionSize { .. }),
),
"a 36-byte Regtest solution must be rejected on {network}",
);
}
assert!(
!matches!(
header
.solution
.check(&header, &Network::new_regtest(Default::default())),
Err(Error::InvalidSolutionSize { .. }),
),
"a Regtest solution must be accepted for verification on Regtest",
);
}
#[test]
fn real_regtest_solution_is_bound_to_regtest_parameters() {
let _init_guard = zakura_test::init();
let block = regtest_genesis_block();
let header = block.header.as_ref();
let regtest = Network::new_regtest(Default::default());
header
.solution
.check(header, ®test)
.expect("the hard-coded Regtest genesis solution must verify on Regtest");
for network in [Network::Mainnet, Network::new_default_testnet()] {
assert!(
matches!(
header.solution.check(header, &network),
Err(Error::InvalidSolutionSize { .. }),
),
"a real Regtest proof must not verify on {network}",
);
}
}
#[test]
fn real_common_solution_is_rejected_on_regtest() {
let _init_guard = zakura_test::init();
let block = Block::zcash_deserialize(zakura_test::vectors::BLOCKS[0])
.expect("block test vector should deserialize");
let header = block.header.as_ref();
let regtest = Network::new_regtest(Default::default());
header
.solution
.check(header, &Network::Mainnet)
.expect("the hard-coded Mainnet solution must verify on Mainnet");
assert!(
matches!(
header.solution.check(header, ®test),
Err(Error::InvalidSolutionSize { .. }),
),
"a real Common proof must not verify on Regtest",
);
}