As cryptocurrency becomes and more popular between people. Importantly, with hight profits return, someone have increased the proportion of digital asset in their asset allocation. For those with strong faith on that blockchain technology are the first time for the human to protect their private prop Erty Cann ' t is infringed and transfer the value without any restriction by third party, is attracted by Blockchain techno Logy. At the same time, the safety of cryptocurrency are more importanter then ever before.
Ethereum become the world's second most valuable cryptocurrency. For those huge amounts of the ETH, need a safety wallet to store private key. This post introduces what is hightest level of safety Ethereum HD cold wallet, including generate mnemonic, KeyStore and passphrse, construct raw transaction and sign it offline Offchain, finally send signed transaction to Ethereum Networ K.
Mnemonic and KeyStore
From mnemonic and KeyStore with Passphrse, we can recovery the account quickly and safely.
Ethereum-hdwallet.png
If you don't know what's HD wallet, pls refer to my preview post digital currency wallet-mnemonic word and HD wallet key principle
- GO-BIP39 A Golang Implementation of the BIP0039 spec for mnemonic seeds
- Hdkeychain Package Hdkeychain provides a API for Bitcoin hierarchical deterministic extended keys
BIP44 Multi-account Hierarchy for deterministic Wallets,jaxx, Metamask and Imtoken for Ethereum default path is m/44 '/ "/0 '/0/0, so we do.
Firstly generate mnemonic, entropy with a length of (multiple of 8), we can derive sequence of Rds
// Generate a mnemonic for memorization or user-friendly seedsmnemonic, err := mnemonicFun()if err != nil { return nil, err}...func mnemonicFun() (*string, error) { // Generate a mnemonic for memorization or user-friendly seeds entropy, err := bip39.NewEntropy(128) if err != nil { return nil, err } mnemonic, err := bip39.NewMnemonic(entropy) if err != nil { return nil, err } return &mnemonic, nil}
Secondly, pass mnemonic string hdWallet(mnemonic string) (*ecdsa.PrivateKey, *string, error)
to, you can custom your specify path also.
Privatekey, path, err: = Hdwallet (*mnemonic) if err! = Nil {return nil, Err}...func Hdwallet (mnemonic string) (*ECDSA. Privatekey, *string, error) {//Generate a BIP32 HD wallet for the mnemonic and a user supplied password seed: = Bi P39. Newseed (Mnemonic, "")//Generate a new master node using the seed. Masterkey, err: = Hdkeychain. Newmaster (seed, &chaincfg. MAINNETPARAMS) If err! = Nil {return nil, nil, err}//This gives the path:m/44h acc44h, err: = Mast Erkey.child (Hdkeychain. Hardenedkeystart + +) If err! = Nil {return nil, nil, err}//This gives the path:m/44h/60h Acc44h6 0H, err: = Acc44h.child (Hdkeychain. Hardenedkeystart + +) If err! = Nil {return nil, nil, err}//This gives the path:m/44h/60h/0h acc4 4H60H0H, err: = Acc44h60h.child (Hdkeychain. Hardenedkeystart + 0) If err! = Nil {return nil, nil, err}//This gives the path:m/44h/60h/0h/0 ACC 44h60h0h0, err: = Acc44h60h0h.chiLD (0) If err! = Nil {return nil, nil, err}//This gives the path:m/44h/60h/0h/0/0 acc44h60h0h00, er r: = Acc44h60h0h0.child (0) If err! = Nil {return nil, nil, err} btcecprivkey, err: = Acc44h60h0h00.ecpri Vkey () if err! = Nil {return nil, nil, err} privatekey: = BTCECPRIVKEY.TOECDSA () path: = "m/44h/60h/0 h/0/0 "Return Privatekey, &path, nil}
Nice, we had implement Ethereum account generation. As we know, from mnemonic with path, a specify account can be recovery; With KeyStore and passphrse, we can recovery account to, safe them seperate!
// save mnemonicsaveMnemonic(address, *mnemonic, *path)// save keystore to configure pathsaveKetstore(privateKey, fixedPwd, randomPwd)// save random pwd with address to configure pathsaveRandomPwd(address, randomPwd)// save fixed pwd with address to configure pathsaveFixedPwd(address, fixedPwd)
Construct RAW Transaction
We own cold wallet now, if we want to our cold wallet ETH, how can we transfer safely?
The answer is: construct raw transaction online, sign raw transaction, and copy the signed TX and then broadcast to Et Hereum Network
Func (db Ormbbalias) Constructtxfield (address string) (*string, *big. Int, *uint64, *big. Int, error) {subaddress, err: = db.getsubaddress (address) if err! = Nil {return nil, nil, nil, nil, err } Switch Node {case "Geth": Balance, Nonce, gasprice, err: = Nodeconstructtxfield ("Geth", *subaddress) If err! = Nil {return nil, nil, nil, nil, err} return subaddress, Balance, nonce, Gasprice, n Il case "parity": Balance, Nonce, gasprice, err: = Nodeconstructtxfield ("Parity", *subaddress) if err! = Nil {return nil, nil, nil, nil, err} return subaddress, Balance, nonce, gasprice, nil case " Etherscan ": Balance, Nonce, gasprice, err: = Etherscan.etherscanconstructtxfield (*subaddress) if err! = Nil {return nil, nil, nil, nil, err} return subaddress, Balance, nonce, Gasprice, nil default: Return nil, nil, nil, nil, errors. New ("only supportGeth, parity, Etherscan ")}}
Construct raw transaction need three key Value:amount, Sendtransactioncount (Nonce in We code, can consider is as Send transaction index for the address) and Gasprice. We can get those from Geth, parity by RPC or Etherscan open API. Next step is pass these the values to new Transaction function.
func constructTx(nonce uint64, balance, gasPrice *big.Int, hexAddressFrom, hexAddressTo string) (*string, *string, *string, *string, *big.Int, error) { gasLimit := uint64(21000) // in units if !common.IsHexAddress(hexAddressTo) { return nil, nil, nil, nil, nil, errors.New(strings.Join([]string{hexAddressTo, "invalidate"}, " ")) } var ( txFee = new(big.Int) value = new(big.Int) ) txFee = txFee.Mul(gasPrice, big.NewInt(int64(gasLimit))) value = value.Sub(balance, txFee) tx := types.NewTransaction(nonce, common.HexToAddress(hexAddressTo), value, gasLimit, gasPrice, nil) rawTxHex, err := encodeTx(tx) if err != nil { return nil, nil, nil, nil, nil, errors.New(strings.Join([]string{"encode raw tx error", err.Error()}, " ")) } txHashHex := tx.Hash().Hex() return &hexAddressFrom, &hexAddressTo, rawTxHex, &txHashHex, value, nil}
Sign RAW transaction offline
There is a air gap computer for signing raw transaction, which are not connect to the network. Copy the raw transaction hex data to the computer by USB Flash drive, and pass it as String to Signtx function
Func signtx (Simpletx *tx) (*string, *string, *string, *string, *big. Int, *uint64, error) {txhex: = Simpletx. Txhex Fromaddresshex: = Simpletx. From TX, Err: = Decodetx (Txhex) if err! = Nil {return nil, nil, nil, nil, nil, nil, errors. New (Strings. Join ([]string{"Decode TX Error", Err. Error ()}, "")} if Strings.compare (strings. ToLower (TX. to (). Hex ()), strings. ToLower (config. To)! = 0 {return nil, nil, nil, nil, nil, nil, errors. New (Strings. Join ([]string{"Unsign TX to field:", TX. to (). Hex (), "can ' t match Configure to:", config. to}, "")} promptsign (TX. to (). Hex ()) key, err: = Decodeks2key (Fromaddresshex) if err! = Nil {return nil, nil, nil, nil, nil, nil, ERRORS.N EW (strings. Join ([]string{"Decode KeyStore to key error", Err. Error ()}, "")}//HTTPS://GITHUB.COM/ETHEREUM/EIPS/BLOB/MASTER/EIPS/EIP-155.MD//Chain ID//1 ethereum ma Innet//Ethereum Classic mainnet//Ethereum classic testnet//1337 Geth PrivAte chains (default) var Chainid *big. Int switch Config.netmode {case "privatenet": Chainid = big. Newint (1337) case "mainnet": Chainid = big. Newint (1) Default:return nil, nil, nil, nil, nil, nil, errors. New ("You must set Net_mode in Configure")} Signtx, err: = types. Signtx (TX, types. Neweip155signer (Chainid), key. Privatekey) If err! = Nil {return nil, nil, nil, nil, nil, nil, errors. New (Strings. Join ([]string{"sign TX error", Err. Error ()}, "")} msg, err: = Signtx. Asmessage (types. Neweip155signer (Chainid)) if err! = Nil {return nil, nil, nil, nil, nil, nil, errors. New (Strings. Join ([]string{"TX to MSG Error", Err. Error ()}, "")} From: = Msg. From (). Hex () To: = Msg. to (). Hex () Value: = Msg. Value () Nonce: = Msg. Nonce () Signtxhex, err: = Encodetx (signtx) Hash: = Signtx. Hash (). Hex () return &from, &to, Signtxhex, &hash, value, &nonce, nil}
Broadcast signed transaction
After signed the Rawtxhex, we would exported signed data to the JSON file and then copy the file for broadcasting.
func sendTxCmd(nodeClient *ethclient.Client) { files, err := ioutil.ReadDir(strings.Join([]string{HomeDir(), config.SignedTx}, "/")) if err != nil { log.Fatalln("read raw tx error", err.Error()) } for _, file := range files { fileName := file.Name() tx, err := readTxHex(&fileName, true) if err != nil { log.Errorln(err.Error()) } signedTxHex := tx.TxHex to := config.To hash, err := sendTx(signedTxHex, to, nodeClient) if err != nil { log.Errorln("send tx: ", fileName, "fail", err.Error()) } else { log.Infoln("send tx: ", *hash, "success") } }}
Demo
Ethereum-service genaccount-n 1info[0000] time:= "Wed June 20 17:06:19 2018" Using Configure file=/users/hww/ethereum-service.ymlwarn[0000] note= "all op Erate is recorded "time:=" Wed June 17:06:19 2018 "Password: * * * Password: ***info[0005] Generate Ethereum account=0xb51bcc5bcd0f58317c71680e2caa37f58bebf093 time:= "Wed June 17:06:24 2018" warn[00 time:= "Wed June 17:06:24 2018" Export address to File=/users/hww/eth_a Ddress.csv ethereum-service construct-n gethinfo[0000] time:= "Thu June 21 03 : 10:42 2018 "Using Configure file=/users/hww/ethereum-service.ymlwarn[0000] Note= "All operate are recorded" time:= "Thu June 03:10:42 [2018] info[0000 csv2db] donewarn[0000 7a54aa7a15b4ff9c15926615 Balance not great than the Configure amountwarn[0000] ignore:0x33600eebcc950aa16be862ad94c8a8c4d7741b60 balance not Grea T than the Configure amountinfo[0000] exported hextx to/users/hww/tx/unsign/unsign_ From.0xb51bcc5bcd0f58317c71680e2caa37f58bebf093.json 671d⚑◒ethereum-service signinfo[0000] time:= "T Hu June 03:10:49 2018 "Using Configure file=/users/hww/ethereum-service.ymlwarn[0000] Note= "All operate is recorded" time:= "Thu June 21 03:10:49 2018" Verify that the forwarding address is a configured address: 0X0CEABC861BEEBE8E57A19C26586C1 4C6F5E7B174:YINFO[0002] Exported hextx to/users/hww/tx/signed/signed_ From.0xb51bcc5bcd0f58317c71680e2caa37f58bebf093.json Ethereum-service sendinfo[0000] time:= "Thu June 03:11:06 2018" Using Configure file=/users/hww/ethereum-service.ymlwarn[0000] Note= "All operate is recorded" time:= "Thu June 03:11:06 2018" info[0000] Send TX: 0X2BF7504FB597A9AE8C17910E305815E0EBCA14851C433E8390889F54A8048D2F success
Finally, if you is insteresting for the code, pls contact me,feel free to donate ETH for me, thx:
0x0cF8eF947DfFf47DD2818f83ac7Da817973790e6