mirror of
https://github.com/luxfi/ledger-lux-go.git
synced 2026-10-30 16:21:22 +00:00
- Update luxfi/crypto to v1.17.15, luxfi/ids to v1.1.3 - Update copyright year to 2025 across source files - Remove ledger.go.disabled (used incorrect luxfi/node/ids import)
185 lines
4.3 KiB
Go
185 lines
4.3 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package keychain
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/luxfi/ids"
|
|
)
|
|
|
|
// Simple Set implementation to avoid node dependency
|
|
type Set[T comparable] map[T]struct{}
|
|
|
|
func (s Set[T]) Add(item T) {
|
|
s[item] = struct{}{}
|
|
}
|
|
|
|
func (s Set[T]) Contains(item T) bool {
|
|
_, exists := s[item]
|
|
return exists
|
|
}
|
|
|
|
func Of[T comparable](items ...T) Set[T] {
|
|
s := make(Set[T], len(items))
|
|
for _, item := range items {
|
|
s.Add(item)
|
|
}
|
|
return s
|
|
}
|
|
|
|
var (
|
|
_ Keychain = (*ledgerKeychain)(nil)
|
|
_ Signer = (*ledgerSigner)(nil)
|
|
|
|
ErrInvalidIndicesLength = errors.New("number of indices should be greater than 0")
|
|
ErrInvalidNumAddrsToDerive = errors.New("number of addresses to derive should be greater than 0")
|
|
ErrInvalidNumAddrsDerived = errors.New("incorrect number of ledger derived addresses")
|
|
ErrInvalidNumSignatures = errors.New("incorrect number of signatures")
|
|
)
|
|
|
|
// Signer implements functions for a keychain to return its main address and
|
|
// to sign a hash
|
|
type Signer interface {
|
|
SignHash([]byte) ([]byte, error)
|
|
Sign([]byte) ([]byte, error)
|
|
Address() ids.ShortID
|
|
}
|
|
|
|
// Keychain maintains a set of addresses together with their corresponding
|
|
// signers
|
|
type Keychain interface {
|
|
// The returned Signer can provide a signature for [addr]
|
|
Get(addr ids.ShortID) (Signer, bool)
|
|
// Returns the set of addresses for which the accessor keeps an associated
|
|
// signer
|
|
Addresses() Set[ids.ShortID]
|
|
}
|
|
|
|
// ledgerKeychain is an abstraction of the underlying ledger hardware device,
|
|
// to be able to get a signer from a finite set of derived signers
|
|
type ledgerKeychain struct {
|
|
ledger Ledger
|
|
addrs Set[ids.ShortID]
|
|
addrToIdx map[ids.ShortID]uint32
|
|
}
|
|
|
|
// ledgerSigner is an abstraction of the underlying ledger hardware device,
|
|
// to be able sign for a specific address
|
|
type ledgerSigner struct {
|
|
ledger Ledger
|
|
idx uint32
|
|
addr ids.ShortID
|
|
}
|
|
|
|
// NewLedgerKeychain creates a new Ledger with [numToDerive] addresses.
|
|
func NewLedgerKeychain(l Ledger, numToDerive int) (Keychain, error) {
|
|
if numToDerive < 1 {
|
|
return nil, ErrInvalidNumAddrsToDerive
|
|
}
|
|
|
|
indices := make([]uint32, numToDerive)
|
|
for i := range indices {
|
|
indices[i] = uint32(i)
|
|
}
|
|
|
|
return NewLedgerKeychainFromIndices(l, indices)
|
|
}
|
|
|
|
// NewLedgerKeychainFromIndices creates a new Ledger with addresses taken from the given [indices].
|
|
func NewLedgerKeychainFromIndices(l Ledger, indices []uint32) (Keychain, error) {
|
|
if len(indices) == 0 {
|
|
return nil, ErrInvalidIndicesLength
|
|
}
|
|
|
|
addrs, err := l.Addresses(indices)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(addrs) != len(indices) {
|
|
return nil, fmt.Errorf(
|
|
"%w. expected %d, got %d",
|
|
ErrInvalidNumAddrsDerived,
|
|
len(indices),
|
|
len(addrs),
|
|
)
|
|
}
|
|
|
|
addrsSet := Of(addrs...)
|
|
|
|
addrToIdx := map[ids.ShortID]uint32{}
|
|
for i := range indices {
|
|
addrToIdx[addrs[i]] = indices[i]
|
|
}
|
|
|
|
return &ledgerKeychain{
|
|
ledger: l,
|
|
addrs: addrsSet,
|
|
addrToIdx: addrToIdx,
|
|
}, nil
|
|
}
|
|
|
|
func (l *ledgerKeychain) Addresses() Set[ids.ShortID] {
|
|
return l.addrs
|
|
}
|
|
|
|
func (l *ledgerKeychain) Get(addr ids.ShortID) (Signer, bool) {
|
|
idx, ok := l.addrToIdx[addr]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
return &ledgerSigner{
|
|
ledger: l.ledger,
|
|
idx: idx,
|
|
addr: addr,
|
|
}, true
|
|
}
|
|
|
|
// expects to receive a hash of the unsigned tx bytes
|
|
func (l *ledgerSigner) SignHash(b []byte) ([]byte, error) {
|
|
// Sign using the address with index l.idx on the ledger device. The number
|
|
// of returned signatures should be the same length as the provided indices.
|
|
sigs, err := l.ledger.SignHash(b, []uint32{l.idx})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if sigsLen := len(sigs); sigsLen != 1 {
|
|
return nil, fmt.Errorf(
|
|
"%w. expected 1, got %d",
|
|
ErrInvalidNumSignatures,
|
|
sigsLen,
|
|
)
|
|
}
|
|
|
|
return sigs[0], err
|
|
}
|
|
|
|
// expects to receive the unsigned tx bytes
|
|
func (l *ledgerSigner) Sign(b []byte) ([]byte, error) {
|
|
// Sign using the address with index l.idx on the ledger device. The number
|
|
// of returned signatures should be the same length as the provided indices.
|
|
sigs, err := l.ledger.Sign(b, []uint32{l.idx})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if sigsLen := len(sigs); sigsLen != 1 {
|
|
return nil, fmt.Errorf(
|
|
"%w. expected 1, got %d",
|
|
ErrInvalidNumSignatures,
|
|
sigsLen,
|
|
)
|
|
}
|
|
|
|
return sigs[0], err
|
|
}
|
|
|
|
func (l *ledgerSigner) Address() ids.ShortID {
|
|
return l.addr
|
|
}
|