diff --git a/README.md b/README.md index ae03df4e..20c22495 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ TBD diff --git a/grammar/tezos/Micheline.ne b/grammar/tezos/Micheline.ne index a8194db1..c008af64 100644 --- a/grammar/tezos/Micheline.ne +++ b/grammar/tezos/Micheline.ne @@ -5,10 +5,22 @@ const moo = require("moo"); const bigInt = require("big-integer"); // taken from https://gitlab.com/nomadic-labs/tezos, lib_protocol/michelson_v1_primitives.ml, prim_encoding enum -const MichelineKeywords = ['"parameter"', '"storage"', '"code"', '"False"', '"Elt"', '"Left"', '"None"', '"Pair"', '"Right"', '"Some"', '"True"', '"Unit"', '"PACK"', '"UNPACK"', '"BLAKE2B"', '"SHA256"', '"SHA512"', '"ABS"', '"ADD"', '"AMOUNT"', '"AND"', '"BALANCE"', '"CAR"', '"CDR"', '"CHECK_SIGNATURE"', '"COMPARE"', '"CONCAT"', '"CONS"', '"CREATE_ACCOUNT"', '"CREATE_CONTRACT"', '"IMPLICIT_ACCOUNT"', '"DIP"', '"DROP"', '"DUP"', '"EDIV"', '"EMPTY_MAP"', '"EMPTY_SET"', '"EQ"', '"EXEC"', '"FAILWITH"', '"GE"', '"GET"', '"GT"', '"HASH_KEY"', '"IF"', '"IF_CONS"', '"IF_LEFT"', '"IF_NONE"', '"INT"', '"LAMBDA"', '"LE"', '"LEFT"', '"LOOP"', '"LSL"', '"LSR"', '"LT"', '"MAP"', '"MEM"', '"MUL"', '"NEG"', '"NEQ"', '"NIL"', '"NONE"', '"NOT"', '"NOW"', '"OR"', '"PAIR"', '"PUSH"', '"RIGHT"', '"SIZE"', '"SOME"', '"SOURCE"', '"SENDER"', '"SELF"', '"STEPS_TO_QUOTA"', '"SUB"', '"SWAP"', '"TRANSFER_TOKENS"', '"SET_DELEGATE"', '"UNIT"', '"UPDATE"', '"XOR"', '"ITER"', '"LOOP_LEFT"', '"ADDRESS"', '"CONTRACT"', '"ISNAT"', '"CAST"', '"RENAME"', '"bool"', '"contract"', '"int"', '"key"', '"key_hash"', '"lambda"', '"list"', '"map"', '"big_map"', '"nat"', '"option"', '"or"', '"pair"', '"set"', '"signature"', '"string"', '"bytes"', '"mutez"', '"timestamp"', '"unit"', '"operation"', '"address"', '"SLICE"', '"DIG"', '"DUG"', '"EMPTY_BIG_MAP"', '"APPLY"', '"chain_id"', '"CHAIN_ID"']; +export const DefaultMichelsonKeywords = ['"parameter"', '"storage"', '"code"', '"False"', '"Elt"', '"Left"', '"None"', '"Pair"', '"Right"', '"Some"', '"True"', '"Unit"', '"PACK"', '"UNPACK"', '"BLAKE2B"', '"SHA256"', '"SHA512"', '"ABS"', '"ADD"', '"AMOUNT"', '"AND"', '"BALANCE"', '"CAR"', '"CDR"', '"CHECK_SIGNATURE"', '"COMPARE"', '"CONCAT"', '"CONS"', '"CREATE_ACCOUNT"', '"CREATE_CONTRACT"', '"IMPLICIT_ACCOUNT"', '"DIP"', '"DROP"', '"DUP"', '"EDIV"', '"EMPTY_MAP"', '"EMPTY_SET"', '"EQ"', '"EXEC"', '"FAILWITH"', '"GE"', '"GET"', '"GT"', '"HASH_KEY"', '"IF"', '"IF_CONS"', '"IF_LEFT"', '"IF_NONE"', '"INT"', '"LAMBDA"', '"LE"', '"LEFT"', '"LOOP"', '"LSL"', '"LSR"', '"LT"', '"MAP"', '"MEM"', '"MUL"', '"NEG"', '"NEQ"', '"NIL"', '"NONE"', '"NOT"', '"NOW"', '"OR"', '"PAIR"', '"PUSH"', '"RIGHT"', '"SIZE"', '"SOME"', '"SOURCE"', '"SENDER"', '"SELF"', '"STEPS_TO_QUOTA"', '"SUB"', '"SWAP"', '"TRANSFER_TOKENS"', '"SET_DELEGATE"', '"UNIT"', '"UPDATE"', '"XOR"', '"ITER"', '"LOOP_LEFT"', '"ADDRESS"', '"CONTRACT"', '"ISNAT"', '"CAST"', '"RENAME"', '"bool"', '"contract"', '"int"', '"key"', '"key_hash"', '"lambda"', '"list"', '"map"', '"big_map"', '"nat"', '"option"', '"or"', '"pair"', '"set"', '"signature"', '"string"', '"bytes"', '"mutez"', '"timestamp"', '"unit"', '"operation"', '"address"', '"SLICE"', '"DIG"', '"DUG"', '"EMPTY_BIG_MAP"', '"APPLY"', '"chain_id"', '"CHAIN_ID"']; +let _languageKeywords = [...DefaultMichelsonKeywords]; + +export const setKeywordList = list => { + _languageKeywords = list; +} + +export const getCodeForKeyword = word => { + return _languageKeywords.indexOf(word); +} + +export const getKeywordForCode = code => { + return _languageKeywords[code]; +} const lexer = moo.compile({ - keyword: MichelineKeywords, lbrace: '{', rbrace: '}', lbracket: '[', @@ -31,10 +43,10 @@ staticString -> %lbrace %_ "\"string\"" %_:* %colon %_ %quotedValue %_ %rbrace { staticBytes -> %lbrace %_ "\"bytes\"" %_:* %colon %_ %quotedValue %_ %rbrace {% staticBytesToHex %} staticObject -> staticInt {% id %} | staticString {% id %} | staticBytes {% id %} -primBare -> %lbrace %_ "\"prim\"" %_:* %colon %_ %keyword %_ %rbrace {% primBareToHex %} -primArg -> %lbrace %_ "\"prim\"" %_:? %colon %_ %keyword %comma %_ "\"args\"" %_:? %colon %_ %lbracket %_ (any %comma:? %_:?):+ %_ %rbracket %_ %rbrace {% primArgToHex %} -primAnn -> %lbrace %_ "\"prim\"" %_:? %colon %_ %keyword %comma %_ "\"annots\"" %_:? %colon %_ %lbracket %_ (%quotedValue %comma:? %_:?):+ %_ %rbracket %_ %rbrace {% primAnnToHex %} -primArgAnn -> %lbrace %_ "\"prim\"" %_:? %colon %_ %keyword %comma %_ "\"args\"" %_:? %colon %_ %lbracket %_ (any %comma:? %_:?):+ %_ %rbracket %comma %_ "\"annots\"" %_:? %colon %_ %lbracket %_ (%quotedValue %comma:? %_:?):+ %_ %rbracket %_ %rbrace {% primArgAnnToHex %} +primBare -> %lbrace %_ "\"prim\"" %_:* %colon %_ %quotedValue %_ %rbrace {% primBareToHex %} +primArg -> %lbrace %_ "\"prim\"" %_:? %colon %_ %quotedValue %comma %_ "\"args\"" %_:? %colon %_ %lbracket %_ (any %comma:? %_:?):+ %_ %rbracket %_ %rbrace {% primArgToHex %} +primAnn -> %lbrace %_ "\"prim\"" %_:? %colon %_ %quotedValue %comma %_ "\"annots\"" %_:? %colon %_ %lbracket %_ (%quotedValue %comma:? %_:?):+ %_ %rbracket %_ %rbrace {% primAnnToHex %} +primArgAnn -> %lbrace %_ "\"prim\"" %_:? %colon %_ %quotedValue %comma %_ "\"args\"" %_:? %colon %_ %lbracket %_ (any %comma:? %_:?):+ %_ %rbracket %comma %_ "\"annots\"" %_:? %colon %_ %lbracket %_ (%quotedValue %comma:? %_:?):+ %_ %rbracket %_ %rbrace {% primArgAnnToHex %} primAny -> primBare {% id %} | primArg {% id %} | primAnn {% id %} | primArgAnn {% id %} any -> primAny {% id %} | staticObject {% id %} | anyArray {% id %} @@ -177,7 +189,7 @@ const primArgAnnToHex = d => { } const encodePrimitive = p => { - return ('00' + MichelineKeywords.indexOf(p).toString(16)).slice(-2); + return ('00' + getCodeForKeyword(p).toString(16)).slice(-2); } const encodeLength = l => { diff --git a/integration_test/chain/tezos/lexer/MichelsonParser.spec.ts b/integration_test/chain/tezos/lexer/MichelsonParser.spec.ts index 34296ee1..a8811e6e 100644 --- a/integration_test/chain/tezos/lexer/MichelsonParser.spec.ts +++ b/integration_test/chain/tezos/lexer/MichelsonParser.spec.ts @@ -3,7 +3,6 @@ import { expect } from 'chai'; import * as Michelson from '../../../../src/chain/tezos/lexer/Michelson'; import * as nearley from 'nearley'; -import * as request from 'request-promise'; function michelsonToMicheline(code: string): string { const processedCode = code.trim().split('\n').map(l => l.replace(/\#[\s\S]+$/, '').trim()).join(' '); diff --git a/src/chain/tezos/TezosLanguageUtil.ts b/src/chain/tezos/TezosLanguageUtil.ts index 44176447..e584018c 100644 --- a/src/chain/tezos/TezosLanguageUtil.ts +++ b/src/chain/tezos/TezosLanguageUtil.ts @@ -4,9 +4,6 @@ import * as nearley from 'nearley'; import { TezosMessageUtils } from './TezosMessageUtil'; -// TODO: share this with the parser somehow -const MichelineKeywords = ['"parameter"', '"storage"', '"code"', '"False"', '"Elt"', '"Left"', '"None"', '"Pair"', '"Right"', '"Some"', '"True"', '"Unit"', '"PACK"', '"UNPACK"', '"BLAKE2B"', '"SHA256"', '"SHA512"', '"ABS"', '"ADD"', '"AMOUNT"', '"AND"', '"BALANCE"', '"CAR"', '"CDR"', '"CHECK_SIGNATURE"', '"COMPARE"', '"CONCAT"', '"CONS"', '"CREATE_ACCOUNT"', '"CREATE_CONTRACT"', '"IMPLICIT_ACCOUNT"', '"DIP"', '"DROP"', '"DUP"', '"EDIV"', '"EMPTY_MAP"', '"EMPTY_SET"', '"EQ"', '"EXEC"', '"FAILWITH"', '"GE"', '"GET"', '"GT"', '"HASH_KEY"', '"IF"', '"IF_CONS"', '"IF_LEFT"', '"IF_NONE"', '"INT"', '"LAMBDA"', '"LE"', '"LEFT"', '"LOOP"', '"LSL"', '"LSR"', '"LT"', '"MAP"', '"MEM"', '"MUL"', '"NEG"', '"NEQ"', '"NIL"', '"NONE"', '"NOT"', '"NOW"', '"OR"', '"PAIR"', '"PUSH"', '"RIGHT"', '"SIZE"', '"SOME"', '"SOURCE"', '"SENDER"', '"SELF"', '"STEPS_TO_QUOTA"', '"SUB"', '"SWAP"', '"TRANSFER_TOKENS"', '"SET_DELEGATE"', '"UNIT"', '"UPDATE"', '"XOR"', '"ITER"', '"LOOP_LEFT"', '"ADDRESS"', '"CONTRACT"', '"ISNAT"', '"CAST"', '"RENAME"', '"bool"', '"contract"', '"int"', '"key"', '"key_hash"', '"lambda"', '"list"', '"map"', '"big_map"', '"nat"', '"option"', '"or"', '"pair"', '"set"', '"signature"', '"string"', '"bytes"', '"mutez"', '"timestamp"', '"unit"', '"operation"', '"address"', '"SLICE"', '"DEFAULT_ACCOUNT"', '"tez"']; - /** * A collection of functions to encode and decode Michelson and Micheline code */ @@ -23,130 +20,135 @@ export namespace TezosLanguageUtil { let fieldType = hex.substring(offset, offset + 2); offset += 2; - switch (fieldType) { - case '00': { // literal int or nat - const value = TezosMessageUtils.findInt(hex.substring(offset), 0, true); - code += `{ "int": "${value.value}" }`; - offset += value.length; - break; - } - case '01': { // literal string - const stringEnvelope = michelineHexToString(hex.substring(offset)); - code += `{ "string": "${stringEnvelope.code}" }`; - offset += stringEnvelope.consumed; - break; - } - case '02': { // array - const length = parseInt(hex.substring(offset, offset + 8), 16); - offset += 8; - let buffer: string[] = []; - let consumed = 0; - while (consumed < length) { - let envelope = hexToMicheline(hex.substring(offset)); - buffer.push(envelope.code); - consumed += envelope.consumed / 2; // plain bytes - offset += envelope.consumed; // hex-encoded two-char bytes + try { + switch (fieldType) { + case '00': { // literal int or nat + const value = TezosMessageUtils.findInt(hex.substring(offset), 0, true); + code += `{ "int": "${value.value}" }`; + offset += value.length; + break; } - if (length === 0) { - code += '[]'; - } else { - code += `[ ${buffer.join(', ')} ]`; + case '01': { // literal string + const stringEnvelope = michelineHexToString(hex.substring(offset)); + code += `{ "string": "${stringEnvelope.code}" }`; + offset += stringEnvelope.consumed; + break; } - break; - } - case '03': { // bare primitive - code += `{ "prim": ${hexToMichelineKeyword(hex, offset)} }`; - offset += 2; - break; - } - case '04': { // primitive with a set of annotations - code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; - offset += 2; - - const annEnvelope = michelineHexToAnnotations(hex.substring(offset)); - code += `"annots": [ ${annEnvelope.code} ] }`; - offset += annEnvelope.consumed; - break; - } - case '05': { // primitive with an argument - code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; - offset += 2; - const envelope = hexToMicheline(hex.substring(offset)); - code += `"args": [ ${envelope.code} ] }`; - offset += envelope.consumed; - break; - } - case '06': { // primitive with an argument an a set of annotations - code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; - offset += 2; - - const args = hexToMicheline(hex.substring(offset)); - code += `"args": [ ${args.code} ], `; - offset += args.consumed; - - const anns = michelineHexToAnnotations(hex.substring(offset)); - code += `"annots": [ ${anns.code} ] }`; - offset += anns.consumed; - break; - } - case '07': { // primitive with two arguments - code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; - offset += 2; - - let buffer: string[] = []; - let envelope = hexToMicheline(hex.substring(offset)); - buffer.push(envelope.code); - offset += envelope.consumed; - envelope = hexToMicheline(hex.substring(offset)); - buffer.push(envelope.code); - offset += envelope.consumed; - - code += `"args": [ ${buffer.join(', ')} ] }`; - break; - } - case '08': { // primitive with two arguments and an annotation set - code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; - offset += 2; - - const arg0 = hexToMicheline(hex.substring(offset)); - offset += arg0.consumed; - const arg1 = hexToMicheline(hex.substring(offset)); - offset += arg1.consumed; - code += `"args": [ ${arg0.code}, ${arg1.code} ], `; - - const anns = michelineHexToAnnotations(hex.substring(offset)); - code += `"annots": [ ${anns.code} ] }`; - offset += anns.consumed; - break; - } - case '09': { // primitive with an argument array and an optional anotation set - code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; - offset += 2; - - let envelope = hexToMicheline('02' + hex.substring(offset)); // fake an array to re-use the parsing code - code += `"args": ${envelope.code}`; - offset += envelope.consumed - 2; // account for the inserted '02' above + case '02': { // array + const length = parseInt(hex.substring(offset, offset + 8), 16); + offset += 8; + let buffer: string[] = []; + let consumed = 0; + while (consumed < length) { + let envelope = hexToMicheline(hex.substring(offset)); + buffer.push(envelope.code); + consumed += envelope.consumed / 2; // plain bytes + offset += envelope.consumed; // hex-encoded two-char bytes + } + if (length === 0) { + code += '[]'; + } else { + code += `[ ${buffer.join(', ')} ]`; + } + break; + } + case '03': { // bare primitive + code += `{ "prim": ${hexToMichelineKeyword(hex, offset)} }`; + offset += 2; + break; + } + case '04': { // primitive with a set of annotations + code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; + offset += 2; - if (hex.substring(offset, offset + 8) !== '00000000') { const annEnvelope = michelineHexToAnnotations(hex.substring(offset)); - if (annEnvelope.code.length > 2) { // more than empty quotes - code += `, "annots": [ ${annEnvelope.code} ] }`; - } + code += `"annots": [ ${annEnvelope.code} ] }`; offset += annEnvelope.consumed; - } else { - code += ' }'; + break; + } + case '05': { // primitive with an argument + code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; + offset += 2; + const envelope = hexToMicheline(hex.substring(offset)); + code += `"args": [ ${envelope.code} ] }`; + offset += envelope.consumed; + break; + } + case '06': { // primitive with an argument an a set of annotations + code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; + offset += 2; + + const args = hexToMicheline(hex.substring(offset)); + code += `"args": [ ${args.code} ], `; + offset += args.consumed; + + const anns = michelineHexToAnnotations(hex.substring(offset)); + code += `"annots": [ ${anns.code} ] }`; + offset += anns.consumed; + break; + } + case '07': { // primitive with two arguments + code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; + offset += 2; + + let buffer: string[] = []; + let envelope = hexToMicheline(hex.substring(offset)); + buffer.push(envelope.code); + offset += envelope.consumed; + envelope = hexToMicheline(hex.substring(offset)); + buffer.push(envelope.code); + offset += envelope.consumed; + + code += `"args": [ ${buffer.join(', ')} ] }`; + break; + } + case '08': { // primitive with two arguments and an annotation set + code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; + offset += 2; + + const arg0 = hexToMicheline(hex.substring(offset)); + offset += arg0.consumed; + const arg1 = hexToMicheline(hex.substring(offset)); + offset += arg1.consumed; + code += `"args": [ ${arg0.code}, ${arg1.code} ], `; + + const anns = michelineHexToAnnotations(hex.substring(offset)); + code += `"annots": [ ${anns.code} ] }`; + offset += anns.consumed; + break; + } + case '09': { // primitive with an argument array and an optional anotation set + code += `{ "prim": ${hexToMichelineKeyword(hex, offset)}, `; + offset += 2; + + let envelope = hexToMicheline('02' + hex.substring(offset)); // fake an array to re-use the parsing code + code += `"args": ${envelope.code}`; + offset += envelope.consumed - 2; // account for the inserted '02' above + + if (hex.substring(offset, offset + 8) !== '00000000') { + const annEnvelope = michelineHexToAnnotations(hex.substring(offset)); + if (annEnvelope.code.length > 2) { // more than empty quotes + code += `, "annots": [ ${annEnvelope.code} ] }`; + } + offset += annEnvelope.consumed; + } else { + code += ' }'; + offset += 8; + } + break; + } + case '0a': { // raw bytes + const length = parseInt(hex.substring(offset, offset + 8), 16); offset += 8; + code += `{ "bytes": "${hex.substring(offset, offset + length * 2)}" }`; + offset += length * 2; + break; } - break; + default: { throw new Error(`Unknown Micheline field type '${fieldType}' at offset ${offset} of '${hex}'`); } } - case '0a': { // raw bytes - const length = parseInt(hex.substring(offset, offset + 8), 16); - offset += 8; - code += `{ "bytes": "${hex.substring(offset, offset + length * 2)}" }`; - offset += length * 2; - break; - } - default: { throw new Error(`Unknown Micheline field type '${fieldType}'`); } + } catch (err) { + const m = `Nested hex parsing error (${err.message}) after: ${hex}, ${offset}, ${code}`; + throw new Error(m); } return { code: code, consumed: offset }; @@ -158,133 +160,138 @@ export namespace TezosLanguageUtil { let offset = 0; let fieldType = hex.substring(offset, offset + 2); offset += 2; - - switch (fieldType) { - case '00': { // literal int or nat - const value = TezosMessageUtils.findInt(hex.substring(offset), 0, true); - code += ` ${value.value} `; - offset += value.length; - break; - } - case '01': { // literal string - const stringEnvelope = michelineHexToString(hex.substring(offset)); - code += ` "${stringEnvelope.code}" `; - offset += stringEnvelope.consumed; - break; - } - case '02': { // array - const length = parseInt(hex.substring(offset, offset + 8), 16); - offset += 8; - let buffer: string[] = []; - let consumed = 0; - while (consumed < length) { + + try { + switch (fieldType) { + case '00': { // literal int or nat + const value = TezosMessageUtils.findInt(hex.substring(offset), 0, true); + code += ` ${value.value} `; + offset += value.length; + break; + } + case '01': { // literal string + const stringEnvelope = michelineHexToString(hex.substring(offset)); + code += ` "${stringEnvelope.code}" `; + offset += stringEnvelope.consumed; + break; + } + case '02': { // array + const length = parseInt(hex.substring(offset, offset + 8), 16); + offset += 8; + let buffer: string[] = []; + let consumed = 0; + while (consumed < length) { + let envelope = hexToMichelson(hex.substring(offset)); + buffer.push(envelope.code); + consumed += envelope.consumed / 2; // plain bytes + offset += envelope.consumed; // hex-encoded two-char bytes + } + if (length === 0) { + code += '[]'; + } else { + code += `[ ${buffer.join(' ')} ]`; + } + break; + } + case '03': { // bare primitive + code += `( ${hexToMichelsonKeyword(hex, offset)} )`; + offset += 2; + break; + } + case '04': { // primitive with a set of annotations + code += `( ${hexToMichelsonKeyword(hex, offset)} `; + offset += 2; + + const annEnvelope = michelineHexToAnnotations(hex.substring(offset)); + code += ` ${annEnvelope.code} )`; + offset += annEnvelope.consumed; + break; + } + case '05': { // primitive with an argument + code += `( ${hexToMichelsonKeyword(hex, offset)} `; + offset += 2; + const envelope = hexToMichelson(hex.substring(offset)); + code += ` ${envelope.code} )`; + offset += envelope.consumed; + break; + } + case '06': { // primitive with an argument an a set of annotations + code += `( ${hexToMichelsonKeyword(hex, offset)} `; + offset += 2; + + const args = hexToMichelson(hex.substring(offset)); + code += ` ${args.code} `; + offset += args.consumed; + + const anns = michelineHexToAnnotations(hex.substring(offset)); + code += ` ${anns.code} )`; + offset += anns.consumed; + break; + } + case '07': { // primitive with two arguments + code += `( ${hexToMichelsonKeyword(hex, offset)} `; + offset += 2; + + let buffer: string[] = []; let envelope = hexToMichelson(hex.substring(offset)); buffer.push(envelope.code); - consumed += envelope.consumed / 2; // plain bytes - offset += envelope.consumed; // hex-encoded two-char bytes + offset += envelope.consumed; + envelope = hexToMichelson(hex.substring(offset)); + buffer.push(envelope.code); + offset += envelope.consumed; + + code += ` ${buffer.join(' ')} )`; + break; } - if (length === 0) { - code += '[]'; - } else { - code += `[ ${buffer.join(' ')} ]`; + case '08': { // primitive with two arguments and an annotation set + code += `( ${hexToMichelsonKeyword(hex, offset)} `; + offset += 2; + + const arg0 = hexToMichelson(hex.substring(offset)); + offset += arg0.consumed; + const arg1 = hexToMichelson(hex.substring(offset)); + offset += arg1.consumed; + code += ` ${arg0.code} ${arg1.code} `; + + const anns = michelineHexToAnnotations(hex.substring(offset)); + code += ` ${anns.code} )`; + offset += anns.consumed; + break; } - break; - } - case '03': { // bare primitive - code += `( ${hexToMichelsonKeyword(hex, offset)} )`; - offset += 2; - break; - } - case '04': { // primitive with a set of annotations - code += `( ${hexToMichelsonKeyword(hex, offset)} `; - offset += 2; - - const annEnvelope = michelineHexToAnnotations(hex.substring(offset)); - code += ` ${annEnvelope.code} )`; - offset += annEnvelope.consumed; - break; - } - case '05': { // primitive with an argument - code += `( ${hexToMichelsonKeyword(hex, offset)} `; - offset += 2; - const envelope = hexToMichelson(hex.substring(offset)); - code += ` ${envelope.code} )`; - offset += envelope.consumed; - break; - } - case '06': { // primitive with an argument an a set of annotations - code += `( ${hexToMichelsonKeyword(hex, offset)} `; - offset += 2; - - const args = hexToMichelson(hex.substring(offset)); - code += ` ${args.code} `; - offset += args.consumed; - - const anns = michelineHexToAnnotations(hex.substring(offset)); - code += ` ${anns.code} )`; - offset += anns.consumed; - break; - } - case '07': { // primitive with two arguments - code += `( ${hexToMichelsonKeyword(hex, offset)} `; - offset += 2; - - let buffer: string[] = []; - let envelope = hexToMichelson(hex.substring(offset)); - buffer.push(envelope.code); - offset += envelope.consumed; - envelope = hexToMichelson(hex.substring(offset)); - buffer.push(envelope.code); - offset += envelope.consumed; - - code += ` ${buffer.join(' ')} )`; - break; - } - case '08': { // primitive with two arguments and an annotation set - code += `( ${hexToMichelsonKeyword(hex, offset)} `; - offset += 2; - - const arg0 = hexToMichelson(hex.substring(offset)); - offset += arg0.consumed; - const arg1 = hexToMichelson(hex.substring(offset)); - offset += arg1.consumed; - code += ` ${arg0.code} ${arg1.code} `; - - const anns = michelineHexToAnnotations(hex.substring(offset)); - code += ` ${anns.code} )`; - offset += anns.consumed; - break; - } - case '09': { // primitive with an argument array and an optional anotation set - code += `( ${hexToMichelsonKeyword(hex, offset)} `; - offset += 2; - - let envelope = hexToMichelson('02' + hex.substring(offset)); // fake an array to re-use the parsing code - code += `"args": ${envelope.code}`; - offset += envelope.consumed - 2; // account for the inserted '02' above - - if (hex.substring(offset, offset + 8) !== '00000000') { - const annEnvelope = michelineHexToAnnotations(hex.substring(offset)); - if (annEnvelope.code.length > 2) { // more than empty quotes - code += ` ${annEnvelope.code} )`; + case '09': { // primitive with an argument array and an optional anotation set + code += `( ${hexToMichelsonKeyword(hex, offset)} `; + offset += 2; + + let envelope = hexToMichelson('02' + hex.substring(offset)); // fake an array to re-use the parsing code + code += `"args": ${envelope.code}`; + offset += envelope.consumed - 2; // account for the inserted '02' above + + if (hex.substring(offset, offset + 8) !== '00000000') { + const annEnvelope = michelineHexToAnnotations(hex.substring(offset)); + if (annEnvelope.code.length > 2) { // more than empty quotes + code += ` ${annEnvelope.code} )`; + } + offset += annEnvelope.consumed; + } else { + code += ' )'; + offset += 8; } - offset += annEnvelope.consumed; - } else { - code += ' )'; + break; + } + case '0a': { // raw bytes + const length = parseInt(hex.substring(offset, offset + 8), 16); offset += 8; + code += ` 0x${hex.substring(offset, offset + length * 2)} `; + offset += length * 2; + break; } - break; + default: { throw new Error(`Unknown Michelson field type '${fieldType}' at offset ${offset} of '${hex}'`); } } - case '0a': { // raw bytes - const length = parseInt(hex.substring(offset, offset + 8), 16); - offset += 8; - code += ` 0x${hex.substring(offset, offset + length * 2)} `; - offset += length * 2; - break; - } - default: { throw new Error(`Unknown Micheline field type '${fieldType}'`); } + } catch (err) { + const m = `Nested hex parsing error (${err.message}) after: ${hex}, ${offset}, ${code}`; + throw new Error(m); } - + return { code: code, consumed: offset }; } @@ -370,11 +377,11 @@ export namespace TezosLanguageUtil { * @returns {string} Michelson/Micheline keyword */ function hexToMichelineKeyword(hex: string, offset: number): string { - return MichelineKeywords[parseInt(hex.substring(offset, offset + 2), 16)]; + return Micheline.getKeywordForCode(parseInt(hex.substring(offset, offset + 2), 16)); } function hexToMichelsonKeyword(hex: string, offset: number): string { - return MichelineKeywords[parseInt(hex.substring(offset, offset + 2), 16)].slice(1, -1); + return hexToMichelineKeyword(hex, offset).slice(1, -1); } /** @@ -389,6 +396,17 @@ export namespace TezosLanguageUtil { return { code: stringEnvelope.code.split(' ').map(s => `"${s}"`).join(', '), consumed: stringEnvelope.consumed }; } + /** + * This function is undocumented, if you're using it, please know what you're doing. + */ + export function overrideKeywordList(list: string[]) { + Micheline.setKeywordList(list); + } + + export function restoreKeywordList() { + Micheline.setKeywordList(Micheline.DefaultMichelsonKeywords); + } + /** * Reformats the Michelson code into the order the parser will understand. Input is expected to contains parameter, storage and code sections. */ diff --git a/src/chain/tezos/TezosMessageUtil.ts b/src/chain/tezos/TezosMessageUtil.ts index 42a837bd..69873a3a 100644 --- a/src/chain/tezos/TezosMessageUtil.ts +++ b/src/chain/tezos/TezosMessageUtil.ts @@ -10,6 +10,8 @@ import { SignerCurve } from "../../types/ExternalInterfaces"; /** * A collection of functions to encode and decode various Tezos P2P message components like amounts, addresses, hashes, etc. + * + * Magic prefixes taken from: https://gitlab.com/tezos/tezos/blob/master/src/lib_crypto/base58.ml#L343 */ export namespace TezosMessageUtils { /** @@ -106,7 +108,7 @@ export namespace TezosMessageUtils { * @param {number} offset Offset within the message to start decoding from. */ export function findInt(hex: string, offset: number, signed: boolean = false) { - let buffer = ""; + let buffer = ''; let i = 0; while (offset + i * 2 < hex.length) { let start = offset + i * 2; @@ -115,7 +117,7 @@ export namespace TezosMessageUtils { buffer += part; i += 1; - if (parseInt(part, 16) < 127) { break; } + if (parseInt(part, 16) < 128) { break; } } return signed ? { value: readSignedInt(buffer), length: i * 2 } : { value: readInt(buffer), length: i * 2 }; @@ -262,7 +264,7 @@ export namespace TezosMessageUtils { return "00" + base58check.decode(publicKey).slice(4).toString("hex"); } else if (publicKey.startsWith("sppk")) { // secp256k1 return "01" + base58check.decode(publicKey).slice(4).toString("hex"); - } else if (publicKey.startsWith("p2pk")) { // p256 + } else if (publicKey.startsWith("p2pk")) { // secp256r1 (p256) return "02" + base58check.decode(publicKey).slice(4).toString("hex"); } else { throw new Error('Unrecognized key type'); @@ -270,18 +272,22 @@ export namespace TezosMessageUtils { } /** - * Reads a key without a prefix from binary and decodes it into a Base58-check representation. + * Deserialize a key without a prefix from binary and decodes it into a Base58-check representation. * * @param {Buffer | Uint8Array} b Bytes containing the key. - * @param hint One of 'edsk' (private key), 'edpk' (public key). + * @param hint */ export function readKeyWithHint(b: Buffer | Uint8Array, hint: string): string { const key = !(b instanceof Buffer) ? Buffer.from(b) : b; - if (hint === 'edsk') { + if (hint === 'edsk') { // ed25519 secret key return base58check.encode(Buffer.from('2bf64e07' + key.toString('hex'), 'hex')); - } else if (hint === 'edpk') { + } else if (hint === 'edpk') { // ed25519 public key return readPublicKey(`00${key.toString('hex')}`); + } else if (hint === 'sppk') {// secp256k1 public key + return readPublicKey(`01${key.toString('hex')}`); + } else if (hint === 'p2pk') {// secp256r1 public key + return readPublicKey(`02${key.toString('hex')}`); } else { throw new Error(`Unrecognized key hint, '${hint}'`); } @@ -294,10 +300,8 @@ export namespace TezosMessageUtils { * @param hint Key type, usually the curve it was generated from, eg: 'edsk'. */ export function writeKeyWithHint(key: string, hint: string): Buffer { - if (hint === 'edsk' || hint === 'edpk') { // ed25519 + if (hint === 'edsk' || hint === 'edpk' || hint === 'sppk' || hint === 'p2pk') { return base58check.decode(key).slice(4); - //} else if (hint === 'sppk') { // secp256k1 - //} else if (hint === 'p2pk') { // secp256r1 } else { throw new Error(`Unrecognized key hint, '${hint}'`); } @@ -434,7 +438,7 @@ export namespace TezosMessageUtils { if (format === TezosParameterFormat.Micheline) { return `05${TezosLanguageUtil.translateMichelineToHex(value as string)}`; } else if (format === TezosParameterFormat.Michelson) { - const micheline = TezosLanguageUtil.translateMichelsonToMicheline(value as string) + const micheline = TezosLanguageUtil.translateMichelsonToMicheline(value as string); return `05${TezosLanguageUtil.translateMichelineToHex(micheline)}`; } else { throw new Error(`Unsupported format, ${format}, provided`); diff --git a/src/chain/tezos/TezosNodeWriter.ts b/src/chain/tezos/TezosNodeWriter.ts index d28fafda..bd516d60 100644 --- a/src/chain/tezos/TezosNodeWriter.ts +++ b/src/chain/tezos/TezosNodeWriter.ts @@ -3,6 +3,7 @@ import * as blakejs from 'blakejs'; import { KeyStore, Signer } from '../../types/ExternalInterfaces'; import * as TezosTypes from '../../types/tezos/TezosChainTypes'; import { TezosConstants } from '../../types/tezos/TezosConstants'; +import { ServiceResponseError } from '../../types/ErrorTypes'; import * as TezosP2PMessageTypes from '../../types/tezos/TezosP2PMessageTypes'; import { TezosNodeReader } from './TezosNodeReader'; import { TezosMessageCodec } from './TezosMessageCodec'; @@ -741,7 +742,7 @@ export namespace TezosNodeWriter { if (errors.length > 0) { log.debug(`errors found in response:\n${response}`); - throw new Error(errors); // TODO: use TezosResponseError + throw new ServiceResponseError(200, '', '', '', response); } } } diff --git a/src/chain/tezos/lexer/Micheline.ts b/src/chain/tezos/lexer/Micheline.ts index 5ade39e9..be3fe0d6 100644 --- a/src/chain/tezos/lexer/Micheline.ts +++ b/src/chain/tezos/lexer/Micheline.ts @@ -8,7 +8,6 @@ declare var _: any; declare var colon: any; declare var quotedValue: any; declare var rbrace: any; -declare var keyword: any; declare var comma: any; declare var lbracket: any; declare var rbracket: any; @@ -17,10 +16,22 @@ const moo = require("moo"); const bigInt = require("big-integer"); // taken from https://gitlab.com/nomadic-labs/tezos, lib_protocol/michelson_v1_primitives.ml, prim_encoding enum -const MichelineKeywords = ['"parameter"', '"storage"', '"code"', '"False"', '"Elt"', '"Left"', '"None"', '"Pair"', '"Right"', '"Some"', '"True"', '"Unit"', '"PACK"', '"UNPACK"', '"BLAKE2B"', '"SHA256"', '"SHA512"', '"ABS"', '"ADD"', '"AMOUNT"', '"AND"', '"BALANCE"', '"CAR"', '"CDR"', '"CHECK_SIGNATURE"', '"COMPARE"', '"CONCAT"', '"CONS"', '"CREATE_ACCOUNT"', '"CREATE_CONTRACT"', '"IMPLICIT_ACCOUNT"', '"DIP"', '"DROP"', '"DUP"', '"EDIV"', '"EMPTY_MAP"', '"EMPTY_SET"', '"EQ"', '"EXEC"', '"FAILWITH"', '"GE"', '"GET"', '"GT"', '"HASH_KEY"', '"IF"', '"IF_CONS"', '"IF_LEFT"', '"IF_NONE"', '"INT"', '"LAMBDA"', '"LE"', '"LEFT"', '"LOOP"', '"LSL"', '"LSR"', '"LT"', '"MAP"', '"MEM"', '"MUL"', '"NEG"', '"NEQ"', '"NIL"', '"NONE"', '"NOT"', '"NOW"', '"OR"', '"PAIR"', '"PUSH"', '"RIGHT"', '"SIZE"', '"SOME"', '"SOURCE"', '"SENDER"', '"SELF"', '"STEPS_TO_QUOTA"', '"SUB"', '"SWAP"', '"TRANSFER_TOKENS"', '"SET_DELEGATE"', '"UNIT"', '"UPDATE"', '"XOR"', '"ITER"', '"LOOP_LEFT"', '"ADDRESS"', '"CONTRACT"', '"ISNAT"', '"CAST"', '"RENAME"', '"bool"', '"contract"', '"int"', '"key"', '"key_hash"', '"lambda"', '"list"', '"map"', '"big_map"', '"nat"', '"option"', '"or"', '"pair"', '"set"', '"signature"', '"string"', '"bytes"', '"mutez"', '"timestamp"', '"unit"', '"operation"', '"address"', '"SLICE"', '"DIG"', '"DUG"', '"EMPTY_BIG_MAP"', '"APPLY"', '"chain_id"', '"CHAIN_ID"']; +export const DefaultMichelsonKeywords = ['"parameter"', '"storage"', '"code"', '"False"', '"Elt"', '"Left"', '"None"', '"Pair"', '"Right"', '"Some"', '"True"', '"Unit"', '"PACK"', '"UNPACK"', '"BLAKE2B"', '"SHA256"', '"SHA512"', '"ABS"', '"ADD"', '"AMOUNT"', '"AND"', '"BALANCE"', '"CAR"', '"CDR"', '"CHECK_SIGNATURE"', '"COMPARE"', '"CONCAT"', '"CONS"', '"CREATE_ACCOUNT"', '"CREATE_CONTRACT"', '"IMPLICIT_ACCOUNT"', '"DIP"', '"DROP"', '"DUP"', '"EDIV"', '"EMPTY_MAP"', '"EMPTY_SET"', '"EQ"', '"EXEC"', '"FAILWITH"', '"GE"', '"GET"', '"GT"', '"HASH_KEY"', '"IF"', '"IF_CONS"', '"IF_LEFT"', '"IF_NONE"', '"INT"', '"LAMBDA"', '"LE"', '"LEFT"', '"LOOP"', '"LSL"', '"LSR"', '"LT"', '"MAP"', '"MEM"', '"MUL"', '"NEG"', '"NEQ"', '"NIL"', '"NONE"', '"NOT"', '"NOW"', '"OR"', '"PAIR"', '"PUSH"', '"RIGHT"', '"SIZE"', '"SOME"', '"SOURCE"', '"SENDER"', '"SELF"', '"STEPS_TO_QUOTA"', '"SUB"', '"SWAP"', '"TRANSFER_TOKENS"', '"SET_DELEGATE"', '"UNIT"', '"UPDATE"', '"XOR"', '"ITER"', '"LOOP_LEFT"', '"ADDRESS"', '"CONTRACT"', '"ISNAT"', '"CAST"', '"RENAME"', '"bool"', '"contract"', '"int"', '"key"', '"key_hash"', '"lambda"', '"list"', '"map"', '"big_map"', '"nat"', '"option"', '"or"', '"pair"', '"set"', '"signature"', '"string"', '"bytes"', '"mutez"', '"timestamp"', '"unit"', '"operation"', '"address"', '"SLICE"', '"DIG"', '"DUG"', '"EMPTY_BIG_MAP"', '"APPLY"', '"chain_id"', '"CHAIN_ID"']; +let _languageKeywords = [...DefaultMichelsonKeywords]; + +export const setKeywordList = list => { + _languageKeywords = list; +} + +export const getCodeForKeyword = word => { + return _languageKeywords.indexOf(word); +} + +export const getKeywordForCode = code => { + return _languageKeywords[code]; +} const lexer = moo.compile({ - keyword: MichelineKeywords, lbrace: '{', rbrace: '}', lbracket: '[', @@ -167,7 +178,7 @@ const primArgAnnToHex = d => { } const encodePrimitive = p => { - return ('00' + MichelineKeywords.indexOf(p).toString(16)).slice(-2); + return ('00' + getCodeForKeyword(p).toString(16)).slice(-2); } const encodeLength = l => { @@ -255,7 +266,7 @@ const grammar: Grammar = { {"name": "staticObject", "symbols": ["staticBytes"], "postprocess": id}, {"name": "primBare$ebnf$1", "symbols": []}, {"name": "primBare$ebnf$1", "symbols": ["primBare$ebnf$1", (lexer.has("_") ? {type: "_"} : _)], "postprocess": (d) => d[0].concat([d[1]])}, - {"name": "primBare", "symbols": [(lexer.has("lbrace") ? {type: "lbrace"} : lbrace), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"prim\""}, "primBare$ebnf$1", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("keyword") ? {type: "keyword"} : keyword), (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbrace") ? {type: "rbrace"} : rbrace)], "postprocess": primBareToHex}, + {"name": "primBare", "symbols": [(lexer.has("lbrace") ? {type: "lbrace"} : lbrace), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"prim\""}, "primBare$ebnf$1", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("quotedValue") ? {type: "quotedValue"} : quotedValue), (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbrace") ? {type: "rbrace"} : rbrace)], "postprocess": primBareToHex}, {"name": "primArg$ebnf$1", "symbols": [(lexer.has("_") ? {type: "_"} : _)], "postprocess": id}, {"name": "primArg$ebnf$1", "symbols": [], "postprocess": () => null}, {"name": "primArg$ebnf$2", "symbols": [(lexer.has("_") ? {type: "_"} : _)], "postprocess": id}, @@ -272,7 +283,7 @@ const grammar: Grammar = { {"name": "primArg$ebnf$3$subexpression$2$ebnf$2", "symbols": [], "postprocess": () => null}, {"name": "primArg$ebnf$3$subexpression$2", "symbols": ["any", "primArg$ebnf$3$subexpression$2$ebnf$1", "primArg$ebnf$3$subexpression$2$ebnf$2"]}, {"name": "primArg$ebnf$3", "symbols": ["primArg$ebnf$3", "primArg$ebnf$3$subexpression$2"], "postprocess": (d) => d[0].concat([d[1]])}, - {"name": "primArg", "symbols": [(lexer.has("lbrace") ? {type: "lbrace"} : lbrace), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"prim\""}, "primArg$ebnf$1", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("keyword") ? {type: "keyword"} : keyword), (lexer.has("comma") ? {type: "comma"} : comma), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"args\""}, "primArg$ebnf$2", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("lbracket") ? {type: "lbracket"} : lbracket), (lexer.has("_") ? {type: "_"} : _), "primArg$ebnf$3", (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbracket") ? {type: "rbracket"} : rbracket), (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbrace") ? {type: "rbrace"} : rbrace)], "postprocess": primArgToHex}, + {"name": "primArg", "symbols": [(lexer.has("lbrace") ? {type: "lbrace"} : lbrace), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"prim\""}, "primArg$ebnf$1", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("quotedValue") ? {type: "quotedValue"} : quotedValue), (lexer.has("comma") ? {type: "comma"} : comma), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"args\""}, "primArg$ebnf$2", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("lbracket") ? {type: "lbracket"} : lbracket), (lexer.has("_") ? {type: "_"} : _), "primArg$ebnf$3", (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbracket") ? {type: "rbracket"} : rbracket), (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbrace") ? {type: "rbrace"} : rbrace)], "postprocess": primArgToHex}, {"name": "primAnn$ebnf$1", "symbols": [(lexer.has("_") ? {type: "_"} : _)], "postprocess": id}, {"name": "primAnn$ebnf$1", "symbols": [], "postprocess": () => null}, {"name": "primAnn$ebnf$2", "symbols": [(lexer.has("_") ? {type: "_"} : _)], "postprocess": id}, @@ -289,7 +300,7 @@ const grammar: Grammar = { {"name": "primAnn$ebnf$3$subexpression$2$ebnf$2", "symbols": [], "postprocess": () => null}, {"name": "primAnn$ebnf$3$subexpression$2", "symbols": [(lexer.has("quotedValue") ? {type: "quotedValue"} : quotedValue), "primAnn$ebnf$3$subexpression$2$ebnf$1", "primAnn$ebnf$3$subexpression$2$ebnf$2"]}, {"name": "primAnn$ebnf$3", "symbols": ["primAnn$ebnf$3", "primAnn$ebnf$3$subexpression$2"], "postprocess": (d) => d[0].concat([d[1]])}, - {"name": "primAnn", "symbols": [(lexer.has("lbrace") ? {type: "lbrace"} : lbrace), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"prim\""}, "primAnn$ebnf$1", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("keyword") ? {type: "keyword"} : keyword), (lexer.has("comma") ? {type: "comma"} : comma), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"annots\""}, "primAnn$ebnf$2", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("lbracket") ? {type: "lbracket"} : lbracket), (lexer.has("_") ? {type: "_"} : _), "primAnn$ebnf$3", (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbracket") ? {type: "rbracket"} : rbracket), (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbrace") ? {type: "rbrace"} : rbrace)], "postprocess": primAnnToHex}, + {"name": "primAnn", "symbols": [(lexer.has("lbrace") ? {type: "lbrace"} : lbrace), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"prim\""}, "primAnn$ebnf$1", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("quotedValue") ? {type: "quotedValue"} : quotedValue), (lexer.has("comma") ? {type: "comma"} : comma), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"annots\""}, "primAnn$ebnf$2", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("lbracket") ? {type: "lbracket"} : lbracket), (lexer.has("_") ? {type: "_"} : _), "primAnn$ebnf$3", (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbracket") ? {type: "rbracket"} : rbracket), (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbrace") ? {type: "rbrace"} : rbrace)], "postprocess": primAnnToHex}, {"name": "primArgAnn$ebnf$1", "symbols": [(lexer.has("_") ? {type: "_"} : _)], "postprocess": id}, {"name": "primArgAnn$ebnf$1", "symbols": [], "postprocess": () => null}, {"name": "primArgAnn$ebnf$2", "symbols": [(lexer.has("_") ? {type: "_"} : _)], "postprocess": id}, @@ -320,7 +331,7 @@ const grammar: Grammar = { {"name": "primArgAnn$ebnf$5$subexpression$2$ebnf$2", "symbols": [], "postprocess": () => null}, {"name": "primArgAnn$ebnf$5$subexpression$2", "symbols": [(lexer.has("quotedValue") ? {type: "quotedValue"} : quotedValue), "primArgAnn$ebnf$5$subexpression$2$ebnf$1", "primArgAnn$ebnf$5$subexpression$2$ebnf$2"]}, {"name": "primArgAnn$ebnf$5", "symbols": ["primArgAnn$ebnf$5", "primArgAnn$ebnf$5$subexpression$2"], "postprocess": (d) => d[0].concat([d[1]])}, - {"name": "primArgAnn", "symbols": [(lexer.has("lbrace") ? {type: "lbrace"} : lbrace), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"prim\""}, "primArgAnn$ebnf$1", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("keyword") ? {type: "keyword"} : keyword), (lexer.has("comma") ? {type: "comma"} : comma), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"args\""}, "primArgAnn$ebnf$2", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("lbracket") ? {type: "lbracket"} : lbracket), (lexer.has("_") ? {type: "_"} : _), "primArgAnn$ebnf$3", (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbracket") ? {type: "rbracket"} : rbracket), (lexer.has("comma") ? {type: "comma"} : comma), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"annots\""}, "primArgAnn$ebnf$4", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("lbracket") ? {type: "lbracket"} : lbracket), (lexer.has("_") ? {type: "_"} : _), "primArgAnn$ebnf$5", (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbracket") ? {type: "rbracket"} : rbracket), (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbrace") ? {type: "rbrace"} : rbrace)], "postprocess": primArgAnnToHex}, + {"name": "primArgAnn", "symbols": [(lexer.has("lbrace") ? {type: "lbrace"} : lbrace), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"prim\""}, "primArgAnn$ebnf$1", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("quotedValue") ? {type: "quotedValue"} : quotedValue), (lexer.has("comma") ? {type: "comma"} : comma), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"args\""}, "primArgAnn$ebnf$2", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("lbracket") ? {type: "lbracket"} : lbracket), (lexer.has("_") ? {type: "_"} : _), "primArgAnn$ebnf$3", (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbracket") ? {type: "rbracket"} : rbracket), (lexer.has("comma") ? {type: "comma"} : comma), (lexer.has("_") ? {type: "_"} : _), {"literal":"\"annots\""}, "primArgAnn$ebnf$4", (lexer.has("colon") ? {type: "colon"} : colon), (lexer.has("_") ? {type: "_"} : _), (lexer.has("lbracket") ? {type: "lbracket"} : lbracket), (lexer.has("_") ? {type: "_"} : _), "primArgAnn$ebnf$5", (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbracket") ? {type: "rbracket"} : rbracket), (lexer.has("_") ? {type: "_"} : _), (lexer.has("rbrace") ? {type: "rbrace"} : rbrace)], "postprocess": primArgAnnToHex}, {"name": "primAny", "symbols": ["primBare"], "postprocess": id}, {"name": "primAny", "symbols": ["primArg"], "postprocess": id}, {"name": "primAny", "symbols": ["primAnn"], "postprocess": id}, diff --git a/src/types/ErrorTypes.ts b/src/types/ErrorTypes.ts index a93a659a..af680438 100644 --- a/src/types/ErrorTypes.ts +++ b/src/types/ErrorTypes.ts @@ -6,15 +6,15 @@ export class ServiceRequestError extends Error { httpStatus: number; httpMessage: string; serverURL: string; - data: string | null; + request: string | null; - constructor(httpStatus: number, httpMessage: string, serverURL: string, data: string | null){ + constructor(httpStatus: number, httpMessage: string, serverURL: string, request: string | null) { super(); this.httpStatus = httpStatus; this.httpMessage = httpMessage; this.serverURL = serverURL; - this.data = data; + this.request = request; } } @@ -25,16 +25,16 @@ export class ServiceResponseError extends Error { httpStatus: number; httpMessage: string; serverURL: string; - data: string | null; + request: string | null; response: any; - constructor(httpStatus: number, httpMessage: string, serverURL: string, data: string | null, response: any){ + constructor(httpStatus: number, httpMessage: string, serverURL: string, request: string | null, response: any) { super(); this.httpStatus = httpStatus; this.httpMessage = httpMessage; this.serverURL = serverURL; - this.data = data; + this.request = request; this.response = response; } } diff --git a/src/types/tezos/TezosErrorTypes.ts b/src/types/tezos/TezosErrorTypes.ts index 5dc79ef1..a60896e2 100644 --- a/src/types/tezos/TezosErrorTypes.ts +++ b/src/types/tezos/TezosErrorTypes.ts @@ -21,14 +21,12 @@ export class TezosRequestError extends ServiceRequestError { * A specialization of ServiceResponseError for Tezos services. */ export class TezosResponseError extends ServiceResponseError { - requestBody: string | null; kind: TezosResponseErrorKind; constructor(httpStatus: number, httpMessage: string, serverURL: string, requestBody: string | null, response: any, kind: TezosResponseErrorKind){ - super(httpStatus, httpMessage, serverURL, null, response); + super(httpStatus, httpMessage, serverURL, requestBody, response); this.kind = kind; - this.requestBody = requestBody; } } diff --git a/test/chain/tezos/lexer/MichelineParser.spec.ts b/test/chain/tezos/lexer/MichelineParser.spec.ts index 0e0f5e78..9273ee27 100644 --- a/test/chain/tezos/lexer/MichelineParser.spec.ts +++ b/test/chain/tezos/lexer/MichelineParser.spec.ts @@ -86,7 +86,6 @@ describe('Micheline binary encoding complex tests', () => { const result = TezosLanguageUtil.translateMichelineToHex('[ { "prim":"CAR" }, [ [ { "prim":"DUP" }, { "prim":"CAR" }, { "prim":"DIP", "args":[ [ { "prim":"CDR" } ] ] } ] ], { "prim":"NIL", "args":[ { "prim":"int" } ] } ]'); expect(result).to.equal('020000001d03160200000012020000000d03210316051f02000000020317053d035b'); }); - }); describe('Micheline to hex contract tests', async () => { @@ -114,3 +113,9 @@ describe('Micheline to hex contract tests', async () => { }); } }); + +describe('Set external keyword list', () => { + TezosLanguageUtil.overrideKeywordList([]); + + TezosLanguageUtil.restoreKeywordList(); +});