zoukankan      html  css  js  c++  java
  • trufflesuite/truffle-hdwallet-provider

    https://github.com/trufflesuite/truffle-hdwallet-provider/blob/master/index.js

    实现代码

    truffle-hdwallet-provider/index.js

    const bip39 = require("bip39");
    const ethJSWallet = require('ethereumjs-wallet');
    const hdkey = require('ethereumjs-wallet/hdkey');
    const debug = require('debug')('truffle-hdwallet-provider')
    const ProviderEngine = require("web3-provider-engine");
    const FiltersSubprovider = require('web3-provider-engine/subproviders/filters.js');
    const NonceSubProvider = require('web3-provider-engine/subproviders/nonce-tracker.js');
    const HookedSubprovider = require('web3-provider-engine/subproviders/hooked-wallet.js');
    const ProviderSubprovider = require("web3-provider-engine/subproviders/provider.js");
    const Web3 = require("web3");
    const Transaction = require('ethereumjs-tx');
    const ethUtil = require('ethereumjs-util');
    
    // This line shares nonce state across multiple provider instances. Necessary
    // because within truffle the wallet is repeatedly newed if it's declared in the config within a
    // function, resetting nonce from tx to tx. An instance can opt out
    // of this behavior by passing `shareNonce=false` to the constructor.
    // See issue #65 for more
    const singletonNonceSubProvider = new NonceSubProvider();
    
    function HDWalletProvider(
      mnemonic,
      provider,
      address_index=0,//从给的mnemonic数组的第几下标开始取
      num_addresses=1,//取里面的几个mnemonic,即生成几个address
      shareNonce=true, //共享nonce状态,这样钱包进行刷新时nonce值还是原来的值
      wallet_hdpath="m/44'/60'/0'/0/"//指明到第四层,即上面的操作是要在该账户的外链上生成num_addresses个公钥
    ) {
    
      if (mnemonic && mnemonic.indexOf(' ') === -1 || Array.isArray(mnemonic)) {//传入的是privateKey,一个或多个(数组)
    
        const privateKeys = Array.isArray(mnemonic) ? mnemonic : [mnemonic];//如果是一个也要将其转成数组形式
        this.wallets = {};
        this.addresses = [];
    
        for (let i = address_index; i < address_index + num_addresses; i++){
          const privateKey = Buffer.from(privateKeys[i].replace('0x', ''), 'hex');//将privateKey转成Buffer格式
          if (ethUtil.isValidPrivate(privateKey)) {//查看privateKey是否正确
            const wallet = ethJSWallet.fromPrivateKey(privateKey);//正确则能从主私钥得到wallet
            const address = wallet.getAddressString();//得到wallet的address
            this.addresses.push(address);//并记录到wallets和addresses中
            this.wallets[address] = wallet;
          }
        }
      } else {//否则传入的就是一个mnemonic
        this.mnemonic = mnemonic;
        this.hdwallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(mnemonic));//根据mnemonic生成hdwallet
        this.wallet_hdpath = wallet_hdpath;
        this.wallets = {};
        this.addresses = [];
    
        if (!bip39.validateMnemonic(mnemonic)) {//如果mnemonic不对则报错
          throw new Error("Mnemonic invalid or undefined")
        }
    
        for (let i = address_index; i < address_index + num_addresses; i++){
          const wallet = this.hdwallet.derivePath(this.wallet_hdpath + i).getWallet();// getWallet()-return a Wallet instance,这样这个m/44'/60'/0'/0/i就能够使用Wallet的API了
          const addr = '0x' + wallet.getAddress().toString('hex');//得到m/44'/60'/0'/0/i的address
          this.addresses.push(addr);//并记录下来
          this.wallets[addr] = wallet;
        }
      }
      //得到上面根据privateKey和mnemonic生成的账户address和wallet
      const tmp_accounts = this.addresses;
      const tmp_wallets = this.wallets;
    
      this.engine = new ProviderEngine();
      this.engine.addProvider(new HookedSubprovider({ //先将HookedSubprovider添加进engine,并定义能够进行的rpc操作
        getAccounts: function(cb) { cb(null, tmp_accounts) },
        getPrivateKey: function(address, cb) {
          if (!tmp_wallets[address]) { return cb('Account not found'); }
          else { cb(null, tmp_wallets[address].getPrivateKey().toString('hex')); }
        },
        signTransaction: function(txParams, cb) {
          let pkey;
          const from = txParams.from.toLowerCase()
          if (tmp_wallets[from]) { pkey = tmp_wallets[from].getPrivateKey(); }
          else { cb('Account not found'); }
          const tx = new Transaction(txParams);
          tx.sign(pkey);
          const rawTx = '0x' + tx.serialize().toString('hex');
          cb(null, rawTx);
        },
        signMessage(message, cb) {
          const dataIfExists = message.data;
          if (!dataIfExists) {
            cb('No data to sign');
          }
          if (!tmp_wallets[message.from]) {
            cb('Account not found');
          }
          let pkey = tmp_wallets[message.from].getPrivateKey();
          const dataBuff = ethUtil.toBuffer(dataIfExists);
          const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
          const sig = ethUtil.ecsign(msgHashBuff, pkey);
          const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s);
          cb(null, rpcSig);
          }             
      }));
    
      (!shareNonce)//NonceSubProvider在HookedSubprovider和ProviderSubprovider之间被添加,以免多个钱包处理同一类数字资产的情况下同时生成交易时会导致nonce值相同造成冲突
        ? this.engine.addProvider(new NonceSubProvider())//如果shareNonce = false,不共享nonce值,则添加的NonceSubProvider为局部对象
        : this.engine.addProvider(singletonNonceSubProvider);////如果shareNonce = true,共享nonce值,则添加的singletonNonceSubProvider为全局常量
    
      this.engine.addProvider(new FiltersSubprovider());//添加FiltersSubprovider
      if (typeof provider === 'string') {//添加ProviderSubprovider
        this.engine.addProvider(new ProviderSubprovider(new Web3.providers.HttpProvider(provider)));
      } else {
        this.engine.addProvider(new ProviderSubprovider(provider));
      }
      this.engine.start(); // Required by the provider engine.
    };
    
    HDWalletProvider.prototype.sendAsync = function() {
      this.engine.sendAsync.apply(this.engine, arguments);
    };
    
    HDWalletProvider.prototype.send = function() {
      return this.engine.send.apply(this.engine, arguments);
    };
    
    // returns the address of the given address_index, first checking the cache
    HDWalletProvider.prototype.getAddress = function(idx) {
      debug('getting addresses', this.addresses[0], idx)
      if (!idx) { return this.addresses[0]; }
      else { return this.addresses[idx]; }
    }
    
    // returns the addresses cache
    HDWalletProvider.prototype.getAddresses = function() {
      return this.addresses;
    }
    
    module.exports = HDWalletProvider;

    使用:

    安装:

    npm install truffle-hdwallet-provider
    //+ truffle-hdwallet-provider@0.0.6
    var HDWalletProvider = require("truffle-hdwallet-provider");
    var Web3 = require("web3");
    var web3 = new Web3();
    const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat';
    var provider = new HDWalletProvider(mnemonic, "http://localhost:8202", 0, 1);
    console.log(provider.getAddresses());
    web3.setProvider(provider);
    web3.eth.getBlockNumber((err, number) => {
        console.log(number);
    });
    // At termination, `provider.engine.stop()' should be called to finish the process elegantly.
    provider.engine.stop();//一定要记得添加
    //不能运行下面的内容,因为Error: Web3ProviderEngine does not support synchronous requests.
    // web3.eth.coinbase((err, coinbase) => {
    //     console.log(coinbase);
    // });
    // web3.eth.mining((err, state) => {
    //     console.log(state);
    // });

    返回:

    就成功生成了m/44'/60'/0'/0/0的address

    userdeMacBook-Pro:test-hd-wallet user$ node index.js 
    [ '0x627306090abab3a6e1400e9345bc60c78a8bef57' ]
    301

    如果改成:

    var provider = new HDWalletProvider(mnemonic, "http://localhost:8202", 0, 5);

    则为:

    userdeMacBook-Pro:test-hd-wallet user$ node index.js 
    [ '0x627306090abab3a6e1400e9345bc60c78a8bef57',
      '0xf17f52151ebef6c7334fad080c5704d77216b732',
      '0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
      '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
      '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2' ]
    301

    在geth中运行sendTransaction后:

    > web3.eth.sendTransaction({from:eth.accounts[0],to:'0x627306090abab3a6e1400e9345bc60c78a8bef57',value:web3.toWei(4,'ether')})
    "0xd45b223fa40b36b214f178bb2c343d8a9cc7c94a7bbfacd8d69efd297f346786"

    再查看账户余额:

    var addresses = provider.getAddresses();
    console.log(addresses);
    web3.setProvider(provider);
    web3.eth.getBlockNumber((err, number) => {
        console.log(number);
    });
    web3.eth.getBalance(addresses[0],(err,balance) => {
        if(!err){
            console.log(balance.toString());        
        }
    });

    返回:

    userdeMacBook-Pro:test-hd-wallet user$ node index.js 
    [ '0x627306090abab3a6e1400e9345bc60c78a8bef57',
      '0xf17f52151ebef6c7334fad080c5704d77216b732',
      '0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
      '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
      '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2' ]
    4000000000000000000
    328

    接下来部署一个合约看看:

    var HDWalletProvider = require("truffle-hdwallet-provider");
    var Web3 = require("web3");
    const fs = require("fs");
    const solc = require("solc");
    
    var web3 = new Web3();
    const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat';
    var provider = new HDWalletProvider(mnemonic, "http://localhost:8202", 0, 5);
    var addresses = provider.getAddresses();
    // console.log(addresses);
    web3.setProvider(provider);
    web3.eth.getBlockNumber((err, number) => {
        console.log(number);
    });
    web3.eth.getBalance(addresses[0],(err,balance) => {
        if(!err){
            console.log(balance.toString());        
        }
    });
    
    
    let source = fs.readFileSync("./Adoption.sol",'utf8');//读取sol智能合约文件
    //对智能合约进行编译,第二个参数设置为1可以激活优化器optimiser
    let compiledContract = solc.compile(source,1);
    var bytecode,abi;
    for (let contractName in compiledContract.contracts) {
        bytecode = compiledContract.contracts[contractName].bytecode;//获得编译后合约的bytecode
        console.log(bytecode)
        abi = JSON.parse(compiledContract.contracts[contractName].interface); //获得编译后合约的abi并写成json形式
        console.log(abi)
    }
    
    let MyContract = web3.eth.contract(abi);
    var instance = MyContract.new({from:addresses[0],data:'0x'+bytecode,gas:30000000},function(e,contract){
             if(typeof contract.address !== 'undefined'){
                console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
            }else{
                console.log(e);
            }
    });
    
    setTimeout(()=>{
            web3.eth.getBalance(addresses[0],(err,balance) => {
            if(!err){
                console.log(balance.toString());        
            }
        })
    },3000);

    部署完之后就可以直接使用该合约:

    var instance = MyContract.at('0xF12b5dd4EAD5F743C6BaA640B0216200e89B60Da');
    instance.adopt(1,{from:addresses[0],gas:3000000},(err,result) => {
        if(!err){
            console.log(result);        
        }else{
            console.log(err);
        }
    });

    然后再调用:

    instance.getAdopters({gas:3000000},(err,result) =>{
        if(!err){
            console.log(result);        
        }else{
            console.log(err);
        }
    });

    可以得到结果:

    [ '0x627306090abab3a6e1400e9345bc60c78a8bef57',
      '0x627306090abab3a6e1400e9345bc60c78a8bef57',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000',
      '0x0000000000000000000000000000000000000000' ]
  • 相关阅读:
    Splay 详解
    莫队套值域分块
    浅谈区间众数
    回滚莫队分块
    带修莫队分块
    微服务规划准则
    mysql查询包含逗号的数据,并逗号拆分为多行展现
    python mysql 单连接和连接池简单示例
    代理模式八:装饰者模式
    代理模式七:迭代器模式
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/9985192.html
Copyright © 2011-2022 走看看