github.com/MetalBlockchain/metalgo@v1.11.9/vms/secp256k1fx/keychain.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package secp256k1fx 5 6 import ( 7 "errors" 8 "fmt" 9 "strings" 10 11 "github.com/ethereum/go-ethereum/common" 12 "github.com/ethereum/go-ethereum/crypto" 13 14 "github.com/MetalBlockchain/metalgo/ids" 15 "github.com/MetalBlockchain/metalgo/utils/crypto/keychain" 16 "github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1" 17 "github.com/MetalBlockchain/metalgo/utils/formatting" 18 "github.com/MetalBlockchain/metalgo/utils/set" 19 "github.com/MetalBlockchain/metalgo/vms/components/verify" 20 ) 21 22 var ( 23 errCantSpend = errors.New("unable to spend this UTXO") 24 25 _ keychain.Keychain = (*Keychain)(nil) 26 ) 27 28 // Keychain is a collection of keys that can be used to spend outputs 29 type Keychain struct { 30 avaxAddrToKeyIndex map[ids.ShortID]int 31 ethAddrToKeyIndex map[common.Address]int 32 33 // These can be used to iterate over. However, they should not be modified 34 // externally. 35 Addrs set.Set[ids.ShortID] 36 EthAddrs set.Set[common.Address] 37 Keys []*secp256k1.PrivateKey 38 } 39 40 // NewKeychain returns a new keychain containing [keys] 41 func NewKeychain(keys ...*secp256k1.PrivateKey) *Keychain { 42 kc := &Keychain{ 43 avaxAddrToKeyIndex: make(map[ids.ShortID]int), 44 ethAddrToKeyIndex: make(map[common.Address]int), 45 } 46 for _, key := range keys { 47 kc.Add(key) 48 } 49 return kc 50 } 51 52 // Add a new key to the key chain 53 func (kc *Keychain) Add(key *secp256k1.PrivateKey) { 54 pk := key.PublicKey() 55 avaxAddr := pk.Address() 56 if _, ok := kc.avaxAddrToKeyIndex[avaxAddr]; !ok { 57 kc.avaxAddrToKeyIndex[avaxAddr] = len(kc.Keys) 58 ethAddr := publicKeyToEthAddress(pk) 59 kc.ethAddrToKeyIndex[ethAddr] = len(kc.Keys) 60 kc.Keys = append(kc.Keys, key) 61 kc.Addrs.Add(avaxAddr) 62 kc.EthAddrs.Add(ethAddr) 63 } 64 } 65 66 // Get a key from the keychain and return whether the key existed. 67 func (kc Keychain) Get(id ids.ShortID) (keychain.Signer, bool) { 68 return kc.get(id) 69 } 70 71 // Get a key from the keychain and return whether the key existed. 72 func (kc Keychain) GetEth(addr common.Address) (keychain.Signer, bool) { 73 if i, ok := kc.ethAddrToKeyIndex[addr]; ok { 74 return kc.Keys[i], true 75 } 76 return nil, false 77 } 78 79 // Addresses returns a list of addresses this keychain manages 80 func (kc Keychain) Addresses() set.Set[ids.ShortID] { 81 return kc.Addrs 82 } 83 84 // EthAddresses returns a list of addresses this keychain manages 85 func (kc Keychain) EthAddresses() set.Set[common.Address] { 86 return kc.EthAddrs 87 } 88 89 // New returns a newly generated private key 90 func (kc *Keychain) New() (*secp256k1.PrivateKey, error) { 91 sk, err := secp256k1.NewPrivateKey() 92 if err != nil { 93 return nil, err 94 } 95 96 kc.Add(sk) 97 return sk, nil 98 } 99 100 // Spend attempts to create an input 101 func (kc *Keychain) Spend(out verify.Verifiable, time uint64) (verify.Verifiable, []*secp256k1.PrivateKey, error) { 102 switch out := out.(type) { 103 case *MintOutput: 104 if sigIndices, keys, able := kc.Match(&out.OutputOwners, time); able { 105 return &Input{ 106 SigIndices: sigIndices, 107 }, keys, nil 108 } 109 return nil, nil, errCantSpend 110 case *TransferOutput: 111 if sigIndices, keys, able := kc.Match(&out.OutputOwners, time); able { 112 return &TransferInput{ 113 Amt: out.Amt, 114 Input: Input{ 115 SigIndices: sigIndices, 116 }, 117 }, keys, nil 118 } 119 return nil, nil, errCantSpend 120 } 121 return nil, nil, fmt.Errorf("can't spend UTXO because it is unexpected type %T", out) 122 } 123 124 // Match attempts to match a list of addresses up to the provided threshold 125 func (kc *Keychain) Match(owners *OutputOwners, time uint64) ([]uint32, []*secp256k1.PrivateKey, bool) { 126 if time < owners.Locktime { 127 return nil, nil, false 128 } 129 sigs := make([]uint32, 0, owners.Threshold) 130 keys := make([]*secp256k1.PrivateKey, 0, owners.Threshold) 131 for i := uint32(0); i < uint32(len(owners.Addrs)) && uint32(len(keys)) < owners.Threshold; i++ { 132 if key, exists := kc.get(owners.Addrs[i]); exists { 133 sigs = append(sigs, i) 134 keys = append(keys, key) 135 } 136 } 137 return sigs, keys, uint32(len(keys)) == owners.Threshold 138 } 139 140 // PrefixedString returns the key chain as a string representation with [prefix] 141 // added before every line. 142 func (kc *Keychain) PrefixedString(prefix string) string { 143 sb := strings.Builder{} 144 format := fmt.Sprintf("%%sKey[%s]: Key: %%s Address: %%s\n", 145 formatting.IntFormat(len(kc.Keys)-1)) 146 for i, key := range kc.Keys { 147 // We assume that the maximum size of a byte slice that 148 // can be stringified is at least the length of a SECP256K1 private key 149 keyStr, _ := formatting.Encode(formatting.HexNC, key.Bytes()) 150 sb.WriteString(fmt.Sprintf(format, 151 prefix, 152 i, 153 keyStr, 154 key.PublicKey().Address(), 155 )) 156 } 157 158 return strings.TrimSuffix(sb.String(), "\n") 159 } 160 161 func (kc *Keychain) String() string { 162 return kc.PrefixedString("") 163 } 164 165 // to avoid internals type assertions 166 func (kc Keychain) get(id ids.ShortID) (*secp256k1.PrivateKey, bool) { 167 if i, ok := kc.avaxAddrToKeyIndex[id]; ok { 168 return kc.Keys[i], true 169 } 170 return nil, false 171 } 172 173 func publicKeyToEthAddress(pk *secp256k1.PublicKey) common.Address { 174 return crypto.PubkeyToAddress(*(pk.ToECDSA())) 175 }