Skip to content

Commit

Permalink
Merge pull request #21 from Planxnx/feature/only-privatekey-mode
Browse files Browse the repository at this point in the history
Only private key mode
  • Loading branch information
Planxnx authored Apr 8, 2023
2 parents 7292d39 + 795f016 commit 83e0584
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 71 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ or
$ docker pull planxthanee/ethereum-wallet-generator:latest
```

## Modes

- **[1] Normal Mode** - Generate wallets with mnemonic phrase. (default)
- **[2] Fast Mode** - Generate wallets with a mnemonic phrase, **but less secure**. Use cumulative entropy instead of generating new random entropy(changing only the first or second words of mnemonic phrases). It will generate new random entropy every 2048 wallets.
- **[3] Only Private Key Mode** - Generate wallets with private key only. **This mode is the fastest, but you will not get a mnemonic phrase.**

## Usage

```console
Expand All @@ -77,6 +83,7 @@ Usage of ethereum-wallet-generator:
-db string set sqlite output file name eg. wallets.db (db file will create in `/db` folder)
-c int set concurrency value (default 1)
-bit int set number of entropy bits [128 for 12 words, 256 for 24 words] (default 128)
-mode int set mode of wallet generator [1: normal mode, 2: fast mode, 3: only private key mode]
-strict bool strict contains mode, resolve only the addresses that contain all the given letters (required contains to use)
-contains string show only result that contained with the given letters (support for multiple characters)
-prefix string show only result that prefix was matched with the given letters (support for single character)
Expand All @@ -88,11 +95,11 @@ Usage of ethereum-wallet-generator:

## Benchmark

We've dryrun the generator with 8 concurrents for 60,000 wallets on MacBook Air M1 2020 Memory 16 GB <br/>
We've dryrun the generator on normal mode with 8 concurrents for 60,000 wallets on MacBook Air M1 2020 Memory 16 GB <br/>
and got speed up to 6,468.58 wallet/sec.

```console
ethereum-wallet-generator -n 60000 -dryrun -c 8
ethereum-wallet-generator -n 60000 -dryrun -c 8 -mode 1
===============ETH Wallet Generator===============

60000 / 60000 | [██████████████████████████████████████████████████████] | 100.00% | 6469 p/s | resolved: 60000
Expand Down
8 changes: 8 additions & 0 deletions bip39/bip39.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ package bip39
import (
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"strings"

"github.com/holiman/uint256"
"github.com/pkg/errors"
"golang.org/x/crypto/pbkdf2"
)

var (
Expand Down Expand Up @@ -88,6 +90,12 @@ func NewMnemonic(entropy []byte) (string, error) {
return w.String(), nil
}

// NewSeed creates a hashed seed output given a provided string and password.
// No checking is performed to validate that the string provided is a valid mnemonic.
func NewSeed(mnemonic, password string) []byte {
return pbkdf2.Key([]byte(mnemonic), []byte("mnemonic"+password), 2048, 64, sha512.New)
}

// Appends to data the first (len(data) / 32)bits of the result of sha256(data)
// abd returns the result as a uint256.Int.
//
Expand Down
22 changes: 14 additions & 8 deletions internal/generators/generators.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
)

type Config struct {
BitSize int
AddresValidator func(address string) bool
ProgressBar progressbar.ProgressBar
DryRun bool
Expand All @@ -24,16 +23,18 @@ type Config struct {
}

type Generator struct {
repo repository.Repository
config Config
walletGen wallets.Generator
repo repository.Repository
config Config

isShutdown atomic.Bool
shutdownSignal chan struct{}
shutDownWg sync.WaitGroup
}

func New(repo repository.Repository, cfg Config) *Generator {
func New(walletGen wallets.Generator, repo repository.Repository, cfg Config) *Generator {
return &Generator{
walletGen: walletGen,
repo: repo,
config: cfg,
shutdownSignal: make(chan struct{}),
Expand All @@ -59,14 +60,19 @@ func (g *Generator) Start() (err error) {
}

if w := g.repo.Result(); len(w) > 0 && !g.config.DryRun {
col2Name := "Seed"
var result strings.Builder
for _, wallet := range w {
if _, err := fmt.Fprintf(&result, "%-42s %s\n", wallet.Address, wallet.Mnemonic); err != nil {
col2 := wallet.Mnemonic
if wallet.Mnemonic == "" {
col2 = wallet.PrivateKey
col2Name = "Private Key"
}
if _, err := fmt.Fprintf(&result, "%-42s %s\n", wallet.Address, col2); err != nil {
continue
}
}

fmt.Printf("\n%-42s %s\n", "Address", "Seed")
fmt.Printf("\n%-42s %s\n", "Address", col2Name)
fmt.Printf("%-42s %s\n", strings.Repeat("-", 42), strings.Repeat("-", 90))
fmt.Println(result.String())
}
Expand All @@ -90,7 +96,7 @@ func (g *Generator) Start() (err error) {
return
}

wallet, err := wallets.NewWallet(g.config.BitSize)
wallet, err := g.walletGen()
if err != nil {
// Ignore error
log.Printf("[ERROR] failed to generate wallet: %+v\n", err)
Expand Down
22 changes: 20 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ func main() {
fmt.Println("===============ETH Wallet Generator===============")
fmt.Println(" ")

// Parse flags
number := flag.Int("n", 10, "set number of generate times (not number of result wallets) (set number to 0 for Infinite loop ∞)")
limit := flag.Int("limit", 0, "set limit number of result wallets. stop generate when result of vanity wallets reach the limit (set number to 0 for no limit)")
dbPath := flag.String("db", "", "set sqlite output name eg. wallets.db (db file will create in /db)")
concurrency := flag.Int("c", 1, "set concurrency value (default 1)")
concurrency := flag.Int("c", 1, "set concurrency value")
bits := flag.Int("bit", 128, "set number of entropy bits [128, 256]")
strict := flag.Bool("strict", false, "strict contains mode (required contains to use)")
contain := flag.String("contains", "", "show only result that contained with the given letters (support for multiple characters)")
Expand All @@ -55,8 +56,10 @@ func main() {
regEx := flag.String("regex", "", "show only result that was matched with given regex (eg. ^0x99 or ^0x00)")
isDryrun := flag.Bool("dryrun", false, "generate wallet without a result (used for benchmark speed)")
isCompatible := flag.Bool("compatible", false, "logging compatible mode (turn this on to fix logging glitch)")
mode := flag.Int("mode", 1, "wallet generate mode [1: normal mode, 2: fast mode(reduce entropy random times, but less secure), 3: only private key mode(generate only privatekey, this fastest mode)]")
flag.Parse()

// Wallet Address Validator
r, err := regexp.Compile(*regEx)
if err != nil {
panic(err)
Expand Down Expand Up @@ -105,13 +108,15 @@ func main() {
*limit = *number
}

// Progress bar
var bar progressbar.ProgressBar
if *isCompatible {
bar = progressbar.NewCompatibleProgressBar(*number)
} else {
bar = progressbar.NewStandardProgressBar(*number)
}

// Repository
var repo repository.Repository
switch {
case *dbPath != "":
Expand Down Expand Up @@ -140,10 +145,23 @@ func main() {
repo = repository.NewInMemoryRepository()
}

// Wallet generator
var walletGenerator wallets.Generator
switch *mode {
case 1:
walletGenerator = wallets.NewGeneratorMnemonic(*bits)
case 2:
panic("Fast mode is not supported yet")
case 3:
walletGenerator = wallets.NewGeneratorPrivatekey()
default:
panic("Invalid mode. See: https://github.com/Planxnx/ethereum-wallet-generator#Modes")
}

generator := generators.New(
walletGenerator,
repo,
generators.Config{
BitSize: *bits,
AddresValidator: validateAddress,
ProgressBar: bar,
DryRun: *isDryrun,
Expand Down
62 changes: 62 additions & 0 deletions wallets/mnemonic.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,50 @@
package wallets

import (
"crypto/ecdsa"

"github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/ethereum/go-ethereum/accounts"
"github.com/pkg/errors"
"github.com/planxnx/ethereum-wallet-generator/bip39"
)

var (
// DefaultBaseDerivationPath is the base path from which custom derivation endpoints
// are incremented. As such, the first account will be at m/44'/60'/0'/0, the second
// at m/44'/60'/0'/1, etc
DefaultBaseDerivationPath = accounts.DefaultBaseDerivationPath
DefaultBaseDerivationPathString = DefaultBaseDerivationPath.String()
)

// NewGeneratorMnemonic returns a new wallet generator that uses a mnemonic(BIP39) and a derivation path(BIP44) to generate a wallet.
func NewGeneratorMnemonic(bitSize int) Generator {
return func() (*Wallet, error) {
mnemonic, err := NewMnemonic(bitSize)
if err != nil {
return nil, err
}

privateKey, err := deriveWallet(bip39.NewSeed(mnemonic, ""), DefaultBaseDerivationPath)
if err != nil {
return nil, err
}

wallet, err := NewFromPrivatekey(privateKey)
if err != nil {
return nil, errors.WithStack(err)
}

wallet.Bits = bitSize
wallet.Mnemonic = mnemonic
wallet.HDPath = DefaultBaseDerivationPathString

return wallet, nil
}
}

// NewMnemonic returns a new mnemonic(BIP39) with the given bit size.
func NewMnemonic(bitSize int) (string, error) {
entropy, err := bip39.NewEntropy(bitSize)
if err != nil {
Expand All @@ -18,3 +58,25 @@ func NewMnemonic(bitSize int) (string, error) {

return mnemonic, nil
}

func deriveWallet(seed []byte, path accounts.DerivationPath) (*ecdsa.PrivateKey, error) {
key, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
if err != nil {
return nil, errors.WithStack(err)
}

for _, n := range path {
key, err = key.Derive(n)
if err != nil {
return nil, errors.WithStack(err)
}
}

privateKey, err := key.ECPrivKey()
privateKeyECDSA := privateKey.ToECDSA()
if err != nil {
return nil, errors.WithStack(err)
}

return privateKeyECDSA, nil
}
22 changes: 22 additions & 0 deletions wallets/privatekey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package wallets

import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/pkg/errors"
)

func NewGeneratorPrivatekey() Generator {
return func() (*Wallet, error) {
privateKey, err := crypto.GenerateKey()
if err != nil {
return nil, errors.WithStack(err)
}

wallet, err := NewFromPrivatekey(privateKey)
if err != nil {
return nil, errors.WithStack(err)
}

return wallet, nil
}
}
8 changes: 0 additions & 8 deletions wallets/utils.go

This file was deleted.

Loading

0 comments on commit 83e0584

Please sign in to comment.