97 lines
2.3 KiB
Go
97 lines
2.3 KiB
Go
|
package libwallet
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
|
||
|
"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) (*HDPublicKey, error) {
|
||
|
|
||
|
key, network, err := keyFromString(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) {
|
||
|
|
||
|
child, err := p.key.Child(uint32(index))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
path := fmt.Sprintf("%v/%v", p.Path, index)
|
||
|
return &HDPublicKey{key: *child, Network: p.Network, Path: path}, nil
|
||
|
}
|
||
|
|
||
|
func (p *HDPublicKey) DeriveTo(path string) (*HDPublicKey, error) {
|
||
|
|
||
|
if !strings.HasPrefix(path, p.Path) {
|
||
|
return nil, errors.Errorf("derivation path %v is not prefix of the keys path %v", path, p.Path)
|
||
|
}
|
||
|
|
||
|
firstPath, err := parseDerivationPath(p.Path)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrapf(err, "couldn't parse derivation path %v", p.Path)
|
||
|
}
|
||
|
|
||
|
secondPath, err := parseDerivationPath(path)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrapf(err, "couldn't parse derivation path %v", path)
|
||
|
}
|
||
|
|
||
|
indexes := secondPath.indexes[len(firstPath.indexes):]
|
||
|
derivedKey := p
|
||
|
for depth, index := range indexes {
|
||
|
if index.hardened {
|
||
|
return nil, errors.Errorf("can't derive a hardened pub key (path %v)", path)
|
||
|
}
|
||
|
|
||
|
derivedKey, err = derivedKey.DerivedAt(int64(index.i))
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrapf(err, "failed to derive key at path %v on depth %v", path, depth)
|
||
|
}
|
||
|
}
|
||
|
// 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()
|
||
|
}
|