Browse Source
git-subtree-dir: libnatspec/natspecjs git-subtree-split: 4f31ed9698be96c347ea769b8b7e77a9507fb615cl-refactor
Marek Kotewicz
10 years ago
commit
dc97c3d3ad
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