diff --git a/CLAUDE.md b/CLAUDE.md index 48aa60e..85346ac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,9 +1,21 @@ # CLAUDE.md -Pure Go implementation of the Lethean blockchain protocol (config, types, wire, difficulty). +Go implementation of the Lethean blockchain protocol with CGo crypto bridge. Module: `forge.lthn.ai/core/go-blockchain` +## Build + +```bash +# First time: build the crypto C++ library (requires cmake, g++, libssl-dev, libboost-dev) +cmake -S crypto -B crypto/build -DCMAKE_BUILD_TYPE=Release +cmake --build crypto/build --parallel + +# Then: run tests +go test -race ./... +go vet ./... +``` + ## Commands ```bash @@ -11,6 +23,7 @@ go test ./... # run all tests go test -race ./... # race detector (required before commit) go test -v -run Name ./... # single test go vet ./... # vet check +cmake --build crypto/build --parallel # rebuild C++ after bridge.cpp changes ``` ## Standards diff --git a/docs/architecture.md b/docs/architecture.md index 10a6630..a178fc5 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -16,6 +16,7 @@ config/ Chain parameters (mainnet/testnet), hardfork schedule types/ Core data types: Hash, PublicKey, Address, Block, Transaction wire/ Binary serialisation (CryptoNote varint encoding) difficulty/ PoW + PoS difficulty adjustment (LWMA variant) +crypto/ CGo bridge to vendored C++ libcryptonote (keys, signatures, proofs) ``` ### config/ @@ -294,11 +295,55 @@ are available (early chain), the algorithm uses whatever data exists. Division by zero is prevented by clamping the time span to a minimum of 1 second. `StarterDifficulty` (value 1) is returned when insufficient data is available. +### crypto/ -- CGo Bridge to libcryptonote + +Bridges Go to the upstream CryptoNote C++ crypto library via CGo. The C++ code +is vendored in `crypto/upstream/` (37 files from Zano commit `fa1608cf`) and +built as a static library (`libcryptonote.a`) via CMake. + +**Build flow:** `CMakeLists.txt` → `cmake --build` → `libcryptonote.a` → CGo links. + +**C API contract:** `bridge.h` defines the stable C boundary. Go code calls ONLY +these functions -- no C++ types cross the boundary. All parameters are raw +`uint8_t*` pointers with explicit sizes. This is the same CGo pattern used by +`core/go-ai` for the MLX backend. + +**Compat layer:** `crypto/compat/` provides minimal stubs replacing epee/Boost +dependencies (logging macros, warnings pragmas, zero-init, profile tools). The +upstream files are unmodified copies; all adaptation lives in the compat headers. + +**Provenance:** `crypto/PROVENANCE.md` maps each vendored file to its upstream +origin path and modification status, with an update workflow for tracking Zano +upstream changes. + +**Exposed operations:** + +| Category | Functions | +|----------|-----------| +| Hashing | `FastHash` (Keccak-256) | +| Key ops | `GenerateKeys`, `SecretToPublic`, `CheckKey` | +| Key derivation | `GenerateKeyDerivation`, `DerivePublicKey`, `DeriveSecretKey` | +| Key images | `GenerateKeyImage`, `ValidateKeyImage` | +| Standard sigs | `GenerateSignature`, `CheckSignature` | +| Ring sigs (NLSAG) | `GenerateRingSignature`, `CheckRingSignature` | +| CLSAG (HF4+) | `GenerateCLSAGGG`, `VerifyCLSAGGG`, `VerifyCLSAGGGX`, `VerifyCLSAGGGXXG` | +| Point helpers | `PointMul8`, `PointDiv8` (cofactor 1/8 premultiplication) | +| Proof verification | `VerifyBPPE`, `VerifyBGE`, `VerifyZarcanum` (stubs -- Phase 4) | + +**Ring buffer convention:** Ring entries are flat byte arrays. CLSAG ring entries +pack 32-byte public keys per dimension (GG=64B, GGX=96B, GGXXG=128B per entry). +Signatures are serialised as flat buffers with documented layouts in `bridge.h`. + +**1/8 premultiplication:** On-chain commitments are stored premultiplied by the +cofactor inverse (1/8). The `PointMul8`/`PointDiv8` helpers convert between +representations. CLSAG generate takes full points; CLSAG verify takes +premultiplied values. + ### ADR-001: Go Shell + C++ Crypto Library This package follows ADR-001. All protocol logic, data types, serialisation, -and configuration live in pure Go. Only the mathematically complex cryptographic -primitives (ring signatures, bulletproofs, Zarcanum proofs) will be delegated to -a cleaned C++ library via CGo in later phases. This boundary keeps the Go code -testable without a C toolchain while preserving access to battle-tested +and configuration live in pure Go. The mathematically complex cryptographic +primitives (ring signatures, bulletproofs, Zarcanum proofs) are delegated to +the vendored C++ library in `crypto/` via CGo. This boundary keeps the pure Go +code testable without a C toolchain while preserving access to battle-tested cryptographic implementations. diff --git a/docs/history.md b/docs/history.md index 29b8631..697a830 100644 --- a/docs/history.md +++ b/docs/history.md @@ -160,12 +160,73 @@ Phase 0 types had several mismatches with the C++ wire format, corrected here: 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 (Planned) +## Phase 2 -- Crypto Bridge -Create `crypto/` package with CGo bridge to the cleaned C++ `libcryptonote` -library. Implement key derivation (`generate_key_derivation`, -`derive_public_key`), one-time address generation, and key image computation. -Follow the same CGo pattern used by the MLX backend in `go-ai`. +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) @@ -210,9 +271,17 @@ hash computation and coinstake transaction construction. 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. -**No cryptographic operations.** Key derivation, ring signatures, bulletproofs, -and all other cryptographic primitives are deferred to Phase 2. Address -encoding/decoding works but key generation and output scanning are not possible. +**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