diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2015-03-10 18:24:30 +0800 |
---|---|---|
committer | Jeffrey Wilcke <jeffrey@ethereum.org> | 2015-03-10 18:24:30 +0800 |
commit | 08d396fdba1cbe7d04084db37b2089889a72086e (patch) | |
tree | b12587d04a73fe236817a76452f95c307d27efac | |
parent | 0d64163fea3a266ceb71cb4c4ee5682052c9ca6c (diff) | |
parent | 0743d68d4c4938549dde881428edd4141fa1a5f9 (diff) | |
download | go-tangerine-08d396fdba1cbe7d04084db37b2089889a72086e.tar go-tangerine-08d396fdba1cbe7d04084db37b2089889a72086e.tar.gz go-tangerine-08d396fdba1cbe7d04084db37b2089889a72086e.tar.bz2 go-tangerine-08d396fdba1cbe7d04084db37b2089889a72086e.tar.lz go-tangerine-08d396fdba1cbe7d04084db37b2089889a72086e.tar.xz go-tangerine-08d396fdba1cbe7d04084db37b2089889a72086e.tar.zst go-tangerine-08d396fdba1cbe7d04084db37b2089889a72086e.zip |
Merge pull request #437 from ethersphere/poc9/natspec
[WIP] Poc9/natspec
-rw-r--r-- | ethutil/natspec/natspec.go | 58 | ||||
-rw-r--r-- | ethutil/natspec/natspec_js.go (renamed from ethutil/natspec/natspec.js) | 91 | ||||
-rw-r--r-- | ethutil/natspec/natspec_test.go | 104 |
3 files changed, 150 insertions, 103 deletions
diff --git a/ethutil/natspec/natspec.go b/ethutil/natspec/natspec.go index 00e6f8720..793bf59ae 100644 --- a/ethutil/natspec/natspec.go +++ b/ethutil/natspec/natspec.go @@ -1,24 +1,21 @@ package natspec import ( + "fmt" "github.com/obscuren/otto" - "io/ioutil" ) type NatSpec struct { jsvm *otto.Otto } -func NewNATSpec(transaction string) (self *NatSpec, err error) { +// TODO: should initialise with abi and userdoc jsons +func New() (self *NatSpec, err error) { self = new(NatSpec) self.jsvm = otto.New() - code, err := ioutil.ReadFile("natspec.js") - if err != nil { - return - } - _, err = self.jsvm.Run(string(code)) + _, err = self.jsvm.Run(natspecJS) if err != nil { return } @@ -27,39 +24,40 @@ func NewNATSpec(transaction string) (self *NatSpec, err error) { return } - self.jsvm.Run("var transaction = " + transaction + ";") - - return -} - -func (self *NatSpec) SetDescription(desc string) (err error) { - - _, err = self.jsvm.Run("var expression = \"" + desc + "\";") return - } -func (self *NatSpec) SetABI(abi string) (err error) { - - _, err = self.jsvm.Run("var abi = " + abi + ";") - return - -} - -func (self *NatSpec) SetMethod(method string) (err error) { +func (self *NatSpec) Notice(transaction, abi, method, expression string) (string, error) { + var err error + if _, err = self.jsvm.Run("var transaction = " + transaction + ";"); err != nil { + return "", fmt.Errorf("natspec.js error setting transaction: %v", err) + } - _, err = self.jsvm.Run("var method = '" + method + "';") - return + if _, err = self.jsvm.Run("var abi = " + abi + ";"); err != nil { + return "", fmt.Errorf("natspec.js error setting abi: %v", err) + } -} + if _, err = self.jsvm.Run("var method = '" + method + "';"); err != nil { + return "", fmt.Errorf("natspec.js error setting method: %v", err) + } -func (self *NatSpec) Parse() string { + if _, err = self.jsvm.Run("var expression = \"" + expression + "\";"); err != nil { + return "", fmt.Errorf("natspec.js error setting expression: %v", err) + } self.jsvm.Run("var call = {method: method,abi: abi,transaction: transaction};") value, err := self.jsvm.Run("natspec.evaluateExpression(expression, call);") if err != nil { - return err.Error() + return "", fmt.Errorf("natspec.js error evaluating expression: %v", err) + } + evalError := "Natspec evaluation failed, wrong input params" + if value.String() == evalError { + return "", fmt.Errorf("natspec.js error evaluating expression: wrong input params in expression '%s'", expression) } - return value.String() + if len(value.String()) == 0 { + return "", fmt.Errorf("natspec.js error evaluating expression") + } + + return value.String(), nil } diff --git a/ethutil/natspec/natspec.js b/ethutil/natspec/natspec_js.go index 419ccd5c9..7acaaee4f 100644 --- a/ethutil/natspec/natspec.js +++ b/ethutil/natspec/natspec_js.go @@ -1,4 +1,6 @@ -require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +package natspec + +const natspecJS = `require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ },{}],2:[function(require,module,exports){ // shim for using process in browser @@ -291,26 +293,26 @@ if (process.env.NODE_ENV !== 'build') { var BigNumber = require('bignumber.js'); // jshint ignore:line } -var ETH_UNITS = [ - 'wei', - 'Kwei', - 'Mwei', - 'Gwei', - 'szabo', - 'finney', - 'ether', - 'grand', - 'Mether', - 'Gether', - 'Tether', - 'Pether', - 'Eether', - 'Zether', - 'Yether', - 'Nether', - 'Dether', - 'Vether', - 'Uether' +var ETH_UNITS = [ + 'wei', + 'Kwei', + 'Mwei', + 'Gwei', + 'szabo', + 'finney', + 'ether', + 'grand', + 'Mether', + 'Gether', + 'Tether', + 'Pether', + 'Eether', + 'Zether', + 'Yether', + 'Nether', + 'Dether', + 'Vether', + 'Uether' ]; module.exports = { @@ -375,7 +377,7 @@ var formatInputInt = function (value) { BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); value = value.round(); - if (value.lessThan(0)) + if (value.lessThan(0)) value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1); value = value.toString(16); } @@ -404,7 +406,7 @@ var formatInputBool = function (value) { /// Values are multiplied by 2^m and encoded as integers /// @returns byte representation of real var formatInputReal = function (value) { - return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); + return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); }; @@ -436,12 +438,12 @@ var formatOutputUInt = function (value) { /// @returns input bytes formatted to real var formatOutputReal = function (value) { - return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); + return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); }; /// @returns input bytes formatted to ureal var formatOutputUReal = function (value) { - return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); + return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); }; /// @returns right-aligned input bytes formatted to hex @@ -524,14 +526,14 @@ var namedType = function (name) { }; /// Setups input formatters for solidity types -/// @returns an array of input formatters +/// @returns an array of input formatters var inputTypes = function () { - + return [ { type: prefixedType('uint'), format: f.formatInputInt }, { type: prefixedType('int'), format: f.formatInputInt }, { type: prefixedType('hash'), format: f.formatInputInt }, - { type: prefixedType('string'), format: f.formatInputString }, + { type: prefixedType('string'), format: f.formatInputString }, { type: prefixedType('real'), format: f.formatInputReal }, { type: prefixedType('ureal'), format: f.formatInputReal }, { type: namedType('address'), format: f.formatInputInt }, @@ -620,7 +622,7 @@ var toAscii = function(hex) { return str; }; - + var toHex = function(str) { var hex = ""; for(var i = 0; i < str.length; i++) { @@ -642,7 +644,7 @@ var fromAscii = function(str, pad) { /// @returns display name for function/event eg. multiply(uint256) -> multiply var extractDisplayName = function (name) { - var length = name.indexOf('('); + var length = name.indexOf('('); return length !== -1 ? name.substr(0, length) : name; }; @@ -657,8 +659,8 @@ var extractTypeName = function (name) { /// @returns abi array with filtered objects of type 'function' var filterFunctions = function (json) { return json.filter(function (current) { - return current.type === 'function'; - }); + return current.type === 'function'; + }); }; /// Filters all events form input abi @@ -3404,7 +3406,7 @@ module.exports = { * @date 2015 */ -var abi = require('./node_modules/ethereum.js/lib/abi.js'); +var abi = require('./node_modules/ethereum.js/lib/abi.js'); /** * This object should be used to evaluate natspec expression @@ -3418,7 +3420,7 @@ var natspec = (function () { context[key] = obj[key]; }); } - + /// generate codes, which will be evaluated var generateCode = function (obj) { return Object.keys(obj).reduce(function (acc, key) { @@ -3440,20 +3442,20 @@ var natspec = (function () { /// @returns hashmap 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)); + 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 to evaluate expression var mapExpressionsToEvaluate = function (expression, cb) { var evaluatedExpression = ""; - // match everything in `` quotes - var pattern = /\`(?:\\.|[^`\\])*\`/gim + // match everything in backtick + var pattern = /\` + "`" + `(?:\\.|[^` + "`" + `\\])*\` + "`" + `/gim var match; var lastIndex = 0; while ((match = pattern.exec(expression)) !== null) { @@ -3464,9 +3466,9 @@ var natspec = (function () { evaluatedExpression += evaluatedPart; lastIndex = pattern.lastIndex; } - + evaluatedExpression += expression.slice(lastIndex); - + return evaluatedExpression; }; @@ -3478,11 +3480,11 @@ var natspec = (function () { 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); + var params = getMethodInputParams(method, call.transaction); copyToContext(params, context); } catch (err) { @@ -3498,7 +3500,7 @@ var natspec = (function () { return fn(context).toString(); } catch (err) { - return 'undefined'; + return 'undefined'; } }); @@ -3511,7 +3513,8 @@ var natspec = (function () { })(); -module.exports = natspec; +module.exports = natspec; },{"./node_modules/ethereum.js/lib/abi.js":3}]},{},[]); +` diff --git a/ethutil/natspec/natspec_test.go b/ethutil/natspec/natspec_test.go index 48a9cb25c..498f8d78e 100644 --- a/ethutil/natspec/natspec_test.go +++ b/ethutil/natspec/natspec_test.go @@ -6,41 +6,48 @@ import ( func TestNotice(t *testing.T) { - ns, err := NewNATSpec(` + tx := ` { - "jsonrpc": "2.0", - "method": "eth_call", - "params": [{ - "to": "0x8521742d3f456bd237e312d6e30724960f72517a", - "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" - }], - "id": 6 - } - `) + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" + }], + "id": 6 + } + ` + abi := ` + [{ + "name": "multiply", + "constant": false, + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint256" + }], + "outputs": [{ + "name": "d", + "type": "uint256" + }] + }] + ` + + desc := "Will multiply `a` by 7 and return `a * 7`." + + method := "multiply" + + ns, err := New() if err != nil { t.Errorf("NewNATSpec error %v", err) } - ns.SetABI(` - [{ - "name": "multiply", - "constant": false, - "type": "function", - "inputs": [{ - "name": "a", - "type": "uint256" - }], - "outputs": [{ - "name": "d", - "type": "uint256" - }] - }] - `) - ns.SetDescription("Will multiply `a` by 7 and return `a * 7`.") - ns.SetMethod("multiply") - - notice := ns.Parse() + notice, err := ns.Notice(tx, abi, method, desc) + + if err != nil { + t.Errorf("expected no error got %v", err) + } expected := "Will multiply 122 by 7 and return 854." if notice != expected { @@ -48,4 +55,43 @@ func TestNotice(t *testing.T) { } else { t.Logf("returned notice \"%v\"", notice) } + + notice, err = ns.Notice(tx, abi, method, "Will multiply 122 by \"7\" and return 854.") + + expected = "natspec.js error setting expression: (anonymous): Line 1:41 Unexpected number" + + if err == nil { + t.Errorf("expected error, got nothing (notice: '%v')", err, notice) + } else { + if err.Error() != expected { + t.Errorf("expected error '%s' got '%v' (notice: '%v')", expected, err, notice) + } + } + + // https://github.com/ethereum/natspec.js/issues/1 + // badDesc := "Will multiply `e` by 7 and return `a * 7`." + // notice, err = ns.Notice(tx, abi, method, badDesc) + + // expected = "natspec.js error evaluating expression: wrong input param in expression ''" + + // if err == nil { + // t.Errorf("expected error, got nothing (notice: '%v')", notice) + // } else { + // if err.Error() != expected { + // t.Errorf("expected error '%s' got '%v' (notice: '%v')", expected, err, notice) + // } + // } + + notice, err = ns.Notice(tx, abi, "missing_method", desc) + + expected = "natspec.js error evaluating expression: wrong input params in expression 'Will multiply `a` by 7 and return `a * 7`.'" + + if err == nil { + t.Errorf("expected error, got nothing (notice: '%v')", notice) + } else { + if err.Error() != expected { + t.Errorf("expected error '%s' got '%v' (notice: '%v')", expected, err, notice) + } + } + } |