From 8f73f53c95d8ba887558863b8b726a2b3f5b7e2b Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 20:05:41 -0800 Subject: Moved calldata iterator logic into its own iterator clas --- .../abi_encoder/abstract_data_types/types/blob.ts | 8 +- .../abstract_data_types/types/pointer.ts | 21 +++-- .../utils/src/abi_encoder/calldata/calldata.ts | 56 +++---------- .../utils/src/abi_encoder/calldata/iterator.ts | 94 ++++++++++++++++++++++ .../src/abi_encoder/evm_data_types/pointer.ts | 2 +- 5 files changed, 119 insertions(+), 62 deletions(-) create mode 100644 packages/utils/src/abi_encoder/calldata/iterator.ts (limited to 'packages/utils') diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts index f4246c893..35ccc0586 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts @@ -8,11 +8,11 @@ import { DataType } from '../data_type'; import { DataTypeFactory } from '../interfaces'; export abstract class Blob extends DataType { - protected _hasConstantSize: boolean; + protected _sizeKnownAtCompileTime: boolean; - public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { + public constructor(dataItem: DataItem, factory: DataTypeFactory, sizeKnownAtCompileTime: boolean) { super(dataItem, factory); - this._hasConstantSize = hasConstantSize; + this._sizeKnownAtCompileTime = sizeKnownAtCompileTime; } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlocks.Blob { @@ -30,7 +30,7 @@ export abstract class Blob extends DataType { } public isStatic(): boolean { - return this._hasConstantSize; + return this._sizeKnownAtCompileTime; } public abstract encodeValue(value: any): Buffer; diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts index 47efac521..46e60979a 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts @@ -1,3 +1,4 @@ +/* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -10,26 +11,24 @@ import { DataType } from '../data_type'; import { DataTypeFactory } from '../interfaces'; export abstract class Pointer extends DataType { - protected _dependency: DataType; + protected _destination: DataType; protected _parent: DataType; - private readonly _isStatic: boolean; - public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { + public constructor(dataItem: DataItem, factory: DataTypeFactory, destination: DataType, parent: DataType) { super(dataItem, factory); - this._dependency = dependency; + this._destination = destination; this._parent = parent; - this._isStatic = true; } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlocks.Pointer { - if (parentBlock === undefined) { + if (!parentBlock) { throw new Error(`DependentDataType requires a parent block to generate its block`); } - const dependencyBlock = this._dependency.generateCalldataBlock(value, parentBlock); + const destinationBlock = this._destination.generateCalldataBlock(value, parentBlock); const name = this.getDataItem().name; const signature = this.getSignature(); - const parentName = parentBlock ? parentBlock.getName() : ''; - const block = new CalldataBlocks.Pointer(name, signature, parentName, dependencyBlock, parentBlock); + const parentName = parentBlock.getName(); + const block = new CalldataBlocks.Pointer(name, signature, parentName, destinationBlock, parentBlock); return block; } @@ -40,12 +39,12 @@ export abstract class Pointer extends DataType { const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); const currentOffset = calldata.getOffset(); calldata.setOffset(destinationOffsetAbsolute); - const value = this._dependency.generateValue(calldata, rules); + const value = this._destination.generateValue(calldata, rules); calldata.setOffset(currentOffset); return value; } public isStatic(): boolean { - return this._isStatic; + return true; } } diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index b2396ee8f..dd9d47def 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -2,11 +2,11 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import * as Constants from '../utils/constants'; -import { Queue } from '../utils/queue'; import { EncodingRules } from '../utils/rules'; import * as CalldataBlocks from './blocks'; import { CalldataBlock } from './calldata_block'; +import { CalldataIterator, ReverseCalldataIterator } from './iterator'; export class Calldata { private readonly _rules: EncodingRules; @@ -14,41 +14,6 @@ export class Calldata { private _sizeInBytes: number; private _root: CalldataBlock | undefined; - private static _createQueue(block: CalldataBlock): Queue { - const blockQueue = new Queue(); - - // Base Case - if (!(block instanceof CalldataBlocks.Set)) { - blockQueue.pushBack(block); - return blockQueue; - } - - // This is a Member Block - const memberBlock = block; - _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof CalldataBlocks.Set) { - blockQueue.mergeFront(Calldata._createQueue(member)); - } else { - blockQueue.pushFront(member); - } - }); - - // Children - _.each(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof CalldataBlocks.Pointer && member.getAlias() === undefined) { - const dependency = member.getDependency(); - if (dependency instanceof CalldataBlocks.Set) { - blockQueue.mergeBack(Calldata._createQueue(dependency)); - } else { - blockQueue.pushBack(dependency); - } - } - }); - - blockQueue.pushFront(memberBlock); - return blockQueue; - } - public constructor(rules: EncodingRules) { this._rules = rules; this._selector = ''; @@ -65,9 +30,9 @@ export class Calldata { // 1. Create a queue of subtrees by hash // Note that they are ordered the same as - const subtreeQueue = Calldata._createQueue(this._root); + const iterator = new ReverseCalldataIterator(this._root); let block: CalldataBlock | undefined; - for (block = subtreeQueue.popBack(); block !== undefined; block = subtreeQueue.popBack()) { + while (block = iterator.next()) { if (block instanceof CalldataBlocks.Pointer) { const dependencyBlockHashBuf = block.getDependency().computeHash(); const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf); @@ -97,10 +62,10 @@ export class Calldata { this.optimize(); } - const offsetQueue = Calldata._createQueue(this._root); + const iterator = new CalldataIterator(this._root); let block: CalldataBlock | undefined; let offset = 0; - for (block = offsetQueue.popFront(); block !== undefined; block = offsetQueue.popFront()) { + while (block = iterator.next()) { block.setOffset(offset); offset += block.getSizeInBytes(); } @@ -136,13 +101,12 @@ export class Calldata { throw new Error('expected root'); } - const valueQueue = Calldata._createQueue(this._root); + const iterator = new CalldataIterator(this._root); let block: CalldataBlock | undefined; let offset = 0; - const functionBlock = valueQueue.peekFront(); - const functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); - for (block = valueQueue.popFront(); block !== undefined; block = valueQueue.popFront()) { + const functionName: string = this._root.getName(); + while (block = iterator.next()) { // Process each block 1 word at a time const size = block.getSizeInBytes(); const name = block.getName(); @@ -209,10 +173,10 @@ export class Calldata { throw new Error('expected root'); } - const valueQueue = Calldata._createQueue(this._root); + const iterator = new CalldataIterator(this._root); const valueBufs: Buffer[] = [selectorBuffer]; let block: CalldataBlock | undefined; - for (block = valueQueue.popFront(); block !== undefined; block = valueQueue.popFront()) { + while (block = iterator.next()) { valueBufs.push(block.toBuffer()); } diff --git a/packages/utils/src/abi_encoder/calldata/iterator.ts b/packages/utils/src/abi_encoder/calldata/iterator.ts new file mode 100644 index 000000000..3e3367e10 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/iterator.ts @@ -0,0 +1,94 @@ +/* tslint:disable max-classes-per-file */ +import * as _ from 'lodash'; + +import { Queue } from '../utils/queue'; + +import * as CalldataBlocks from './blocks'; +import { CalldataBlock } from './calldata_block'; + +/** + * Iterator class for Calldata Blocks. Blocks follows the order + * they should be put into calldata that is passed to he EVM. + * + * Example #1: + * Let root = Set { + * Blob{} A, + * Pointer { + * Blob{} a + * } B, + * Blob{} C + * } + * It will iterate as follows: [A, B, C, B.a] + * + * Example #2: + * Let root = Set { + * Blob{} A, + * Pointer { + * Blob{} a + * Pointer { + * Blob{} b + * } + * } B, + * Pointer { + * Blob{} c + * } C + * } + * It will iterate as follows: [A, B, C, B.a, B.b, C.c] + */ +abstract class BaseIterator { + protected readonly _root: CalldataBlock; + protected readonly _queue: Queue; + + private static _createQueue(block: CalldataBlock): Queue { + const queue = new Queue(); + // Base case + if (!(block instanceof CalldataBlocks.Set)) { + queue.pushBack(block); + return queue; + } + // This is a set; add members + const set = block; + _.eachRight(set.getMembers(), (member: CalldataBlock) => { + queue.mergeFront(BaseIterator._createQueue(member)); + }); + // Add children + _.each(set.getMembers(), (member: CalldataBlock) => { + // Traverse child if it is a unique pointer. + // A pointer that is an alias for another pointer is ignored. + if (member instanceof CalldataBlocks.Pointer && member.getAlias() === undefined) { + const dependency = member.getDependency(); + queue.mergeBack(BaseIterator._createQueue(dependency)); + } + }); + // Put set block at the front of the queue + queue.pushFront(set); + return queue; + } + + public constructor(root: CalldataBlock) { + this._root = root; + this._queue = BaseIterator._createQueue(root); + } + + public abstract next(): CalldataBlock | undefined; +} + +export class CalldataIterator extends BaseIterator { + public constructor(root: CalldataBlock) { + super(root); + } + + public next(): CalldataBlock | undefined { + return this._queue.popFront(); + } +} + +export class ReverseCalldataIterator extends BaseIterator { + public constructor(root: CalldataBlock) { + super(root); + } + + public next(): CalldataBlock | undefined { + return this._queue.popBack(); + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts index 00c743d2b..e7c172afb 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts @@ -10,6 +10,6 @@ export class Pointer extends AbstractDataTypes.Pointer { } public getSignature(): string { - return this._dependency.getSignature(); + return this._destination.getSignature(); } } -- cgit v1.2.3