From 5a83dd4478d1a248651cadc7e0a53cfb8974a0f9 Mon Sep 17 00:00:00 2001 From: Snider Date: Thu, 2 Apr 2026 02:20:15 +0100 Subject: [PATCH] feat(lns): scaffold CoreGO package with primitives and covenants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- go.mod | 5 + lns.go | 41 ++++++++ pkg/covenant/covenant.go | 115 ++++++++++++++++++++++ pkg/dns/resolve.go | 48 +++++++++ pkg/primitives/types.go | 206 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 415 insertions(+) create mode 100644 go.mod create mode 100644 lns.go create mode 100644 pkg/covenant/covenant.go create mode 100644 pkg/dns/resolve.go create mode 100644 pkg/primitives/types.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..341cef6 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module dappco.re/go/lns + +go 1.26.0 + +require dappco.re/go/core v0.8.0-alpha.1 diff --git a/lns.go b/lns.go new file mode 100644 index 0000000..c8bc536 --- /dev/null +++ b/lns.go @@ -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} +} diff --git a/pkg/covenant/covenant.go b/pkg/covenant/covenant.go new file mode 100644 index 0000000..fb21718 --- /dev/null +++ b/pkg/covenant/covenant.go @@ -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) +} diff --git a/pkg/dns/resolve.go b/pkg/dns/resolve.go new file mode 100644 index 0000000..e6c7adc --- /dev/null +++ b/pkg/dns/resolve.go @@ -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 + } +} diff --git a/pkg/primitives/types.go b/pkg/primitives/types.go new file mode 100644 index 0000000..3ecc3b2 --- /dev/null +++ b/pkg/primitives/types.go @@ -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 +}