go-blockchain/docs/history.md
Claude 8af95d457a
docs: Phase 2 crypto bridge documentation
Updated architecture.md with crypto/ package details (CGo bridge pattern,
build flow, exposed operations, 1/8 premultiplication semantics).
Updated history.md with Phase 2 completion (files, findings, tests).
Updated CLAUDE.md with CMake build prerequisite.

Co-Authored-By: Charon <charon@lethean.io>
2026-02-20 18:49:21 +00:00

14 KiB

Project History

Origin

go-blockchain implements the Lethean blockchain protocol in pure Go, following ADR-001 (Go Shell + C++ Crypto Library). The chain lineage is CryptoNote (2014) to IntenseCoin (2017) to Lethean to a Zano rebase. All consensus parameters are derived from the canonical C++ source files currency_config.h.in and default.cmake.

The package was created as part of the broader effort to rewrite the Lethean node tooling in Go, keeping protocol logic in Go while deferring only the mathematically complex cryptographic primitives (ring signatures, bulletproofs, Zarcanum proofs) to a cleaned C++ library via CGo in later phases.


Phase 0 -- Scaffold

Commit: 4c0b7f2 -- feat: Phase 0 scaffold -- config, types, wire, difficulty

Phase 0 established the foundational four packages with complete test suites and full coverage of the consensus-critical configuration surface.

Packages implemented

  • config/ -- All chain constants from currency_config.h.in and default.cmake: tokenomics (Coin, BlockReward, fees, premine), address prefixes (standard, integrated, auditable, auditable integrated), network ports (mainnet 36940-36942, testnet 46940-46942), difficulty parameters (window 720, lag 15, cut 60, targets 120s), block and transaction limits, version constants, PoS parameters, P2P constants, network identity, currency identity, and alias rules. ChainConfig struct with pre-populated Mainnet and Testnet globals. Hardfork schedule (HF0-HF6) with VersionAtHeight() and IsHardForkActive() lookup functions.

  • types/ -- Fixed-size cryptographic types: Hash (32 bytes), PublicKey (32 bytes), SecretKey (32 bytes), KeyImage (32 bytes), Signature (64 bytes). Hex encode/decode for Hash and PublicKey. CryptoNote base58 address encoding with Keccak-256 checksums. Address struct with Encode()/DecodeAddress() round-trip. BlockHeader, Block, Transaction structs. Input types (TxInputGenesis, TxInputToKey) and output types (TxOutputBare, TxOutputZarcanum) with wire type tags. TxInput and TxOutput interfaces.

  • wire/ -- CryptoNote varint encoding (7-bit LEB128 with MSB continuation). EncodeVarint() and DecodeVarint() with sentinel errors for overflow and empty input. Maximum 10 bytes per uint64.

  • difficulty/ -- LWMA difficulty adjustment algorithm. NextDifficulty() examines a window of timestamps and cumulative difficulties to compute the next target. Handles insufficient data (returns StarterDifficulty), zero time spans, and negative difficulty deltas.

Tests added

75 test cases across 5 test files, all passing with the race detector:

  • config/config_test.go -- 7 test functions validating every constant group against C++ source values: tokenomics, address prefixes, ports (mainnet and testnet), difficulty parameters, network identity, ChainConfig struct fields, transaction limits, and transaction version constants.

  • config/hardfork_test.go -- 7 test functions covering VersionAtHeight() on both mainnet and testnet fork schedules, IsHardForkActive() for boundary conditions, unknown version queries, empty fork lists, single-element fork lists, and full fork schedule validation for both networks.

  • types/address_test.go -- 8 test functions covering encode/decode round-trips for all four address types, deterministic encoding, auditable flag detection, integrated prefix detection, invalid input rejection (empty, invalid base58 characters, too short), checksum corruption detection, base58 round-trip, and base58 edge cases (empty encode/decode).

  • difficulty/difficulty_test.go -- 7 test functions covering stable difficulty with constant intervals, empty input, single entry, fast blocks (difficulty increases), slow blocks (difficulty decreases), zero time span handling, and algorithm constants.

  • wire/varint_test.go -- 5 test functions covering encoding known values, decoding known values, round-trip across all bit boundaries (0 through MaxUint64), empty input errors, and overflow detection.

Coverage

Package Coverage
config 100.0%
difficulty 81.0%
types 73.4%
wire 95.2%

go test -race ./... passed clean. go vet ./... produced no warnings.


Phase 1 -- Wire Serialisation

Phase 1 added consensus-critical binary serialisation for blocks and transactions, verified to be bit-identical to the C++ daemon output. The definitive proof is the genesis block hash test: serialising the testnet genesis block and computing its Keccak-256 hash produces the exact value returned by the C++ daemon (cb9d5455ccb79451931003672c405f5e2ac51bff54021aa30bc4499b1ffc4963).

Type corrections from Phase 0

Phase 0 types had several mismatches with the C++ wire format, corrected here:

  • BlockHeader.MinorVersion changed from uint8 to uint64 (varint on wire)
  • BlockHeader.Flags added (uint8, 1 byte fixed)
  • Transaction.Version changed from uint8 to uint64 (varint on wire)
  • Transaction.UnlockTime removed (lives in extra variants, not top-level)
  • All variant tags corrected to match SET_VARIANT_TAGS from currency_basic.h: InputTypeGenesis=0, InputTypeToKey=1, OutputTypeBare=36, OutputTypeZarcanum=38
  • TxOutToKey struct added (public key + mix_attr, 33 bytes packed)
  • TxOutRef variant type added (global index or ref_by_id)
  • Transaction.Signatures, Transaction.Attachment, Transaction.Proofs fields added

Files added

File Purpose
wire/encoder.go Sticky-error streaming encoder
wire/decoder.go Sticky-error streaming decoder
wire/block.go Block/BlockHeader encode/decode
wire/transaction.go Transaction encode/decode (v0/v1 + v2+ stubs)
wire/treehash.go Keccak-256 + CryptoNote Merkle tree hash
wire/hash.go BlockHash, TransactionPrefixHash, TransactionHash
wire/encoder_test.go Encoder round-trip tests
wire/decoder_test.go Decoder round-trip tests
wire/block_test.go Block header + full block round-trip tests
wire/transaction_test.go Coinbase, ToKey, signatures, variant tag tests
wire/treehash_test.go Tree hash for 0-8 hashes
wire/hash_test.go Genesis block hash verification

Key findings

  • Block hash length prefix: The C++ get_object_hash(blobdata) serialises the string through binary_archive before hashing, prepending varint(length). The actual block hash input is varint(len) || block_hashing_blob, not just the blob itself.

  • Genesis data sources: The _genesis_tn.cpp.gen uint64 array is the canonical genesis transaction data, not the .genesis_tn.txt hex dump (which was stale from a different wallet generation).

  • Extra as raw bytes: Transaction extra, attachment, and proofs are stored as opaque raw wire bytes with tag-level boundary detection. This enables bit-identical round-tripping without implementing all 20+ extra variant types.

Coverage

Package Coverage
config 100.0%
difficulty 81.0%
types 73.4%
wire 76.8%

Wire coverage is reduced by v2+ code paths (0% -- Phase 2 scope). Excluding v2+ stubs, the v0/v1 serialisation code exceeds 85% coverage.

Phase 2 -- Crypto Bridge

Phase 2 created the crypto/ package with a CGo bridge to the vendored C++ CryptoNote crypto library. The upstream code (37 files from Zano commit fa1608cf) is built as a static library via CMake, with a thin C API (bridge.h) separating Go from C++ types.

Files added

File Purpose
crypto/upstream/ 37 vendored C++ files (unmodified copies)
crypto/compat/ 11 compatibility stubs replacing epee/Boost
crypto/build/ CMake build output (libcryptonote.a, ~680KB)
crypto/CMakeLists.txt C11/C++17 static library build
crypto/bridge.h Stable C API contract (29 functions)
crypto/bridge.cpp C→C++ wrappers with memcpy marshalling
crypto/doc.go Package documentation + build instructions
crypto/crypto.go CGo flags + FastHash binding
crypto/keygen.go Key generation, derivation, DerivePublicKey/SecretKey
crypto/keyimage.go Key image generation and validation
crypto/signature.go Standard + NLSAG ring signatures
crypto/clsag.go CLSAG (GG/GGX/GGXXG) + cofactor helpers
crypto/proof.go BPPE, BGE, Zarcanum verification stubs
crypto/PROVENANCE.md Upstream origin mapping + update workflow
crypto/crypto_test.go 22 tests (19 pass, 3 skipped proof stubs)

Key findings

  • CMake build required 5 iterations to resolve all include paths. The upstream files use relative includes (../currency_core/, crypto/crypto-ops.h) that assume the full Zano source tree. Solved with symlinks, additional include paths, and compat stubs.

  • eth_signature.cpp excluded from build -- requires Bitcoin's secp256k1 library which is not needed for CryptoNote consensus crypto.

  • cn_fast_hash name collision between bridge.h and hash-ops.h. Resolved by renaming the bridge wrapper to bridge_fast_hash.

  • Zero key is valid on Ed25519 -- it represents the identity element. Tests use 0xFF...FF for invalid key checks instead.

  • 1/8 premultiplication is critical for CLSAG correctness. On-chain commitments are stored as (1/8)*P. Generate takes full points; verify takes premultiplied values. PointMul8/PointDiv8 helpers convert between forms.

  • Proof verification stubs return "not implemented" -- the serialisation format for BPPE/BGE/Zarcanum proofs requires matching the exact on-chain binary layout, which needs real chain data via RPC (Phase 4).

Tests added

22 test cases in crypto/crypto_test.go:

  • Hashing (2): Known Keccak-256 vector (empty input), non-zero hash
  • Key ops (3): GenerateKeys round-trip, CheckKey negative, uniqueness
  • Key derivation (2): ECDH commutativity, output scanning round-trip (send → receive → derive ephemeral secret → verify public key match)
  • Key images (3): Generation, determinism, invalid input rejection
  • Standard sigs (3): Round-trip, wrong key, wrong message
  • Ring sigs NLSAG (2): 4-member ring round-trip, wrong message
  • CLSAG GG (2): Generate+verify round-trip with cofactor handling, wrong message
  • CLSAG size (2): GGX and GGXXG signature size calculations
  • Proof stubs (3): Skipped -- pending Phase 4 chain data

All passing with -race and go vet.

Phase 3 -- P2P Levin Protocol (Planned)

Implement the Levin binary protocol in p2p/ for peer-to-peer communication. Handshake, ping, timed sync, and block/transaction relay. Integrate with go-p2p for connection management.

Phase 4 -- RPC Layer (Planned)

Implement rpc/ with daemon JSON-RPC (get_block, get_transaction, submit_block) and wallet JSON-RPC (transfer, get_balance, get_address). Provide both client and server implementations.

Phase 5 -- Chain Storage and Validation (Planned)

Implement chain/ with blockchain storage (using go-store for persistence), block validation, transaction verification, and mempool management. UTXO set tracking with output index.

Phase 6 -- Wallet Core (Planned)

Implement wallet/ with key management, output scanning, transaction construction, and balance calculation. Deterministic key derivation from seed phrases. Support for all address types.

Phase 7 -- Consensus Rules (Planned)

Implement consensus/ with hardfork-aware block reward calculation, fee policy enforcement, and full block/transaction validation rules per hardfork version.

Phase 8 -- Mining (Planned)

PoW mining support with stratum protocol client. PoS staking with kernel hash computation and coinstake transaction construction.


Known Limitations

v2+ transaction serialisation is stubbed. The v0/v1 wire format is complete and verified. The v2+ (Zarcanum) code paths compile but are untested -- they will be validated in Phase 2 when post-HF4 transactions appear on-chain.

Proof verification not yet wired. Key generation, derivation, signatures (standard, NLSAG, CLSAG GG) are fully operational. BPPE, BGE, and Zarcanum proof verification stubs exist but return "not implemented" -- the deserialisation of on-chain proof blobs requires matching the exact binary format from chain data (Phase 4). CLSAG GGX/GGXXG verify functions are wired but untested without real ring data.

CGo toolchain required. The crypto/ package requires CMake, GCC/Clang, OpenSSL, and Boost headers to build libcryptonote.a. Pure Go packages (config/, types/, wire/, difficulty/) remain buildable without a C toolchain.

Base58 uses math/big. The CryptoNote base58 implementation converts each 8-byte block via big.Int arithmetic. This is correct but not optimised for high-throughput scenarios. A future phase may replace this with a lookup-table approach if profiling identifies it as a bottleneck.

Difficulty coverage at 81%. The window-capping branch in NextDifficulty() that limits the window to BlocksCount (735) entries is not fully exercised by the current test suite. Adding test vectors with 1,000+ block entries would cover this path.

Types coverage at 73.4%. Unexported base58 helper functions have branches that are exercised indirectly through the public API but are not fully reached by the current test vectors. Additional edge-case address strings would improve coverage.

Future forks are placeholders. HF3 through HF6 are defined with activation height 999,999,999 on mainnet. These heights will be updated when each fork is scheduled for activation on the live network.