github.com/MetalBlockchain/metalgo@v1.11.9/utils/crypto/keychain/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 keychain 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/MetalBlockchain/metalgo/ids" 11 "github.com/MetalBlockchain/metalgo/utils/set" 12 ) 13 14 var ( 15 _ Keychain = (*ledgerKeychain)(nil) 16 _ Signer = (*ledgerSigner)(nil) 17 18 ErrInvalidIndicesLength = errors.New("number of indices should be greater than 0") 19 ErrInvalidNumAddrsToDerive = errors.New("number of addresses to derive should be greater than 0") 20 ErrInvalidNumAddrsDerived = errors.New("incorrect number of ledger derived addresses") 21 ErrInvalidNumSignatures = errors.New("incorrect number of signatures") 22 ) 23 24 // Signer implements functions for a keychain to return its main address and 25 // to sign a hash 26 type Signer interface { 27 SignHash([]byte) ([]byte, error) 28 Sign([]byte) ([]byte, error) 29 Address() ids.ShortID 30 } 31 32 // Keychain maintains a set of addresses together with their corresponding 33 // signers 34 type Keychain interface { 35 // The returned Signer can provide a signature for [addr] 36 Get(addr ids.ShortID) (Signer, bool) 37 // Returns the set of addresses for which the accessor keeps an associated 38 // signer 39 Addresses() set.Set[ids.ShortID] 40 } 41 42 // ledgerKeychain is an abstraction of the underlying ledger hardware device, 43 // to be able to get a signer from a finite set of derived signers 44 type ledgerKeychain struct { 45 ledger Ledger 46 addrs set.Set[ids.ShortID] 47 addrToIdx map[ids.ShortID]uint32 48 } 49 50 // ledgerSigner is an abstraction of the underlying ledger hardware device, 51 // to be able sign for a specific address 52 type ledgerSigner struct { 53 ledger Ledger 54 idx uint32 55 addr ids.ShortID 56 } 57 58 // NewLedgerKeychain creates a new Ledger with [numToDerive] addresses. 59 func NewLedgerKeychain(l Ledger, numToDerive int) (Keychain, error) { 60 if numToDerive < 1 { 61 return nil, ErrInvalidNumAddrsToDerive 62 } 63 64 indices := make([]uint32, numToDerive) 65 for i := range indices { 66 indices[i] = uint32(i) 67 } 68 69 return NewLedgerKeychainFromIndices(l, indices) 70 } 71 72 // NewLedgerKeychainFromIndices creates a new Ledger with addresses taken from the given [indices]. 73 func NewLedgerKeychainFromIndices(l Ledger, indices []uint32) (Keychain, error) { 74 if len(indices) == 0 { 75 return nil, ErrInvalidIndicesLength 76 } 77 78 addrs, err := l.Addresses(indices) 79 if err != nil { 80 return nil, err 81 } 82 83 if len(addrs) != len(indices) { 84 return nil, fmt.Errorf( 85 "%w. expected %d, got %d", 86 ErrInvalidNumAddrsDerived, 87 len(indices), 88 len(addrs), 89 ) 90 } 91 92 addrsSet := set.Of(addrs...) 93 94 addrToIdx := map[ids.ShortID]uint32{} 95 for i := range indices { 96 addrToIdx[addrs[i]] = indices[i] 97 } 98 99 return &ledgerKeychain{ 100 ledger: l, 101 addrs: addrsSet, 102 addrToIdx: addrToIdx, 103 }, nil 104 } 105 106 func (l *ledgerKeychain) Addresses() set.Set[ids.ShortID] { 107 return l.addrs 108 } 109 110 func (l *ledgerKeychain) Get(addr ids.ShortID) (Signer, bool) { 111 idx, ok := l.addrToIdx[addr] 112 if !ok { 113 return nil, false 114 } 115 116 return &ledgerSigner{ 117 ledger: l.ledger, 118 idx: idx, 119 addr: addr, 120 }, true 121 } 122 123 // expects to receive a hash of the unsigned tx bytes 124 func (l *ledgerSigner) SignHash(b []byte) ([]byte, error) { 125 // Sign using the address with index l.idx on the ledger device. The number 126 // of returned signatures should be the same length as the provided indices. 127 sigs, err := l.ledger.SignHash(b, []uint32{l.idx}) 128 if err != nil { 129 return nil, err 130 } 131 132 if sigsLen := len(sigs); sigsLen != 1 { 133 return nil, fmt.Errorf( 134 "%w. expected 1, got %d", 135 ErrInvalidNumSignatures, 136 sigsLen, 137 ) 138 } 139 140 return sigs[0], err 141 } 142 143 // expects to receive the unsigned tx bytes 144 func (l *ledgerSigner) Sign(b []byte) ([]byte, error) { 145 // Sign using the address with index l.idx on the ledger device. The number 146 // of returned signatures should be the same length as the provided indices. 147 sigs, err := l.ledger.Sign(b, []uint32{l.idx}) 148 if err != nil { 149 return nil, err 150 } 151 152 if sigsLen := len(sigs); sigsLen != 1 { 153 return nil, fmt.Errorf( 154 "%w. expected 1, got %d", 155 ErrInvalidNumSignatures, 156 sigsLen, 157 ) 158 } 159 160 return sigs[0], err 161 } 162 163 func (l *ledgerSigner) Address() ids.ShortID { 164 return l.addr 165 }