Skip to content

Commit

Permalink
init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
kumavis committed Mar 1, 2017
0 parents commit 4b74fa3
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 0 deletions.
93 changes: 93 additions & 0 deletions .gitignore
@@ -0,0 +1,93 @@

# Created by https://www.gitignore.io/api/osx,node

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env


### OSX ###
*.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# End of https://www.gitignore.io/api/osx,node
72 changes: 72 additions & 0 deletions block-ref.js
@@ -0,0 +1,72 @@
module.exports = BlockRefRewriteMiddleware

function BlockRefRewriteMiddleware({ blockTracker }) {
if (!blockTracker) {
throw Error('BlockRefRewriteMiddleware - mandatory "blockTracker" option is missing.')
}

let requestQueue = []
let currentHandler = null

if (blockTracker.getCurrentBlock()) {
// block tracker is already ready
currentHandler = handleRequest
} else {
// buffer all requests for first block
currentHandler = addToQueue
// after first block
blockTracker.once('latest', () => {
// update handler
currentHandler = handleRequest
// process backlog
requestQueue.forEach((args) => handleRequest.apply(null, args))
requestQueue = null
})
}

return (req, res, next, end) => {
currentHandler(req, res, next, end)
}

// add requst to queue if blockRef is "latest"
function addToQueue(req, res, next, end) {
const blockRefIndex = blockRefParamIndex(req.method)
const blockRef = req.params[blockRefIndex]
if (blockRef === 'latest') {
requestQueue.push([req, res, next, end])
} else {
next()
}
}

// if blockRef is "latest", rewrite to latest block number
function handleRequest(req, res, next, end) {
const blockRefIndex = blockRefParamIndex(req.method)
const blockRef = req.params[blockRefIndex]
if (blockRef === 'latest') {
let block = blockTracker.getCurrentBlock()
req.params[blockRefIndex] = block.number
}
next()
}
}


function blockRefParamIndex(rpcMethod) {
switch (rpcMethod) {
// blockRef is second param
case 'eth_getBalance':
case 'eth_getCode':
case 'eth_getTransactionCount':
case 'eth_getStorageAt':
case 'eth_call':
case 'eth_estimateGas':
return 1
// blockRef is first param
case 'eth_getBlockByNumber':
return 0
// there is no blockRef
default:
return undefined
}
}
20 changes: 20 additions & 0 deletions package.json
@@ -0,0 +1,20 @@
{
"name": "eth-json-rpc-middleware",
"version": "1.0.0",
"main": "block-ref.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "node test/block-ref.js"
},
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"eth-block-tracker": "^1.0.3",
"eth-query": "^2.1.0",
"json-rpc-engine": "^2.1.0",
"tape": "^4.6.3"
}
}
18 changes: 18 additions & 0 deletions scaffold.js
@@ -0,0 +1,18 @@
module.exports = ScaffoldMiddleware

function ScaffoldMiddleware(handlers) {
return (req, res, next, end) => {
const handler = handlers[req.method]
// if no handler, return
if (handler === undefined) {
return next()
}
// if handler is fn, call as middleware
if (typeof handler === 'function') {
return handler(req, res, next, end)
}
// if handler is some other value, use as result
res.result = handler
end()
}
}
77 changes: 77 additions & 0 deletions test/block-ref.js
@@ -0,0 +1,77 @@
const test = require('tape')
const JsonRpcEngine = require('json-rpc-engine')
const asMiddleware = require('json-rpc-engine/lib/asMiddleware')
const RpcBlockTracker = require('eth-block-tracker')
const EthQuery = require('eth-query')
const TestBlockMiddleware = require('eth-block-tracker/test/util/testBlockMiddleware')
const BlockRefMiddleware = require('../block-ref')
const ScaffoldMiddleware = require('../scaffold')

test('contructor - no opts', (t) => {
t.plan(1)

t.throws(() => {
BlockRefMiddleware()
}, Error, 'Constructor without options fails')
t.end()
})

test('contructor - empty opts', (t) => {
t.plan(1)

t.throws(() => {
BlockRefMiddleware({})
}, Error, 'Constructor without empty options')
t.end()
})

test('provider not ready - shouldnt hang non-"latest" requests', (t) => {
t.plan(3)

const { engine, dataEngine, testBlockSource } = createTestSetup()

// add handler for `test_method`
dataEngine.push(ScaffoldMiddleware({
test_method: true,
}))

// fire request for `test_method`
engine.handle({ id: 1, method: 'test_method', params: [] }, (err, res) => {
t.notOk(err, 'No error in response')
t.ok(res, 'Has response')
t.equal(res.result, true, 'Response result is correct.')
t.end()
})

})

// util

function createTestSetup() {
// raw data source
const { engine: dataEngine, testBlockSource } = createEngineForTestData()
const dataProvider = providerFromEngine(dataEngine)
// create block tracker
const blockTracker = new RpcBlockTracker({ provider: dataProvider })
// create higher level
const engine = new JsonRpcEngine()
const provider = providerFromEngine(engine)
// add block ref middleware
engine.push(BlockRefMiddleware({ blockTracker }))
// add data source
engine.push(asMiddleware(dataEngine))
const query = new EthQuery(provider)
return { engine, provider, dataEngine, dataProvider, query, blockTracker, testBlockSource }
}

function createEngineForTestData() {
const engine = new JsonRpcEngine()
const testBlockSource = new TestBlockMiddleware()
engine.push(testBlockSource.createMiddleware())
return { engine, testBlockSource }
}

function providerFromEngine(engine) {
const provider = { sendAsync: engine.handle.bind(engine) }
return provider
}

0 comments on commit 4b74fa3

Please sign in to comment.