通过看其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() // 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, , 'providerA did see "test_rpc"') //首先是先去访问providerA,将test_rpc的访问添加进payloadsWitnessed
t.equal(providerA.getHandled('test_rpc').length, , 'providerA did NOT handle "test_rpc"') //因为它不能处理这个方法,所以payloadsHandled中没有它 t.equal(providerB.getWitnessed('test_rpc').length, , 'providerB did see "test_rpc"')//然后是去访问providerB,将test_rpc的访问添加进payloadsWitnessed
t.equal(providerB.getHandled('test_rpc').length, , 'providerB did handle "test_rpc"')//因为它能处理这个方法,所以payloadsHandled中有它 t.equal(providerC.getWitnessed('test_rpc').length, , 'providerC did NOT see "test_rpc"')//因为providerB上面已经成功处理这个方法了,不会再next(),所以providerC的
t.equal(providerC.getHandled('test_rpc').length, , '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 did not error
ok has response
ok providerA did see "test_rpc"
ok providerA did NOT handle "test_rpc"
ok providerB did see "test_rpc"
ok providerB did handle "test_rpc"
ok providerC did NOT see "test_rpc"
ok 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: , jsonrpc: '2.0', params: ['latest', false], method: 'eth_getBlockByNumber'}
const payload2 = {id: , 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: },
{jsonrpc: '2.0', method: 'eth_getCode', params: ['0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b', '0x1234'], id: },
{jsonrpc: '2.0', method: 'eth_getTransactionCount', params: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1','0x1234'], id: },
{jsonrpc: '2.0', method: 'eth_getStorageAt', params: ['0x295a70b2de5e3953354a6a8344e616ed314d7251', '0x0', '0x1234'], id: },
{jsonrpc: '2.0', method: 'eth_call', params: [{to: '0x295a70b2de5e3953354a6a8344e616ed314d7251'}, '0x1234'], id: },
{jsonrpc: '2.0', method: 'eth_estimateGas', params: [{to: '0x295a70b2de5e3953354a6a8344e616ed314d7251'}, '0x1234'], id: },
{jsonrpc: '2.0', method: 'eth_getBlockByNumber', params: ['0x1234', true], id: },
] 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();
} return payload.params.slice(,index);
} function blockTagParamIndex(payload){
switch(payload.method) {
// blockTag is third param
case 'eth_getStorageAt':
return
// blockTag is second param
case 'eth_getBalance':
case 'eth_getCode':
case 'eth_getTransactionCount':
case 'eth_call':
case 'eth_estimateGas':
return
// blockTag is first param
case 'eth_getBlockByNumber':
return
// 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 cacheIds are unique
# blockTagForPayload for different methods
ok block tag for eth_getBalance is correct
ok block tag for eth_getCode is correct
ok block tag for eth_getTransactionCount is correct
ok block tag for eth_getStorageAt is correct
ok block tag for eth_call is correct
ok block tag for eth_estimateGas is correct
ok 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() // 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[] === '0x00000000000000000000000000000000000000000000000000deadbeefcafe00') {
end(null, null)
} else if (payload.params[] === '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[],
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[].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[].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, , 'cacheProvider did see "'+method+'"')
t.equal(cacheProvider.getHandled(method).length, , 'cacheProvider did NOT handle "'+method+'"')//第一次cache不能够handle这个方法 t.equal(handlingProvider.getWitnessed(method).length, , 'handlingProvider did see "'+method+'"')
t.equal(handlingProvider.getHandled(method).length, , '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, , 'cacheProvider did see "'+method+'"')
t.equal(cacheProvider.getHandled(method).length, , 'cacheProvider did handle "'+method+'"') t.equal(handlingProvider.getWitnessed(method).length, , 'handlingProvider did NOT see "'+method+'"')//这样就不会轮到handlingProvider了
t.equal(handlingProvider.getHandled(method).length, , 'handlingProvider did NOT handle "'+method+'"')
} else {
t.equal(cacheProvider.getWitnessed(method).length, , 'cacheProvider did see "'+method+'"')
t.equal(cacheProvider.getHandled(method).length, , 'cacheProvider did not handle "'+method+'"') t.equal(handlingProvider.getWitnessed(method).length, , 'handlingProvider did NOT see "'+method+'"')
t.equal(handlingProvider.getHandled(method).length, , 'handlingProvider did NOT handle "'+method+'"')
} cb()
})
} function requestTwice(payloads, afterFirst, afterSecond){//就是这个method请求第一次的时候,回调afterFirst函数,请求第二次的时候回调afterSecond
engine.sendAsync(createPayload(payloads[]), function(err, result){
afterFirst(err, result)
engine.sendAsync(createPayload(payloads[]), 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() 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[])
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, , 'providerB did see "eth_getTransactionCount"')
t.equal(providerB.getHandled('eth_getTransactionCount').length, , 'providerB did NOT handle "eth_getTransactionCount"')//因为nonceProvider只handle类型为pending的
t.equal(providerC.getWitnessed('eth_getTransactionCount').length, , 'providerC did see "eth_getTransactionCount"')
t.equal(providerC.getHandled('eth_getTransactionCount').length, , 'providerC did handle "eth_getTransactionCount"')//所以一直next()到providerC才被执行
// send raw tx
t.equal(providerC.getWitnessed('eth_sendRawTransaction').length, , 'providerC did see "eth_sendRawTransaction"')
t.equal(providerC.getHandled('eth_sendRawTransaction').length, , '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() 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[])
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, , 'providerA did see "signTransaction"')
t.equal(providerA.getHandled('eth_sendTransaction').length, , 'providerA did handle "signTransaction"') // tx nonce
t.equal(providerB.getWitnessed('eth_getTransactionCount').length, , 'providerB did see "eth_getTransactionCount"')
t.equal(providerB.getHandled('eth_getTransactionCount').length, , 'providerB did NOT handle "eth_getTransactionCount"')//不在nonceProvider处handle是因为它只处理pending的
t.equal(providerC.getWitnessed('eth_getTransactionCount').length, , 'providerC did see "eth_getTransactionCount"')
t.equal(providerC.getHandled('eth_getTransactionCount').length, , 'providerC did handle "eth_getTransactionCount"') // gas price
t.equal(providerC.getWitnessed('eth_gasPrice').length, , 'providerB did see "eth_gasPrice"')
t.equal(providerC.getHandled('eth_gasPrice').length, , 'providerB did handle "eth_gasPrice"') // send raw tx
t.equal(providerC.getWitnessed('eth_sendRawTransaction').length, , 'providerC did see "eth_sendRawTransaction"')
t.equal(providerC.getHandled('eth_sendRawTransaction').length, , '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)
//
05-11 19:38