gecko/vms/secp256k1fx/keychain.go

133 lines
3.6 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package secp256k1fx
import (
"errors"
"fmt"
"strings"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/vms/components/verify"
)
var (
errLockedFunds = errors.New("funds currently locked")
errCantSpend = errors.New("utxo couldn't be spent")
)
// Keychain is a collection of keys that can be used to spend outputs
type Keychain struct {
factory *crypto.FactorySECP256K1R
addrToKeyIndex map[[20]byte]int
// These can be used to iterate over. However, they should not be modified externally.
Addrs ids.ShortSet
Keys []*crypto.PrivateKeySECP256K1R
}
// NewKeychain returns a new, empty, keychain
func NewKeychain() *Keychain {
return &Keychain{
factory: &crypto.FactorySECP256K1R{},
addrToKeyIndex: make(map[[20]byte]int),
}
}
// Add a new key to the key chain
func (kc *Keychain) Add(key *crypto.PrivateKeySECP256K1R) {
addr := key.PublicKey().Address()
addrHash := addr.Key()
if _, ok := kc.addrToKeyIndex[addrHash]; !ok {
kc.addrToKeyIndex[addrHash] = len(kc.Keys)
kc.Keys = append(kc.Keys, key)
kc.Addrs.Add(addr)
}
}
// Get a key from the keychain. If the key is unknown, the
func (kc Keychain) Get(id ids.ShortID) (*crypto.PrivateKeySECP256K1R, bool) {
if i, ok := kc.addrToKeyIndex[id.Key()]; ok {
return kc.Keys[i], true
}
return &crypto.PrivateKeySECP256K1R{}, false
}
// Addresses returns a list of addresses this keychain manages
func (kc Keychain) Addresses() ids.ShortSet { return kc.Addrs }
// New returns a newly generated private key
func (kc *Keychain) New() (*crypto.PrivateKeySECP256K1R, error) {
skGen, err := kc.factory.NewPrivateKey()
if err != nil {
return nil, err
}
sk := skGen.(*crypto.PrivateKeySECP256K1R)
kc.Add(sk)
return sk, nil
}
// Spend attempts to create an input
func (kc *Keychain) Spend(out verify.Verifiable, time uint64) (verify.Verifiable, []*crypto.PrivateKeySECP256K1R, error) {
switch out := out.(type) {
case *MintOutput:
if sigIndices, keys, able := kc.Match(&out.OutputOwners); able {
return &MintInput{
Input: Input{
SigIndices: sigIndices,
},
}, keys, nil
}
case *TransferOutput:
if time < out.Locktime {
return nil, nil, errLockedFunds
}
if sigIndices, keys, able := kc.Match(&out.OutputOwners); able {
return &TransferInput{
Amt: out.Amt,
Input: Input{
SigIndices: sigIndices,
},
}, keys, nil
}
}
return nil, nil, errCantSpend
}
// Match attempts to match a list of addresses up to the provided threshold
func (kc *Keychain) Match(owners *OutputOwners) ([]uint32, []*crypto.PrivateKeySECP256K1R, bool) {
sigs := []uint32{}
keys := []*crypto.PrivateKeySECP256K1R{}
for i := uint32(0); i < uint32(len(owners.Addrs)) && uint32(len(keys)) < owners.Threshold; i++ {
if key, exists := kc.Get(owners.Addrs[i]); exists {
sigs = append(sigs, i)
keys = append(keys, key)
}
}
return sigs, keys, uint32(len(keys)) == owners.Threshold
}
// PrefixedString returns the key chain as a string representation with [prefix]
// added before every line.
func (kc *Keychain) PrefixedString(prefix string) string {
s := strings.Builder{}
format := fmt.Sprintf("%%sKey[%s]: Key: %%s Address: %%s\n",
formatting.IntFormat(len(kc.Keys)-1))
for i, key := range kc.Keys {
s.WriteString(fmt.Sprintf(format,
prefix,
i,
formatting.CB58{Bytes: key.Bytes()},
key.PublicKey().Address()))
}
return strings.TrimSuffix(s.String(), "\n")
}
func (kc *Keychain) String() string { return kc.PrefixedString("") }