From 58a2dfbc4d191ea21e6a749371e586dcff3b3239 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 17:04:50 -0800 Subject: Final rounds on evm data types --- .../src/abi_encoder/evm_data_types/static_bytes.ts | 58 ++++++++++++---------- .../utils/src/abi_encoder/evm_data_types/string.ts | 33 +++++++----- .../utils/src/abi_encoder/evm_data_types/tuple.ts | 8 +-- packages/utils/src/abi_encoder/utils/queue.ts | 2 +- 4 files changed, 59 insertions(+), 42 deletions(-) (limited to 'packages') diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 9a2a99ec7..afa9afdf2 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -11,7 +11,6 @@ export class StaticBytes extends PayloadDataType { private static readonly _matcher = RegExp( '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', ); - private static readonly _DEFAULT_WIDTH = 1; private readonly _width: number; @@ -19,16 +18,20 @@ export class StaticBytes extends PayloadDataType { return StaticBytes._matcher.test(type); } + private static _decodeWidthFromType(type: string): number { + const matches = StaticBytes._matcher.exec(type); + const width = (matches !== null && matches.length === 3 && matches[2] !== undefined) + ? parseInt(matches[2], Constants.DEC_BASE) + : StaticBytes._DEFAULT_WIDTH; + return width; + } + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { super(dataItem, dataTypeFactory, StaticBytes._SIZE_KNOWN_AT_COMPILE_TIME); - const matches = StaticBytes._matcher.exec(dataItem.type); if (!StaticBytes.matchType(dataItem.type)) { - throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); + throw new Error(`Tried to instantiate Static Bytes with bad input: ${dataItem}`); } - this._width = - matches !== null && matches.length === 3 && matches[2] !== undefined - ? parseInt(matches[2], Constants.DEC_BASE) - : StaticBytes._DEFAULT_WIDTH; + this._width = StaticBytes._decodeWidthFromType(dataItem.type); } public getSignature(): string { @@ -37,11 +40,30 @@ export class StaticBytes extends PayloadDataType { } public encodeValue(value: string | Buffer): Buffer { - // Sanity check if string - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); + // 1/2 Convert value into a buffer and do bounds checking + this._sanityCheckValue(value); + const valueBuf = ethUtil.toBuffer(value); + // 2/2 Store value as hex + const valuePadded = ethUtil.setLengthRight(valueBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + return valuePadded; + } + + public decodeValue(calldata: RawCalldata): string { + const valueBufPadded = calldata.popWord(); + const valueBuf = valueBufPadded.slice(0, this._width); + const value = ethUtil.bufferToHex(valueBuf); + this._sanityCheckValue(value); + return value; + } + + private _sanityCheckValue(value: string | Buffer): void { + if (typeof value === 'string') { + if (!value.startsWith('0x')) { + throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } } - // Convert value into a buffer and do bounds checking const valueBuf = ethUtil.toBuffer(value); if (valueBuf.byteLength > this._width) { throw new Error( @@ -49,20 +71,6 @@ export class StaticBytes extends PayloadDataType { valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); - } else if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); } - - // Store value as hex - const evmWordWidth = 32; - const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); - return paddedValue; - } - - public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(0, this._width); - const value = ethUtil.bufferToHex(valueBuf); - return value; } } diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index 47ad7cb97..15b93e447 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -13,7 +13,7 @@ export class String extends PayloadDataType { public static matchType(type: string): boolean { return type === 'string'; } - + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { super(dataItem, dataTypeFactory, String._SIZE_KNOWN_AT_COMPILE_TIME); if (!String.matchType(dataItem.type)) { @@ -22,21 +22,30 @@ export class String extends PayloadDataType { } public encodeValue(value: string): Buffer { - const wordsForValue = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; - const valueBuf = ethUtil.setLengthRight(new Buffer(value), paddedDynamicBytesForValue); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), Constants.EVM_WORD_WIDTH_IN_BYTES); - const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + // Encoded value is of the form: , with each field padded to be word-aligned. + // 1/3 Construct the length + const wordsToStoreValuePadded = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const bytesToStoreValuePadded = wordsToStoreValuePadded * Constants.EVM_WORD_WIDTH_IN_BYTES; + const lengthBuf = ethUtil.toBuffer(value.length); + const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + // 2/3 Construct the value + const valueBuf = new Buffer(value); + const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded); + // 3/3 Combine length and value + const encodedValueBuf = Buffer.concat([lengthBufPadded, valueBufPadded]); return encodedValueBuf; } public decodeValue(calldata: RawCalldata): string { - const lengthBuf = calldata.popWord(); - const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, Constants.HEX_BASE); - const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); + // Encoded value is of the form: , with each field padded to be word-aligned. + // 1/2 Decode length + const lengthBufPadded = calldata.popWord(); + const lengthHexPadded = ethUtil.bufferToHex(lengthBufPadded); + const length = parseInt(lengthHexPadded, Constants.HEX_BASE); + // 2/2 Decode value + const wordsToStoreValuePadded = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const valueBufPadded = calldata.popWords(wordsToStoreValuePadded); + const valueBuf = valueBufPadded.slice(0, length); const value = valueBuf.toString('ascii'); return value; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts index 4a90e375a..89dd6604d 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts @@ -3,8 +3,8 @@ import { DataItem } from 'ethereum-types'; import { DataTypeFactory, MemberDataType } from '../abstract_data_types'; export class Tuple extends MemberDataType { - private readonly _tupleSignature: string; - + private readonly _signature: string; + public static matchType(type: string): boolean { return type === 'tuple'; } @@ -14,10 +14,10 @@ export class Tuple extends MemberDataType { if (!Tuple.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); } - this._tupleSignature = this._computeSignatureOfMembers(); + this._signature = this._computeSignatureOfMembers(); } public getSignature(): string { - return this._tupleSignature; + return this._signature; } } diff --git a/packages/utils/src/abi_encoder/utils/queue.ts b/packages/utils/src/abi_encoder/utils/queue.ts index 53afb7e11..506a0b56e 100644 --- a/packages/utils/src/abi_encoder/utils/queue.ts +++ b/packages/utils/src/abi_encoder/utils/queue.ts @@ -32,7 +32,7 @@ export class Queue { public getStore(): T[] { return this._store; } - + public peekFront(): T | undefined { return this._store.length >= 0 ? this._store[0] : undefined; } -- cgit v1.2.3