forked from ava-labs/avalanchego
-
Notifications
You must be signed in to change notification settings - Fork 4
/
keychain.go
150 lines (131 loc) · 4.24 KB
/
keychain.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package secp256k1fx
import (
"errors"
"fmt"
"strings"
"github.com/MetalBlockchain/metalgo/ids"
"github.com/MetalBlockchain/metalgo/utils/crypto/keychain"
"github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1"
"github.com/MetalBlockchain/metalgo/utils/formatting"
"github.com/MetalBlockchain/metalgo/utils/set"
"github.com/MetalBlockchain/metalgo/vms/components/verify"
)
var (
errCantSpend = errors.New("unable to spend this UTXO")
_ keychain.Keychain = (*Keychain)(nil)
)
// Keychain is a collection of keys that can be used to spend outputs
type Keychain struct {
factory *secp256k1.Factory
addrToKeyIndex map[ids.ShortID]int
// These can be used to iterate over. However, they should not be modified externally.
Addrs set.Set[ids.ShortID]
Keys []*secp256k1.PrivateKey
}
// NewKeychain returns a new keychain containing [keys]
func NewKeychain(keys ...*secp256k1.PrivateKey) *Keychain {
kc := &Keychain{
factory: &secp256k1.Factory{},
addrToKeyIndex: make(map[ids.ShortID]int),
}
for _, key := range keys {
kc.Add(key)
}
return kc
}
// Add a new key to the key chain
func (kc *Keychain) Add(key *secp256k1.PrivateKey) {
addr := key.PublicKey().Address()
if _, ok := kc.addrToKeyIndex[addr]; !ok {
kc.addrToKeyIndex[addr] = len(kc.Keys)
kc.Keys = append(kc.Keys, key)
kc.Addrs.Add(addr)
}
}
// Get a key from the keychain. If the key is unknown, return a pointer to an empty key.
// In both cases also return a boolean telling whether the key is known.
func (kc Keychain) Get(id ids.ShortID) (keychain.Signer, bool) {
return kc.get(id)
}
// Addresses returns a list of addresses this keychain manages
func (kc Keychain) Addresses() set.Set[ids.ShortID] {
return kc.Addrs
}
// New returns a newly generated private key
func (kc *Keychain) New() (*secp256k1.PrivateKey, error) {
sk, err := kc.factory.NewPrivateKey()
if err != nil {
return nil, err
}
kc.Add(sk)
return sk, nil
}
// Spend attempts to create an input
func (kc *Keychain) Spend(out verify.Verifiable, time uint64) (verify.Verifiable, []*secp256k1.PrivateKey, error) {
switch out := out.(type) {
case *MintOutput:
if sigIndices, keys, able := kc.Match(&out.OutputOwners, time); able {
return &Input{
SigIndices: sigIndices,
}, keys, nil
}
return nil, nil, errCantSpend
case *TransferOutput:
if sigIndices, keys, able := kc.Match(&out.OutputOwners, time); able {
return &TransferInput{
Amt: out.Amt,
Input: Input{
SigIndices: sigIndices,
},
}, keys, nil
}
return nil, nil, errCantSpend
}
return nil, nil, fmt.Errorf("can't spend UTXO because it is unexpected type %T", out)
}
// Match attempts to match a list of addresses up to the provided threshold
func (kc *Keychain) Match(owners *OutputOwners, time uint64) ([]uint32, []*secp256k1.PrivateKey, bool) {
if time < owners.Locktime {
return nil, nil, false
}
sigs := make([]uint32, 0, owners.Threshold)
keys := make([]*secp256k1.PrivateKey, 0, owners.Threshold)
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 {
sb := strings.Builder{}
format := fmt.Sprintf("%%sKey[%s]: Key: %%s Address: %%s\n",
formatting.IntFormat(len(kc.Keys)-1))
for i, key := range kc.Keys {
// We assume that the maximum size of a byte slice that
// can be stringified is at least the length of a SECP256K1 private key
keyStr, _ := formatting.Encode(formatting.HexNC, key.Bytes())
sb.WriteString(fmt.Sprintf(format,
prefix,
i,
keyStr,
key.PublicKey().Address(),
))
}
return strings.TrimSuffix(sb.String(), "\n")
}
func (kc *Keychain) String() string {
return kc.PrefixedString("")
}
// to avoid internals type assertions
func (kc Keychain) get(id ids.ShortID) (*secp256k1.PrivateKey, bool) {
if i, ok := kc.addrToKeyIndex[id]; ok {
return kc.Keys[i], true
}
return nil, false
}