feat(lns): scaffold CoreGO package with primitives and covenants

Lethean Name System — CoreGO native conversion of the HSD JavaScript
sidechain. Includes Address, Transaction, BlockHeader, NameState, and
Covenant types extracted from itns-sidechain/lib/primitives/, plus
covenant type constants from rules.js, and a DNS resolution service stub.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-04-02 02:20:15 +01:00
commit 5a83dd4478
5 changed files with 415 additions and 0 deletions

5
go.mod Normal file
View file

@ -0,0 +1,5 @@
module dappco.re/go/lns
go 1.26.0
require dappco.re/go/core v0.8.0-alpha.1

41
lns.go Normal file
View file

@ -0,0 +1,41 @@
// SPDX-License-Identifier: EUPL-1.2
// Package lns is the Lethean Name System — a CoreGO native conversion of the
// HSD (Handshake) JavaScript sidechain. It provides name auctions, DNS
// resolution, and covenant-based name management for .lthn domains.
//
// Register LNS as a Core service:
//
// c := core.New(core.WithService(lns.Register))
//
// Sub-packages (primitives, covenant, dns) provide the domain types
// and resolution logic used by the service.
package lns
import core "dappco.re/go/core"
// ServiceName is the registered name for the LNS service group.
const ServiceName = "lns"
// Service is the LNS service that manages .lthn name resolution,
// covenant processing, and name-chain state queries.
//
// c := core.New(core.WithService(lns.Register))
// svc := c.Service("lns")
type Service struct {
*core.ServiceRuntime[serviceOptions]
}
// serviceOptions holds configuration for the LNS service.
type serviceOptions struct{}
// Register constructs an LNS Service bound to the provided Core instance.
// Pass to core.WithService during Core construction:
//
// c := core.New(core.WithService(lns.Register))
func Register(c *core.Core) core.Result {
svc := &Service{
ServiceRuntime: core.NewServiceRuntime(c, serviceOptions{}),
}
return core.Result{Value: svc, OK: true}
}

115
pkg/covenant/covenant.go Normal file
View file

@ -0,0 +1,115 @@
// SPDX-License-Identifier: EUPL-1.2
// Package covenant defines the covenant type constants for the Lethean Name System.
// Covenants encode name-system operations (auctions, registrations, transfers)
// as typed payloads attached to transaction outputs.
//
// Check a covenant type:
//
// if covType == covenant.TypeRegister { /* name registration */ }
//
// Look up a type name:
//
// name := covenant.TypeName(covenant.TypeBid) // "BID"
package covenant
// CovenantType represents the type of name-system operation encoded in a covenant.
// Values mirror the HSD rules.js type enumeration.
//
// var ct covenant.CovenantType = covenant.TypeOpen
type CovenantType uint8
const (
// TypeNone is a plain transfer with no name-system operation.
TypeNone CovenantType = 0
// TypeClaim imports an existing ICANN/Alexa name into the system.
TypeClaim CovenantType = 1
// TypeOpen begins a name auction, making the name available for bidding.
TypeOpen CovenantType = 2
// TypeBid places a sealed bid during the auction's bidding phase.
TypeBid CovenantType = 3
// TypeReveal reveals a previously sealed bid after the bidding phase closes.
TypeReveal CovenantType = 4
// TypeRedeem reclaims coins from a losing bid after the reveal phase.
TypeRedeem CovenantType = 5
// TypeRegister completes name registration after winning an auction.
TypeRegister CovenantType = 6
// TypeUpdate modifies the DNS resource data for a registered name.
TypeUpdate CovenantType = 7
// TypeRenew extends the registration period for a name.
TypeRenew CovenantType = 8
// TypeTransfer initiates a name transfer to a new address.
TypeTransfer CovenantType = 9
// TypeFinalize completes a name transfer after the lock-up period.
TypeFinalize CovenantType = 10
// TypeRevoke permanently burns a name, making it unspendable and
// available for re-auction.
TypeRevoke CovenantType = 11
)
// typeNames maps covenant type values to their string representations.
var typeNames = map[CovenantType]string{
TypeNone: "NONE",
TypeClaim: "CLAIM",
TypeOpen: "OPEN",
TypeBid: "BID",
TypeReveal: "REVEAL",
TypeRedeem: "REDEEM",
TypeRegister: "REGISTER",
TypeUpdate: "UPDATE",
TypeRenew: "RENEW",
TypeTransfer: "TRANSFER",
TypeFinalize: "FINALIZE",
TypeRevoke: "REVOKE",
}
// TypeName returns the human-readable name for a covenant type.
// Returns "UNKNOWN" for unrecognised type values.
//
// covenant.TypeName(covenant.TypeBid) // "BID"
// covenant.TypeName(covenant.TypeRevoke) // "REVOKE"
// covenant.TypeName(99) // "UNKNOWN"
func TypeName(ct CovenantType) string {
if name, ok := typeNames[ct]; ok {
return name
}
return "UNKNOWN"
}
// IsName returns true if the covenant type is a name-related operation
// (anything from CLAIM through REVOKE, inclusive).
//
// covenant.TypeOpen.IsName() // true
// covenant.TypeNone.IsName() // false
func (ct CovenantType) IsName() bool {
return ct >= TypeClaim && ct <= TypeRevoke
}
// IsKnown returns true if the covenant type is a recognised type
// (NONE through REVOKE, inclusive).
//
// covenant.TypeBid.IsKnown() // true
// covenant.CovenantType(99).IsKnown() // false
func (ct CovenantType) IsKnown() bool {
return ct <= TypeRevoke
}
// String returns the human-readable name for a covenant type.
// Satisfies the fmt.Stringer interface.
//
// ct := covenant.TypeRegister
// println(ct.String()) // "REGISTER"
func (ct CovenantType) String() string {
return TypeName(ct)
}

48
pkg/dns/resolve.go Normal file
View file

@ -0,0 +1,48 @@
// SPDX-License-Identifier: EUPL-1.2
// Package dns provides name resolution for the Lethean Name System.
// It resolves .lthn names to their registered DNS resource records by
// querying the name-chain state tree.
//
// Create a resolver and look up a name:
//
// svc := dns.NewService()
// // svc.Resolve("example") — implementation pending
package dns
import core "dappco.re/go/core"
// Service handles DNS resolution for .lthn names against the LNS chain state.
// The full resolution logic (672 lines from the existing Go implementation)
// will be merged in a subsequent pass.
//
// svc := dns.NewService()
type Service struct {
core *core.Core
}
// NewService creates a new DNS resolution service.
// The core instance is optional during scaffolding and can be nil.
//
// svc := dns.NewService()
// svc := dns.NewService(dns.WithCore(c))
func NewService(opts ...ServiceOption) *Service {
s := &Service{}
for _, opt := range opts {
opt(s)
}
return s
}
// ServiceOption configures a DNS Service during construction.
type ServiceOption func(*Service)
// WithCore sets the Core instance for the DNS service, enabling
// access to the service registry, logging, and configuration.
//
// svc := dns.NewService(dns.WithCore(c))
func WithCore(c *core.Core) ServiceOption {
return func(s *Service) {
s.core = c
}
}

206
pkg/primitives/types.go Normal file
View file

@ -0,0 +1,206 @@
// SPDX-License-Identifier: EUPL-1.2
// Package primitives provides the core data types for the Lethean Name System.
// These types mirror the HSD (Handshake) sidechain primitives, converted from
// the JavaScript reference implementation at itns-sidechain/lib/primitives/.
//
// Create an address from a public key hash:
//
// addr := primitives.Address{Version: 0, Hash: pubkeyHash}
//
// Build a transaction with inputs and outputs:
//
// tx := primitives.Transaction{Version: 0, LockTime: 0}
// tx.Inputs = append(tx.Inputs, input)
// tx.Outputs = append(tx.Outputs, output)
package primitives
// Hash is a 32-byte hash used throughout the name system for block references,
// transaction identifiers, and name hashes.
//
// var h primitives.Hash // zero-value is a valid empty hash
type Hash [32]byte
// Address represents a bech32-encoded witness address on the Lethean name chain.
// Version 0 addresses use either 20-byte (pubkey hash) or 32-byte (script hash)
// programmes. Version 31 is reserved for nulldata (unspendable) outputs.
//
// addr := primitives.Address{Version: 0, Hash: blake2bDigest}
// if addr.IsPubkeyHash() { /* 20-byte witness programme */ }
type Address struct {
// Version is the witness programme version (0-31).
// Version 0 = standard witness, version 31 = nulldata.
Version uint8
// Hash is the witness programme hash.
// 20 bytes for pubkey hash, 32 bytes for script hash at version 0.
Hash []byte
}
// Outpoint references a specific output from a previous transaction.
// Used by transaction inputs to identify the coin being spent.
//
// ref := primitives.Outpoint{TxHash: prevTxHash, Index: 0}
type Outpoint struct {
// TxHash is the hash of the transaction containing the referenced output.
TxHash Hash
// Index is the zero-based position of the output within that transaction.
Index uint32
}
// Input represents a transaction input that spends a previous output.
// Each input references an outpoint and carries a witness for validation.
//
// in := primitives.Input{Prevout: ref, Sequence: 0xffffffff}
type Input struct {
// Prevout identifies the output being spent.
Prevout Outpoint
// Witness contains the witness stack items (signatures, public keys).
Witness [][]byte
// Sequence is the input sequence number, used for relative timelocks.
Sequence uint32
}
// Output represents a transaction output: a value sent to an address,
// optionally carrying a covenant for name operations.
//
// out := primitives.Output{Value: 1000, Address: addr, Covenant: cov}
type Output struct {
// Value is the amount in the smallest denomination (dollarydoos).
Value uint64
// Address is the destination witness address.
Address Address
// Covenant carries name-system operation data (NONE for plain transfers).
Covenant Covenant
}
// Covenant attaches name-system semantics to a transaction output.
// The type field determines the operation (OPEN, BID, REVEAL, REGISTER, etc.)
// and the items carry operation-specific data (name hashes, heights, records).
//
// cov := primitives.Covenant{Type: covenant.TypeOpen, Items: [][]byte{nameHash, heightBytes, rawName}}
type Covenant struct {
// Type is the covenant operation type (see pkg/covenant for constants).
Type uint8
// Items is the ordered list of data payloads for this covenant.
// Contents vary by type (name hash, height, resource data, blind, etc.).
Items [][]byte
}
// Transaction is a complete name-chain transaction containing inputs that
// spend previous outputs and outputs that create new coins or name operations.
//
// tx := primitives.Transaction{Version: 0, LockTime: 0}
type Transaction struct {
// Version is the transaction format version.
Version uint32
// Inputs is the list of inputs spending previous outputs.
Inputs []Input
// Outputs is the list of new outputs created by this transaction.
Outputs []Output
// LockTime is the earliest time or block height at which this
// transaction may be included in a block.
LockTime uint32
}
// BlockHeader contains the fields present in every LNS block header.
// The header is split into a preheader (essential for SPV) and a subheader
// (miner-mutable data), following the HSD dual-hash PoW design.
//
// hdr := primitives.BlockHeader{Version: 1, Time: uint64(time.Now().Unix())}
type BlockHeader struct {
// Version determines which consensus rules apply to this block.
Version uint32
// PrevBlock is the hash of the previous block in the chain.
PrevBlock Hash
// MerkleRoot is the root hash of the transaction merkle tree.
MerkleRoot Hash
// WitnessRoot is the root hash of the witness merkle tree.
WitnessRoot Hash
// TreeRoot is the root hash of the name/UTXO Urkel tree.
TreeRoot Hash
// ReservedRoot is reserved for future use (must be zero for now).
ReservedRoot Hash
// Time is the Unix epoch timestamp (seconds) when the block was created.
Time uint64
// Bits encodes the target difficulty in compact form.
Bits uint32
// Nonce is iterated by the miner to find a valid PoW solution.
Nonce uint32
// ExtraNonce provides additional entropy for miners (24 bytes in HSD).
ExtraNonce []byte
// Mask is the PoW mask used for pool block-withholding protection (32 bytes).
Mask []byte
}
// NameState tracks the current state of a registered name on the chain.
// Names progress through auction phases (OPENING -> BIDDING -> REVEAL -> CLOSED)
// and can be transferred, renewed, or revoked.
//
// ns := primitives.NameState{Name: []byte("example"), Height: 1000}
// if ns.IsExpired() { /* name can be re-auctioned */ }
type NameState struct {
// Name is the raw name bytes (1-63 characters).
Name []byte
// NameHash is the blake2b hash of the name used for lookups.
NameHash Hash
// Height is the block height at which the current state was established.
Height uint32
// Renewal is the block height of the last renewal.
Renewal uint32
// Owner references the outpoint that currently owns this name.
Owner Outpoint
// Value is the locked value (winning bid amount).
Value uint64
// Highest is the highest bid seen during the auction.
Highest uint64
// Data is the DNS resource data attached to this name.
Data []byte
// Transfer is the block height at which a transfer was initiated (0 if none).
Transfer uint32
// Revoked is the block height at which the name was revoked (0 if not revoked).
Revoked uint32
// Claimed is the number of times this name has been claimed.
Claimed uint32
// Renewals is the total number of renewals for this name.
Renewals uint32
// Registered indicates whether the name has completed the REGISTER covenant.
Registered bool
// Expired indicates whether the name has passed its renewal window.
Expired bool
// Weak indicates a weak claim (imported from ICANN without full proof).
Weak bool
}