MetaMask/provider-engine-3-test

通过看其test的代码去好好看看它是怎么使用的

1.

provider-engine/test/basic.js

const test = require('tape')
const ProviderEngine = require('../index.js')
const PassthroughProvider = require('./util/passthrough.js')
const FixtureProvider = require('../subproviders/fixture.js')
const TestBlockProvider = require('./util/block.js')
const createPayload = require('../util/create-payload.js')
const injectMetrics = require('./util/inject-metrics')


test('fallthrough test', function(t){
  t.plan(8)

  // handle nothing
  var providerA = injectMetrics(new PassthroughProvider())
  // handle "test_rpc"
  var providerB = injectMetrics(new FixtureProvider({ //写入了能够处理的方法test_rpc
    test_rpc: true,
  }))
  // handle block requests
  var providerC = injectMetrics(new TestBlockProvider())

  var engine = new ProviderEngine()
  engine.addProvider(providerA)
  engine.addProvider(providerB)
  engine.addProvider(providerC)

  engine.start()
  engine.sendAsync(createPayload({ method: 'test_rpc' }), function(err, response){//当要访问test_rpc时,就会按照subprovider被添加进engine的顺序一个个去查看能不能被处理
    t.ifError(err, 'did not error') //下面是根据返回的信息去分析处理的过程
    t.ok(response, 'has response')

    t.equal(providerA.getWitnessed('test_rpc').length, 1, 'providerA did see "test_rpc"') //首先是先去访问providerA,将test_rpc的访问添加进payloadsWitnessed
    t.equal(providerA.getHandled('test_rpc').length, 0, 'providerA did NOT handle "test_rpc"') //因为它不能处理这个方法,所以payloadsHandled中没有它

    t.equal(providerB.getWitnessed('test_rpc').length, 1, 'providerB did see "test_rpc"')//然后是去访问providerB,将test_rpc的访问添加进payloadsWitnessed
    t.equal(providerB.getHandled('test_rpc').length, 1, 'providerB did handle "test_rpc"')//因为它能处理这个方法,所以payloadsHandled中有它

    t.equal(providerC.getWitnessed('test_rpc').length, 0, 'providerC did NOT see "test_rpc"')//因为providerB上面已经成功处理这个方法了,不会再next(),所以providerC的
    t.equal(providerC.getHandled('test_rpc').length, 0, 'providerC did NOT handle "test_rpc"')//payloadsWitnessedpayloadsHandled都没有它

    engine.stop()
    t.end()
  })

})

 

injectMetrics= require('./util/inject-metrics'):添加两个记录指标payloadsWitnessed={method:[payload1,payload2,...]}(记录要被运行的method及其不同时候传进来的payload)和payloadsHandled={}(记录已经处理的method及其handle)。并且给了两个获得method的payload数组的方法:getWitnessed(method)和getHandled(method)
PassthroughProvider和TestBlockProvider都是继承了FixtureProvider的

返回结果:

# fallthrough test
ok 1 did not error
ok 2 has response
ok 3 providerA did see "test_rpc"
ok 4 providerA did NOT handle "test_rpc"
ok 5 providerB did see "test_rpc"
ok 6 providerB did handle "test_rpc"
ok 7 providerC did NOT see "test_rpc"
ok 8 providerC did NOT handle "test_rpc"

 

2.

provider-engine/test/cache-utils.js

const test = require('tape')
const cacheUtils = require('../util/rpc-cache-utils')

test('cacheIdentifierForPayload for latest block', function (t) {
  const payload1 = {id: 1, jsonrpc: '2.0', params: ['latest', false], method: 'eth_getBlockByNumber'}
  const payload2 = {id: 2, jsonrpc: '2.0', params: ['0x0', false], method: 'eth_getBlockByNumber'}
  const cacheId1 = cacheUtils.cacheIdentifierForPayload(payload1, { includeBlockRef: true })//返回eth_getBlockByNumber:['latest', false]
  const cacheId2 = cacheUtils.cacheIdentifierForPayload(payload2, { includeBlockRef: true })//返回eth_getBlockByNumber:['0x0', false]

  t.notEqual(cacheId1, cacheId2, 'cacheIds are unique')
  t.end()
})

test('blockTagForPayload for different methods', function (t) {
  const payloads = [
    {jsonrpc: '2.0', method: 'eth_getBalance', params: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1', '0x1234'], id: 1},
    {jsonrpc: '2.0', method: 'eth_getCode', params: ['0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b', '0x1234'], id: 1},
    {jsonrpc: '2.0', method: 'eth_getTransactionCount', params: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1','0x1234'], id: 1},
    {jsonrpc: '2.0', method: 'eth_getStorageAt', params: ['0x295a70b2de5e3953354a6a8344e616ed314d7251', '0x0', '0x1234'], id: 1},
    {jsonrpc: '2.0', method: 'eth_call', params: [{to: '0x295a70b2de5e3953354a6a8344e616ed314d7251'}, '0x1234'], id: 1},
    {jsonrpc: '2.0', method: 'eth_estimateGas', params: [{to: '0x295a70b2de5e3953354a6a8344e616ed314d7251'}, '0x1234'], id: 1},
    {jsonrpc: '2.0', method: 'eth_getBlockByNumber', params: ['0x1234', true], id: 1},
  ]


  payloads.forEach(function (payload) {
    const blockTag = cacheUtils.blockTagForPayload(payload)
    t.isEqual(blockTag, '0x1234', 'block tag for ' + payload.method + ' is correct')//上面的payload都能够正确地得到blockTag的值为0x1234
  })

  t.end()
})

provider-engine/util/rpc-cache-utils.js

const stringify = require('json-stable-stringify')

module.exports = {
  cacheIdentifierForPayload: cacheIdentifierForPayload, //6 根据传入的opts.includeBlockRef(params是否需要blockTag参数),来得到payload.params,返回payload.methodpayload.params
  canCache: canCache, //2 分类后类型不为'never'的都能够cache
  blockTagForPayload: blockTagForPayload,//5 得到payload.params中的blockTag参数
  paramsWithoutBlockTag: paramsWithoutBlockTag,//4 返回去掉blockTag参数payload.params
  blockTagParamIndex: blockTagParamIndex, //3 得到blockTag参数在payload.params的下标位置
  cacheTypeForPayload: cacheTypeForPayload,//1 根据payload中的method来对需要对其进行cache存储的操作进行分类,有得方法的内容需要永久存储,有的甚至不需存储(never)
}

function cacheIdentifierForPayload(payload, opts = {}){
  if (!canCache(payload)) return null
  const { includeBlockRef } = opts
  const params = includeBlockRef ? payload.params : paramsWithoutBlockTag(payload)
  return payload.method + ':' + stringify(params)
}

function canCache(payload){
  return cacheTypeForPayload(payload) !== 'never'
}

function blockTagForPayload(payload){
  var index = blockTagParamIndex(payload);

  // Block tag param not passed.
  if (index >= payload.params.length) {
    return null;
  }

  return payload.params[index];
}

function paramsWithoutBlockTag(payload){
  var index = blockTagParamIndex(payload);

  // Block tag param not passed.
  if (index >= payload.params.length) {
    return payload.params;
  }

  // eth_getBlockByNumber has the block tag first, then the optional includeTx? param
  if (payload.method === 'eth_getBlockByNumber') {
    return payload.params.slice(1);
  }

  return payload.params.slice(0,index);
}

function blockTagParamIndex(payload){
  switch(payload.method) {
    // blockTag is third param
    case 'eth_getStorageAt':
      return 2
    // blockTag is second param
    case 'eth_getBalance':
    case 'eth_getCode':
    case 'eth_getTransactionCount':
    case 'eth_call':
    case 'eth_estimateGas':
      return 1
    // blockTag is first param
    case 'eth_getBlockByNumber':
      return 0
    // there is no blockTag
    default:
      return undefined
  }
}

function cacheTypeForPayload(payload) {
  switch (payload.method) {
    // cache permanently
    case 'web3_clientVersion':
    case 'web3_sha3':
    case 'eth_protocolVersion':
    case 'eth_getBlockTransactionCountByHash':
    case 'eth_getUncleCountByBlockHash':
    case 'eth_getCode':
    case 'eth_getBlockByHash':
    case 'eth_getTransactionByHash':
    case 'eth_getTransactionByBlockHashAndIndex':
    case 'eth_getTransactionReceipt':
    case 'eth_getUncleByBlockHashAndIndex':
    case 'eth_getCompilers':
    case 'eth_compileLLL':
    case 'eth_compileSolidity':
    case 'eth_compileSerpent':
    case 'shh_version':
      return 'perma'

    // cache until fork
    case 'eth_getBlockByNumber':
    case 'eth_getBlockTransactionCountByNumber':
    case 'eth_getUncleCountByBlockNumber':
    case 'eth_getTransactionByBlockNumberAndIndex':
    case 'eth_getUncleByBlockNumberAndIndex':
      return 'fork'

    // cache for block
    case 'eth_gasPrice':
    case 'eth_blockNumber':
    case 'eth_getBalance':
    case 'eth_getStorageAt':
    case 'eth_getTransactionCount':
    case 'eth_call':
    case 'eth_estimateGas':
    case 'eth_getFilterLogs':
    case 'eth_getLogs':
    case 'net_peerCount':
      return 'block'

    // never cache
    case 'net_version':
    case 'net_peerCount':
    case 'net_listening':
    case 'eth_syncing':
    case 'eth_sign':
    case 'eth_coinbase':
    case 'eth_mining':
    case 'eth_hashrate':
    case 'eth_accounts':
    case 'eth_sendTransaction':
    case 'eth_sendRawTransaction':
    case 'eth_newFilter':
    case 'eth_newBlockFilter':
    case 'eth_newPendingTransactionFilter':
    case 'eth_uninstallFilter':
    case 'eth_getFilterChanges':
    case 'eth_getWork':
    case 'eth_submitWork':
    case 'eth_submitHashrate':
    case 'db_putString':
    case 'db_getString':
    case 'db_putHex':
    case 'db_getHex':
    case 'shh_post':
    case 'shh_newIdentity':
    case 'shh_hasIdentity':
    case 'shh_newGroup':
    case 'shh_addToGroup':
    case 'shh_newFilter':
    case 'shh_uninstallFilter':
    case 'shh_getFilterChanges':
    case 'shh_getMessages':
      return 'never'
  }
}

返回:

# cacheIdentifierForPayload for latest block
ok 9 cacheIds are unique
# blockTagForPayload for different methods
ok 10 block tag for eth_getBalance is correct
ok 11 block tag for eth_getCode is correct
ok 12 block tag for eth_getTransactionCount is correct
ok 13 block tag for eth_getStorageAt is correct
ok 14 block tag for eth_call is correct
ok 15 block tag for eth_estimateGas is correct
ok 16 block tag for eth_getBlockByNumber is correct

 

 

3.

provider-engine/test/cache.js

const test = require('tape')
const ProviderEngine = require('../index.js')
const FixtureProvider = require('../subproviders/fixture.js')
const CacheProvider = require('../subproviders/cache.js')
const TestBlockProvider = require('./util/block.js')
const createPayload = require('../util/create-payload.js')
const injectMetrics = require('./util/inject-metrics')

// skip cache

cacheTest('skipCache - true', {
  method: 'eth_getBalance',
  skipCache: true,
}, false)

cacheTest('skipCache - false', {
  method: 'eth_getBalance',
  skipCache: false,
}, true)

// block tags

cacheTest('getBalance + undefined blockTag', {
  method: 'eth_getBalance',
  params: ['0x1234'],
}, true)

cacheTest('getBalance + latest blockTag', {
  method: 'eth_getBalance',
  params: ['0x1234', 'latest'],
}, true)

cacheTest('getBalance + pending blockTag', {
  method: 'eth_getBalance',
  params: ['0x1234', 'pending'],
}, false)

// tx by hash

cacheTest('getTransactionByHash for transaction that doesn\'t exist', {
  method: 'eth_getTransactionByHash',
  params: ['0x00000000000000000000000000000000000000000000000000deadbeefcafe00'],
}, false)

cacheTest('getTransactionByHash for transaction that\'s pending', {
  method: 'eth_getTransactionByHash',
  params: ['0x00000000000000000000000000000000000000000000000000deadbeefcafe01'],
}, false)

cacheTest('getTransactionByHash for mined transaction', {
  method: 'eth_getTransactionByHash',
  params: ['0x00000000000000000000000000000000000000000000000000deadbeefcafe02'],
}, true)

// code

cacheTest('getCode for latest block, then for earliest block, should not return cached response on second request', [{
  method: 'eth_getCode',
  params: ['0x1234', 'latest'],
}, {
  method: 'eth_getCode',
  params: ['0x1234', 'earliest'],
}], false)

cacheTest('getCode for a specific block, then for the one before it, should not return cached response on second request', [{
  method: 'eth_getCode',
  params: ['0x1234', '0x3'],
}, {
  method: 'eth_getCode',
  params: ['0x1234', '0x2'],
}], false)

cacheTest('getCode for a specific block, then the one after it, should return cached response on second request', [{
  method: 'eth_getCode',
  params: ['0x1234', '0x2'],
}, {
  method: 'eth_getCode',
  params: ['0x1234', '0x3'],
}], true)

cacheTest('getCode for an unspecified block, then for the latest, should return cached response on second request', [{
  method: 'eth_getCode',
  params: ['0x1234'],
}, {
  method: 'eth_getCode',
  params: ['0x1234', 'latest'],
}], true)

// blocks

cacheTest('getBlockForNumber for latest then block 0', [{
  method: 'eth_getBlockByNumber',
  params: ['latest'],
}, {
  method: 'eth_getBlockByNumber',
  params: ['0x0'],
}], false)

cacheTest('getBlockForNumber for latest then block 1', [{
  method: 'eth_getBlockByNumber',
  params: ['latest'],
}, {
  method: 'eth_getBlockByNumber',
  params: ['0x1'],
}], false)

cacheTest('getBlockForNumber for 0 then block 1', [{
  method: 'eth_getBlockByNumber',
  params: ['0x0'],
}, {
  method: 'eth_getBlockByNumber',
  params: ['0x1'],
}], false)

cacheTest('getBlockForNumber for block 0', [{
  method: 'eth_getBlockByNumber',
  params: ['0x0'],
}, {
  method: 'eth_getBlockByNumber',
  params: ['0x0'],
}], true)

cacheTest('getBlockForNumber for block 1', [{
  method: 'eth_getBlockByNumber',
  params: ['0x1'],
}, {
  method: 'eth_getBlockByNumber',
  params: ['0x1'],
}], true)

// storage

cacheTest('getStorageAt for same block should cache', [{
  method: 'eth_getStorageAt',
  params: ['0x295a70b2de5e3953354a6a8344e616ed314d7251', '0x0', '0x1234'],
}, {
  method: 'eth_getStorageAt',
  params: ['0x295a70b2de5e3953354a6a8344e616ed314d7251', '0x0', '0x1234'],
}], true)

cacheTest('getStorageAt for different block should not cache', [{
  method: 'eth_getStorageAt',
  params: ['0x295a70b2de5e3953354a6a8344e616ed314d7251', '0x0', '0x1234'],
}, {
  method: 'eth_getStorageAt',
  params: ['0x295a70b2de5e3953354a6a8344e616ed314d7251', '0x0', '0x4321'],
}], false)


// test helper for caching
// 1. Sets up caching and data provider
// 2. Performs first request
// 3. Performs second request
// 4. checks if cache hit or missed

function cacheTest(label, payloads, shouldHitCacheOnSecondRequest){//就是如果连着两次访问就触发cacheProvider进行存储该method
  if (!Array.isArray(payloads)) {
    payloads = [payloads, payloads]//为了连着两次请求该payloads
  }

  test('cache - '+label, function(t){
    t.plan(12)

    // cache layer
    var cacheProvider = injectMetrics(new CacheProvider())
    // handle balance
    var dataProvider = injectMetrics(new FixtureProvider({
      eth_getBalance: '0xdeadbeef',
      eth_getCode: '6060604052600560005560408060156000396000f3606060405260e060020a60003504633fa4f245811460245780635524107714602c575b005b603660005481565b6004356000556022565b6060908152602090f3',
      eth_getTransactionByHash: function(payload, next, end) {
        // represents a pending tx
        if (payload.params[0] === '0x00000000000000000000000000000000000000000000000000deadbeefcafe00') {
          end(null, null)
        } else if (payload.params[0] === '0x00000000000000000000000000000000000000000000000000deadbeefcafe01') {
          end(null, {
            hash: '0x00000000000000000000000000000000000000000000000000deadbeefcafe01',
            nonce: '0xd',
            blockHash: null,
            blockNumber: null,
            transactionIndex: null,
            from: '0xb1cc05ab12928297911695b55ee78c1188f8ef91',
            to: '0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98',
            value: '0xddb66b2addf4800',
            gas: '0x5622',
            gasPrice: '0xba43b7400',
            input: '0x',
          })
        } else {
          end(null, {
            hash: payload.params[0],
            nonce: '0xd',
            blockHash: '0x1',
            blockNumber: '0x1',
            transactionIndex: '0x0',
            from: '0xb1cc05ab12928297911695b55ee78c1188f8ef91',
            to: '0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98',
            value: '0xddb66b2addf4800',
            gas: '0x5622',
            gasPrice: '0xba43b7400',
            input: '0x',
          })
        }
      },
      eth_getStorageAt: '0x00000000000000000000000000000000000000000000000000000000deadbeef',
    }))
    // handle dummy block
    var blockProvider = injectMetrics(new TestBlockProvider())

    var engine = new ProviderEngine()
    engine.addProvider(cacheProvider)
    engine.addProvider(dataProvider)
    engine.addProvider(blockProvider)

    // run polling until first block
    engine.start()
    engine.once('block', () => {//监听到第一个block生成时进入
      // stop polling
      engine.stop()//然后停止拉取block
      // clear subprovider metrics ,然后清空所有subprovider的payloadWitnessed和payloadHandled
      cacheProvider.clearMetrics()
      dataProvider.clearMetrics()
      blockProvider.clearMetrics()

      // determine which provider will handle the request,决定处理该payload的是哪一个subprovider
      const isBlockTest = (payloads[0].method === 'eth_getBlockByNumber')
      const handlingProvider = isBlockTest ? blockProvider : dataProvider

      // begin cache test
      cacheCheck(t, engine, cacheProvider, handlingProvider, payloads, function(err, response) {
        t.end()
      })
    })

    function cacheCheck(t, engine, cacheProvider, handlingProvider, payloads, cb) {
      var method = payloads[0].method
      requestTwice(payloads, function(err, response){
        // first request
        t.ifError(err || response.error && response.error.message, 'did not error')
        t.ok(response, 'has response')

        t.equal(cacheProvider.getWitnessed(method).length, 1, 'cacheProvider did see "'+method+'"')
        t.equal(cacheProvider.getHandled(method).length, 0, 'cacheProvider did NOT handle "'+method+'"')//第一次cache不能够handle这个方法

        t.equal(handlingProvider.getWitnessed(method).length, 1, 'handlingProvider did see "'+method+'"')
        t.equal(handlingProvider.getHandled(method).length, 1, 'handlingProvider did handle "'+method+'"')

      }, function(err, response){
        // second request
        t.ifError(err || response.error && response.error.message, 'did not error')
        t.ok(response, 'has response')

        if (shouldHitCacheOnSecondRequest) {//如果设置shouldHitCacheOnSecondRequest为true,则之前第一次的时候就会写入cache,这样第二次的时候就能够从cache处运行
          t.equal(cacheProvider.getWitnessed(method).length, 2, 'cacheProvider did see "'+method+'"')
          t.equal(cacheProvider.getHandled(method).length, 1, 'cacheProvider did handle "'+method+'"')

          t.equal(handlingProvider.getWitnessed(method).length, 1, 'handlingProvider did NOT see "'+method+'"')//这样就不会轮到handlingProvider了
          t.equal(handlingProvider.getHandled(method).length, 1, 'handlingProvider did NOT handle "'+method+'"')
        } else {
          t.equal(cacheProvider.getWitnessed(method).length, 2, 'cacheProvider did see "'+method+'"')
          t.equal(cacheProvider.getHandled(method).length, 0, 'cacheProvider did not handle "'+method+'"')

          t.equal(handlingProvider.getWitnessed(method).length, 2, 'handlingProvider did NOT see "'+method+'"')
          t.equal(handlingProvider.getHandled(method).length, 2, 'handlingProvider did NOT handle "'+method+'"')
        }

        cb()
      })
    }

    function requestTwice(payloads, afterFirst, afterSecond){//就是这个method请求第一次的时候,回调afterFirst函数,请求第二次的时候回调afterSecond
      engine.sendAsync(createPayload(payloads[0]), function(err, result){
        afterFirst(err, result)
        engine.sendAsync(createPayload(payloads[1]), afterSecond)
      })
    }

  })

}

 

 

4.

provider-engine/subproviders/filters.js

module.exports = FilterSubprovider

// handles the following RPC methods:
//   eth_newBlockFilter
//   eth_newPendingTransactionFilter
//   eth_newFilter
//   eth_getFilterChanges
//   eth_uninstallFilter
//   eth_getFilterLogs

 

5.

provider-engine/subproviders/nonce-tracker.js

// handles the following RPC methods:
//   eth_getTransactionCount (pending only)
// observes the following RPC methods:
//   eth_sendRawTransaction

 

provider-engine/test/nonce.js

const test = require('tape')
const Transaction = require('ethereumjs-tx')
const ethUtil = require('ethereumjs-util')
const ProviderEngine = require('../index.js')
const FixtureProvider = require('../subproviders/fixture.js')
const NonceTracker = require('../subproviders/nonce-tracker.js')
const HookedWalletProvider = require('../subproviders/hooked-wallet.js')
const TestBlockProvider = require('./util/block.js')
const createPayload = require('../util/create-payload.js')
const injectMetrics = require('./util/inject-metrics')


test('basic nonce tracking', function(t){
  t.plan(11)

  var privateKey = new Buffer('cccd8f4d88de61f92f3747e4a9604a0395e6ad5138add4bec4a2ddf231ee24f9', 'hex')
  var address = new Buffer('1234362ef32bcd26d3dd18ca749378213625ba0b', 'hex')
  var addressHex = '0x'+address.toString('hex')
  
  // sign all tx's
  var providerA = injectMetrics(new HookedWalletProvider({
    signTransaction: function(txParams, cb){
      var tx = new Transaction(txParams)
      tx.sign(privateKey)
      var rawTx = '0x'+tx.serialize().toString('hex')
      cb(null, rawTx)
    },
  }))

  // handle nonce requests
  var providerB = injectMetrics(new NonceTracker())
  // handle all bottom requests
  var providerC = injectMetrics(new FixtureProvider({
    eth_gasPrice: '0x1234',
    eth_getTransactionCount: '0x00',
    eth_sendRawTransaction: function(payload, next, done){
      var rawTx = ethUtil.toBuffer(payload.params[0])
      var tx = new Transaction(rawTx)
      var hash = '0x'+tx.hash().toString('hex')
      done(null, hash)
    },
  }))
  // handle block requests
  var providerD = injectMetrics(new TestBlockProvider())

  var engine = new ProviderEngine()
  engine.addProvider(providerA)
  engine.addProvider(providerB)
  engine.addProvider(providerC)
  engine.addProvider(providerD)

  var txPayload = {
    method: 'eth_sendTransaction',//需要调用eth_getTransactionCounteth_sendRawTransaction
    params: [{
      from: addressHex,
      to: addressHex,
      value: '0x01',
      gas: '0x1234567890',
    }]
  }

  engine.start()
  engine.sendAsync(createPayload(txPayload), function(err, response){
    t.ifError(err, 'did not error')
    t.ok(response, 'has response')

    // tx nonce
    t.equal(providerB.getWitnessed('eth_getTransactionCount').length, 1, 'providerB did see "eth_getTransactionCount"')
    t.equal(providerB.getHandled('eth_getTransactionCount').length, 0, 'providerB did NOT handle "eth_getTransactionCount"')//因为nonceProvider只handle类型为pending的
    t.equal(providerC.getWitnessed('eth_getTransactionCount').length, 1, 'providerC did see "eth_getTransactionCount"')
    t.equal(providerC.getHandled('eth_getTransactionCount').length, 1, 'providerC did handle "eth_getTransactionCount"')//所以一直next()到providerC才被执行
    // send raw tx
    t.equal(providerC.getWitnessed('eth_sendRawTransaction').length, 1, 'providerC did see "eth_sendRawTransaction"')
    t.equal(providerC.getHandled('eth_sendRawTransaction').length, 1, 'providerC did handle "eth_sendRawTransaction"')

    engine.sendAsync(createPayload({
      method: 'eth_getTransactionCount',
      params: [addressHex, 'pending'],
    }), function(err, response){
      t.ifError(err, 'did not error')
      t.ok(response, 'has response')

      // tx nonce did increment
      t.equal(response.result, '0x01', 'the provider gives the correct pending nonce')

      engine.stop()
      t.end()

    })

  })

})

 

 

6.

provider-engine/subproviders/subscriptions.js

相当于web3的subscribe

 

7.

provider-engine/test/wallet.js

const test = require('tape')
const Transaction = require('ethereumjs-tx')
const ethUtil = require('ethereumjs-util')
const ProviderEngine = require('../index.js')
const FixtureProvider = require('../subproviders/fixture.js')
const NonceTracker = require('../subproviders/nonce-tracker.js')
const HookedWalletProvider = require('../subproviders/hooked-wallet.js')
const HookedWalletTxProvider = require('../subproviders/hooked-wallet-ethtx.js')
const TestBlockProvider = require('./util/block.js')
const createPayload = require('../util/create-payload.js')
const injectMetrics = require('./util/inject-metrics')


test('tx sig', function(t){
  t.plan(12)

  var privateKey = new Buffer('cccd8f4d88de61f92f3747e4a9604a0395e6ad5138add4bec4a2ddf231ee24f9', 'hex')
  var address = new Buffer('1234362ef32bcd26d3dd18ca749378213625ba0b', 'hex')
  var addressHex = '0x'+address.toString('hex')

  // sign all tx's
  var providerA = injectMetrics(new HookedWalletProvider({
    getAccounts: function(cb){
      cb(null, [addressHex])
    },
    signTransaction: function(txParams, cb){
      var tx = new Transaction(txParams)
      tx.sign(privateKey)
      var rawTx = '0x'+tx.serialize().toString('hex')
      cb(null, rawTx)
    },
  }))

  // handle nonce requests
  var providerB = injectMetrics(new NonceTracker())
  // handle all bottom requests
  var providerC = injectMetrics(new FixtureProvider({
    eth_gasPrice: '0x1234',
    eth_getTransactionCount: '0x00',
    eth_sendRawTransaction: function(payload, next, done){
      var rawTx = ethUtil.toBuffer(payload.params[0])
      var tx = new Transaction(rawTx)
      var hash = '0x'+tx.hash().toString('hex')
      done(null, hash)
    },
  }))
  // handle block requests
  var providerD = injectMetrics(new TestBlockProvider())

  var engine = new ProviderEngine()
  engine.addProvider(providerA)
  engine.addProvider(providerB)
  engine.addProvider(providerC)
  engine.addProvider(providerD)

  var txPayload = {
    method: 'eth_sendTransaction',//会调用signTransaction/eth_getTransactionCount/eth_gasPrice/eth_sendRawTransaction
    params: [{
      from: addressHex,
      to: addressHex,
      value: '0x01',
      gas: '0x1234567890',
    }]
  }

  engine.start()
  engine.sendAsync(createPayload(txPayload), function(err, response){
    t.ifError(err, 'did not error')
    t.ok(response, 'has response')

    // intial tx request
    t.equal(providerA.getWitnessed('eth_sendTransaction').length, 1, 'providerA did see "signTransaction"')
    t.equal(providerA.getHandled('eth_sendTransaction').length, 1, 'providerA did handle "signTransaction"')

    // tx nonce
    t.equal(providerB.getWitnessed('eth_getTransactionCount').length, 1, 'providerB did see "eth_getTransactionCount"')
    t.equal(providerB.getHandled('eth_getTransactionCount').length, 0, 'providerB did NOT handle "eth_getTransactionCount"')//不在nonceProvider处handle是因为它只处理pending的
    t.equal(providerC.getWitnessed('eth_getTransactionCount').length, 1, 'providerC did see "eth_getTransactionCount"')
    t.equal(providerC.getHandled('eth_getTransactionCount').length, 1, 'providerC did handle "eth_getTransactionCount"')

    // gas price
    t.equal(providerC.getWitnessed('eth_gasPrice').length, 1, 'providerB did see "eth_gasPrice"')
    t.equal(providerC.getHandled('eth_gasPrice').length, 1, 'providerB did handle "eth_gasPrice"')

    // send raw tx
    t.equal(providerC.getWitnessed('eth_sendRawTransaction').length, 1, 'providerC did see "eth_sendRawTransaction"')
    t.equal(providerC.getHandled('eth_sendRawTransaction').length, 1, 'providerC did handle "eth_sendRawTransaction"')

    engine.stop()
    t.end()
  })

})

 

provider-engine/subproviders/hooked-wallet.js

// handles the following RPC methods:
//   eth_coinbase
//   eth_accounts
//   eth_sendTransaction
//   eth_sign
//   eth_signTypedData
//   personal_sign
//   personal_ecRecover
//   parity_postTransaction
//   parity_checkRequest
//   parity_defaultAccount

//
// Tx Signature Flow,交易签名需要做的事情
//
// handleRequest: eth_sendTransaction
//   validateTransaction (basic validity check)
//     validateSender (checks that sender is in accounts)
//   processTransaction (sign tx and submit to network)
//     approveTransaction (UI approval hook)
//     checkApproval
//     finalizeAndSubmitTx (tx signing)
//       nonceLock.take (bottle neck to ensure atomic nonce)
//         fillInTxExtras (set fallback gasPrice, nonce, etc)
//         signTransaction (perform the signature)
//         publishTransaction (publish signed tx to network)
//

 

posted @ 2018-11-15 15:27  慢行厚积  阅读(526)  评论(0编辑  收藏  举报