深圳BDG小组分享--以太坊钱包开发

以太坊钱包开发

导读:深圳首期技术分享活动于上周三开始,Tiny-熊丽兵主讲第一期内容 以太坊钱包开发。

由于Tiny这个懒虫没空,所以这次暂时有我代劳,把他分享的内容整理出来,大家在现场的同学如果有针对此课程的讨论,请在下边留言,并且告诉大家一个好消息,BG论坛针对此系列活动又一个技术文章奖励活动,大家在课后写的技术文章都会有相应的HPB奖励,在此感谢HPB基金会的赞助,如若了解具体信息,请参考此处.

作者:熊丽兵(Tiny熊)

《深⼊入浅出区块链》&《精通以太坊智能合约开发》作者

–登链学院创始⼈

本期Demo地址

大纲

  • 钱包账号、地址、私钥

  • 钱包账号管理理

  • 以太转账

  • ERC20 Token转账 Ethers.js库

(数字)钱包-- 钱包存钱了了吗?其实 钱包实际是⼀一个管理私钥的工具!

钱包账号看上去是⼀一个地址,关键是私钥 !

Tiny_wallet_key

var Crypto = require('crypto')
var secp256k1=require('secp256k1')
var createKeccakHash=require('keccak')
// ⼀一个32字节的随机数(1~2^256-1), 直接把他当成私钥 var privateKey=Crypto.randomBytes(32); 

console.log(privateKey.toString('hex')); // 由secp256k1椭圆曲线算法先计算出公钥 

var pubKey=secp256k1.publicKeyCreate(privateKey,false).slice(1); // 进⾏行行keccak256 hash运算再取后40位得到 
var address =createKeccakHash('keccak256')
                .update(pubKey).digest().slice(-20);
console.log("0x" + address.toString('hex'));

创建钱包账号

  • 随机⽣生成私钥(32字节)

  • 分层确定性推倒

随机数⽣生成私钥

随机在1到2^256之间选⼀一个数字 不不可预测或不不可重复

分层确定性推倒

随机生成的私钥,备份麻烦,不易管理,所以出现了BIP (Bitcoin Improvement Proposals) Bip32 提案(HD钱包) (HD钱包)

HD钱包

随机数种⼦子推倒⽣生成私钥


Tiny_wallet_hd

分层推倒过程

Tiny_wallet_layer

秘钥路路径

HD钱包中的密钥是⽤用“路路径”命名的,每个级别之间⽤用斜杠(/)字符来表示
如: 第⼀一个主密钥⽣生成的⼦子私钥是m/0。第⼀一个公共钥匙是M/0。第⼀一个⼦子密钥的⼦子密钥就是m/0/1

BIP44

m / purpose’ / coin_type’ / account’ / change / address_index

https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki https://github.com/satoshilabs/slips/blob/master/slip-0044.md

Tiny_wallet_BIP44

随机数 or 助记词

090ABCB3A6e1400e9345bC60c78a8BE7 
candy maple cake sugar pudding cream honey rich
smooth crumble sweet treat

⽣生成助记词

Tiny_wallet_memoric

助记词推倒出种⼦

这里会用到密钥拉伸函数:主要用来增强弱密钥的安全性

Tiny_wallet_seed

var bip39 = require('bip39')
var hdkey = require('ethereumjs-wallet/hdkey')
var util = require('ethereumjs-util')
// ⽣生成助记词
 var mnemonic = bip39.generateMnemonic() 
var seed = bip39.mnemonicToSeed(mnemonic);
var hdWallet = hdkey.fromMasterSeed(seed);
var key1 = hdWallet.derivePath("m/44'/60'/0'/0/0"); console.log("私钥:"+util.bufferToHex(key1.hdkey.privateKey)); 

var address1 = util.pubToAddress(key1.hdkey.publicKey, true); console.log("地址:"+util.bufferToHex(address1)); 

https://iancoleman.io/bip39/

私钥存储

  1. 加密存储 - 对称加密私钥

  2. 如何选择加密秘钥(KDF)

Tiny_wallet_prikey_backup

私钥存储

V3 KeyStore

{
   "address":"856e604698f79cef417aab...",
     "crypto":{
      “cipher":"aes-128-ctr",  //对称加密算法 
      “ciphertext":"13a3ad2135bef1ff228e399dfc8d74a....",//密⽂
      "cipherparams":{
            "iv":"92e7468e8625653f85322fb3c..."
      },
         "kdf":"scrypt",
      "kdfparams":{
         "dklen":32,
         "n":262144,
         "p":1,//秘钥派⽣生算法参数 
         "r":8,
         "salt":"3ca198ce53513ce01bd651aee54b16b6a...."
             "mac":"10423d837830594c18a91097d09b7f2316..."//校验码 
   },
        "id":"5346bac5-0a6f-4ac6-baba-e2f3ad464f3f",
           "version":3
}

V3 KeyStrore 解密

Tiny_wallet_v3keyStore

如何确认密码正确性?

mac= sha3(DK[16:32], ciphertext)

Tiny_wallet_correct1

Tiny_wallet_correct2

转账

一个交易易⻓长这样:

const txParams = {
  nonce: '0x00',
  gasPrice: '0x09184e72a000',
  gasLimit: '0x2710',
  to: '0x0000000000000000000000000000000000000000',
  value: '0x00',
  data: ‘0x7f74657374320000000000000000000000000000...’,
  // EIP 155 chainId - mainnet: 1, ropsten: 3
  chainId: 3

}

Gas

矿工费(预算)= gasLimit * gasPrice gasLimit: 有⼯工作量量决定,少了了 out-of-gas ,多了了退回,普通转账是21000

gasPrice: 决定矿⼯工打包的优先级,出价过低打包慢,出价过⾼高不不划算。

签名及广播

const tx = new EthereumTx(txParams)
tx.sign(privateKey)
const serializedTx = tx.serialize()
web3.eth.sendRawTransaction(serializedTx, function (err, transactionHash) {
    console.log(transactionHash);

});

Token转账

Token = 符合 ERC20 接⼝口的合约

Token转账 = 调⽤用合约的转账函数 = 向合约地址发起交易易 -->交易易的data 是 ABI编码数据

Token转账代码

ABI: Application Binary Interface :描述合约提供的接⼝口

   [
    {
    "constant": false,
    "inputs": [{
      "name": "to",
      "type": "address"
    },
       {
        "name": "tokens",
        "type": "uint256"
        }],
        "name": "transfer",
        "outputs": [{
        	 "name": "success",
        	 "type": "bool"
    }],
    	"payable": false,
		"stateMutability": "nonpayable",
		"type": "function"
		} 
		] 

Token转账代码2

ABI: Application Binary Interface 描述合约提供的接口

var abi = [...];
var addr = "0x...";
var contract = new ethers.Contract(address, abi, provider);
contract.transfer(targetAddress, amount)
      .then(function(tx) {
             console.log(tx);
     });

ethers.js

和web3.js ⼀一样,是一套和以太坊区块链进⾏行行交互的库

ethers.js 对BIP32 BIP39 BIP44等相关的提案进⾏行行了了实现

4个功能模块:
Wallets & Signers Contracts

Providers Utilities

https://docs.ethers.io/ethers.js/html/

ethers.js

随机数私钥创建钱包账号

var privateKey = ethers.utils.randomBytes(32); 

var wallet = new ethers.Wallet(privateKey); 

助记词⽅方式创建钱包账号

var rand = ethers.utils.randomBytes(16);

 var mnemonic = ethers.utils.HDNode.entropyToMnemonic(rand); 

var path = "m/44'/60'/0'/0/0"; ethers.Wallet.fromMnemonic(mnemonic, path); 

ethers.js

KeyStore 加解密

wallet.encrypt(pwd.val()).then(function(json) {
 var blob = new Blob([json], {type: "text/plain;charset=utf-8"}); saveAs(blob, "keystore.json"); 

}); 

ethers.Wallet.fromEncryptedJson(json, password).then(function(wallet) { 

// 

}, function(error) {

 // 

}); 


ethers.js

连接provider

 var provider = new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545"); 

var activeWallet = wallet.connect(provider); 

activeWallet.sendTransaction({

 to: targetAddress, 

value: amountWei,
 //gasPrice: activeWallet.provider.getGasPrice(), 

//gasLimit: 21000, 

}).then(function(tx) { 

}); 

钱包安全性

  • 谨慎谨慎谨慎(没有后悔悔药)
  • 强密码 + 离线保存(勿⽤用⽹网络分享)
  • 开源钱包 / 硬件钱包 / 多签钱包

–本期由土星代为整理

谢谢