├── verify.bat ├── .gitignore ├── verify.command ├── go.mod ├── LICENSE ├── .github └── workflows │ └── go.yml ├── goclean.sh ├── README.md ├── go.sum └── main.go /verify.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | dcraddrgen -verify 3 | pause 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .vscode 3 | dcraddrgen 4 | keys.txt 5 | vendor 6 | -------------------------------------------------------------------------------- /verify.command: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 3 | cd "${DIR}" 4 | ./dcraddrgen -verify 5 | echo "Press [Enter] to finish" 6 | read v 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/decred/dcraddrgen 2 | 3 | go 1.13 4 | 5 | require ( 6 | decred.org/dcrwallet v1.6.0-rc2 7 | github.com/decred/dcrd/chaincfg/v3 v3.0.0 8 | github.com/decred/dcrd/dcrec v1.0.0 9 | github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 10 | github.com/decred/dcrd/dcrutil/v3 v3.0.0 11 | github.com/decred/dcrd/hdkeychain/v3 v3.0.0 12 | ) 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2020 The Decred Developers 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | name: Go CI 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | go: [1.14, 1.15] 10 | steps: 11 | - name: Set up Go 12 | uses: actions/setup-go@v2 13 | with: 14 | go-version: ${{ matrix.go }} 15 | - name: Check out source 16 | uses: actions/checkout@v2 17 | - name: Install Linters 18 | run: "curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.32.1" 19 | - name: Build 20 | env: 21 | GO111MODULE: "on" 22 | run: go build ./... 23 | - name: Test 24 | env: 25 | GO111MODULE: "on" 26 | run: | 27 | sh ./goclean.sh 28 | -------------------------------------------------------------------------------- /goclean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # The script does automatic checking on a Go package and its sub-packages, including: 3 | # 1. gofmt (http://golang.org/cmd/gofmt/) 4 | # 2. golint (https://github.com/golang/lint) 5 | # 3. go vet (http://golang.org/cmd/vet) 6 | # 4. gosimple (https://github.com/dominikh/go-simple) 7 | # 5. unconvert (https://github.com/mdempsky/unconvert) 8 | # 6. race detector (http://blog.golang.org/race-detector) 9 | # 7. test coverage (http://blog.golang.org/cover) 10 | 11 | set -ex 12 | 13 | # run tests 14 | env GORACE="halt_on_error=1" go test -race ./... 15 | 16 | # golangci-lint (github.com/golangci/golangci-lint) is used to run each each 17 | # static checker. 18 | 19 | # check linters 20 | golangci-lint run --disable-all --deadline=10m \ 21 | --enable=gofmt \ 22 | --enable=golint \ 23 | --enable=vet \ 24 | --enable=gosimple \ 25 | --enable=unconvert \ 26 | --enable=ineffassign 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dcraddrgen 2 | ========== 3 | 4 | [![Build Status](https://github.com/decred/dcraddrgen/workflows/Build%20and%20Test/badge.svg)](https://github.com/decred/dcraddrgen/actions) 5 | [![ISC License](https://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) 6 | [![Doc](https://img.shields.io/badge/doc-reference-blue.svg)](https://pkg.go.dev/github.com/decred/dcraddrgen) 7 | 8 | dcraddrgen is a simple offline address generator for [decred](https://decred.org/). 9 | 10 | It allows one to generate an address (along with either the private 11 | key or a wallet seed) without a running wallet or daemon. 12 | 13 | ## Usage 14 | 15 | ``` 16 | Usage: dcraddrgen [-testnet] [-simnet] [-regtest] [-noseed] [-h] filename 17 | Generate a Decred private and public key or wallet seed. 18 | These are output to the file 'filename'. 19 | 20 | -h Print this message 21 | -testnet Generate a testnet key instead of mainnet 22 | -simnet Generate a simnet key instead of mainnet 23 | -regtest Generate a regtest key instead of mainnet 24 | -noseed Generate a single keypair instead of a seed 25 | -verify Verify a seed by generating the first address 26 | ``` 27 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | decred.org/cspp v0.3.0/go.mod h1:UygjYilC94dER3BEU65Zzyoqy9ngJfWCD2rdJqvUs2A= 3 | decred.org/dcrwallet v1.6.0-rc2 h1:dgTArqOwXrQtF9oEyrkDmA7Tos3JuFHDVxpDdBUSqYc= 4 | decred.org/dcrwallet v1.6.0-rc2/go.mod h1:KfDFVvtUnyCKPp6WwWxgD5Q5nlcgvd826tVOHyPOBU0= 5 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 6 | github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= 7 | github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= 8 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 9 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 10 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 11 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 12 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= 14 | github.com/decred/base58 v1.0.3 h1:KGZuh8d1WEMIrK0leQRM47W85KqCAdl2N+uagbctdDI= 15 | github.com/decred/base58 v1.0.3/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbiSM78E= 16 | github.com/decred/dcrd/addrmgr v1.2.0/go.mod h1:QlZF9vkzwYh0qs25C76SAFZBRscjETga/K28GEE6qIc= 17 | github.com/decred/dcrd/blockchain/stake/v3 v3.0.0/go.mod h1:5GIUwsrHQCJauacgCegIR6t92SaeVi28Qls/BLN9vOw= 18 | github.com/decred/dcrd/blockchain/standalone/v2 v2.0.0/go.mod h1:t2qaZ3hNnxHZ5kzVJDgW5sp47/8T5hYJt7SR+/JtRhI= 19 | github.com/decred/dcrd/blockchain/v3 v3.0.1/go.mod h1:LD5VA95qdb+DlRiPI8VLBimDqvlDCAJsidZ5oD6nc/U= 20 | github.com/decred/dcrd/certgen v1.1.1/go.mod h1:ivkPLChfjdAgFh7ZQOtl6kJRqVkfrCq67dlq3AbZBQE= 21 | github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU= 22 | github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60= 23 | github.com/decred/dcrd/chaincfg/v3 v3.0.0 h1:+TFbu7ZmvBwM+SZz5mrj6cun9ts/6DAL5sqnsaFBHGQ= 24 | github.com/decred/dcrd/chaincfg/v3 v3.0.0/go.mod h1:EspyubQ7D2w6tjP7rBGDIE7OTbuMgBjR2F2kZFnh31A= 25 | github.com/decred/dcrd/connmgr/v3 v3.0.0/go.mod h1:cPI43Aggp1lOhrVG75eJ3c3BwuFx0NhT77FK34ky+ak= 26 | github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= 27 | github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= 28 | github.com/decred/dcrd/crypto/ripemd160 v1.0.1 h1:TjRL4LfftzTjXzaufov96iDAkbY2R3aTvH2YMYa1IOc= 29 | github.com/decred/dcrd/crypto/ripemd160 v1.0.1/go.mod h1:F0H8cjIuWTRoixr/LM3REB8obcWkmYx0gbxpQWR8RPg= 30 | github.com/decred/dcrd/database/v2 v2.0.2/go.mod h1:S78KbTCCJWUTJDVTByiQuB+HmL0DM2vIMsa2WsrF9KM= 31 | github.com/decred/dcrd/dcrec v1.0.0 h1:W+z6Es+Rai3MXYVoPAxYr5U1DGis0Co33scJ6uH2J6o= 32 | github.com/decred/dcrd/dcrec v1.0.0/go.mod h1:HIaqbEJQ+PDzQcORxnqen5/V1FR3B4VpIfmePklt8Q8= 33 | github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1 h1:V6eqU1crZzuoFT4KG2LhaU5xDSdkHuvLQsj25wd7Wb4= 34 | github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1/go.mod h1:d0H8xGMWbiIQP7gN3v2rByWUcuZPm9YsgmnfoxgbINc= 35 | github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 h1:sgNeV1VRMDzs6rzyPpxyM0jp317hnwiq58Filgag2xw= 36 | github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8= 37 | github.com/decred/dcrd/dcrjson/v3 v3.1.0/go.mod h1:fnTHev/ABGp8IxFudDhjGi9ghLiXRff1qZz/wvq12Mg= 38 | github.com/decred/dcrd/dcrutil/v3 v3.0.0 h1:n6uQaTQynIhCY89XsoDk2WQqcUcnbD+zUM9rnZcIOZo= 39 | github.com/decred/dcrd/dcrutil/v3 v3.0.0/go.mod h1:iVsjcqVzLmYFGCZLet2H7Nq+7imV9tYcuY+0lC2mNsY= 40 | github.com/decred/dcrd/gcs/v2 v2.1.0/go.mod h1:MbnJOINFcp42NMRAQ+CjX/xGz+53AwNgMzKZhwBibdM= 41 | github.com/decred/dcrd/hdkeychain/v3 v3.0.0 h1:hOPb4c8+K6bE3a/qFtzt2Z2yzK4SpmXmxvCTFp8vMxI= 42 | github.com/decred/dcrd/hdkeychain/v3 v3.0.0/go.mod h1:Vz7PJSlLzhqmOR2lmjGD9JqAZgmUnM8P6r8hg7U4Zho= 43 | github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.2.0/go.mod h1:krn89ZOgSa8yc7sA4WpDK95p61NnjNWFkNlMnGrKbMc= 44 | github.com/decred/dcrd/txscript/v3 v3.0.0/go.mod h1:pdvnlD4KGdDoc09cvWRJ8EoRQUaiUz41uDevOWuEfII= 45 | github.com/decred/dcrd/wire v1.3.0/go.mod h1:fnKGlUY2IBuqnpxx5dYRU5Oiq392OBqAuVjRVSkIoXM= 46 | github.com/decred/dcrd/wire v1.4.0 h1:KmSo6eTQIvhXS0fLBQ/l7hG7QLcSJQKSwSyzSqJYDk0= 47 | github.com/decred/dcrd/wire v1.4.0/go.mod h1:WxC/0K+cCAnBh+SKsRjIX9YPgvrjhmE+6pZlel1G7Ro= 48 | github.com/decred/go-socks v1.1.0/go.mod h1:sDhHqkZH0X4JjSa02oYOGhcGHYp12FsY1jQ/meV8md0= 49 | github.com/decred/slog v1.1.0/go.mod h1:kVXlGnt6DHy2fV5OjSeuvCJ0OmlmTF6LFpEPMu/fOY0= 50 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 51 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 52 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 53 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 54 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 55 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 56 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 57 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 58 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 59 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 60 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 61 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 62 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 63 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 64 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 65 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 66 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 67 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 68 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 69 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 70 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 71 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 72 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 73 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 74 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 75 | github.com/jessevdk/go-flags v1.4.1-0.20200711081900-c17162fe8fd7/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 76 | github.com/jrick/bitset v1.0.0/go.mod h1:ZOYB5Uvkla7wIEY4FEssPVi3IQXa02arznRaYaAEPe4= 77 | github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 78 | github.com/jrick/wsrpc/v2 v2.3.2/go.mod h1:XPYs8BnRWl99lCvXRM5SLpZmTPqWpSOPkDIqYTwDPfU= 79 | github.com/jrick/wsrpc/v2 v2.3.4/go.mod h1:XPYs8BnRWl99lCvXRM5SLpZmTPqWpSOPkDIqYTwDPfU= 80 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 81 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 82 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 83 | github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 84 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 85 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 86 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 87 | github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= 88 | go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= 89 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 90 | golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 91 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 92 | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 93 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 94 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 95 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 96 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 97 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 98 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 99 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 100 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 101 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 102 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 103 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 104 | golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 105 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 106 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 107 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 108 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 109 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 110 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 111 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 112 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 113 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 114 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 115 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 116 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 117 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 118 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 119 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 120 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 121 | golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 122 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 123 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 124 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 125 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 126 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 127 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 128 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 129 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 130 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 131 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 132 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 133 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 134 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 135 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 136 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 137 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 138 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 139 | google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 140 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 141 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 142 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 143 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 144 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 145 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 146 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 147 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 148 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 149 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 150 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 151 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 152 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 153 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2020 The Decred Developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "flag" 11 | "fmt" 12 | "io" 13 | "os" 14 | "runtime" 15 | "strings" 16 | 17 | "decred.org/dcrwallet/walletseed" 18 | "github.com/decred/dcrd/chaincfg/v3" 19 | "github.com/decred/dcrd/dcrec" 20 | "github.com/decred/dcrd/dcrec/secp256k1/v3" 21 | "github.com/decred/dcrd/dcrutil/v3" 22 | "github.com/decred/dcrd/hdkeychain/v3" 23 | ) 24 | 25 | // The hierarchy described by BIP0043 is: 26 | // m/'/* 27 | // This is further extended by BIP0044 to: 28 | // m/44'/'/'//
29 | // 30 | // The branch is 0 for external addresses and 1 for internal addresses. 31 | 32 | // maxCoinType is the maximum allowed coin type used when structuring 33 | // the BIP0044 multi-account hierarchy. This value is based on the 34 | // limitation of the underlying hierarchical deterministic key 35 | // derivation. 36 | const maxCoinType = hdkeychain.HardenedKeyStart - 1 37 | 38 | // MaxAccountNum is the maximum allowed account number. This value was 39 | // chosen because accounts are hardened children and therefore must 40 | // not exceed the hardened child range of extended keys and it provides 41 | // a reserved account at the top of the range for supporting imported 42 | // addresses. 43 | const MaxAccountNum = hdkeychain.HardenedKeyStart - 2 // 2^31 - 2 44 | 45 | // ExternalBranch is the child number to use when performing BIP0044 46 | // style hierarchical deterministic key derivation for the external 47 | // branch. 48 | const ExternalBranch uint32 = 0 49 | 50 | // InternalBranch is the child number to use when performing BIP0044 51 | // style hierarchical deterministic key derivation for the internal 52 | // branch. 53 | const InternalBranch uint32 = 1 54 | 55 | var params = chaincfg.MainNetParams() 56 | 57 | // Flag arguments. 58 | var getHelp = flag.Bool("h", false, "Print help message") 59 | var testnet = flag.Bool("testnet", false, "") 60 | var simnet = flag.Bool("simnet", false, "") 61 | var noseed = flag.Bool("noseed", false, "Generate a single keypair instead of "+ 62 | "an HD extended seed") 63 | var verify = flag.Bool("verify", false, "Verify a seed by generating the first "+ 64 | "address") 65 | 66 | func setupFlags(msg func(), f *flag.FlagSet) { 67 | f.Usage = msg 68 | } 69 | 70 | var newLine = "\n" 71 | 72 | // writeNewFile writes data to a file named by filename. 73 | // Error is returned if the file does exist. Otherwise writeNewFile creates the file with permissions perm; 74 | // Based on ioutil.WriteFile, but produces an err if the file exists. 75 | func writeNewFile(filename string, data []byte, perm os.FileMode) error { 76 | f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) 77 | if err != nil { 78 | return err 79 | } 80 | n, err := f.Write(data) 81 | if err == nil && n < len(data) { 82 | // There was no error, but not all the data was written, so report an error. 83 | err = io.ErrShortWrite 84 | } 85 | if err != nil { 86 | // There was an error, so close file (ignoring any further errors) and return the error. 87 | f.Close() 88 | return err 89 | } 90 | return f.Close() 91 | } 92 | 93 | // generateKeyPair generates and stores a secp256k1 keypair in a file. 94 | func generateKeyPair(filename string) error { 95 | priv, err := secp256k1.GeneratePrivateKey() 96 | if err != nil { 97 | return err 98 | } 99 | pub := priv.PubKey() 100 | 101 | addr, err := dcrutil.NewAddressPubKeyHash( 102 | dcrutil.Hash160(pub.SerializeCompressed()), 103 | params, 104 | dcrec.STEcdsaSecp256k1) 105 | if err != nil { 106 | return err 107 | } 108 | 109 | privWif, err := dcrutil.NewWIF(priv.Serialize(), params.PrivateKeyID, dcrec.STEcdsaSecp256k1) 110 | if err != nil { 111 | return err 112 | } 113 | 114 | var buf bytes.Buffer 115 | buf.WriteString("Address: ") 116 | buf.WriteString(addr.Address()) 117 | buf.WriteString(newLine) 118 | buf.WriteString("Private key: ") 119 | buf.WriteString(privWif.String()) 120 | buf.WriteString(newLine) 121 | 122 | return writeNewFile(filename, buf.Bytes(), 0600) 123 | } 124 | 125 | // deriveCoinTypeKey derives the cointype key which can be used to derive the 126 | // extended key for an account according to the hierarchy described by BIP0044 127 | // given the coin type key. 128 | // 129 | // In particular this is the hierarchical deterministic extended key path: 130 | // m/44'/' 131 | func deriveCoinTypeKey(masterNode *hdkeychain.ExtendedKey, 132 | coinType uint32) (*hdkeychain.ExtendedKey, error) { 133 | // Enforce maximum coin type. 134 | if coinType > maxCoinType { 135 | return nil, fmt.Errorf("bad coin type") 136 | } 137 | 138 | // The hierarchy described by BIP0043 is: 139 | // m/'/* 140 | // This is further extended by BIP0044 to: 141 | // m/44'/'/'//
142 | // 143 | // The branch is 0 for external addresses and 1 for internal addresses. 144 | 145 | // Derive the purpose key as a child of the master node. 146 | purpose, err := masterNode.Child(44 + hdkeychain.HardenedKeyStart) 147 | if err != nil { 148 | return nil, err 149 | } 150 | 151 | // Derive the coin type key as a child of the purpose key. 152 | coinTypeKey, err := purpose.Child(coinType + hdkeychain.HardenedKeyStart) 153 | if err != nil { 154 | return nil, err 155 | } 156 | 157 | return coinTypeKey, nil 158 | } 159 | 160 | // deriveAccountKey derives the extended key for an account according to the 161 | // hierarchy described by BIP0044 given the master node. 162 | // 163 | // In particular this is the hierarchical deterministic extended key path: 164 | // m/44'/'/' 165 | func deriveAccountKey(coinTypeKey *hdkeychain.ExtendedKey, 166 | account uint32) (*hdkeychain.ExtendedKey, error) { 167 | // Enforce maximum account number. 168 | if account > MaxAccountNum { 169 | return nil, fmt.Errorf("account num too high") 170 | } 171 | 172 | // Derive the account key as a child of the coin type key. 173 | return coinTypeKey.Child(account + hdkeychain.HardenedKeyStart) 174 | } 175 | 176 | // checkBranchKeys ensures deriving the extended keys for the internal and 177 | // external branches given an account key does not result in an invalid child 178 | // error which means the chosen seed is not usable. This conforms to the 179 | // hierarchy described by BIP0044 so long as the account key is already derived 180 | // accordingly. 181 | // 182 | // In particular this is the hierarchical deterministic extended key path: 183 | // m/44'/'/'/ 184 | // 185 | // The branch is 0 for external addresses and 1 for internal addresses. 186 | func checkBranchKeys(acctKey *hdkeychain.ExtendedKey) error { 187 | // Derive the external branch as the first child of the account key. 188 | if _, err := acctKey.Child(ExternalBranch); err != nil { 189 | return err 190 | } 191 | 192 | // Derive the external branch as the second child of the account key. 193 | _, err := acctKey.Child(InternalBranch) 194 | return err 195 | } 196 | 197 | // generateSeed derives an address from an HDKeychain for use in wallet. It 198 | // outputs the seed, address, and extended public key to the file specified. 199 | func generateSeed(filename string) error { 200 | seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) 201 | if err != nil { 202 | return err 203 | } 204 | 205 | // Derive the master extended key from the seed. 206 | root, err := hdkeychain.NewMaster(seed, params) 207 | if err != nil { 208 | return err 209 | } 210 | defer root.Zero() 211 | 212 | // Derive the cointype key according to BIP0044. 213 | coinTypeKeyPriv, err := deriveCoinTypeKey(root, params.SLIP0044CoinType) 214 | if err != nil { 215 | return err 216 | } 217 | defer coinTypeKeyPriv.Zero() 218 | 219 | // Derive the account key for the first account according to BIP0044. 220 | acctKeyPriv, err := deriveAccountKey(coinTypeKeyPriv, 0) 221 | if err != nil { 222 | // The seed is unusable if the any of the children in the 223 | // required hierarchy can't be derived due to invalid child. 224 | if err == hdkeychain.ErrInvalidChild { 225 | return fmt.Errorf("the provided seed is unusable") 226 | } 227 | 228 | return err 229 | } 230 | 231 | // Ensure the branch keys can be derived for the provided seed according 232 | // to BIP0044. 233 | if err := checkBranchKeys(acctKeyPriv); err != nil { 234 | // The seed is unusable if the any of the children in the 235 | // required hierarchy can't be derived due to invalid child. 236 | if err == hdkeychain.ErrInvalidChild { 237 | return fmt.Errorf("the provided seed is unusable") 238 | } 239 | 240 | return err 241 | } 242 | 243 | // The address manager needs the public extended key for the account. 244 | acctKeyPub := acctKeyPriv.Neuter() 245 | index := uint32(0) // First address 246 | branch := uint32(0) // External 247 | 248 | // The next address can only be generated for accounts that have already 249 | // been created. 250 | acctKey := acctKeyPub 251 | defer acctKey.Zero() 252 | 253 | // Derive the appropriate branch key and ensure it is zeroed when done. 254 | branchKey, err := acctKey.Child(branch) 255 | if err != nil { 256 | return err 257 | } 258 | defer branchKey.Zero() // Ensure branch key is zeroed when done. 259 | 260 | key, err := branchKey.Child(index) 261 | if err != nil { 262 | return err 263 | } 264 | defer key.Zero() 265 | 266 | pk := key.SerializedPubKey() 267 | pkHash := dcrutil.Hash160(pk) 268 | addr, err := dcrutil.NewAddressPubKeyHash(pkHash, params, dcrec.STEcdsaSecp256k1) 269 | if err != nil { 270 | return err 271 | } 272 | 273 | // Require the user to write down the seed. 274 | reader := bufio.NewReader(os.Stdin) 275 | seedStr := walletseed.EncodeMnemonic(seed) 276 | seedStrSplit := strings.Split(seedStr, " ") 277 | fmt.Println("WRITE DOWN THE SEED GIVEN BELOW. YOU WILL NOT BE GIVEN " + 278 | "ANOTHER CHANCE TO.\n") 279 | fmt.Printf("Your wallet generation seed is:\n\n") 280 | for i := 0; i < hdkeychain.RecommendedSeedLen+1; i++ { 281 | fmt.Printf("%v ", seedStrSplit[i]) 282 | 283 | if (i+1)%6 == 0 { 284 | fmt.Printf("\n") 285 | } 286 | } 287 | 288 | fmt.Printf("\n\nHex: %x\n", seed) 289 | fmt.Println("IMPORTANT: Keep the seed in a safe place as you\n" + 290 | "will NOT be able to restore your wallet without it.") 291 | fmt.Println("Please keep in mind that anyone who has access\n" + 292 | "to the seed can also restore your wallet thereby\n" + 293 | "giving them access to all your funds, so it is\n" + 294 | "imperative that you keep it in a secure location.\n") 295 | 296 | for { 297 | fmt.Print("Once you have stored the seed in a safe \n" + 298 | "and secure location, enter OK here to erase the \n" + 299 | "seed and all derived keys from memory. Derived \n" + 300 | "public keys and an address will be stored in the \n" + 301 | "file specified (default: keys.txt): ") 302 | confirmSeed, err := reader.ReadString('\n') 303 | if err != nil { 304 | return err 305 | } 306 | confirmSeed = strings.TrimSpace(confirmSeed) 307 | confirmSeed = strings.Trim(confirmSeed, `"`) 308 | if confirmSeed == "OK" { 309 | break 310 | } 311 | } 312 | 313 | var buf bytes.Buffer 314 | buf.WriteString("First address: ") 315 | buf.WriteString(addr.Address()) 316 | buf.WriteString(newLine) 317 | buf.WriteString("Extended public key: ") 318 | buf.WriteString(acctKey.String()) 319 | buf.WriteString(newLine) 320 | 321 | // Zero the seed array. 322 | copy(seed[:], bytes.Repeat([]byte{0x00}, 32)) 323 | 324 | return writeNewFile(filename, buf.Bytes(), 0600) 325 | } 326 | 327 | // promptSeed is used to prompt for the wallet seed which maybe required during 328 | // upgrades. 329 | func promptSeed(seedA *[32]byte) error { 330 | reader := bufio.NewReader(os.Stdin) 331 | for { 332 | fmt.Print("Enter existing wallet seed: ") 333 | seedStr, err := reader.ReadString('\n') 334 | if err != nil { 335 | return err 336 | } 337 | 338 | seedStrTrimmed := strings.TrimSpace(seedStr) 339 | 340 | seed, err := walletseed.DecodeUserInput(seedStrTrimmed) 341 | if err != nil || len(seed) < hdkeychain.MinSeedBytes || 342 | len(seed) > hdkeychain.MaxSeedBytes { 343 | if err != nil { 344 | fmt.Printf("Input error: %v\n", err.Error()) 345 | } 346 | 347 | fmt.Printf("Invalid seed specified. Must be "+ 348 | "the words of the seed and at least %d bits and "+ 349 | "at most %d bits\n", hdkeychain.MinSeedBytes*8, 350 | hdkeychain.MaxSeedBytes*8) 351 | continue 352 | } 353 | 354 | copy(seedA[:], seed[:]) 355 | 356 | // Zero the seed slice. 357 | copy(seed[:], bytes.Repeat([]byte{0x00}, 32)) 358 | return nil 359 | } 360 | } 361 | 362 | func verifySeed() error { 363 | seed := new([32]byte) 364 | err := promptSeed(seed) 365 | if err != nil { 366 | return err 367 | } 368 | 369 | // Derive the master extended key from the seed. 370 | root, err := hdkeychain.NewMaster(seed[:], params) 371 | if err != nil { 372 | return err 373 | } 374 | defer root.Zero() 375 | 376 | // Derive the cointype key according to BIP0044. 377 | coinTypeKeyPriv, err := deriveCoinTypeKey(root, params.SLIP0044CoinType) 378 | if err != nil { 379 | return err 380 | } 381 | defer coinTypeKeyPriv.Zero() 382 | 383 | // Derive the account key for the first account according to BIP0044. 384 | acctKeyPriv, err := deriveAccountKey(coinTypeKeyPriv, 0) 385 | if err != nil { 386 | // The seed is unusable if the any of the children in the 387 | // required hierarchy can't be derived due to invalid child. 388 | if err == hdkeychain.ErrInvalidChild { 389 | return fmt.Errorf("the provided seed is unusable") 390 | } 391 | 392 | return err 393 | } 394 | 395 | // Ensure the branch keys can be derived for the provided seed according 396 | // to BIP0044. 397 | if err := checkBranchKeys(acctKeyPriv); err != nil { 398 | // The seed is unusable if the any of the children in the 399 | // required hierarchy can't be derived due to invalid child. 400 | if err == hdkeychain.ErrInvalidChild { 401 | return fmt.Errorf("the provided seed is unusable") 402 | } 403 | 404 | return err 405 | } 406 | 407 | // The address manager needs the public extended key for the account. 408 | acctKeyPub := acctKeyPriv.Neuter() 409 | index := uint32(0) // First address 410 | branch := uint32(0) // External 411 | 412 | // The next address can only be generated for accounts that have already 413 | // been created. 414 | acctKey := acctKeyPub 415 | defer acctKey.Zero() 416 | 417 | // Derive the appropriate branch key and ensure it is zeroed when done. 418 | branchKey, err := acctKey.Child(branch) 419 | if err != nil { 420 | return err 421 | } 422 | defer branchKey.Zero() // Ensure branch key is zeroed when done. 423 | 424 | key, err := branchKey.Child(index) 425 | if err != nil { 426 | return err 427 | } 428 | defer key.Zero() 429 | 430 | pk := key.SerializedPubKey() 431 | pkHash := dcrutil.Hash160(pk) 432 | addr, err := dcrutil.NewAddressPubKeyHash(pkHash, params, dcrec.STEcdsaSecp256k1) 433 | if err != nil { 434 | return err 435 | } 436 | 437 | fmt.Printf("First derived address of given seed: \n%v\n", 438 | addr.Address()) 439 | 440 | // Zero the seed array. 441 | copy(seed[:], bytes.Repeat([]byte{0x00}, 32)) 442 | 443 | return nil 444 | } 445 | 446 | func main() { 447 | if runtime.GOOS == "windows" { 448 | newLine = "\r\n" 449 | } 450 | helpMessage := func() { 451 | fmt.Println( 452 | "Usage: dcraddrgen [-testnet] [-simnet] [-noseed] [-verify] [-h] filename") 453 | fmt.Println("Generate a Decred private and public key or wallet seed. \n" + 454 | "These are output to the file 'filename'.\n") 455 | fmt.Println(" -h \t\tPrint this message") 456 | fmt.Println(" -testnet \tGenerate a testnet key instead of mainnet") 457 | fmt.Println(" -simnet \tGenerate a simnet key instead of mainnet") 458 | fmt.Println(" -noseed \tGenerate a single keypair instead of a seed") 459 | fmt.Println(" -verify \tVerify a seed by generating the first address") 460 | } 461 | 462 | setupFlags(helpMessage, flag.CommandLine) 463 | flag.Parse() 464 | 465 | if *getHelp { 466 | helpMessage() 467 | return 468 | } 469 | 470 | if *verify { 471 | err := verifySeed() 472 | if err != nil { 473 | fmt.Printf("Error verifying seed: %v\n", err.Error()) 474 | return 475 | } 476 | return 477 | } 478 | 479 | var fn string 480 | if flag.Arg(0) != "" { 481 | fn = flag.Arg(0) 482 | } else { 483 | fn = "keys.txt" 484 | } 485 | 486 | // Alter the globals to specified network. 487 | if *testnet { 488 | if *simnet { 489 | fmt.Println("Error: Only specify one network.") 490 | return 491 | } 492 | params = chaincfg.TestNet3Params() 493 | } 494 | if *simnet { 495 | params = chaincfg.SimNetParams() 496 | } 497 | 498 | // Single keypair generation. 499 | if *noseed { 500 | err := generateKeyPair(fn) 501 | if err != nil { 502 | fmt.Printf("Error generating key pair: %v\n", err.Error()) 503 | return 504 | } 505 | fmt.Printf("Successfully generated keypair and stored it in %v.\n", 506 | fn) 507 | fmt.Printf("Your private key is used to spend your funds. Do not " + 508 | "reveal it to anyone.\n") 509 | return 510 | } 511 | 512 | // Derivation of an address from an HDKeychain for use in wallet. 513 | err := generateSeed(fn) 514 | if err != nil { 515 | fmt.Printf("Error generating seed: %v\n", err.Error()) 516 | return 517 | } 518 | fmt.Printf("\nSuccessfully generated an extended public \n"+ 519 | "key and address and stored them in %v.\n", fn) 520 | fmt.Printf("\nYour extended public key can be used to " + 521 | "derive all your addresses. Keep it private.\n") 522 | } 523 | --------------------------------------------------------------------------------