:: lib::std::bytes::buffer
:: lib::std::bytes::sequence
:: lib::std::encoding::hex
:: lib::std::tests::asserts
:: lib::std::tests::testing
:: lib::std::text::strings;
const stringv ALPHA_DIGEST =
"aba5ed27f8ee302525deccc60adfb950fd3d71699082bb3b5c0bcad6a36ec139";
const stringv BETA_DIGEST =
"d4298ba679357d6f92c5ea1da21f8895a3e6d514b35e51b3e68081744d9f6b4a";
const stringv ALPHA_RECORD =
"000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000000616c706861aba5ed27f8ee302525deccc60adfb950fd3d71699082bb3b5c0bcad6a36ec139";
test canonical_block_matches_published_golden_vector(&testing::UnitTest t) {
record, digest, encodeError := codec::encode_block(
asserts::is_true(t, encodeError == none, "golden block encodes");
recordHex, recordHexError := hex::encode_bytes(record.bytes());
digestHex, digestHexError := hex::encode_bytes(digest[:]);
recordHexError == none and recordHex.view() == ALPHA_RECORD,
"canonical record bytes match the golden vector"
digestHexError == none and digestHex.view() == ALPHA_DIGEST,
"domain-separated block hash matches the golden vector"
test chain_round_trips_and_links_every_block(&testing::UnitTest t) {
chain, chainError := make_chain("alpha", "beta");
asserts::is_true(t, chainError == none, "chain construction succeeds");
blocks, parseError := codec::parse(chain.view());
asserts::is_true(t, parseError == none, "canonical chain parses");
asserts::is_true(t, blocks.len() == 2, "both blocks reload");
if parseError != none or blocks.len() != 2 {
firstDigest, firstHexError := hex::encode_bytes(values[0].hash());
secondDigest, secondHexError := hex::encode_bytes(values[1].hash());
values[0].index() == 0 and values[0].payload() == "alpha",
"the genesis block preserves its fields"
values[1].index() == 1 and values[1].payload() == "beta",
"the second block preserves its fields"
firstHexError == none and firstDigest.view() == ALPHA_DIGEST,
"genesis digest is stable"
secondHexError == none and secondDigest.view() == BETA_DIGEST,
sequence::equal(values[1].previousHash(), values[0].hash()),
"the second block links to the genesis digest"
test identical_chains_produce_identical_bytes(&testing::UnitTest t) {
first, firstError := make_chain("alpha", "beta");
second, secondError := make_chain("alpha", "beta");
firstError == none and secondError == none and
first.view() == second.view(),
"the canonical chain is reproducible"
test malformed_and_truncated_storage_is_rejected(&testing::UnitTest t) {
malformed, malformedError := codec::parse("not-a-ledger");
malformedError == model::LedgerError.InvalidFormat and
"a malformed header is rejected"
chain, chainError := make_chain("alpha", "beta");
truncated, truncatedError := codec::parse(
chain.view().substr(0, chain.len() - 1)
truncatedError == model::LedgerError.InvalidFormat and
"a truncated final digest is rejected after the valid prefix"
test tampered_payload_and_digest_are_rejected(&testing::UnitTest t) {
chain, chainError := make_chain("alpha", "beta");
tamperedPayload, payloadError := changed_byte(chain.view(), 64, 'A');
if payloadError != none {
_, tamperError := codec::parse(tamperedPayload.view());
tamperError == model::LedgerError.IntegrityFailure,
"payload tampering is detected"
tamperedDigest, digestError := changed_byte(chain.view(), 84, 0);
_, storedDigestError := codec::parse(tamperedDigest.view());
storedDigestError == model::LedgerError.IntegrityFailure,
"stored digest tampering is detected"
test reordered_blocks_are_rejected(&testing::UnitTest t) {
chain, chainError := make_chain("alpha", "bravo");
source := chain.view().cast<[]const u8>;
reordered, allocationError := buffer::Buffer::withCapacity(source.len());
if allocationError != none or
reordered.write(codec::FILE_HEADER.cast<[]const u8>) != none or
reordered.write(source[101:186]) != none or
reordered.write(source[16:101]) != none {
reorderedText, textError := strings::string_from_bytes(reordered.bytes());
_, parseError := codec::parse(reorderedText.view());
parseError == model::LedgerError.IntegrityFailure,
"record reordering breaks the index and hash chain"
(string, error) make_chain(stringv firstPayload, stringv secondPayload) {
output, allocationError := buffer::Buffer::withCapacity(256);
if allocationError != none {
return (none, allocationError);
error writeError = output.write(codec::FILE_HEADER.cast<[]const u8>);
return (none, writeError);
first, firstDigest, firstError := codec::encode_block(
return (none, firstError);
second, _, secondError := codec::encode_block(
return (none, secondError);
writeError = output.write(first.bytes());
writeError = output.write(second.bytes());
return (none, writeError);
return strings::string_from_bytes(output.bytes());
(string, error) changed_byte(stringv source, usize offset, u8 value) {
bytes, allocationError := new<own []u8>(source.len().cast<usize>);
if allocationError != none {
return (none, allocationError);
loop usize index = 0; index < bytes.len(); index++ {
bytes[index] = source[index.cast<u32>];
return strings::string_from_bytes(bytes[:]);