-
Notifications
You must be signed in to change notification settings - Fork 32
keys package: fundraiser compatibility and HD keys (BIP 39 & BIP 32 / BIP 44) #118
Conversation
0802437
to
ca85172
Compare
…approach # Conflicts: # signature_test.go
…approach # Conflicts: # signature_test.go
adbcf91
to
8ef5d95
Compare
8ef5d95
to
54b9bd5
Compare
0e710fb
to
e9506fc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 🍡
Some minor nitpicks. Given my limited understanding from the design doc this looks good to me.
armor.go
Outdated
"golang.org/x/crypto/openpgp/armor" | ||
) | ||
|
||
func EncodeArmor(blockType string, headers map[string]string, data []byte) string { | ||
buf := new(bytes.Buffer) | ||
w, err := armor.Encode(buf, blockType, headers) | ||
if err != nil { | ||
PanicSanity("Error encoding ascii armor: " + err.Error()) | ||
cmn.PanicSanity("Error encoding ascii armor: " + err.Error()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK we want to transition away from panic methods from common
and use naked panic statements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx! done
keys/hd/fundraiser_test.go
Outdated
seed := bip39.NewSeed(d.Mnemonic, "") | ||
|
||
//fmt.Println("================================") | ||
//fmt.Println("ROUND:", i, "MNEMONIC:", d.Mnemonic) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not always print those and use t.Log
to have it only presented on verbosre test runs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, why not 👍done
keys/hd/hdpath.go
Outdated
} | ||
// m / purpose' / coin_type' / account' / change / address_index | ||
return fmt.Sprintf("%d'/%d'/%d'/%s/%d", | ||
p.purpose, p.coinType, p.account, changeStr, p.addressIdx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we break method calls into multiple lines, can we consistently have the following format:
namespace.Method(
arg1,
arg2,
arg3,
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
// ComputeMastersFromSeed returns the master public key, master secret, and chain code in hex. | ||
func ComputeMastersFromSeed(seed []byte) (secret [32]byte, chainCode [32]byte) { | ||
masterSecret := []byte("Bitcoin seed") | ||
secret, chainCode = i64(masterSecret, seed) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there significance to the named returns, or can we just return the result from i64
directly?
keys/hd/hdpath.go
Outdated
|
||
// 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) (derivedKey [32]byte, err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Named returns for function bodies of this complexity seem hard to decypher what the state at what point is. It could be clearer with early and explicit returns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
fmt.Println() | ||
|
||
seed = bip39.MnemonicToSeed( | ||
"advice process birth april short trust crater change bacon monkey medal garment " + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use an unescaped raw string?
keys/keybase.go
Outdated
const ( | ||
// English is the default language to create a mnemonic. | ||
// It is the only supported language by this package. | ||
English Language = iota |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to have the first value at iota + 1
to prevent accidental zero value passing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we consider prefixing with Language
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iota+1 it is 👍
Should we consider prefixing with Language.
You mean s/English/LanguageEnglish/ ? Why? Isn't that a bit redundant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a case to make for either on the caller side it's a bit more clear when reading, yet the type system should inform that already. Could be either way, in general it's good to prefix if the namespace of the package gets a bit more crowded.
keys/keybase.go
Outdated
priv, err := generate(algo, secret) | ||
func (kb dbKeybase) CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, mnemonic string, err error) { | ||
if language != English { | ||
return nil, "", fmt.Errorf("unsupported language: currently only english is supported") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to have solid error handling, either with sentinal values or types, so that callers get control flow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is not much the callers can control here: In these cases (currently) only one possible language is supported (all other will err). This means, if the caller calls this with a different language then engl. he (currently) isn't using the lib correctly (and he can't handle this error case differently then by picking a supported language). Maybe, the sentinel types are still useful as they additionally document the behaviour (additionally to the documentation).
keys/keybase.go
Outdated
} | ||
// TODO(ismail): we have to be careful with the separator in non-ltr languages. Ideally, our package should provide | ||
// a helper function for that | ||
mnemonic = strings.Join(mnemonicS, " ") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The least we can do with the separators is to have them in constants, and ideally later configurable. This is the second occurrence of the literal string and a TODO, looks like a good candidate to fix before merge.
} | ||
} | ||
// Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters. | ||
Secp256k1 = SigningAlgo("secp256k1") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again would prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
… for word list and supported curve/params for signing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor nits, needs a rebase on develop for the new PubKey()
API
keys/bip39/wordcodec.go
Outdated
return | ||
} | ||
|
||
// MnemonicToSeedWithErrChecking is completely equivalent to MnemonicToSeed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment and the one two lines below seem to contradict each other - by "equivalent" do you mean that it returns the same seed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
keys/hd/hdpath.go
Outdated
// m / 44' / 118' / account' / 0 / address_index | ||
// The fixed parameters (purpose', coin_type', and change) are determined by what was used in the fundraiser. | ||
func NewFundraiserParams(account uint32, addressIdx uint32) *BIP44Params { | ||
return &BIP44Params{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be a bit more idiomatic to call our own NewParams
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
keys/hd/hdpath.go
Outdated
// using the given chainCode. | ||
func DerivePrivateKeyForPath(privKeyBytes [32]byte, chainCode [32]byte, path string) (derivedKey [32]byte, err error) { | ||
data := privKeyBytes | ||
// TODO(ismail): refactor this out and provide a constructor for BIP44 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we turn this into an issue if it will not be included in this PR?
keys/hd/hdpath.go
Outdated
data = append([]byte{byte(0)}, privKeyBytes[:]...) | ||
} else { | ||
// this can't return an error: | ||
pubkey, err := crypto.PrivKeySecp256k1(privKeyBytes).PubKey() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be rebased on develop
, which no longer errors on .PubKey()
keys/hd/hdpath.go
Outdated
} | ||
|
||
// derivePrivateKey derives the private key with index and chainCode. | ||
// If harden is true, the derivation is 'hardened'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purposing of "hardening" - can we link to the spec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added link to the spec which contains further info.
|
||
func ExampleStringifyPathParams() { | ||
path := NewParams(44, 0, 0, false, 0) | ||
fmt.Println(path.String()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this print instead of checking the result?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Output: 44'/0'/0'/0/0 | ||
} | ||
|
||
func ExampleSomeBIP32TestVecs() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do these print instead of checking whether or not the outputs are correct in the testcase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This prints do exactly that: https://blog.golang.org/examples
keys/keybase.go
Outdated
if passwd != "" { | ||
info = kb.writeLocalKey(crypto.PrivKeySecp256k1(derivedPriv), name, passwd) | ||
} else { | ||
pubk, err := crypto.PrivKeySecp256k1(derivedPriv).PubKey() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error check can be removed with a rebase on develop
random.go
Outdated
@@ -82,15 +82,15 @@ func (ri *randInfo) MixEntropy(seedBytes []byte) { | |||
hashBytes32 := [32]byte{} | |||
copy(hashBytes32[:], hashBytes) | |||
ri.seedBytes = xorBytes32(ri.seedBytes, hashBytes32) | |||
// Create new cipher.Block | |||
// CreateMnemonic new cipher.Block |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accidental find replace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
- fix comments - call NewParams - remove accidental change
- remove accidental changes
…ip39-2nd-approach
resolves #90
resolves #89
resolves #57
partly resolves #124
closes cosmos/cosmos-sdk#872