-
Notifications
You must be signed in to change notification settings - Fork 2
/
hdpublickey.go
executable file
·121 lines (93 loc) · 2.86 KB
/
hdpublickey.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
package libwallet
import (
"errors"
"fmt"
"strings"
"github.com/muun/libwallet/hdpath"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
)
// HDPublicKey is an HD capable pub key
type HDPublicKey struct {
key hdkeychain.ExtendedKey
Network *Network
Path string
}
// NewHDPublicKeyFromString creates an HD pub key from a base58-encoded string
// If the parsed key is private, it returns an error
func NewHDPublicKeyFromString(str, path string, network *Network) (*HDPublicKey, error) {
key, err := hdkeychain.NewKeyFromString(str)
if err != nil {
return nil, err
}
if key.IsPrivate() {
return nil, errors.New("encoded key was not a public key")
}
return &HDPublicKey{key: *key, Network: network, Path: path}, nil
}
// String return the key base58-encoded
func (p *HDPublicKey) String() string {
return p.key.String()
}
// DerivedAt derives a new child pub key
// index should be uint32 but for java compat we use int64
func (p *HDPublicKey) DerivedAt(index int64) (*HDPublicKey, error) {
if index&hdkeychain.HardenedKeyStart != 0 {
return nil, fmt.Errorf("can't derive a hardened pub key (index %v)", index)
}
child, err := p.key.Child(uint32(index))
if err != nil {
return nil, err
}
parentPath, err := hdpath.Parse(p.Path)
if err != nil {
return nil, err
}
path := parentPath.Child(uint32(index))
return &HDPublicKey{key: *child, Network: p.Network, Path: path.String()}, nil
}
func (p *HDPublicKey) DeriveTo(path string) (*HDPublicKey, error) {
if !strings.HasPrefix(path, p.Path) {
return nil, fmt.Errorf("derivation path %v is not prefix of the keys path %v", path, p.Path)
}
firstPath, err := hdpath.Parse(p.Path)
if err != nil {
return nil, fmt.Errorf("couldn't parse derivation path %v: %w", p.Path, err)
}
secondPath, err := hdpath.Parse(path)
if err != nil {
return nil, fmt.Errorf("couldn't parse derivation path %v: %w", path, err)
}
indexes := secondPath.IndexesFrom(firstPath)
derivedKey := p
for depth, index := range indexes {
if index.Hardened {
return nil, fmt.Errorf("can't derive a hardened pub key (path %v)", path)
}
derivedKey, err = derivedKey.DerivedAt(int64(index.Index))
if err != nil {
return nil, fmt.Errorf("failed to derive key at path %v on depth %v: %w", path, depth, err)
}
}
// The generated path has no names in it, so replace it
derivedKey.Path = path
return derivedKey, nil
}
// Raw returns the backing EC compressed raw key
func (p *HDPublicKey) Raw() []byte {
key, err := p.key.ECPubKey()
if err != nil {
panic("failed to extract pub key")
}
return key.SerializeCompressed()
}
// Fingerprint returns the 4-byte fingerprint for this pubkey
func (p *HDPublicKey) Fingerprint() []byte {
key, err := p.key.ECPubKey()
if err != nil {
panic("failed to extract pub key")
}
bytes := key.SerializeCompressed()
hash := btcutil.Hash160(bytes)
return hash[:4]
}