keys

package module
v1.0.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 1, 2026 License: BSD-3-Clause Imports: 26 Imported by: 3

Documentation

Overview

Package keys provides validator key management for Lux networks. It handles generation, loading, and storage of: - TLS staking keys (for node identity) - BLS signer keys (for validator consensus) - EC private keys (for P/X/C-chain addresses)

Index

Constants

View Source
const (
	MicroLux uint64 = 1                       // Base unit (6 decimals)
	Lux      uint64 = 1_000_000               // 10^6 microLux
	KiloLux  uint64 = 1_000 * Lux             // 10^9
	MegaLux  uint64 = 1_000_000 * Lux         // 10^12
	GigaLux  uint64 = 1_000_000_000 * Lux     // 10^15 (1B LUX)
	TeraLux  uint64 = 1_000_000_000_000 * Lux // 10^18 (1T LUX)

	// C-chain uses 18 decimals (wei), P/X-chain use 6 decimals
	// Multiply P-chain amount by this to get C-chain wei
	CChainDecimalShift = 1_000_000_000_000 // 10^12

	// Default validator stake: 1M LUX
	DefaultValidatorStake = MegaLux

	// Default fee account amount: 10M LUX (for chain creation, transactions)
	DefaultFeeAccountAmount = 10 * MegaLux
)

Unit constants for LUX amounts

View Source
const BIP44Purpose = 44

BIP44Purpose is the BIP-44 purpose constant. Pinned to 44 even though service identities don't carry funds — staying inside the BIP-44 purpose tree keeps the same mnemonic interoperable with the Lux wallet's existing derivation tree.

View Source
const CoinTypeEVM = 60

CoinTypeEVM is the SLIP-0044 coin_type for EVM chains (60', shared with Ethereum). C-Chain and any non-Lux L1 EVM derive under this tree.

View Source
const CoinTypeUTXO = 9000

CoinTypeUTXO is the SLIP-0044 coin_type for the UTXO-layout DAG-like chains — X-Chain (AVM) + P-Chain. Service identities derive under m/44'/9000'/<serviceIndex>'/0'/0' so they share the coin-type tree with X/P staking keys but never collide with account-zero derivations (serviceIndex is hashed from servicePath, never 0 in practice for any well-formed path).

View Source
const EnvelopeDomain = "lux-svc-envelope-v1"

EnvelopeDomain is the customisation prefix mixed into every envelope signature so signatures from one envelope shape (a KMS opcode call) cannot be replayed against another (e.g. a future RPC over the same wire). Pinned at v1.

Variables

View Source
var ErrInvalidServicePath = errors.New("keys: service path is required")

ErrInvalidServicePath is returned when the servicePath argument is empty after trim. Empty paths would collapse every service to the same NodeID, which would silently mask configuration drift.

View Source
var ServiceChainID = mustHashChainID("lux-service-identity")

ServiceChainID is the well-known chain identifier under which all service NodeIDs are derived. Distinct from any L1 chain ID so a service NodeID never accidentally validates against a chain's validator-set commitment. Set once and never bumped — the empty "service" string is the canonical seed.

Use the helper ServiceChainIDForCluster if a deployment ever needs per-cluster service NodeID separation. The default (empty seed) is what every Hanzo cluster uses today.

Functions

func LoadMnemonic

func LoadMnemonic(ctx context.Context, addr, env, path string) (string, error)

LoadMnemonic returns the BIP-39 mnemonic for the calling service. MNEMONIC env wins when set; otherwise dials KMS over native ZAP at `addr` and reads the secret at `path` under `env`.

addr  KMS host:port (e.g. "liquid-kms.liquidity.svc:9999")
env   KMS env scope ("mainnet" | "testnet" | "devnet")
path  KMS secret path (e.g. "/mnemonic" or "/foo/master")

Returns the validated BIP-39 phrase. Caller is responsible for keeping it on the goroutine stack and not logging / persisting it.

func LoadMnemonicFromKMS

func LoadMnemonicFromKMS(ctx context.Context, addr, env, path string) (string, error)

LoadMnemonicFromKMS is the production-only path (no MNEMONIC env short-circuit). Useful when a caller wants to force the KMS read regardless of ambient env — e.g., a regression test pinning the production code path.

func ServiceChainIDForCluster added in v1.0.10

func ServiceChainIDForCluster(clusterSeed string) ids.ID

ServiceChainIDForCluster is a future hook for per-cluster NodeID separation. Today every Hanzo cluster shares ServiceChainID; if a future deployment ever needs to isolate two clusters' NodeID spaces (e.g. lux-mainnet vs hanzo-prod), the operator can override ServiceChainID at boot via this helper. Pure function — no global state mutated on call.

func SplitSecretPath

func SplitSecretPath(full string) (dir, name string)

SplitSecretPath turns "/foo/bar/baz" into ("/foo/bar/", "baz"). When there is no '/' or only a leading one, the directory is "" and the whole remainder is the name (e.g., "/mnemonic" → ("", "mnemonic")).

Exported so every consumer can address secrets with the same convention — one and only one addressing scheme across mnemonic, staking keys, gas-payer keys, etc.

func VerifyServiceEnvelope added in v1.0.10

func VerifyServiceEnvelope(pubKey []byte, fullDigest ids.FullDigest, envelope, sig []byte) error

VerifyServiceEnvelope verifies an ML-DSA-65 signature against an envelope produced by Sign. The caller supplies the signer's FullDigest (the 48-byte commitment carried in the envelope header) and public key bytes — both authenticated by the consensus layer.

Pure function: no I/O, no time dependency.

Types

type Allocation

type Allocation struct {
	// ETHAddr is the C-chain compatible address (0x...)
	ETHAddr string `json:"evmAddr"`

	// LUXAddr is the P/X-chain address (P-lux1...)
	LUXAddr string `json:"utxoAddr"`

	// InitialAmount is immediately available on X-chain (usually 0)
	InitialAmount uint64 `json:"initialAmount"`

	// UnlockSchedule defines when funds become available on P-chain
	UnlockSchedule []LockedAmount `json:"unlockSchedule"`
}

Allocation represents a P-chain genesis allocation

type AllocationBuilder

type AllocationBuilder struct {
	// contains filtered or unexported fields
}

AllocationBuilder helps build genesis allocations from validator keys

func NewAllocationBuilder

func NewAllocationBuilder(networkID uint32, keys []*ValidatorKey) *AllocationBuilder

NewAllocationBuilder creates a new builder for the given keys

func (*AllocationBuilder) Build

func (ab *AllocationBuilder) Build() (*GenesisAllocations, error)

Build creates the genesis allocations

func (*AllocationBuilder) WithAmount

func (ab *AllocationBuilder) WithAmount(amount uint64) *AllocationBuilder

WithAmount sets the amount per validator

func (*AllocationBuilder) WithFeeAccount

func (ab *AllocationBuilder) WithFeeAccount(index int, extra uint64) *AllocationBuilder

WithFeeAccount sets which validator gets extra funds for fees

func (*AllocationBuilder) WithImmediateUnlock

func (ab *AllocationBuilder) WithImmediateUnlock() *AllocationBuilder

WithImmediateUnlock makes funds immediately unlocked (locktime=0)

func (*AllocationBuilder) WithNoVesting

func (ab *AllocationBuilder) WithNoVesting() *AllocationBuilder

WithNoVesting makes all funds immediately available

func (*AllocationBuilder) WithVesting

func (ab *AllocationBuilder) WithVesting(start uint64, interval uint64, periods int) *AllocationBuilder

WithVesting configures the vesting schedule

type CChainAlloc

type CChainAlloc struct {
	Balance string `json:"balance"` // Hex-encoded wei amount
}

CChainAlloc represents a C-chain genesis allocation

type GenesisAllocations

type GenesisAllocations struct {
	// P-chain allocations
	PChainAllocations []Allocation `json:"allocations"`

	// Initial staked funds (addresses that are staked at genesis)
	InitialStakedFunds []string `json:"initialStakedFunds"`

	// Initial stakers (validators at genesis)
	InitialStakers []Staker `json:"initialStakers"`

	// C-chain allocations (address -> balance)
	CChainAllocations map[string]CChainAlloc `json:"cchain"`
}

GenesisAllocations contains all allocations for network genesis

func GenerateAndAllocate

func GenerateAndAllocate(keyStore *KeyStore, networkID uint32, count int, prefix string, amountPerKey uint64) (*GenesisAllocations, error)

GenerateAndAllocate generates keys and creates allocations in one step

func LoadAndAllocate

func LoadAndAllocate(keyStore *KeyStore, networkID uint32, amountPerKey uint64) (*GenesisAllocations, error)

LoadAndAllocate loads existing keys and creates allocations

func MainnetAllocations

func MainnetAllocations(networkID uint32, keys []*ValidatorKey) (*GenesisAllocations, error)

MainnetAllocations creates allocations suitable for mainnet (100-year vesting)

func QuickAllocations

func QuickAllocations(networkID uint32, keys []*ValidatorKey, amountPerKey uint64) (*GenesisAllocations, error)

QuickAllocations creates allocations with immediate unlock for testing

func TestnetAllocations

func TestnetAllocations(networkID uint32, keys []*ValidatorKey) (*GenesisAllocations, error)

TestnetAllocations creates allocations suitable for testnet (no vesting)

type KeyStore

type KeyStore struct {
	// contains filtered or unexported fields
}

KeyStore manages validator keys with filesystem persistence

func NewKeyStore

func NewKeyStore(baseDir string) *KeyStore

NewKeyStore creates a new key store at the given directory

func (*KeyStore) BaseDir

func (ks *KeyStore) BaseDir() string

BaseDir returns the base directory for the key store

func (*KeyStore) GenerateMultiple

func (ks *KeyStore) GenerateMultiple(count int, prefix string) ([]*ValidatorKey, error)

GenerateMultiple generates multiple validator keys

func (*KeyStore) List

func (ks *KeyStore) List() ([]string, error)

List returns all validator keys in the store

func (*KeyStore) Load

func (ks *KeyStore) Load(name string) (*ValidatorKey, error)

Load reads a validator key from the filesystem

func (*KeyStore) LoadAll

func (ks *KeyStore) LoadAll() ([]*ValidatorKey, error)

LoadAll loads all validator keys from the store

func (*KeyStore) Save

func (ks *KeyStore) Save(name string, vk *ValidatorKey) error

Save persists a validator key to the filesystem

type LockedAmount

type LockedAmount struct {
	Amount   uint64 `json:"amount"`
	Locktime uint64 `json:"locktime"`
}

LockedAmount represents a locked amount with unlock time

type MnemonicReader

type MnemonicReader interface {
	GetAt(ctx context.Context, path, name, env string) (string, error)
	Close()
}

MnemonicReader is the minimum surface LoadMnemonicFromKMS needs from a KMS client. The real *zapclient.Client satisfies it; tests inject fakes via the dialKMS seam below.

type ServiceIdentity added in v1.0.10

type ServiceIdentity struct {
	// ServicePath is the canonical path string (verbatim) used to derive
	// the BIP-32 hardened index. Stored for diagnostics; the
	// authoritative input is the derived NodeID.
	ServicePath string

	// NodeID is the 20-byte canonical NodeID derived under
	// NodeIDSchemeMLDSA65. Map-key safe.
	NodeID ids.NodeID

	// TypedNodeID is the wire-form NodeID (scheme byte || NodeID).
	// Travels in envelope headers so the receiver knows which verifier
	// to dispatch.
	TypedNodeID ids.TypedNodeID

	// FullDigest is the 48-byte SHAKE256-384 commitment to the
	// identity. Bound into envelope signatures to prevent cross-scheme
	// confusion attacks.
	FullDigest ids.FullDigest

	// PublicKey is the ML-DSA-65 public key bytes.
	PublicKey []byte
	// contains filtered or unexported fields
}

ServiceIdentity binds a mnemonic-derived ML-DSA-65 signing key to its canonical NodeID. The struct owns the private key bytes; call Wipe() when done.

Safe for concurrent use after construction — every field is read-only after NewServiceIdentity returns. Wipe is the only mutating method and the caller serialises it (typically a single defer).

func NewServiceIdentity added in v1.0.10

func NewServiceIdentity(mnemonic, servicePath string) (*ServiceIdentity, error)

NewServiceIdentity is the canonical constructor. mnemonic must be a valid BIP-39 phrase; servicePath must be non-empty. Returns the derived ServiceIdentity ready to Sign().

Pure function: given the same (mnemonic, servicePath) you get the same NodeID, byte-for-byte. No I/O, no randomness, no clock reads.

func (*ServiceIdentity) Sign added in v1.0.10

func (s *ServiceIdentity) Sign(envelope []byte) ([]byte, error)

Sign produces a deterministic ML-DSA-65 signature over the envelope digest. The caller is responsible for serialising the envelope into canonical bytes before calling Sign — see SignEnvelope for the canonical (method, path, payload, timestamp, nonce) shape.

The signed bytes are the SHAKE256 digest of:

left_encode(|domain|·8) || EnvelopeDomain ||
left_encode(|full_digest|·8) || FullDigest ||
left_encode(|envelope|·8) || envelope

Binding the FullDigest into the prehash means a verifier always rejects an envelope signed by a different identity — even a key with the same NodeID prefix.

func (*ServiceIdentity) Wipe added in v1.0.10

func (s *ServiceIdentity) Wipe()

Wipe zeroes the private key in place. Idempotent. Safe to call from a defer.

type Signer

type Signer struct {
	PublicKey         string `json:"publicKey"`
	ProofOfPossession string `json:"proofOfPossession"`
}

Signer contains BLS key information for a validator

type Staker

type Staker struct {
	NodeID        string  `json:"nodeID"`
	RewardAddress string  `json:"rewardAddress"`
	DelegationFee uint32  `json:"delegationFee"`
	Signer        *Signer `json:"signer,omitempty"`
}

Staker represents an initial validator in genesis

type ValidatorKey

type ValidatorKey struct {
	// NodeID is the unique identifier for the node (derived from TLS cert)
	NodeID ids.NodeID

	// TLS keys for node identity
	StakerKey  []byte // PEM-encoded private key
	StakerCert []byte // PEM-encoded certificate

	// BLS keys for consensus
	BLSSecretKey []byte // Raw BLS secret key bytes
	BLSPublicKey []byte // Compressed BLS public key
	BLSPoP       []byte // Proof of Possession signature

	// EC key for addresses
	ECPrivateKey []byte // Raw 32-byte secp256k1 private key

	// Derived addresses
	PChainAddr ids.ShortID // P/X chain address (20 bytes)
	CChainAddr ids.ShortID // C-chain address (20 bytes, Ethereum format)
}

ValidatorKey contains all keys needed for a validator node

func DeriveValidatorFromMnemonic

func DeriveValidatorFromMnemonic(mnemonic string, accountIndex uint32) (*ValidatorKey, error)

DeriveValidatorFromMnemonic derives a single validator key from mnemonic at given index. All keys (EC, TLS, BLS) are now derived deterministically from the mnemonic.

func DeriveValidatorsFromMnemonic

func DeriveValidatorsFromMnemonic(mnemonic string, count int) ([]*ValidatorKey, error)

DeriveValidatorsFromMnemonic derives N validator keys from a BIP39 mnemonic. Each validator uses BIP44 path m/44'/60'/0'/0/{index} for the EC key. TLS staking certs and BLS keys are generated fresh (not deterministic from mnemonic). This is designed for runtime use - no files are written to disk.

func GenerateValidatorKey

func GenerateValidatorKey() (*ValidatorKey, error)

GenerateValidatorKey creates a complete set of validator keys

func LoadFromDir

func LoadFromDir(nodeDir string) (*ValidatorKey, error)

LoadFromDir loads a validator key from a specific directory

func (*ValidatorKey) BLSKeyBase64

func (vk *ValidatorKey) BLSKeyBase64() string

BLSKeyBase64 returns the BLS secret key as base64 (for node config)

func (*ValidatorKey) BLSPoPHex

func (vk *ValidatorKey) BLSPoPHex() string

BLSPoPHex returns the BLS proof of possession as hex with 0x prefix

func (*ValidatorKey) BLSPublicKeyHex

func (vk *ValidatorKey) BLSPublicKeyHex() string

BLSPublicKeyHex returns the BLS public key as hex with 0x prefix

func (*ValidatorKey) CChainAddrHex

func (vk *ValidatorKey) CChainAddrHex() string

CChainAddrHex returns the C-chain address as hex with 0x prefix

Directories

Path Synopsis
cmd
keys command
Command keys manages validator keys for Lux networks.
Command keys manages validator keys for Lux networks.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL