Skip to content

Commit

Permalink
add badges
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelmota committed Jul 8, 2018
1 parent 353f4d0 commit 1a42950
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 42 deletions.
9 changes: 9 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
language: go

go:
- "1.9.x"
- "1.10.x"
- "master"

script:
- make test
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

> Ethereum HD Wallet derivations from mnemonic in Go (golang). Implements the [go-ethereum](https://github.com/ethereum/go-ethereum) [`accounts.Wallet`](https://github.com/ethereum/go-ethereum/blob/master/accounts/accounts.go) interface.
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/miguelmota/go-ethereum-hdwallet/master/LICENSE.md) [![Build Status](https://travis-ci.org/miguelmota/go-ethereum-hdwallet.svg?branch=master)](https://travis-ci.org/miguelmota/go-ethereum-hdwallet) [![Go Report Card](https://goreportcard.com/badge/github.com/miguelmota/go-ethereum-hdwallet?)](https://goreportcard.com/report/github.com/miguelmota/go-ethereum-hdwallet) [![GoDoc](https://godoc.org/github.com/miguelmota/go-ethereum-hdwallet?status.svg)](https://godoc.org/github.com/miguelmota/go-ethereum-hdwallet)

## Install

```bash
Expand Down
109 changes: 67 additions & 42 deletions hdwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/tyler-smith/go-bip39"
)

// Wallet ...
// Wallet is the underlying wallet struct.
type Wallet struct {
mnemonic string
masterKey *hdkeychain.ExtendedKey
Expand All @@ -29,47 +29,65 @@ type Wallet struct {
stateLock sync.RWMutex
}

// NewFromMnemonic ...
func NewFromMnemonic(mnemonic string) (*Wallet, error) {
if mnemonic == "" {
return nil, errors.New("mnemonic is required")
}

seed := bip39.NewSeed(mnemonic, "")

func newWallet(seed []byte) (*Wallet, error) {
masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
if err != nil {
return nil, err
}

wallet := &Wallet{
mnemonic: mnemonic,
seed: seed,
return &Wallet{
masterKey: masterKey,
seed: seed,
accounts: []accounts.Account{},
paths: map[common.Address]accounts.DerivationPath{},
}, nil
}

// NewFromMnemonic returns a new wallet from a BIP-39 mnemonic.
func NewFromMnemonic(mnemonic string) (*Wallet, error) {
if mnemonic == "" {
return nil, errors.New("mnemonic is required")
}

seed, err := NewSeedFromMnemonic(mnemonic)
if err != nil {
return nil, err
}
wallet, err := newWallet(seed)
if err != nil {
return nil, err
}
wallet.mnemonic = mnemonic

return wallet, nil
}

// URL implements accounts.Wallet, returning the URL of the USB hardware device, however this does nothing since this is not a USB device.
// NewFromSeed returns a new wallet from a BIP-39 seed.
func NewFromSeed(seed []byte) (*Wallet, error) {
if len(seed) == 0 {
return nil, errors.New("seed is required")
}

return newWallet(seed)
}

// URL implements accounts.Wallet, returning the URL of the device that the wallet is on, however this does nothing since this is not a hardware device.
func (w *Wallet) URL() accounts.URL {
return w.url
}

// Status implements accounts.Wallet, returning a custom status message from the
// underlying vendor-specific hardware wallet implementation.
// underlying vendor-specific hardware wallet implementation, however this does nothing since this is not a hardware device.
func (w *Wallet) Status() (string, error) {
return "ok", nil
}

// Open implements the accounts wallet Close function. Since this is not a USB device, this methods does nothing.
// Open implements accounts.Wallet, however this does nothing since this is not a hardware device.
func (w *Wallet) Open(passphrase string) error {
return nil
}

// Close implements the accounts wallet Close function. Since this is not a USB device, this methods does nothing.
// Close implements accounts.Wallet, however this does nothing since this is not a hardware device.
func (w *Wallet) Close() error {
return nil
}
Expand Down Expand Up @@ -155,19 +173,13 @@ func (w *Wallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainSt
*/
}

// SignHash implements accounts.Wallet which allows signing of arbitrary data
// SignHash implements accounts.Wallet, which allows signing arbitrary data.
func (w *Wallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) {
return nil, nil

}

// SignTx implements accounts.Wallet. It sends the transaction over to the Ledger
// wallet to request a confirmation from the user. It returns either the signed
// transaction or a failure if the user denied the transaction.
//
// Note, if the version of the Ethereum application running on the Ledger wallet is
// too old to sign EIP-155 transactions, but such is requested nonetheless, an error
// will be returned opposed to silently signing in Homestead mode.
// SignTx implements accounts.Wallet, which allows the account to sign an Ethereum transaction.
func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
w.stateLock.RLock() // Comms have own mutex, this is for the state fields
defer w.stateLock.RUnlock()
Expand All @@ -193,27 +205,26 @@ func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
return nil, nil
}

// SignHashWithPassphrase implements accounts.Wallet
// SignHashWithPassphrase implements accounts.Wallet, with signs a hash.
func (w *Wallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
return nil, nil
}

// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
// transaction with the given account using passphrase as extra authentication.
// Since USB wallets don't rely on passphrases, these are silently ignored.
func (w *Wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
return w.SignTx(account, tx, chainID)
}

// Mnemonic ...
// Mnemonic returns the wallet's mnemonic, if using a mnemonic as a seed.
func (w *Wallet) Mnemonic() (string, error) {
if w.mnemonic == "" {
return "", errors.New("mnemonic not found")
return "", errors.New("this wallet is not using a mnemonic")
}
return w.mnemonic, nil
}

// PrivateKey ...
// PrivateKey returns the ECDSA private key of the account.
func (w *Wallet) PrivateKey(account accounts.Account) (*ecdsa.PrivateKey, error) {
path, err := ParseDerivationPath(account.URL.Path)
if err != nil {
Expand All @@ -223,7 +234,7 @@ func (w *Wallet) PrivateKey(account accounts.Account) (*ecdsa.PrivateKey, error)
return w.derivePrivateKey(path)
}

// PrivateKeyBytes ...
// PrivateKeyBytes returns the ECDSA private key in bytes format of the account.
func (w *Wallet) PrivateKeyBytes(account accounts.Account) ([]byte, error) {
privateKey, err := w.PrivateKey(account)
if err != nil {
Expand All @@ -233,7 +244,7 @@ func (w *Wallet) PrivateKeyBytes(account accounts.Account) ([]byte, error) {
return crypto.FromECDSA(privateKey), nil
}

// PrivateKeyHex ...
// PrivateKeyHex return the ECDSA private key in hex string format of the account.
func (w *Wallet) PrivateKeyHex(account accounts.Account) (string, error) {
privateKeyBytes, err := w.PrivateKeyBytes(account)
if err != nil {
Expand All @@ -243,7 +254,7 @@ func (w *Wallet) PrivateKeyHex(account accounts.Account) (string, error) {
return hexutil.Encode(privateKeyBytes)[2:], nil
}

// PublicKey ...
// PublicKey returns the ECDSA public key of the account.
func (w *Wallet) PublicKey(account accounts.Account) (*ecdsa.PublicKey, error) {
path, err := ParseDerivationPath(account.URL.Path)
if err != nil {
Expand All @@ -253,7 +264,7 @@ func (w *Wallet) PublicKey(account accounts.Account) (*ecdsa.PublicKey, error) {
return w.derivePublicKey(path)
}

// PublicKeyBytes ...
// PublicKeyBytes returns the ECDSA public key in bytes format of the account.
func (w *Wallet) PublicKeyBytes(account accounts.Account) ([]byte, error) {
publicKey, err := w.PublicKey(account)
if err != nil {
Expand All @@ -263,7 +274,7 @@ func (w *Wallet) PublicKeyBytes(account accounts.Account) ([]byte, error) {
return crypto.FromECDSAPub(publicKey), nil
}

// PublicKeyHex ...
// PublicKeyHex return the ECDSA public key in hex string format of the account.
func (w *Wallet) PublicKeyHex(account accounts.Account) (string, error) {
publicKeyBytes, err := w.PublicKeyBytes(account)
if err != nil {
Expand All @@ -273,7 +284,7 @@ func (w *Wallet) PublicKeyHex(account accounts.Account) (string, error) {
return hexutil.Encode(publicKeyBytes)[4:], nil
}

// Address ...
// Address returns the address of the account.
func (w *Wallet) Address(account accounts.Account) (common.Address, error) {
publicKey, err := w.PublicKey(account)
if err != nil {
Expand All @@ -283,7 +294,7 @@ func (w *Wallet) Address(account accounts.Account) (common.Address, error) {
return crypto.PubkeyToAddress(*publicKey), nil
}

// AddressBytes ...
// AddressBytes returns the address in bytes format of the account.
func (w *Wallet) AddressBytes(account accounts.Account) ([]byte, error) {
address, err := w.Address(account)
if err != nil {
Expand All @@ -292,7 +303,7 @@ func (w *Wallet) AddressBytes(account accounts.Account) ([]byte, error) {
return address.Bytes(), nil
}

// AddressHex ...
// AddressHex returns the address in hex string format of the account.
func (w *Wallet) AddressHex(account accounts.Account) (string, error) {
address, err := w.Address(account)
if err != nil {
Expand All @@ -301,17 +312,17 @@ func (w *Wallet) AddressHex(account accounts.Account) (string, error) {
return address.Hex(), nil
}

// Path ...
// Path return the derivation path of the account.
func (w *Wallet) Path(account accounts.Account) (string, error) {
return account.URL.Path, nil
}

// ParseDerivationPath ...
// ParseDerivationPath parses the derivation path in string format into []uint32
func ParseDerivationPath(path string) (accounts.DerivationPath, error) {
return accounts.ParseDerivationPath(path)
}

// MustParseDerivationPath ...
// MustParseDerivationPath parses the derivation path in string format into []uint32 but will panic if it can't parse it.
func MustParseDerivationPath(path string) accounts.DerivationPath {
parsed, err := accounts.ParseDerivationPath(path)
if err != nil {
Expand All @@ -321,7 +332,7 @@ func MustParseDerivationPath(path string) accounts.DerivationPath {
return parsed
}

// NewMnemonic ...
// NewMnemonic returns a randomly generated BIP-39 mnemonic.
func NewMnemonic() (string, error) {
entropy, err := bip39.NewEntropy(128)
if err != nil {
Expand All @@ -330,11 +341,23 @@ func NewMnemonic() (string, error) {
return bip39.NewMnemonic(entropy)
}

// NewSeed ...
// NewSeed returns a randomly generated BIP-39 seed.
func NewSeed() ([]byte, error) {
return bip32.NewSeed()
}

// NewSeedFromMnemonic ...
func NewSeedFromMnemonic(mnemonic string) ([]byte, error) {
if mnemonic == "" {
return nil, errors.New("mnemonic is required")
}

seed := bip39.NewSeed(mnemonic, "")

return seed, nil
}

// DerivePrivateKey derives the private key of the derivation path
func (w *Wallet) derivePrivateKey(path accounts.DerivationPath) (*ecdsa.PrivateKey, error) {
var err error
key := w.masterKey
Expand All @@ -354,6 +377,7 @@ func (w *Wallet) derivePrivateKey(path accounts.DerivationPath) (*ecdsa.PrivateK
return privateKeyECDSA, nil
}

// DerivePublicKey derives the public key of the derivation path
func (w *Wallet) derivePublicKey(path accounts.DerivationPath) (*ecdsa.PublicKey, error) {
privateKeyECDSA, err := w.derivePrivateKey(path)
if err != nil {
Expand All @@ -369,6 +393,7 @@ func (w *Wallet) derivePublicKey(path accounts.DerivationPath) (*ecdsa.PublicKey
return publicKeyECDSA, nil
}

// DeriveAddress derives the account address of the derivation path
func (w *Wallet) deriveAddress(path accounts.DerivationPath) (common.Address, error) {
publicKeyECDSA, err := w.derivePublicKey(path)
if err != nil {
Expand Down
22 changes: 22 additions & 0 deletions hdwallet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,26 @@ func TestWallet(t *testing.T) {
if addressHex != "0xC49926C4124cEe1cbA0Ea94Ea31a6c12318df947" {
t.Error("wrong address")
}

// seed test

seed, err := NewSeedFromMnemonic(mnemonic)
if err != nil {
t.Error(err)
}

wallet, err = NewFromSeed(seed)
if err != nil {
t.Error(err)
}

path = MustParseDerivationPath("m/44'/60'/0'/0/0")
account, err = wallet.Derive(path, false)
if err != nil {
t.Error(err)
}

if account.Address.Hex() != "0xC49926C4124cEe1cbA0Ea94Ea31a6c12318df947" {
t.Error("wrong address")
}
}

0 comments on commit 1a42950

Please sign in to comment.