mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
Kills pcodecs from 3 complete subsystems (verified go test ./... = 155 ok / 0 FAIL):
- proposervm: block/state/summary struct-is-wire (blockwire.go + statewire.go;
deleted block/state/summary codec.go). blockKind bytes: reserved=0 signed=1
option=2. Epoch inlined in unsigned object. proposer/windower chainSource seed
= binary.LittleEndian (byte-identical to old wrappers.Packer.UnpackLong).
- example/xsvm: tx/block/genesis native Marshal (deleted 3 codec.go); create-chain
example uses genesis.Marshal().
- components: message.Tx native (deleted codec.go); keystore codec shell removed;
index pcodecs.LongLen → local const.
- evm/{predicate,lp176}: off pcodecs.
- platformvm residual: state metadata + genesis + metrics + signer + stakeable
native, vestigial serialize tags removed.
REMAINING (careful solo, agents weekly-capped): warp (reverted — agent left an
ID≠Marshal consensus inconsistency in ChainToL1Conversion; needs proper review,
not a golden-regen) + xvm (reverted — barely started). pcodecs package stays
until those 2 land. /ext/→/v1/ endpoint change also pending.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
212 lines
5.4 KiB
Go
212 lines
5.4 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package keystore
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/luxfi/crypto/secp256k1"
|
|
"github.com/luxfi/database"
|
|
"github.com/luxfi/database/encdb"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/math/set"
|
|
"github.com/luxfi/node/service/keystore"
|
|
"github.com/luxfi/utxo/secp256k1fx"
|
|
)
|
|
|
|
// Max number of addresses allowed for a single keystore user
|
|
const maxKeystoreAddresses = 5000
|
|
|
|
var (
|
|
// Key in the database whose corresponding value is the list of addresses
|
|
// this user controls
|
|
addressesKey = ids.Empty[:]
|
|
|
|
errMaxAddresses = fmt.Errorf("keystore user has reached its limit of %d addresses", maxKeystoreAddresses)
|
|
|
|
_ User = (*user)(nil)
|
|
)
|
|
|
|
type User interface {
|
|
io.Closer
|
|
|
|
// Get the addresses controlled by this user
|
|
GetAddresses() ([]ids.ShortID, error)
|
|
|
|
// PutKeys persists [privKeys]
|
|
PutKeys(privKeys ...*secp256k1.PrivateKey) error
|
|
|
|
// GetKey returns the private key that controls the given address
|
|
GetKey(address ids.ShortID) (*secp256k1.PrivateKey, error)
|
|
}
|
|
|
|
type user struct {
|
|
db *encdb.Database
|
|
}
|
|
|
|
// NewUserFromKeystore tracks a keystore user from the provided keystore
|
|
func NewUserFromKeystore(ks keystore.BlockchainKeystore, username, password string) (User, error) {
|
|
db, err := ks.GetDatabase(username, password)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("problem retrieving user %q: %w", username, err)
|
|
}
|
|
return NewUserFromDB(db), nil
|
|
}
|
|
|
|
// NewUserFromDB tracks a keystore user from a database
|
|
func NewUserFromDB(db *encdb.Database) User {
|
|
return &user{db: db}
|
|
}
|
|
|
|
func (u *user) GetAddresses() ([]ids.ShortID, error) {
|
|
// Get user's addresses
|
|
addressBytes, err := u.db.Get(addressesKey)
|
|
if err == database.ErrNotFound {
|
|
// If user has no addresses, return empty list
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return parseAddresses(addressBytes)
|
|
}
|
|
|
|
// marshalAddresses encodes the user's controlled addresses as the flat
|
|
// concatenation of their 20-byte values. ids.ShortID is fixed-width, so the
|
|
// count is implied by len/ShortIDLen — no length prefix or codec is needed.
|
|
func marshalAddresses(addresses []ids.ShortID) []byte {
|
|
b := make([]byte, 0, len(addresses)*ids.ShortIDLen)
|
|
for i := range addresses {
|
|
b = append(b, addresses[i][:]...)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// parseAddresses is the inverse of marshalAddresses.
|
|
func parseAddresses(b []byte) ([]ids.ShortID, error) {
|
|
if len(b)%ids.ShortIDLen != 0 {
|
|
return nil, fmt.Errorf("keystore: address blob length %d is not a multiple of %d", len(b), ids.ShortIDLen)
|
|
}
|
|
n := len(b) / ids.ShortIDLen
|
|
if n == 0 {
|
|
return nil, nil
|
|
}
|
|
addresses := make([]ids.ShortID, n)
|
|
for i := 0; i < n; i++ {
|
|
copy(addresses[i][:], b[i*ids.ShortIDLen:])
|
|
}
|
|
return addresses, nil
|
|
}
|
|
|
|
func (u *user) PutKeys(privKeys ...*secp256k1.PrivateKey) error {
|
|
toStore := make([]*secp256k1.PrivateKey, 0, len(privKeys))
|
|
for _, privKey := range privKeys {
|
|
address := privKey.PublicKey().Address() // address the privKey controls
|
|
hasAddress, err := u.db.Has(address.Bytes())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !hasAddress {
|
|
toStore = append(toStore, privKey)
|
|
}
|
|
}
|
|
|
|
// there's nothing to store
|
|
if len(toStore) == 0 {
|
|
return nil
|
|
}
|
|
|
|
addresses, err := u.GetAddresses()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(toStore) > maxKeystoreAddresses || len(addresses) > maxKeystoreAddresses-len(toStore) {
|
|
return errMaxAddresses
|
|
}
|
|
|
|
for _, privKey := range toStore {
|
|
pk := privKey.PublicKey()
|
|
// Convert public key to Lux address using hash160
|
|
pkBytes := pk.Bytes()
|
|
addressBytes := secp256k1.PubkeyBytesToAddress(pkBytes)
|
|
address, err := ids.ToShortID(addressBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Address --> private key
|
|
if err := u.db.Put(address[:], privKey.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
addresses = append(addresses, address)
|
|
}
|
|
|
|
return u.db.Put(addressesKey, marshalAddresses(addresses))
|
|
}
|
|
|
|
func (u *user) GetKey(address ids.ShortID) (*secp256k1.PrivateKey, error) {
|
|
bytes, err := u.db.Get(address.Bytes())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return secp256k1.ToPrivateKey(bytes)
|
|
}
|
|
|
|
func (u *user) Close() error {
|
|
return u.db.Close()
|
|
}
|
|
|
|
// Create and store a new key that will be controlled by this user.
|
|
func NewKey(u User) (*secp256k1.PrivateKey, error) {
|
|
keys, err := NewKeys(u, 1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return keys[0], nil
|
|
}
|
|
|
|
// Create and store [numKeys] new keys that will be controlled by this user.
|
|
func NewKeys(u User, numKeys int) ([]*secp256k1.PrivateKey, error) {
|
|
keys := make([]*secp256k1.PrivateKey, numKeys)
|
|
for i := range keys {
|
|
sk, err := secp256k1.NewPrivateKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
keys[i] = sk
|
|
}
|
|
return keys, u.PutKeys(keys...)
|
|
}
|
|
|
|
// Keychain returns a new keychain from the [user].
|
|
// If [addresses] is non-empty it fetches only the keys in addresses. If a key
|
|
// is missing, it will be ignored.
|
|
// If [addresses] is empty, then it will create a keychain using every address
|
|
// in the provided [user].
|
|
func GetKeychain(u User, addresses set.Set[ids.ShortID]) (*secp256k1fx.Keychain, error) {
|
|
addrsList := addresses.List()
|
|
if len(addrsList) == 0 {
|
|
var err error
|
|
addrsList, err = u.GetAddresses()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
kc := secp256k1fx.NewKeychain()
|
|
for _, addr := range addrsList {
|
|
sk, err := u.GetKey(addr)
|
|
if err == database.ErrNotFound {
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("problem retrieving private key for address %s: %w", addr, err)
|
|
}
|
|
kc.Add(sk)
|
|
}
|
|
return kc, nil
|
|
}
|