gecko/vms/spchainvm/keychain.go

116 lines
3.1 KiB
Go
Raw Normal View History

2020-03-10 12:20:34 -07:00
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package spchainvm
import (
"errors"
"fmt"
"strings"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/formatting"
)
var (
errUnknownAccount = errors.New("unknown account")
)
// Keychain is a collection of keys that can be used to spend utxos
type Keychain struct {
2020-03-17 13:07:33 -07:00
factory crypto.FactorySECP256K1R
2020-03-10 12:20:34 -07:00
networkID uint32
chainID ids.ID
2020-03-17 13:07:33 -07:00
// Key: The id of a private key (namely, [privKey].PublicKey().Address().Key())
// Value: The index in Keys of that private key
2020-03-10 12:20:34 -07:00
keyMap map[[20]byte]int
2020-03-17 13:07:33 -07:00
// Each element is an address controlled by a key in [Keys]
// This can be used to iterate over. It should not be modified externally.
Addrs ids.ShortSet
// List of keys this keychain manages
// This can be used to iterate over. It should not be modified externally.
Keys []*crypto.PrivateKeySECP256K1R
2020-03-10 12:20:34 -07:00
}
// NewKeychain creates a new keychain for a chain
func NewKeychain(networkID uint32, chainID ids.ID) *Keychain {
return &Keychain{
2020-03-17 13:07:33 -07:00
networkID: networkID,
chainID: chainID,
keyMap: make(map[[20]byte]int),
2020-03-10 12:20:34 -07:00
}
}
// New returns a newly generated private key
2020-03-17 13:07:33 -07:00
func (kc *Keychain) New() (*crypto.PrivateKeySECP256K1R, error) {
skGen, err := kc.factory.NewPrivateKey()
if err != nil {
return nil, err
}
2020-03-10 12:20:34 -07:00
sk := skGen.(*crypto.PrivateKeySECP256K1R)
kc.Add(sk)
2020-03-17 13:07:33 -07:00
return sk, nil
2020-03-10 12:20:34 -07:00
}
// Add a new key to the key chain
func (kc *Keychain) Add(key *crypto.PrivateKeySECP256K1R) {
2020-03-10 12:20:34 -07:00
addr := key.PublicKey().Address()
addrHash := addr.Key()
if _, ok := kc.keyMap[addrHash]; !ok {
kc.keyMap[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) {
2020-03-10 12:20:34 -07:00
if i, ok := kc.keyMap[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 }
2020-03-10 12:20:34 -07:00
// Spend attempts to create a new transaction
func (kc *Keychain) Spend(account Account, amount uint64, destination ids.ShortID) (*Tx, Account, error) {
2020-03-10 12:20:34 -07:00
key, exists := kc.Get(account.ID())
if !exists {
return nil, Account{}, errUnknownAccount
}
ctx := snow.DefaultContextTest()
ctx.NetworkID = kc.networkID
ctx.ChainID = kc.chainID
return account.CreateTx(amount, destination, ctx, key)
}
// PrefixedString returns a string representation of this keychain with each
// line prepended with [prefix]
func (kc *Keychain) PrefixedString(prefix string) string {
2020-03-10 12:20:34 -07:00
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 {
2020-03-10 12:20:34 -07:00
return kc.PrefixedString("")
}