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  }