Update go.mod module line, all require/replace directives, and every .go import path from forge.lthn.ai/core/go-blockchain to dappco.re/go/core/blockchain. Add replace directives to bridge dappco.re paths to existing forge.lthn.ai registry during migration. Update CLAUDE.md, README, and docs to reflect the new module path. Co-Authored-By: Virgil <virgil@lethean.io>
5.1 KiB
Development Guide
Prerequisites
- Go 1.25 or later (the module declares
go 1.25) golang.org/x/cryptofor 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
# 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 valuesconfig/hardfork_test.go-- hardfork schedule and version lookuptypes/address_test.go-- address encode/decode round-trips, base58, checksumsdifficulty/difficulty_test.go-- LWMA algorithm behaviourwire/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():
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: descriptionformat (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, thendappco.re, 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.