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
- Variables
- func LoadMnemonic(ctx context.Context, addr, env, path string) (string, error)
- func LoadMnemonicFromKMS(ctx context.Context, addr, env, path string) (string, error)
- func ServiceChainIDForCluster(clusterSeed string) ids.ID
- func SplitSecretPath(full string) (dir, name string)
- func VerifyServiceEnvelope(pubKey []byte, fullDigest ids.FullDigest, envelope, sig []byte) error
- type Allocation
- type AllocationBuilder
- func (ab *AllocationBuilder) Build() (*GenesisAllocations, error)
- func (ab *AllocationBuilder) WithAmount(amount uint64) *AllocationBuilder
- func (ab *AllocationBuilder) WithFeeAccount(index int, extra uint64) *AllocationBuilder
- func (ab *AllocationBuilder) WithImmediateUnlock() *AllocationBuilder
- func (ab *AllocationBuilder) WithNoVesting() *AllocationBuilder
- func (ab *AllocationBuilder) WithVesting(start uint64, interval uint64, periods int) *AllocationBuilder
- type CChainAlloc
- type GenesisAllocations
- func GenerateAndAllocate(keyStore *KeyStore, networkID uint32, count int, prefix string, ...) (*GenesisAllocations, error)
- func LoadAndAllocate(keyStore *KeyStore, networkID uint32, amountPerKey uint64) (*GenesisAllocations, error)
- func MainnetAllocations(networkID uint32, keys []*ValidatorKey) (*GenesisAllocations, error)
- func QuickAllocations(networkID uint32, keys []*ValidatorKey, amountPerKey uint64) (*GenesisAllocations, error)
- func TestnetAllocations(networkID uint32, keys []*ValidatorKey) (*GenesisAllocations, error)
- type KeyStore
- func (ks *KeyStore) BaseDir() string
- func (ks *KeyStore) GenerateMultiple(count int, prefix string) ([]*ValidatorKey, error)
- func (ks *KeyStore) List() ([]string, error)
- func (ks *KeyStore) Load(name string) (*ValidatorKey, error)
- func (ks *KeyStore) LoadAll() ([]*ValidatorKey, error)
- func (ks *KeyStore) Save(name string, vk *ValidatorKey) error
- type LockedAmount
- type MnemonicReader
- type ServiceIdentity
- type Signer
- type Staker
- type ValidatorKey
Constants ¶
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
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.
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.
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).
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 ¶
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.
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 ¶
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 ¶
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
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 ¶
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 ¶
NewKeyStore creates a new key store at the given directory
func (*KeyStore) GenerateMultiple ¶
func (ks *KeyStore) GenerateMultiple(count int, prefix string) ([]*ValidatorKey, error)
GenerateMultiple generates multiple validator keys
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
type LockedAmount ¶
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