docs: graduate Phase 0 into production documentation
Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
parent
4c0b7f290e
commit
37cc3d7342
5 changed files with 707 additions and 36 deletions
53
CLAUDE.md
53
CLAUDE.md
|
|
@ -1,46 +1,27 @@
|
|||
# go-blockchain
|
||||
# CLAUDE.md
|
||||
|
||||
Go implementation of the Lethean blockchain protocol. Pure Go package providing
|
||||
chain configuration, core data types, wire serialisation, and difficulty
|
||||
calculation for the Lethean CryptoNote/Zano-fork chain.
|
||||
Pure Go implementation of the Lethean blockchain protocol (config, types, wire, difficulty).
|
||||
|
||||
This package follows ADR-001: Go Shell + C++ Crypto Library. Protocol logic
|
||||
lives in Go; only the mathematically complex cryptographic primitives (ring
|
||||
signatures, bulletproofs, Zarcanum proofs) are delegated to a cleaned C++
|
||||
library via CGo in later phases.
|
||||
Module: `forge.lthn.ai/core/go-blockchain`
|
||||
|
||||
Lineage: CryptoNote -> IntenseCoin (2017) -> Lethean -> Zano rebase.
|
||||
|
||||
Licence: EUPL-1.2
|
||||
|
||||
## Build and Test
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
go build ./...
|
||||
go test -v -race ./...
|
||||
go vet ./...
|
||||
go test ./... # run all tests
|
||||
go test -race ./... # race detector (required before commit)
|
||||
go test -v -run Name ./... # single test
|
||||
go vet ./... # vet check
|
||||
```
|
||||
|
||||
## Package Layout
|
||||
## Standards
|
||||
|
||||
```
|
||||
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/ (future) CGo bridge to libcryptonote
|
||||
p2p/ (future) Levin TCP protocol
|
||||
rpc/ (future) Daemon and wallet JSON-RPC
|
||||
chain/ (future) Blockchain storage, validation, mempool
|
||||
wallet/ (future) Key management, output scanning, tx construction
|
||||
consensus/ (future) Hardfork rules, block reward, fee policy
|
||||
```
|
||||
- UK English
|
||||
- `go test -race ./...` and `go vet ./...` must pass before commit
|
||||
- Conventional commits: `type(scope): description`
|
||||
- Co-Author: `Co-Authored-By: Charon <charon@lethean.io>`
|
||||
|
||||
## Coding Standards
|
||||
## Docs
|
||||
|
||||
- **Language:** UK English in all comments and documentation (colour, organisation, centre)
|
||||
- **Commits:** Conventional commits (`feat:`, `fix:`, `refactor:`, `test:`, `docs:`)
|
||||
- **Co-Author:** All commits include `Co-Authored-By: Charon <charon@lethean.io>`
|
||||
- **Test naming:** `_Good` (happy path), `_Bad` (expected errors), `_Ugly` (panics/edge cases)
|
||||
- **Imports:** stdlib, then `golang.org/x`, then `forge.lthn.ai`, each separated by a blank line
|
||||
- **No emojis** in code or comments
|
||||
- `docs/architecture.md` -- package structure, key types, design decisions, ADR-001
|
||||
- `docs/development.md` -- prerequisites, test patterns, coding standards
|
||||
- `docs/history.md` -- completed phases with commit hashes, known limitations
|
||||
|
|
|
|||
55
README.md
Normal file
55
README.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# go-blockchain
|
||||
|
||||
Pure Go implementation of the Lethean blockchain protocol. Provides chain configuration, core cryptographic data types, CryptoNote wire serialisation, and LWMA difficulty adjustment for the Lethean CryptoNote/Zano-fork chain. Follows ADR-001: protocol logic in Go, cryptographic primitives deferred to a C++ bridge in later phases. Lineage: CryptoNote to IntenseCoin (2017) to Lethean to Zano rebase.
|
||||
|
||||
**Module**: `forge.lthn.ai/core/go-blockchain`
|
||||
**Licence**: EUPL-1.2
|
||||
**Language**: Go 1.25
|
||||
|
||||
## Quick Start
|
||||
|
||||
```go
|
||||
import (
|
||||
"forge.lthn.ai/core/go-blockchain/config"
|
||||
"forge.lthn.ai/core/go-blockchain/types"
|
||||
"forge.lthn.ai/core/go-blockchain/wire"
|
||||
"forge.lthn.ai/core/go-blockchain/difficulty"
|
||||
)
|
||||
|
||||
// Query the active hardfork version at a given block height
|
||||
version := config.VersionAtHeight(config.MainnetForks, 10081) // returns HF2
|
||||
|
||||
// Check if a specific hardfork is active
|
||||
active := config.IsHardForkActive(config.MainnetForks, config.HF4Zarcanum, 50000) // false
|
||||
|
||||
// Encode and decode a Lethean address
|
||||
addr := &types.Address{SpendPublicKey: spendKey, ViewPublicKey: viewKey}
|
||||
encoded := addr.Encode(config.AddressPrefix)
|
||||
decoded, prefix, err := types.DecodeAddress(encoded)
|
||||
|
||||
// Varint encoding for the wire protocol
|
||||
buf := wire.EncodeVarint(0x1eaf7)
|
||||
val, n, err := wire.DecodeVarint(buf)
|
||||
|
||||
// Calculate next block difficulty
|
||||
nextDiff := difficulty.NextDifficulty(timestamps, cumulativeDiffs, 120)
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Architecture](docs/architecture.md) -- package structure, key types, design decisions, ADR-001
|
||||
- [Development Guide](docs/development.md) -- prerequisites, test patterns, coding standards
|
||||
- [Project History](docs/history.md) -- completed phases with commit hashes, known limitations
|
||||
|
||||
## Build & Test
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
go test -race ./...
|
||||
go vet ./...
|
||||
go build ./...
|
||||
```
|
||||
|
||||
## Licence
|
||||
|
||||
European Union Public Licence 1.2 -- see [LICENCE](LICENCE) for details.
|
||||
257
docs/architecture.md
Normal file
257
docs/architecture.md
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
# Architecture
|
||||
|
||||
go-blockchain is a pure Go implementation of the Lethean blockchain protocol. It
|
||||
provides chain configuration, core cryptographic data types, consensus-critical
|
||||
wire serialisation, and difficulty adjustment for the Lethean CryptoNote/Zano-fork
|
||||
chain.
|
||||
|
||||
Module path: `forge.lthn.ai/core/go-blockchain`
|
||||
|
||||
---
|
||||
|
||||
## Package Structure
|
||||
|
||||
```
|
||||
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)
|
||||
```
|
||||
|
||||
### config/
|
||||
|
||||
Defines every consensus-critical constant for the Lethean chain, derived directly
|
||||
from the canonical C++ source files `currency_config.h.in` and `default.cmake`.
|
||||
Constants cover tokenomics, address prefixes, network ports, difficulty parameters,
|
||||
block and transaction limits, version numbers, PoS parameters, P2P constants,
|
||||
network identity, currency identity, and alias rules.
|
||||
|
||||
The `ChainConfig` struct aggregates all parameters into a single value.
|
||||
Pre-populated `Mainnet` and `Testnet` variables are provided as package-level
|
||||
globals. The hardfork schedule is defined in `hardfork.go` with lookup functions
|
||||
for querying active versions at any block height.
|
||||
|
||||
### types/
|
||||
|
||||
Fixed-size byte array types matching the CryptoNote specification:
|
||||
|
||||
- `Hash` (32 bytes) -- Keccak-256 hash values
|
||||
- `PublicKey` (32 bytes) -- Ed25519 public keys
|
||||
- `SecretKey` (32 bytes) -- Ed25519 secret keys
|
||||
- `KeyImage` (32 bytes) -- double-spend detection images
|
||||
- `Signature` (64 bytes) -- cryptographic signatures
|
||||
|
||||
Also contains the full address encoding/decoding implementation (CryptoNote
|
||||
base58 with Keccak-256 checksums), block header and block structures, and all
|
||||
transaction types across versions 0 through 3.
|
||||
|
||||
### wire/
|
||||
|
||||
Consensus-critical binary serialisation primitives. Currently implements
|
||||
CryptoNote varint encoding (7-bit LEB128 with MSB continuation). All encoding
|
||||
must be bit-identical to the C++ reference implementation.
|
||||
|
||||
### difficulty/
|
||||
|
||||
LWMA (Linear Weighted Moving Average) difficulty adjustment algorithm for both
|
||||
PoW and PoS blocks. Examines a window of recent block timestamps and cumulative
|
||||
difficulties to calculate the next target difficulty.
|
||||
|
||||
---
|
||||
|
||||
## Key Types
|
||||
|
||||
### ChainConfig
|
||||
|
||||
```go
|
||||
type ChainConfig struct {
|
||||
Name string
|
||||
Abbreviation string
|
||||
IsTestnet bool
|
||||
CurrencyFormationVersion uint64
|
||||
Coin uint64
|
||||
DisplayDecimalPoint uint8
|
||||
BlockReward uint64
|
||||
DefaultFee uint64
|
||||
MinimumFee uint64
|
||||
Premine uint64
|
||||
AddressPrefix uint64
|
||||
IntegratedAddressPrefix uint64
|
||||
AuditableAddressPrefix uint64
|
||||
AuditableIntegratedAddressPrefix uint64
|
||||
P2PPort uint16
|
||||
RPCPort uint16
|
||||
StratumPort uint16
|
||||
DifficultyPowTarget uint64
|
||||
DifficultyPosTarget uint64
|
||||
DifficultyWindow uint64
|
||||
DifficultyLag uint64
|
||||
DifficultyCut uint64
|
||||
DifficultyPowStarter uint64
|
||||
DifficultyPosStarter uint64
|
||||
MaxBlockNumber uint64
|
||||
TxMaxAllowedInputs uint64
|
||||
TxMaxAllowedOutputs uint64
|
||||
DefaultDecoySetSize uint64
|
||||
HF4MandatoryDecoySetSize uint64
|
||||
MinedMoneyUnlockWindow uint64
|
||||
P2PMaintainersPubKey string
|
||||
}
|
||||
```
|
||||
|
||||
Pre-populated globals `Mainnet` and `Testnet` contain the complete parameter
|
||||
sets for each network. Mainnet uses ports 36940-36942; testnet uses 46940-46942.
|
||||
|
||||
### HardFork
|
||||
|
||||
```go
|
||||
type HardFork struct {
|
||||
Version uint8
|
||||
Height uint64
|
||||
Mandatory bool
|
||||
Description string
|
||||
}
|
||||
```
|
||||
|
||||
Seven hardfork versions are defined (HF0 through HF6). On mainnet, HF0 is
|
||||
active from genesis, HF1 and HF2 activate after block 10,080, and HF3 through
|
||||
HF6 are scheduled at height 999,999,999 (effectively future). On testnet, most
|
||||
forks activate early for testing.
|
||||
|
||||
### Address
|
||||
|
||||
```go
|
||||
type Address struct {
|
||||
SpendPublicKey PublicKey
|
||||
ViewPublicKey PublicKey
|
||||
Flags uint8
|
||||
}
|
||||
```
|
||||
|
||||
Four address types are supported via distinct prefixes:
|
||||
|
||||
| Type | Prefix | Leading chars |
|
||||
|------|--------|---------------|
|
||||
| Standard | `0x1eaf7` | `iTHN` |
|
||||
| Integrated | `0xdeaf7` | `iTHn` |
|
||||
| Auditable | `0x3ceff7` | `iThN` |
|
||||
| Auditable integrated | `0x8b077` | `iThn` |
|
||||
|
||||
### Block and Transaction
|
||||
|
||||
```go
|
||||
type BlockHeader struct {
|
||||
MajorVersion uint8
|
||||
MinorVersion uint8
|
||||
Timestamp uint64
|
||||
PrevID Hash
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
BlockHeader
|
||||
MinerTx Transaction
|
||||
TxHashes []Hash
|
||||
}
|
||||
|
||||
type Transaction struct {
|
||||
Version uint8
|
||||
UnlockTime uint64
|
||||
Vin []TxInput
|
||||
Vout []TxOutput
|
||||
Extra []byte
|
||||
}
|
||||
```
|
||||
|
||||
Transaction versions progress through the hardfork schedule:
|
||||
|
||||
| Version | Era | Description |
|
||||
|---------|-----|-------------|
|
||||
| 0 | Genesis | Coinbase transactions |
|
||||
| 1 | Pre-HF4 | Standard transparent transactions |
|
||||
| 2 | Post-HF4 | Zarcanum confidential transactions (CLSAG) |
|
||||
| 3 | Post-HF5 | Confidential assets with surjection proofs |
|
||||
|
||||
Input types: `TxInputGenesis` (coinbase, tag `0xFF`) and `TxInputToKey` (standard
|
||||
spend with ring signature, tag `0x02`).
|
||||
|
||||
Output types: `TxOutputBare` (transparent, tag `0x02`) and `TxOutputZarcanum`
|
||||
(confidential with Pedersen commitments, tag `0x03`).
|
||||
|
||||
---
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### Why CryptoNote Base58
|
||||
|
||||
CryptoNote uses its own base58 variant (not Bitcoin's base58check). The alphabet
|
||||
omits `0`, `O`, `I`, and `l` to avoid visual ambiguity. Data is split into 8-byte
|
||||
blocks, each encoded independently into 11 base58 characters. The final partial
|
||||
block produces fewer characters according to a fixed mapping table
|
||||
(`base58BlockSizes`). This block-based approach differs from Bitcoin's
|
||||
whole-number division and produces different output for the same input bytes.
|
||||
|
||||
The implementation uses `math/big` for block conversion rather than a lookup
|
||||
table. This is correct for all uint64 ranges but is not optimised for
|
||||
high-throughput address generation. Performance optimisation is deferred to a
|
||||
later phase if profiling identifies it as a bottleneck.
|
||||
|
||||
### Why Keccak-256 (Not SHA3-256)
|
||||
|
||||
CryptoNote predates the NIST SHA-3 standard. It uses the original Keccak-256
|
||||
submission (`sha3.NewLegacyKeccak256()`), which differs from the finalised
|
||||
SHA3-256 in padding. This is consensus-critical -- using SHA3-256 would produce
|
||||
different checksums and break address compatibility with the C++ node.
|
||||
|
||||
### Address Encoding Algorithm
|
||||
|
||||
1. Encode the address prefix as a CryptoNote varint
|
||||
2. Append the 32-byte spend public key
|
||||
3. Append the 32-byte view public key
|
||||
4. Append the 1-byte flags field
|
||||
5. Compute Keccak-256 over bytes 1-4, take the first 4 bytes as checksum
|
||||
6. Append the 4-byte checksum
|
||||
7. Encode the entire blob using CryptoNote base58
|
||||
|
||||
Decoding reverses this process: base58 decode, extract and validate the varint
|
||||
prefix, verify the Keccak-256 checksum, then extract the two keys and flags.
|
||||
|
||||
### Varint Encoding
|
||||
|
||||
The wire format uses 7-bit variable-length integers identical to protobuf
|
||||
varints. Each byte carries 7 data bits in the low bits with the MSB set to 1
|
||||
if more bytes follow. A uint64 requires at most 10 bytes. The implementation
|
||||
provides sentinel errors (`ErrVarintOverflow`, `ErrVarintEmpty`) for malformed
|
||||
input.
|
||||
|
||||
### Hardfork System (Reverse-Scan VersionAtHeight)
|
||||
|
||||
`VersionAtHeight()` iterates all hardforks and returns the highest version whose
|
||||
activation height has been passed. A fork with `Height=0` is active from genesis.
|
||||
A fork with `Height=N` is active at heights strictly greater than N.
|
||||
|
||||
This scan approach (rather than a sorted binary search) is deliberate: the fork
|
||||
list is small (7 entries) and correctness is trivially verifiable. The same list
|
||||
drives both `VersionAtHeight()` and `IsHardForkActive()`.
|
||||
|
||||
### LWMA Difficulty Adjustment
|
||||
|
||||
The difficulty algorithm uses the LWMA (Linear Weighted Moving Average) approach:
|
||||
|
||||
```
|
||||
nextDiff = difficultyDelta * targetInterval / timeSpan
|
||||
```
|
||||
|
||||
The window examines up to 735 blocks (720 window + 15 lag). When fewer blocks
|
||||
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.
|
||||
|
||||
### 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
|
||||
cryptographic implementations.
|
||||
202
docs/development.md
Normal file
202
docs/development.md
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
# Development Guide
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Go 1.25 or later (the module declares `go 1.25`)
|
||||
- `golang.org/x/crypto` for Keccak-256 (legacy, pre-NIST)
|
||||
- No CGO required
|
||||
|
||||
No C toolchain, no system libraries, no external build tools. A plain
|
||||
`go build ./...` is sufficient.
|
||||
|
||||
---
|
||||
|
||||
## Build and Test
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
go test ./...
|
||||
|
||||
# Run all tests with the race detector (required before every commit)
|
||||
go test -race ./...
|
||||
|
||||
# Run a single test by name
|
||||
go test -v -run TestVersionAtHeight ./config/
|
||||
|
||||
# Run a single subtest
|
||||
go test -v -run "TestVersionAtHeight_Good/genesis" ./config/
|
||||
|
||||
# Check for vet issues
|
||||
go vet ./...
|
||||
|
||||
# Tidy dependencies
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
All three commands (`go test -race ./...`, `go vet ./...`, and `go mod tidy`)
|
||||
must produce no errors or warnings before a commit is pushed.
|
||||
|
||||
---
|
||||
|
||||
## Test Patterns
|
||||
|
||||
### File Organisation
|
||||
|
||||
Each package has a corresponding `_test.go` file in the same package (white-box
|
||||
tests):
|
||||
|
||||
- `config/config_test.go` -- constant validation against C++ source values
|
||||
- `config/hardfork_test.go` -- hardfork schedule and version lookup
|
||||
- `types/address_test.go` -- address encode/decode round-trips, base58, checksums
|
||||
- `difficulty/difficulty_test.go` -- LWMA algorithm behaviour
|
||||
- `wire/varint_test.go` -- varint encode/decode round-trips, boundary values
|
||||
|
||||
### Naming Convention
|
||||
|
||||
All tests follow the `_Good`, `_Bad`, `_Ugly` suffix pattern:
|
||||
|
||||
- `_Good` -- happy path (correct inputs produce correct outputs)
|
||||
- `_Bad` -- expected error conditions (invalid input, checksum corruption)
|
||||
- `_Ugly` -- edge cases (empty slices, nil inputs, overflow, zero time spans)
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
TestAddressEncodeDecodeRoundTrip_Good
|
||||
TestDecodeAddress_Bad
|
||||
TestBase58Empty_Ugly
|
||||
TestIsHardForkActive_Bad
|
||||
TestVersionAtHeight_Ugly
|
||||
TestNextDifficulty_Ugly
|
||||
TestDecodeVarint_Ugly
|
||||
```
|
||||
|
||||
### Table-Driven Subtests
|
||||
|
||||
Most test functions use table-driven subtests with `t.Run()`:
|
||||
|
||||
```go
|
||||
tests := []struct {
|
||||
name string
|
||||
height uint64
|
||||
want uint8
|
||||
}{
|
||||
{"genesis", 0, HF0Initial},
|
||||
{"before_hf1", 10080, HF0Initial},
|
||||
{"at_hf1_hf2", 10081, HF2},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := VersionAtHeight(MainnetForks, tt.height)
|
||||
if got != tt.want {
|
||||
t.Errorf("got %d, want %d", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Test Helpers
|
||||
|
||||
`makeTestAddress(flags uint8)` creates an address with deterministic byte
|
||||
patterns (sequential values 0-31 for the spend key, 32-63 for the view key).
|
||||
This produces reproducible base58 output for round-trip testing.
|
||||
|
||||
### Assertion Style
|
||||
|
||||
Tests use the standard library `testing` package directly with `t.Error`,
|
||||
`t.Errorf`, and `t.Fatal`. Testify is not used in this package. Error messages
|
||||
include both the got and want values with descriptive context.
|
||||
|
||||
---
|
||||
|
||||
## Coverage
|
||||
|
||||
Current coverage by package:
|
||||
|
||||
| Package | Coverage |
|
||||
|---------|----------|
|
||||
| config | 100.0% |
|
||||
| difficulty | 81.0% |
|
||||
| types | 73.4% |
|
||||
| wire | 95.2% |
|
||||
|
||||
Total test functions: 75 (across 5 test files).
|
||||
|
||||
The lower coverage in `types/` reflects unexported helper functions
|
||||
(`encodeBlock`, `decodeBlock`, `base58CharIndex`) that are exercised indirectly
|
||||
through the public API but have branches not fully reached by the current test
|
||||
vectors. The lower coverage in `difficulty/` reflects the window-capping branch
|
||||
that only triggers with more than 735 block entries.
|
||||
|
||||
`go test -race ./...` passes clean across all packages.
|
||||
|
||||
---
|
||||
|
||||
## Coding Standards
|
||||
|
||||
### Language
|
||||
|
||||
UK English throughout: colour, organisation, serialise, initialise, behaviour.
|
||||
Do not use American spellings in identifiers, comments, or documentation.
|
||||
|
||||
### Go Style
|
||||
|
||||
- All exported types, functions, and fields must have doc comments
|
||||
- Error strings use the `package: description` format (e.g. `types: invalid hex
|
||||
for hash`)
|
||||
- Error wrapping uses `fmt.Errorf("types: description: %w", err)`
|
||||
- Every source file carries the EUPL-1.2 copyright header
|
||||
- No emojis in code or comments
|
||||
- Imports are ordered: stdlib, then `golang.org/x`, then `forge.lthn.ai`, each
|
||||
separated by a blank line
|
||||
|
||||
### Dependencies
|
||||
|
||||
Direct dependencies are intentionally minimal:
|
||||
|
||||
| Dependency | Purpose |
|
||||
|------------|---------|
|
||||
| `golang.org/x/crypto` | Keccak-256 (legacy, pre-NIST) for address checksums |
|
||||
| `golang.org/x/sys` | Indirect, required by `golang.org/x/crypto` |
|
||||
|
||||
No test-only dependencies beyond the standard library `testing` package.
|
||||
|
||||
---
|
||||
|
||||
## Licence
|
||||
|
||||
EUPL-1.2. Every source file carries the standard copyright header:
|
||||
|
||||
```
|
||||
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
||||
//
|
||||
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commit Convention
|
||||
|
||||
Format: `type(scope): description`
|
||||
|
||||
Common types: `feat`, `fix`, `test`, `refactor`, `docs`, `perf`, `chore`
|
||||
|
||||
Common scopes: `config`, `types`, `wire`, `difficulty`, `address`, `hardfork`
|
||||
|
||||
Every commit must include:
|
||||
|
||||
```
|
||||
Co-Authored-By: Charon <charon@lethean.io>
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
feat(types): add Zarcanum confidential output type
|
||||
|
||||
Co-Authored-By: Charon <charon@lethean.io>
|
||||
```
|
||||
|
||||
Commits must not be pushed unless `go test -race ./...` and `go vet ./...` both
|
||||
pass. `go mod tidy` must produce no changes.
|
||||
176
docs/history.md
Normal file
176
docs/history.md
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
# 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 (Planned)
|
||||
|
||||
Extend `wire/` with full block and transaction binary serialisation matching the
|
||||
C++ `binary_archive` format. Add `Serialise()` and `Deserialise()` methods to
|
||||
`Block`, `Transaction`, and all input/output types. Validate against real
|
||||
mainnet block blobs.
|
||||
|
||||
## Phase 2 -- Crypto Bridge (Planned)
|
||||
|
||||
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 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
|
||||
|
||||
**No wire serialisation.** Block and transaction types are defined as Go structs
|
||||
but cannot yet be serialised to or deserialised from the CryptoNote binary
|
||||
format. This means the types cannot be used to parse real chain data until
|
||||
Phase 1 is complete.
|
||||
|
||||
**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.
|
||||
|
||||
**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.
|
||||
Loading…
Add table
Reference in a new issue