diff options
-rw-r--r-- | .travis.yml | 9 | ||||
-rw-r--r-- | JSONSchema/filler-schema.json | 268 | ||||
-rw-r--r-- | JSONSchema/schema.json | 339 | ||||
-rwxr-xr-x | JSONSchema/validate.js | 73 |
4 files changed, 688 insertions, 1 deletions
diff --git a/.travis.yml b/.travis.yml index dbced1cd9..d9d7c58fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,15 @@ language: python -python: 2.7 +python: + - "2.7" +node_js: + - "8" sudo: false +install: "npm install jsonschema" script: # won't fail, but print problems - find . -name "*.json" -not -name "*Filler.json" -exec echo {} \; -exec python -mjson.tool {} /dev/null \; 2>&1 | grep -v -B 1 "^\./" | cat # will fail, if linting fails - find . -name "*.json" -not -name "*Filler.json" -print0 | xargs -I file -n1 -0 python -mjson.tool file /dev/null +# run schema tests against GeneralStateTests +- echo -e "$(find GeneralStateTests -name '*.json')" | node JSONSchema/validate.js JSONSchema/schema.json +- echo -e "$(find src/GeneralStateTestsFiller -name '*.json')" | node JSONSchema/validate.js JSONSchema/filler-schema.json
\ No newline at end of file diff --git a/JSONSchema/filler-schema.json b/JSONSchema/filler-schema.json new file mode 100644 index 000000000..da9f2fea6 --- /dev/null +++ b/JSONSchema/filler-schema.json @@ -0,0 +1,268 @@ +{ + "definitions": { + "EmptyString": { + "type": "string", + "pattern": "^$" + }, + "HexData": { + "description": "Hex data. see https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding", + "pattern": "^0x([0-9a-fA-F][0-9a-fA-F])*$", + "type": "string" + }, + "HexQuantity": { + "description": "Hex quantity. see https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding", + "pattern": "(^0x0$)|(^0x[1-9a-fA-F][0-9a-fA-F]*$)", + "type": "string" + }, + "ConfusedHexType": { + "anyOf": [ + { "$ref": "#/definitions/HexData" }, + { "$ref": "#/definitions/HexQuantity" } + ] + }, + "HexDataOrEmpty": { + "oneOf": [ + { "$ref": "#/definitions/HexData" }, + { "$ref": "#/definitions/EmptyString" } + ] + }, + "HexNoPrefix" : { + "pattern": "^([0-9a-fA-F][0-9a-fA-F])+$", + "type": "string" + }, + "HexMaybePrefix": { + "oneOf": [ + { "$ref": "#/definitions/HexNoPrefix" }, + { "$ref": "#/definitions/HexData" } + ] + }, + "HexMaybePrefixOrEmpty": { + "oneOf": [ + { "$ref": "#/definitions/HexMaybePrefix" }, + { "$ref": "#/definitions/EmptyString" } + ] + }, + "IntegerString": { + "pattern": "^[0-9]+$", + "type": "string" + }, + "IntegerOrEmpty": { + "oneOf": [ + { "$ref": "#/definitions/IntegerString" }, + { "$ref": "#/definitions/EmptyString" } + ] + }, + "IntegerOrNumber": { + "oneOf": [ + { "$ref": "#/definitions/IntegerString" }, + { "type": "number" } + ] + }, + "IntegerOrNumberOrConfusedHex": { + "oneOf": [ + { "$ref": "#/definitions/IntegerOrNumber" }, + { "$ref": "#/definitions/ConfusedHexType" } + ] + }, + "IntegerOrEmptyOrHexQuantity": { + "oneOf": [ + { "$ref": "#/definitions/IntegerOrEmpty" }, + { "$ref": "#/definitions/HexQuantity" } + ] + }, + "IntegerOrHexQuantity": { + "oneOf": [ + { "$ref": "#/definitions/IntegerString" }, + { "$ref": "#/definitions/HexQuantity" } + ] + }, + "IntegerOrConfusedHex": { + "anyOf": [ + { "$ref": "#/definitions/IntegerString" }, + { "$ref": "#/definitions/ConfusedHexType" } + ] + }, + "IntegerOrEmptyOrConfusedHex": { + "oneOf": [ + { "$ref": "#/definitions/IntegerOrEmpty" }, + { "$ref": "#/definitions/ConfusedHexType" } + ] + }, + "PreStateAccount": { + "type": "object", + "additionalproperties": true, + "properties": { + "balance": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "code": { "type": "string" }, + "nonce": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "storage": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^0x[0-9a-f]+": { + "description": "storage key with 0x prefix, just the prefix `0x` is null and thus not permitted, a hex quantity is permitted. for the storage value, only hex data is permitted.", + "$ref": "#/definitions/HexData" + } + } + } + }, + "required": [ + "balance", + "code", + "nonce" + ] + }, + "TxSigR": { + "oneOf": [ + { "enum" : [ "0" ] } + ] + }, + "TxSigS": { + "oneOf": [ + { "enum" : [ "0" ] } + ] + }, + "TxSigV": { + "description": "a value of 0 is an invalid chainId, but used in a test case", + "oneOf": [ + { "enum" : [ "0", "1" ] } + ] + }, + "HexLen40": { + "pattern": "^0x[0-9a-fA-F]{40}$", + "type": "string" + }, + "HexNoPrefixLen40": { + "pattern": "^[0-9a-fA-F]{40}$", + "type": "string" + }, + "AddressMaybePrefixOrEmpty" : { + "oneOf": [ + { "$ref": "#/definitions/HexLen40" }, + { "$ref": "#/definitions/HexNoPrefixLen40" }, + { "$ref": "#/definitions/EmptyString" } + ] + }, + "Transaction": { + "type": "object", + "additionalProperties": true, + "not": { + "required": ["r, s, v"] + }, + "properties": { + "data": { + "items": { "$ref": "#/definitions/HexDataOrEmpty" }, + "type": "array" + }, + "gasLimit": { + "items": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "type": "array" + }, + "gasPrice": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "nonce": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "secretKey": { "$ref": "#/definitions/HexMaybePrefix" }, + "to": { "$ref": "#/definitions/AddressMaybePrefixOrEmpty"}, + "value": { + "items": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "type": "array" + } + }, + "required": [ + "data", + "gasLimit", + "gasPrice", + "nonce", + "secretKey", + "to", + "value" + ] + }, + "NullSenderTransaction": { + "type": "object", + "additionalProperties": true, + "not": { + "required": ["secretKey"] + }, + "properties": { + "data": { + "items": { "$ref": "#/definitions/HexMaybePrefixOrEmpty" }, + "type": "array" + }, + "gasLimit": { + "items": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "type": "array" + }, + "gasPrice": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "nonce": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "r": { "$ref": "#/definitions/TxSigR" }, + "s": { "$ref": "#/definitions/TxSigS" }, + "to": { "$ref": "#/definitions/AddressMaybePrefixOrEmpty"}, + "v": { "$ref": "#/definitions/TxSigV" }, + "value": { + "items": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "type": "array" + } + }, + "required": [ + "data", + "gasLimit", + "gasPrice", + "nonce", + "r", + "s", + "v", + "to", + "value" + ] + } + }, + "patternProperties": { + "^.*$": { + "properties": { + "env": { + "additionalproperties": false, + "properties": { + "currentCoinbase": { "$ref": "#/definitions/HexMaybePrefix" }, + "currentDifficulty": { "$ref": "#/definitions/IntegerOrConfusedHex" }, + "currentGasLimit": { "$ref": "#/definitions/IntegerOrConfusedHex" }, + "currentNumber": { "$ref": "#/definitions/IntegerOrConfusedHex" }, + "currentTimestamp": { "$ref": "#/definitions/IntegerOrNumberOrConfusedHex" }, + "previousHash": { "$ref": "#/definitions/HexMaybePrefix" } + }, + "required": [ + "currentCoinbase", + "currentDifficulty", + "currentGasLimit", + "currentNumber", + "currentTimestamp", + "previousHash" + ], + "type": "object" + }, + "expect": {}, + "pre": { + "additionalProperties": false, + "patternProperties": { + "^[0-9a-fA-F]{40}": { + "description": "filler prestate addresses without 0x prefix", + "$ref": "#/definitions/PreStateAccount" + }, + "^0x[0-9a-fA-F]{40}": { + "description": "filler prestate addresses with 0x prefix", + "$ref": "#/definitions/PreStateAccount" + } + }, + "type": "object" + }, + "transaction": { + "oneOf": [ + { "$ref": "#/definitions/Transaction" }, + { "$ref": "#/definitions/NullSenderTransaction" } + ] + } + }, + "type": "object" + } + }, + "type": "object" +} diff --git a/JSONSchema/schema.json b/JSONSchema/schema.json new file mode 100644 index 000000000..7b1536dae --- /dev/null +++ b/JSONSchema/schema.json @@ -0,0 +1,339 @@ +{ + "definitions": { + "EmptyString": { + "type": "string", + "pattern": "^$" + }, + "HexData": { + "description": "Hex data. see https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding", + "pattern": "^0x([0-9a-fA-F][0-9a-fA-F])*$", + "type": "string" + }, + "HexQuantity": { + "description": "Hex quantity. see https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding", + "pattern": "(^0x0$)|(^0x[1-9a-fA-F][0-9a-fA-F]*$)", + "type": "string" + }, + "ConfusedHexType": { + "anyOf": [ + { "$ref": "#/definitions/HexData" }, + { "$ref": "#/definitions/HexQuantity" } + ] + }, + "HexDataOrEmpty": { + "oneOf": [ + { "$ref": "#/definitions/HexData" }, + { "$ref": "#/definitions/EmptyString" } + ] + }, + "HexNoPrefix" : { + "pattern": "^([0-9a-fA-F][0-9a-fA-F])+$", + "type": "string" + }, + "HexMaybePrefix": { + "oneOf": [ + { "$ref": "#/definitions/HexNoPrefix" }, + { "$ref": "#/definitions/HexData" } + ] + }, + "HexMaybePrefixOrEmpty": { + "oneOf": [ + { "$ref": "#/definitions/HexMaybePrefix" }, + { "$ref": "#/definitions/EmptyString" } + ] + }, + "IntegerString": { + "pattern": "^[0-9]+$", + "type": "string" + }, + "IntegerOrEmpty": { + "oneOf": [ + { "$ref": "#/definitions/IntegerString" }, + { "$ref": "#/definitions/EmptyString" } + ] + }, + "IntegerOrNumber": { + "oneOf": [ + { "$ref": "#/definitions/IntegerString" }, + { "type": "number" } + ] + }, + "IntegerOrNumberOrConfusedHex": { + "oneOf": [ + { "$ref": "#/definitions/IntegerOrNumber" }, + { "$ref": "#/definitions/ConfusedHexType" } + ] + }, + "IntegerOrEmptyOrHexQuantity": { + "oneOf": [ + { "$ref": "#/definitions/IntegerOrEmpty" }, + { "$ref": "#/definitions/HexQuantity" } + ] + }, + "IntegerOrHexQuantity": { + "oneOf": [ + { "$ref": "#/definitions/IntegerString" }, + { "$ref": "#/definitions/HexQuantity" } + ] + }, + "IntegerOrConfusedHex": { + "anyOf": [ + { "$ref": "#/definitions/IntegerString" }, + { "$ref": "#/definitions/ConfusedHexType" } + ] + }, + "IntegerOrEmptyOrConfusedHex": { + "oneOf": [ + { "$ref": "#/definitions/IntegerOrEmpty" }, + { "$ref": "#/definitions/ConfusedHexType" } + ] + }, + "PreStateAccount": { + "type": "object", + "additionalproperties": true, + "properties": { + "balance": { "$ref": "#/definitions/ConfusedHexType" }, + "code": { "$ref": "#/definitions/HexMaybePrefixOrEmpty" }, + "nonce": { "$ref": "#/definitions/ConfusedHexType" }, + "storage": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^0x[0-9a-f]+": { + "description": "storage key with 0x. data is HexData", + "$ref": "#/definitions/HexData" + } + } + } + }, + "required": [ + "balance", + "code", + "nonce" + ] + }, + "TxSigR": { + "oneOf": [ + { "enum" : [ "0x00" ] } + ] + }, + "TxSigS": { + "oneOf": [ + { "enum" : [ "0x00" ] } + ] + }, + "TxSigV": { + "description": "a value of 0 is an invalid chainId, but used in a test case", + "oneOf": [ + { "enum" : [ "0x00", "0x01" ] } + ] + }, + "HexLen40": { + "pattern": "^0x[0-9a-fA-F]{40}$", + "type": "string" + }, + "HexNoPrefixLen40": { + "pattern": "^[0-9a-fA-F]{40}$", + "type": "string" + }, + "AddressMaybePrefixOrEmpty" : { + "oneOf": [ + { "$ref": "#/definitions/HexLen40" }, + { "$ref": "#/definitions/HexNoPrefixLen40" }, + { "$ref": "#/definitions/EmptyString" } + ] + }, + "Transaction": { + "type": "object", + "additionalProperties": true, + "not": { + "required": ["r, s, v"] + }, + "properties": { + "data": { + "items": { "$ref": "#/definitions/HexDataOrEmpty" }, + "type": "array" + }, + "gasLimit": { + "items": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "type": "array" + }, + "gasPrice": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "nonce": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "secretKey": { "$ref": "#/definitions/HexMaybePrefix" }, + "to": { "$ref": "#/definitions/AddressMaybePrefixOrEmpty"}, + "value": { + "items": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "type": "array" + } + }, + "required": [ + "data", + "gasLimit", + "gasPrice", + "nonce", + "secretKey", + "to", + "value" + ] + }, + "NullSenderTransaction": { + "type": "object", + "additionalProperties": true, + "not": { + "required": ["secretKey"] + }, + "properties": { + "data": { + "items": { "$ref": "#/definitions/HexMaybePrefixOrEmpty" }, + "type": "array" + }, + "gasLimit": { + "items": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "type": "array" + }, + "gasPrice": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "nonce": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "r": { "$ref": "#/definitions/TxSigR" }, + "s": { "$ref": "#/definitions/TxSigS" }, + "to": { "$ref": "#/definitions/AddressMaybePrefixOrEmpty"}, + "v": { "$ref": "#/definitions/TxSigV" }, + "value": { + "items": { "$ref": "#/definitions/IntegerOrEmptyOrConfusedHex" }, + "type": "array" + } + }, + "required": [ + "data", + "gasLimit", + "gasPrice", + "nonce", + "r", + "s", + "v", + "to", + "value" + ] + }, + "TransactionResults": { + "items": { + "additionalProperties": false, + "properties": { + "hash": { + "$ref": "#/definitions/HexData" + }, + "logs": {}, + "indexes": { + "additionalProperties": false, + "properties": { + "data": { + "type": "integer" + }, + "gas": { + "type": "integer" + }, + "value": { + "type": "integer" + } + }, + "required": [ + "data", + "gas", + "value" + ], + "type": "object" + } + }, + "required": [ + "hash", + "indexes" + ], + "type": "object" + }, + "type": "array" + } + }, + "patternProperties": { + "^.*$": { + "properties": { + "env": { + "additionalproperties": false, + "properties": { + "currentCoinbase": { + "$ref": "#/definitions/HexData" + }, + "currentDifficulty": { + "$ref": "#/definitions/ConfusedHexType" + }, + "currentGasLimit": { + "$ref": "#/definitions/ConfusedHexType" + }, + "currentNumber": { + "$ref": "#/definitions/ConfusedHexType" + }, + "currentTimestamp": { + "$ref": "#/definitions/ConfusedHexType" + }, + "previousHash": { + "$ref": "#/definitions/HexData" + } + }, + "required": [ + "currentCoinbase", + "currentDifficulty", + "currentGasLimit", + "currentNumber", + "currentTimestamp", + "previousHash" + ], + "type": "object" + }, + "explanation": { + "type": "string" + }, + "post": { + "additionalProperties": false, + "properties": { + "EIP150": { + "$ref": "#/definitions/TransactionResults" + }, + "EIP158": { + "$ref": "#/definitions/TransactionResults" + }, + "Frontier": { + "$ref": "#/definitions/TransactionResults" + }, + "Homestead": { + "$ref": "#/definitions/TransactionResults" + }, + "Byzantium": { + "$ref": "#/definitions/TransactionResults" + }, + "Constantinople": { + "$ref": "#/definitions/TransactionResults" + } + }, + "type": "object" + }, + "pre": { + "additionalProperties": false, + "patternProperties": { + "^0x[0-9a-f]*": { + "description": "prestate account address with 0x prefix", + "$ref": "#/definitions/PreStateAccount" + } + }, + "type": "object" + }, + "transaction": { + "oneOf": [ + { "$ref": "#/definitions/Transaction" }, + { "$ref": "#/definitions/NullSenderTransaction" } + ] + } + }, + "type": "object" + } + }, + "type": "object" +} diff --git a/JSONSchema/validate.js b/JSONSchema/validate.js new file mode 100755 index 000000000..1a1a605db --- /dev/null +++ b/JSONSchema/validate.js @@ -0,0 +1,73 @@ +#! /bin/env node + +var fs = require('fs'); +var validate = require('jsonschema').validate; +var readline = require('readline'); + +var schemaFile = process.argv[2]; +var schema = ''; +var testCode = ''; +var success = true; +var numFiles = 0; +var numFailed = 0; +var numSucceeded = 0; +var fileNames = []; + +var rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: false +}); + +rl.on('line', function(line) { + fileNames.push(line); +}); + +rl.on('close', function() { + schema = JSON.parse(fs.readFileSync(schemaFile)); + + //sort file names alphabetically so that log output ordering is consistent + fileNames.sort(function(a,b) { + if(a<b) { + return -1; + } else { + return 1; + } + + return 0; + }); + + for (var i = 0; i < fileNames.length; i++) { + try { + testCode = JSON.parse(fs.readFileSync(fileNames[i])); + } catch(e) { + console.log('error on file:', fileNames[i]) + console.log(e); + numFailed++; + } + + try { + var x = validate(testCode, schema); + + if (x.errors.length > 0) { + numFailed++; + console.log(fileNames[i]+ ':\n'); + for (var j = 0; j < x.errors.length; j++) { + console.log(' ' + x.errors[j] + '\n') + } + } else { + numSucceeded++; + } + } catch (e) { + console.log(e); + numFailed++; + } + } + + console.log("Valid: "+numSucceeded+"\n"); + console.log("Failed: "+numFailed+"\n"); + + if(numFailed > 0) { + process.exit(-1); + } +}); |