Skip to content

Commit

Permalink
Merge pull request #266 from binance-chain/develop
Browse files Browse the repository at this point in the history
[R4R] prepare for release v0.25.0 binance.25
  • Loading branch information
forcodedancing authored Jan 4, 2022
2 parents 743bbf8 + 6bd393c commit c51134d
Show file tree
Hide file tree
Showing 15 changed files with 262 additions and 125 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

##v0.25.0-binance.25
* [sdk] [\#260](https://github.com/binance-chain/bnc-cosmos-sdk/pull/260) Bug fixes (e.g., unclosed iterator)
* [stake] [\#261](https://github.com/binance-chain/bnc-cosmos-sdk/pull/261) Remove GetValidator caching to fix concurrency error
* [keys] [\#263](https://github.com/binance-chain/bnc-cosmos-sdk/pull/263) Add 'm/' prefix to hd path
* [core] [\#264](https://github.com/binance-chain/bnc-cosmos-sdk/pull/264) Upgrade Tendermint to v0.32.3-binance.6
* Update go version to 1.16
* Fix infinite recursion in Secp256k1 String() method

##v0.25.0-binance.24
* [sdk] [\#248](https://github.com/binance-chain/bnc-cosmos-sdk/pull/248) owner transfer
* [sdk] Allow tx send (generate-only) to actually work offline #4234
Expand Down
2 changes: 1 addition & 1 deletion client/context/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (ctx CLIContext) GetAccount(address []byte) (sdk.Account, error) {
if err != nil {
return nil, err
} else if len(res) == 0 {
return nil, err
return nil, ErrInvalidAccount(address)
}

account, err := ctx.AccDecoder(res)
Expand Down
69 changes: 51 additions & 18 deletions crypto/keys/hd/hdpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"errors"
"fmt"
"math/big"
"path/filepath"
"strconv"
"strings"

Expand All @@ -26,7 +27,7 @@ import (

// BIP44Prefix is the parts of the BIP32 HD path that are fixed by what we used during the fundraiser.
const (
BIP44Prefix = "44'/714'/"
BIP44Prefix = "m/44'/714'/"
FullFundraiserPath = BIP44Prefix + "0'/0/0"
)

Expand Down Expand Up @@ -54,50 +55,63 @@ func NewParams(purpose, coinType, account uint32, change bool, addressIdx uint32
}
}

// Parse the BIP44 path and unmarshal into the struct.
// NewParamsFromPath parses the BIP44 path and unmarshals it into a Bip44Params. It supports both absolute and relative paths.
// nolint: gocyclo
func NewParamsFromPath(path string) (*BIP44Params, error) {
spl := strings.Split(path, "/")

// Handle absolute or relative paths
switch {
case spl[0] == path:
return nil, fmt.Errorf("path %s doesn't contain '/' separators", path)

case strings.TrimSpace(spl[0]) == "":
return nil, fmt.Errorf("ambiguous path %s: use 'm/' prefix for absolute paths, or no leading '/' for relative ones", path)

case strings.TrimSpace(spl[0]) == "m":
spl = spl[1:]
}

if len(spl) != 5 {
return nil, fmt.Errorf("path length is wrong. Expected 5, got %d", len(spl))
return nil, fmt.Errorf("invalid path length %s", path)
}

if spl[0] != "44'" {
return nil, fmt.Errorf("first field in path must be 44', got %v", spl[0])
return nil, fmt.Errorf("first field in path must be 44', got %s", spl[0])
}

if !isHardened(spl[1]) || !isHardened(spl[2]) {
return nil,
fmt.Errorf("second and third field in path must be hardened (ie. contain the suffix ', got %v and %v", spl[1], spl[2])
fmt.Errorf("second and third field in path must be hardened (ie. contain the suffix ', got %s and %s", spl[1], spl[2])
}
if isHardened(spl[3]) || isHardened(spl[4]) {
return nil,
fmt.Errorf("fourth and fifth field in path must not be hardened (ie. not contain the suffix ', got %v and %v", spl[3], spl[4])
fmt.Errorf("fourth and fifth field in path must not be hardened (ie. not contain the suffix ', got %s and %s", spl[3], spl[4])
}

purpose, err := hardenedInt(spl[0])
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid HD path purpose %s: %w", spl[0], err)
}
coinType, err := hardenedInt(spl[1])
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid HD path coin type %s: %w", spl[1], err)
}
account, err := hardenedInt(spl[2])
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid HD path account %s: %w", spl[2], err)
}
change, err := hardenedInt(spl[3])
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid HD path change %s: %w", spl[3], err)
}
if !(change == 0 || change == 1) {
return nil, fmt.Errorf("change field can only be 0 or 1")
}

addressIdx, err := hardenedInt(spl[4])
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid HD path address index %s: %w", spl[4], err)
}

return &BIP44Params{
Expand Down Expand Up @@ -147,15 +161,16 @@ func (p BIP44Params) DerivationPath() []uint32 {
}
}

// String returns the full absolute HD path of the BIP44 (https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) params:
// m / purpose' / coin_type' / account' / change / address_index
func (p BIP44Params) String() string {
var changeStr string
if p.change {
changeStr = "1"
} else {
changeStr = "0"
}
// m / purpose' / coin_type' / account' / change / address_index
return fmt.Sprintf("%d'/%d'/%d'/%s/%d",
return fmt.Sprintf("m/%d'/%d'/%d'/%s/%d",
p.purpose,
p.coinType,
p.account,
Expand All @@ -174,28 +189,46 @@ func ComputeMastersFromSeed(seed []byte) (secret [32]byte, chainCode [32]byte) {
// DerivePrivateKeyForPath derives the private key by following the BIP 32/44 path from privKeyBytes,
// using the given chainCode.
func DerivePrivateKeyForPath(privKeyBytes [32]byte, chainCode [32]byte, path string) ([32]byte, error) {
path = strings.TrimRightFunc(path, func(r rune) bool { return r == filepath.Separator })

data := privKeyBytes
parts := strings.Split(path, "/")
for _, part := range parts {

switch {
case parts[0] == path:
return [32]byte{}, fmt.Errorf("path '%s' doesn't contain '/' separators", path)
case strings.TrimSpace(parts[0]) == "m":
parts = parts[1:]
}

for i, part := range parts {
if part == "" {
return [32]byte{}, fmt.Errorf("path %q with split element #%d is an empty string", part, i)
}

// do we have an apostrophe?
harden := part[len(part)-1:] == "'"
// harden == private derivation, else public derivation:
if harden {
part = part[:len(part)-1]
}
idx, err := strconv.Atoi(part)

// As per the extended keys specification in
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#extended-keys
// index values are in the range [0, 1<<31-1] aka [0, max(int32)]
idx, err := strconv.ParseUint(part, 10, 31)
if err != nil {
return [32]byte{}, fmt.Errorf("invalid BIP 32 path: %s", err)
return [32]byte{}, fmt.Errorf("invalid BIP 32 path %s: %w", path, err)
}
if idx < 0 {
return [32]byte{}, errors.New("invalid BIP 32 path: index negative ot too large")
return [32]byte{}, errors.New("invalid BIP 32 path: index negative or too large")
}
data, chainCode = derivePrivateKey(data, chainCode, uint32(idx), harden)
}
var derivedKey [32]byte
n := copy(derivedKey[:], data[:])
if n != 32 || len(data) != 32 {
return [32]byte{}, fmt.Errorf("expected a (secp256k1) key of length 32, got length: %v", len(data))
return [32]byte{}, fmt.Errorf("expected a key of length 32, got length: %d", len(data))
}

return derivedKey, nil
Expand Down
Loading

0 comments on commit c51134d

Please sign in to comment.