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"')//payloadsWitnessed和payloadsHandled都没有它 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.method:payload.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_getTransactionCount和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') // 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) //