Marek Kotewicz
10 years ago
10 changed files with 4003 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||
# VIM stuff |
|||
*.swp |
|||
node_modules/ |
@ -0,0 +1,8 @@ |
|||
language: node_js |
|||
node_js: |
|||
- "0.11" |
|||
- "0.10" |
|||
before_script: |
|||
- npm install |
|||
after_script: |
|||
- npm run-script test-coveralls |
@ -0,0 +1,47 @@ |
|||
# natspec.js |
|||
Javascript Library used to evaluate natspec expressions |
|||
|
|||
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] |
|||
|
|||
[travis-image]: https://travis-ci.org/ethereum/natspec.js.svg |
|||
[travis-url]: https://travis-ci.org/ethereum/natspec.js |
|||
[coveralls-image]: https://coveralls.io/repos/ethereum/natspec.js/badge.svg?branch=master |
|||
[coveralls-url]: https://coveralls.io/r/ethereum/natspec.js?branch=master |
|||
|
|||
## Usage |
|||
|
|||
It exposes global object `natspec` with method `evaluateExpression`. |
|||
|
|||
```javascript |
|||
var natspec = require('natspec'); |
|||
|
|||
var natspecExpression = "Will multiply `a` by 7 and return `a * 7`."; |
|||
var call = { |
|||
method: 'multiply', |
|||
abi: abi, |
|||
transaction: transaction |
|||
}; |
|||
|
|||
var evaluatedExpression = natspec.evaluateExpression(natspecExpression, call); |
|||
console.log(evaluatedExpression); // "Will multiply 4 by 7 and return 28." |
|||
``` |
|||
|
|||
More examples are available [here](https://github.com/ethereum/natspec.js/blob/master/test/test.js). |
|||
|
|||
## Building |
|||
|
|||
```bash |
|||
npm run-script build |
|||
``` |
|||
|
|||
## Testing (mocha) |
|||
|
|||
```bash |
|||
npm test |
|||
``` |
|||
|
|||
## Wiki |
|||
|
|||
* [Ethereum Natural Specification Format](https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format) |
|||
* [Natspec Example](https://github.com/ethereum/wiki/wiki/Natspec-Example) |
|||
|
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,13 @@ |
|||
<!doctype> |
|||
<html> |
|||
|
|||
<head> |
|||
<script type="text/javascript" src="../dist/natspec.js"></script> |
|||
<script type="text/javascript"> |
|||
var natspec = require('natspec'); |
|||
</script> |
|||
</head> |
|||
<body> |
|||
</body> |
|||
</html> |
|||
|
@ -0,0 +1,186 @@ |
|||
/* |
|||
This file is part of natspec.js. |
|||
|
|||
natspec.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
natspec.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with natspec.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file natspec.js |
|||
* @authors: |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
var abi = require('./node_modules/ethereum.js/lib/abi.js'); |
|||
|
|||
/** |
|||
* This object should be used to evaluate natspec expression |
|||
* It has one method evaluateExpression which shoul be used |
|||
*/ |
|||
var natspec = (function () { |
|||
/** |
|||
* Helper method |
|||
* Should be called to copy values from object to global context |
|||
* |
|||
* @method copyToContext |
|||
* @param {Object} object from which we want to copy properties |
|||
* @param {Object} object to which we copy |
|||
*/ |
|||
var copyToContext = function (obj, context) { |
|||
Object.keys(obj).forEach(function (key) { |
|||
context[key] = obj[key]; |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Should be used to generate codes, which will be evaluated |
|||
* |
|||
* @method generateCode |
|||
* @param {Object} object from which code will be generated |
|||
* @return {String} javascript code which is used to initalized variables |
|||
*/ |
|||
var generateCode = function (obj) { |
|||
return Object.keys(obj).reduce(function (acc, key) { |
|||
return acc + "var " + key + " = context['" + key + "'];\n"; |
|||
}, ""); |
|||
}; |
|||
|
|||
/** |
|||
* Helper method |
|||
* Should be called to get method with given name from the abi |
|||
* |
|||
* @method getMethodWithName |
|||
* @param {Array} contract's abi |
|||
* @param {String} name of the method that we are looking for |
|||
* @return {Object} abi for method with name |
|||
*/ |
|||
var getMethodWithName = function(abi, name) { |
|||
return abi.filter(function (method) { |
|||
return method.name === name; |
|||
})[0]; |
|||
}; |
|||
|
|||
/** |
|||
* Should be used to get all contract method input variables |
|||
* |
|||
* @method getMethodInputParams |
|||
* @param {Object} abi for certain method |
|||
* @param {Object} transaction object |
|||
* @return {Object} object with all contract's method input variables |
|||
*/ |
|||
var getMethodInputParams = function (method, transaction) { |
|||
// do it with output formatter (cause we have to decode)
|
|||
var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10)); |
|||
|
|||
return method.inputs.reduce(function (acc, current, index) { |
|||
acc[current.name] = params[index]; |
|||
return acc; |
|||
}, {}); |
|||
}; |
|||
|
|||
/** |
|||
* Should be called when we want to evaluate natspec expression |
|||
* Replaces all natspec 'subexpressions' with evaluated value |
|||
* |
|||
* @method mapExpressionToEvaluate |
|||
* @param {String} expression to evaluate |
|||
* @param {Function} callback which is called to evaluate te expression |
|||
* @return {String} evaluated expression |
|||
*/ |
|||
var mapExpressionsToEvaluate = function (expression, cb) { |
|||
var evaluatedExpression = ""; |
|||
|
|||
// match everything in `` quotes
|
|||
var pattern = /\`(?:\\.|[^`\\])*\`/gim |
|||
var match; |
|||
var lastIndex = 0; |
|||
try { |
|||
while ((match = pattern.exec(expression)) !== null) { |
|||
var startIndex = pattern.lastIndex - match[0].length; |
|||
var toEval = match[0].slice(1, match[0].length - 1); |
|||
evaluatedExpression += expression.slice(lastIndex, startIndex); |
|||
var evaluatedPart = cb(toEval); |
|||
evaluatedExpression += evaluatedPart; |
|||
lastIndex = pattern.lastIndex; |
|||
} |
|||
|
|||
evaluatedExpression += expression.slice(lastIndex); |
|||
} |
|||
catch (err) { |
|||
throw new Error("Natspec evaluation failed, wrong input params"); |
|||
} |
|||
|
|||
return evaluatedExpression; |
|||
}; |
|||
|
|||
/** |
|||
* Should be called to evaluate single expression |
|||
* Is internally using javascript's 'eval' method |
|||
* |
|||
* @method evaluateExpression |
|||
* @param {String} expression which should be evaluated |
|||
* @param {Object} [call] object containing contract abi, transaction, called method |
|||
* @return {String} evaluated expression |
|||
* @throws exception if method is not found or we are trying to evaluate input params that does not exists |
|||
*/ |
|||
var evaluateExpression = function (expression, call) { |
|||
//var self = this;
|
|||
var context = {}; |
|||
|
|||
if (!!call) { |
|||
try { |
|||
var method = getMethodWithName(call.abi, call.method); |
|||
var params = getMethodInputParams(method, call.transaction); |
|||
copyToContext(params, context); |
|||
} |
|||
catch (err) { |
|||
throw new Error("Natspec evaluation failed, method does not exist"); |
|||
} |
|||
} |
|||
|
|||
var code = generateCode(context); |
|||
|
|||
var evaluatedExpression = mapExpressionsToEvaluate(expression, function (toEval) { |
|||
var fn = new Function("context", code + "return " + toEval + ";"); |
|||
return fn(context).toString(); |
|||
}); |
|||
|
|||
return evaluatedExpression; |
|||
}; |
|||
|
|||
/** |
|||
* Safe version of evaluateExpression |
|||
* Instead of throwing an exception it returns it as a string |
|||
* |
|||
* @method evaluateExpressionSafe |
|||
* @param {String} expression which should be evaluated |
|||
* @param {Object} [call] object containing contract abi, transaction, called method |
|||
* @return {String} evaluated expression |
|||
*/ |
|||
var evaluateExpressionSafe = function (expression, call) { |
|||
try { |
|||
return evaluateExpression(expression, call); |
|||
} |
|||
catch (err) { |
|||
return err.message; |
|||
} |
|||
}; |
|||
|
|||
return { |
|||
evaluateExpression: evaluateExpression, |
|||
evaluateExpressionSafe: evaluateExpressionSafe |
|||
}; |
|||
|
|||
})(); |
|||
|
|||
module.exports = natspec; |
|||
|
@ -0,0 +1,24 @@ |
|||
{ |
|||
"name": "natspec.js", |
|||
"version": "0.0.1", |
|||
"description": "Javascript Library used to evaluate natspec expressions", |
|||
"main": "natspec.js", |
|||
"scripts": { |
|||
"build": "cd dist && browserify -r ../natspec.js:natspec -i crypto -o natspec.js && uglifyjs natspec.js --source-map natspec.js.map -c -m -o natspec.min.js", |
|||
"test": "mocha", |
|||
"test-coveralls": "istanbul cover _mocha -- -R spec && cat coverage/lcov.info | coveralls --verbose" |
|||
}, |
|||
"author": "", |
|||
"dependencies": { |
|||
"ethereum.js": "ethereum/ethereum.js#master" |
|||
}, |
|||
"devDependencies": { |
|||
"browserify": "^9.0.3", |
|||
"chai": "^2.1.0", |
|||
"coveralls": "^2.11.2", |
|||
"istanbul": "^0.3.6", |
|||
"mocha": "^2.1.0", |
|||
"uglify-js": "^2.4.16" |
|||
}, |
|||
"license": "LGPL-3.0" |
|||
} |
@ -0,0 +1,149 @@ |
|||
var chai = require('chai'); |
|||
var natspec = require('../natspec.js'); |
|||
var assert = chai.assert; |
|||
|
|||
describe('natspec', function () { |
|||
it('should evaluate simple expression', function () { |
|||
// given
|
|||
var expression = "`x = 1` + `y = 2` will be equal `x + y`"; |
|||
|
|||
// when
|
|||
var result = natspec.evaluateExpression(expression); |
|||
var result2 = natspec.evaluateExpressionSafe(expression); |
|||
|
|||
// then
|
|||
assert.equal(result, "1 + 2 will be equal 3"); |
|||
assert.equal(result2, "1 + 2 will be equal 3"); |
|||
}); |
|||
|
|||
it('should evalute expression using input params', function () { |
|||
//given
|
|||
var expression = "Will multiply `a` by 7 and return `a * 7`."; |
|||
var method = 'multiply'; |
|||
var abi = [{ |
|||
"name": "multiply", |
|||
"constant": false, |
|||
"type": "function", |
|||
"inputs": [{ |
|||
"name": "a", |
|||
"type": "uint256" |
|||
}], |
|||
"outputs": [{ |
|||
"name": "d", |
|||
"type": "uint256" |
|||
}] |
|||
}]; |
|||
|
|||
var transaction = { |
|||
"jsonrpc": "2.0", |
|||
"method": "eth_call", |
|||
"params": [{ |
|||
"to": "0x8521742d3f456bd237e312d6e30724960f72517a", |
|||
"data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" |
|||
}], |
|||
"id": 6 |
|||
}; |
|||
|
|||
var call = { |
|||
method: method, |
|||
abi: abi, |
|||
transaction: transaction |
|||
}; |
|||
|
|||
// when
|
|||
var result = natspec.evaluateExpression(expression, call); |
|||
var result2 = natspec.evaluateExpressionSafe(expression, call); |
|||
|
|||
// then
|
|||
assert.equal(result, "Will multiply 122 by 7 and return 854."); |
|||
assert.equal(result2, "Will multiply 122 by 7 and return 854."); |
|||
}); |
|||
|
|||
it('should evalute expression using input params', function () { |
|||
//given
|
|||
var expression = "Will multiply `a` by 7 and return `a * 7`."; |
|||
var method = 'multiply'; |
|||
var abi = [{ |
|||
"name": "multiply", |
|||
"constant": false, |
|||
"type": "function", |
|||
"inputs": [{ |
|||
"name": "b", |
|||
"type": "uint256" |
|||
}], |
|||
"outputs": [{ |
|||
"name": "d", |
|||
"type": "uint256" |
|||
}] |
|||
}]; |
|||
|
|||
var transaction = { |
|||
"jsonrpc": "2.0", |
|||
"method": "eth_call", |
|||
"params": [{ |
|||
"to": "0x8521742d3f456bd237e312d6e30724960f72517a", |
|||
"data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" |
|||
}], |
|||
"id": 6 |
|||
}; |
|||
|
|||
var call = { |
|||
method: method, |
|||
abi: abi, |
|||
transaction: transaction |
|||
}; |
|||
|
|||
// when
|
|||
var exceptionThrow = function () { natspec.evaluateExpression(expression, call);} |
|||
var result = natspec.evaluateExpressionSafe(expression, call); |
|||
|
|||
// then
|
|||
assert.equal(result, "Natspec evaluation failed, wrong input params"); |
|||
assert.throws(exceptionThrow, "Natspec evaluation failed, wrong input params"); |
|||
}); |
|||
|
|||
it('should evalute expression using input params', function () { |
|||
//given
|
|||
var expression = "Will multiply `a` by 7 and return `a * 7`."; |
|||
var method = 'multiply2'; |
|||
var abi = [{ |
|||
"name": "multiply", |
|||
"constant": false, |
|||
"type": "function", |
|||
"inputs": [{ |
|||
"name": "a", |
|||
"type": "uint256" |
|||
}], |
|||
"outputs": [{ |
|||
"name": "d", |
|||
"type": "uint256" |
|||
}] |
|||
}]; |
|||
|
|||
var transaction = { |
|||
"jsonrpc": "2.0", |
|||
"method": "eth_call", |
|||
"params": [{ |
|||
"to": "0x8521742d3f456bd237e312d6e30724960f72517a", |
|||
"data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" |
|||
}], |
|||
"id": 6 |
|||
}; |
|||
|
|||
var call = { |
|||
method: method, |
|||
abi: abi, |
|||
transaction: transaction |
|||
}; |
|||
|
|||
// when
|
|||
var exceptionThrow = function () { natspec.evaluateExpression(expression, call);} |
|||
var result = natspec.evaluateExpressionSafe(expression, call); |
|||
|
|||
// then
|
|||
assert.equal(result, "Natspec evaluation failed, method does not exist"); |
|||
assert.throws(exceptionThrow, "Natspec evaluation failed, method does not exist"); |
|||
}); |
|||
}); |
|||
|
|||
|
Loading…
Reference in new issue