mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
chains/cpp/core is what every C++ chain here shares and shares ONE of: the fixed-width names and the hashes that derive them, ZAP, and the durable byte-keyed map the state rests on. The store is the X-Chain's, moved unchanged — it is the only one in the matrix that fsyncs, replays a torn tail and rewrites its log. The names are the two chains' vocabularies reconciled: Id IS lux::consensus::Id, so a block id crosses the seam with no conversion; NodeId stays a type of its own because it shares a width with an address and the compiler should refuse that swap. The X-Chain now declares no id, no codec and no store of its own. Its suite passes unchanged, which is the claim that matters: the wire golden vectors still hold, so the codec that moved is the codec that was there. And the store is proven the only way that counts. core_durability starts a second process, has it commit, and has the kernel SIGKILL it — no destructor, no flush. A third process reopens the file and must find every committed row byte for byte, nothing written after the last commit, and the same root folded over the set. A store that only survives a clean shutdown is a cache with good manners.
120 lines
4.8 KiB
C++
120 lines
4.8 KiB
C++
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
|
// SPDX-License-Identifier: BSD-3-Clause-Eco
|
|
//
|
|
// address_test.cpp — bech32, against strings the GO address module produced.
|
|
//
|
|
// The vectors below were printed by luxfi/address (FormatBech32 over the same
|
|
// twenty-byte addresses fixtures.hpp builds, then ParseBech32 back). An address
|
|
// that reads differently here than it does in Go is an address that sends value
|
|
// somewhere else, so the strings are asserted verbatim rather than round-tripped
|
|
// against themselves.
|
|
|
|
#include "lux/core/check.hpp"
|
|
#include "fixtures.hpp"
|
|
|
|
#include "lux/xvm/address.hpp"
|
|
|
|
using namespace lux::xvm;
|
|
using namespace lux::core::test;
|
|
using namespace lux::xvm::test;
|
|
|
|
namespace {
|
|
|
|
struct Vector {
|
|
std::uint8_t seed;
|
|
const char* text;
|
|
};
|
|
|
|
// GENERATED by luxfi/address: FormatBech32("lux", addr(seed)).
|
|
constexpr Vector kVectors[] = {
|
|
{1, "lux1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5seedvj"},
|
|
{40, "lux19q5j52ev95hz7vp3xgengdfkxuurjw3m9f2n8m"},
|
|
{7, "lux1quyqjzstpsxsurcszyfpx9q4zct3sxg68fn9m0"},
|
|
{0, "lux1qqqsyqcyq5rqwzqfpg9scrgwpugpzysnvdyfmh"},
|
|
{255, "lux1luqqzqsrqszsvpcgpy9qkrqdpc83qygjfml03l"},
|
|
};
|
|
|
|
constexpr const char* kLocalHrp = "local1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5nnu3uf";
|
|
|
|
void formats_the_go_string() {
|
|
for (const auto& v : kVectors) {
|
|
auto s = address::format("lux", view(addr(v.seed)));
|
|
check(s.has_value(), std::string("format addr(") + std::to_string(v.seed) + ")");
|
|
if (s) check_eq(*s, v.text, std::string("addr(") + std::to_string(v.seed) + ") is Go's");
|
|
}
|
|
auto s = address::format("local", view(addr(1)));
|
|
check(s.has_value(), "format under another hrp");
|
|
if (s) check_eq(*s, kLocalHrp, "the hrp is part of the checksum, not a label");
|
|
}
|
|
|
|
void parses_the_go_string() {
|
|
for (const auto& v : kVectors) {
|
|
auto got = address::parse(v.text);
|
|
check(got.has_value(), std::string("parse addr(") + std::to_string(v.seed) + ")");
|
|
if (!got) continue;
|
|
check_eq(got->first, "lux", "the hrp comes back");
|
|
check_eq(hex_of(got->second), hex_of(addr(v.seed)),
|
|
std::string("addr(") + std::to_string(v.seed) + ") payload round-trips");
|
|
}
|
|
}
|
|
|
|
void parse_short_is_the_whole_journey() {
|
|
auto a = address::parse_short(kVectors[0].text);
|
|
check(a.has_value(), "parse_short reads an owner");
|
|
if (a) check(*a == addr(1), "parse_short gives the twenty bytes");
|
|
}
|
|
|
|
void a_typo_is_refused() {
|
|
// One character changed. This is the whole reason the checksum is there.
|
|
std::string bad = kVectors[0].text;
|
|
bad[bad.size() - 1] = (bad[bad.size() - 1] == 'j') ? 'k' : 'j';
|
|
auto got = address::parse(bad);
|
|
check(!got.has_value(), "a single mistyped character is refused");
|
|
if (!got) check_eq(got.error(), address::kErrBadChecksum, "and refused as a bad checksum");
|
|
|
|
// The human-readable part is inside the checksum, so an address written for
|
|
// one network cannot be read as an address on another.
|
|
std::string wrong_hrp = "fuji" + std::string(kVectors[0].text).substr(3);
|
|
check(!address::parse(wrong_hrp).has_value(), "the hrp cannot be swapped out");
|
|
}
|
|
|
|
void the_refusals() {
|
|
check(!address::parse("").has_value(), "an empty string is not an address");
|
|
check_eq(address::parse("lux").error(), address::kErrNoSeparator, "too short to hold one");
|
|
check_eq(address::parse("1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5seedvj").error(),
|
|
address::kErrNoSeparator, "an empty hrp is refused");
|
|
check_eq(address::parse(std::string("Lux1") + (kVectors[0].text + 4)).error(),
|
|
address::kErrMixedCase, "mixed case is refused");
|
|
check_eq(address::parse("lux1qypqxpq9qcrsszg2pvxq6rs0zqg3yybc5seedvj").error(),
|
|
address::kErrBadCharacter, "'b' is not in the charset");
|
|
check_eq(address::parse(std::string(91, 'q')).error(), address::kErrTooLong,
|
|
"over ninety characters is refused");
|
|
|
|
auto too_short = address::short_id(view(Bytes{1, 2, 3}));
|
|
check(!too_short.has_value(), "an owner is exactly twenty bytes");
|
|
}
|
|
|
|
void bits_regroup() {
|
|
// 5→8 of thirty-two groups is exactly twenty bytes, which is why an address
|
|
// needs no padding at all.
|
|
Bytes five(32, 0);
|
|
auto eight = address::convert_bits(view(five), 5, 8, true);
|
|
check(eight.has_value() && eight->size() == 20, "thirty-two five-bit groups are twenty bytes");
|
|
auto back = address::convert_bits(view(*eight), 8, 5, true);
|
|
check(back.has_value() && back->size() == 32, "and twenty bytes are thirty-two groups");
|
|
check(!address::convert_bits(view(five), 0, 8, true).has_value(), "a zero-bit group is refused");
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
std::printf("address\n");
|
|
formats_the_go_string();
|
|
parses_the_go_string();
|
|
parse_short_is_the_whole_journey();
|
|
a_typo_is_refused();
|
|
the_refusals();
|
|
bits_regroup();
|
|
return report("address");
|
|
}
|