Skip to content

Commit

Permalink
building out subproviders
Browse files Browse the repository at this point in the history
  • Loading branch information
kumavis committed Dec 8, 2015
1 parent d3805d8 commit 351b541
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 55 deletions.
59 changes: 42 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
const RpcSource = require('./rpc-source.js')
const RpcSubprovider = require('./subproviders/rpc-source.js')
const VmSubprovider = require('./subproviders/vm.js')
const ProviderEngine = require('./engine.js')
const Web3 = require('web3')


var engine = new ProviderEngine()
var web3 = new Web3(engine)

var rpcSource = new RpcSource({
var rpcSubprovider = new RpcSubprovider({
rpcUrl: 'https://rpc.metamask.io/',
})
engine.addSource(rpcSource)
engine.addSource(rpcSubprovider)

var vmSubprovider = new VmSubprovider({
rootProvider: engine,
})
engine.addSource(vmSubprovider)



Expand All @@ -24,24 +30,43 @@ engine.addSource(rpcSource)
// ],
// })

// http://etherscan.io/address/0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae
vmSubprovider._createAccountStorageTrie('0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae', function(err, storage){

setInterval(function(){

storage.get('0x0000000000000000000000000000000000000000000000000000000000000000', function(err, result){
console.log('storage result:', result.toString('hex'))
})

}, 1000)

})

setInterval(function(){

web3.eth.getBalance('ba0bab3b85c6d190af0f9dbf21a1d8cdbed23830', function(err, result){
if (!err)
console.log('looked up baobab bal:', web3.fromWei(result, 'ether').toString())
else
console.error(err);
})
// vmSubprovider._fetchAccount('ba0bab3b85c6d190af0f9dbf21a1d8cdbed23830', function(err, account){
// console.log('fetched:', account)
// })

// web3.eth.getBalance('ba0bab3b85c6d190af0f9dbf21a1d8cdbed23830', function(err, result){
// if (!err)
// console.log('looked up baobab bal:', web3.fromWei(result, 'ether').toString())
// else
// console.error(err);
// })

web3.eth.getBlock('latest', function(err, result){
if (!err)
console.log('looked up block num:', result.number)
else
console.error(err);
})
// web3.eth.getBlock('latest', function(err, result){
// if (!err)
// console.log('looked up block num:', result.number)
// else
// console.error(err);
// })

}, 1000)

rpcSource.on('block', function(block){
console.log('block changed:', block.number.toString(), block.hash.toString('hex'))
rpcSubprovider.on('block', function(block){
console.log('================================')
console.log('BLOCK CHANGED:', '#'+block.number.toString(), '0x'+block.hash.toString('hex'))
console.log('================================')
})
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"es6-promise": "^3.0.2",
"ethereumjs-tx": "^0.6.11",
"ethereumjs-util": "^2.3.1",
"ethereumjs-vm": "^1.0.3",
"fake-merkle-patricia-tree": "^1.0.1",
"isomorphic-fetch": "^2.2.0",
"request": "^2.67.0",
"semaphore": "^1.0.3",
Expand Down
60 changes: 32 additions & 28 deletions remote-data.js → subproviders/remote-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const EventEmitter = require('events').EventEmitter
const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const Stoplight = require('./util/stoplight.js')
const Stoplight = require('../util/stoplight.js')

module.exports = RemoteDataSource

Expand Down Expand Up @@ -32,7 +32,7 @@ function RemoteDataSource(){
'eth_getUncleCountByBlockHash',
'eth_getUncleCountByBlockNumber',
'eth_getCode',
'eth_sendRawTransaction',
// 'eth_sendRawTransaction',
'eth_getBlockByHash',
'eth_getBlockByNumber',
'eth_getTransactionByHash',
Expand All @@ -53,34 +53,37 @@ function RemoteDataSource(){

RemoteDataSource.prototype.handleAsync = function(payload, cb){
const self = this

// parse blockTag
var blockTag = blockTagForPayload(payload)
if (blockTag === 'latest') blockTag = formatBlockNumber(self._blocks.latest)

// check cache
var blockCache = self._blockCache[blockTag]
var requestIdentifier = requestIdentifierForPayload(payload)
if (blockCache) {
var result = blockCache[requestIdentifier]
if (result !== undefined) {
console.log('CACHE HIT:', blockTag, requestIdentifier)
return cb(null, result)
}
}
self._ready.await(function(){

// perform request
console.log('CACHE MISS:', blockTag, requestIdentifier)
self._handleAsync(payload, function(err, result){
if (err) return cb(err)
// populate cache
// parse blockTag
var blockTag = blockTagForPayload(payload)
if (blockTag === 'latest') blockTag = formatBlockNumber(self._blocks.latest)

// check cache
var blockCache = self._blockCache[blockTag]
var requestIdentifier = requestIdentifierForPayload(payload)
if (blockCache) {
blockCache[requestIdentifier] = result
console.log('CACHE POPULATE:', blockTag, requestIdentifier)
} else {
console.log('CACHE POPULATE MISS:', blockTag, requestIdentifier)
var result = blockCache[requestIdentifier]
if (result !== undefined) {
console.log('CACHE HIT:', blockTag, requestIdentifier)
return cb(null, result)
}
}
cb(null, result)

// perform request
console.log('CACHE MISS:', blockTag, requestIdentifier)
self._handleAsync(payload, function(err, result){
if (err) return cb(err)
// populate cache
if (blockCache) {
blockCache[requestIdentifier] = result
console.log('CACHE POPULATE:', blockTag, requestIdentifier, '->', result)
} else {
console.log('CACHE POPULATE MISS:', blockTag, requestIdentifier)
}
cb(null, result)
})

})
}

Expand Down Expand Up @@ -142,7 +145,8 @@ RemoteDataSource.prototype.setCurrentBlock = function(block){
// util

function formatBlockNumber(block){
return ethUtil.addHexPrefix(block.number.toString('hex'))
// return ethUtil.addHexPrefix(block.number.toString())
return block.number.toString()
}

function requestIdentifierForPayload(payload){
Expand Down
16 changes: 6 additions & 10 deletions rpc-source.js → subproviders/rpc-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const RemoteDataSource = require('./remote-data.js')
const createPayload = require('../util/create-payload.js')


module.exports = RpcSource
Expand Down Expand Up @@ -70,12 +71,7 @@ RpcSource.prototype._handleAsync = function(payload, cb){
RpcSource.prototype.requestFromRpc = function(method, params, cb){
const self = this
var targetUrl = self.rpcUrl
var payload = {
id: getRandomId(),
jsonrpc: '2.0',
method: method,
params: params,
}
var payload = createPayload({ method: method, params: params })
// console.log('uri:', targetUrl)
// console.log('method:', method)
// console.log('params:', params)
Expand All @@ -100,17 +96,17 @@ RpcSource.prototype.requestFromRpc = function(method, params, cb){
return cb(err)
}

// console.log('------------------ network -----------------')
// console.log(payload, '->', data)
// console.log('---------------------------------------------')

cb(null, data.result)
})

}

// util

function getRandomId(){
return Math.floor(Math.random()*Number.MAX_SAFE_INTEGER)
}

function materializeTransaction(data){
var tx = new Transaction({
nonce: data.nonce,
Expand Down
149 changes: 149 additions & 0 deletions subproviders/vm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
const VM = require('ethereumjs-vm')
const async = require('async')
const inherits = require('util').inherits
const FakeMerklePatriciaTree = require('fake-merkle-patricia-tree')
const ethUtil = require('ethereumjs-util')
const createPayload = require('../util/create-payload.js')


module.exports = VmSubprovider


function VmSubprovider(opts){
const self = this
self.rootProvider = opts.rootProvider
self.currentBlock = 'latest'
var vm = self.vm = new VM()

// intercept state lookups
vm.stateManager._lookupStorageTrie = self._createAccountStorageTrie.bind(self)
vm.stateManager.cache._lookupAccount = self._fetchAccount.bind(self)
}

VmSubprovider.prototype._createAccountStorageTrie = function(address, cb){
const self = this
var block = self.currentBlock
var storageTrie = new LazyStorageTrie({
fetchStorage: fetchStorage,
})
cb(null, storageTrie)

function fetchStorage(key, cb){
self._fetchAccountStorage(address, key, block, cb)
}
}

VmSubprovider.prototype._emitPayload = function(payload, cb){
const self = this
// console.log('emit payload!', payload)
self.rootProvider.sendAsync(createPayload(payload), cb)
// self.rootProvider.sendAsync(createPayload(payload), function(){
// // console.log('payload return!', arguments)
// cb.apply(null, arguments)
// })

}

VmSubprovider.prototype._fetchAccount = function(address, cb){
const self = this
var block = self.currentBlock
console.log('fetch account:', address)
async.parallel({
nonce: self._fetchAccountNonce.bind(self, address, block),
balance: self._fetchAccountBalance.bind(self, address, block),
_code: self._fetchAccountCode.bind(self, address, block),
}, function(err, results){
if (err) return cb(err)

results._exists = results.nonce !== '0x0' || results.balance != '0x0' || results._code != '0x'
// console.log('fetch account results:', results)
var account = new LazyAccount(results)

cb(null, account)
})

}

VmSubprovider.prototype._fetchAccountStorage = function(address, key, block, cb){
const self = this
self._emitPayload({ method: 'eth_getStorageAt', params: [address, key, block] }, function(err, results){
if (err) return cb(err)
cb(null, results.result)
})
}

VmSubprovider.prototype._fetchAccountBalance = function(address, block, cb){
const self = this
self._emitPayload({ method: 'eth_getBalance', params: [address, block] }, function(err, results){
if (err) return cb(err)
cb(null, results.result)
})
}

VmSubprovider.prototype._fetchAccountNonce = function(address, block, cb){
const self = this
self._emitPayload({ method: 'eth_getTransactionCount', params: [address, block] }, function(err, results){
if (err) return cb(err)
cb(null, results.result)
})
}

VmSubprovider.prototype._fetchAccountCode = function(address, block, cb){
const self = this
self._emitPayload({ method: 'eth_getCode', params: [address, block] }, function(err, results){
if (err) return cb(err)
cb(null, results.result)
})
}

//

function LazyAccount(opts) {
const self = this
self._code = opts._code
self.exists = opts._exists
self.balance = opts.balance
self.stateRoot = '0x00'
}

LazyAccount.prototype.serialize = function(){
return new Buffer('')
}

LazyAccount.prototype.setCode = function(_, value, cb){
const self = this
self._code = value
cb()
}

LazyAccount.prototype.getCode = function(_, cb){
const self = this
cb(null, self._code)
}

//

inherits(LazyStorageTrie, FakeMerklePatriciaTree)

function LazyStorageTrie(opts) {
const self = this
FakeMerklePatriciaTree.call(self)
self._fetchStorage = opts.fetchStorage
}

LazyStorageTrie.prototype.get = function(key, cb){
const self = this
var _super = FakeMerklePatriciaTree.prototype.get.bind(self)

_super(key, function(err, value){
if (err) return cb(err)
if (value) return cb(null, value)
// if value not in tree, try network
var keyHex = key.toString('hex')
self._fetchStorage(keyHex, function(err, rawValue){
if (err) return cb(err)
var value = new Buffer(ethUtil.stripHexPrefix(rawValue), 'hex')
cb(null, value)
})
})
}
17 changes: 17 additions & 0 deletions util/create-payload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER

module.exports = createPayload


function createPayload(data){
return {
id: getRandomId(),
jsonrpc: '2.0',
method: data.method,
params: data.params || [],
}
}

function getRandomId(){
return Math.floor(Math.random()*MAX_SAFE_INTEGER)
}

0 comments on commit 351b541

Please sign in to comment.