aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Hysen <greg.hysen@gmail.com>2018-11-10 10:10:58 +0800
committerGreg Hysen <greg.hysen@gmail.com>2018-11-29 08:38:10 +0800
commit2802aed79cac4e73e64e1f33e328bcd05a4daf8b (patch)
tree22601e61b601da1e2b4ea7bf3d1cd5ddb785d560
parent78498b7019e02a9b280cc1ffe9fafbf30bff24ec (diff)
downloaddexon-sol-tools-2802aed79cac4e73e64e1f33e328bcd05a4daf8b.tar
dexon-sol-tools-2802aed79cac4e73e64e1f33e328bcd05a4daf8b.tar.gz
dexon-sol-tools-2802aed79cac4e73e64e1f33e328bcd05a4daf8b.tar.bz2
dexon-sol-tools-2802aed79cac4e73e64e1f33e328bcd05a4daf8b.tar.lz
dexon-sol-tools-2802aed79cac4e73e64e1f33e328bcd05a4daf8b.tar.xz
dexon-sol-tools-2802aed79cac4e73e64e1f33e328bcd05a4daf8b.tar.zst
dexon-sol-tools-2802aed79cac4e73e64e1f33e328bcd05a4daf8b.zip
refactored implementation done but not tested
-rw-r--r--packages/order-utils/test/abi/calldata.ts140
-rw-r--r--packages/order-utils/test/abi/data_type.ts98
-rw-r--r--packages/order-utils/test/abi/evm_data_types.ts17
3 files changed, 167 insertions, 88 deletions
diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts
index dc0fcfb2b..725bdce62 100644
--- a/packages/order-utils/test/abi/calldata.ts
+++ b/packages/order-utils/test/abi/calldata.ts
@@ -9,15 +9,23 @@ export abstract class CalldataBlock {
private bodySizeInBytes: number;
private relocatable: boolean;
- constructor(name: string, signature: string, offsetInBytes: number, headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) {
+ constructor(name: string, signature: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) {
this.name = name;
this.signature = signature;
- this.offsetInBytes = offsetInBytes;
+ this.offsetInBytes = 0;
this.headerSizeInBytes = headerSizeInBytes;
this.bodySizeInBytes = bodySizeInBytes;
this.relocatable = relocatable;
}
+ protected setHeaderSize(headerSizeInBytes: number) {
+ this.headerSizeInBytes = headerSizeInBytes;
+ }
+
+ protected setBodySize(bodySizeInBytes: number) {
+ this.bodySizeInBytes = bodySizeInBytes;
+ }
+
public getName(): string {
return this.name;
}
@@ -46,22 +54,25 @@ export abstract class CalldataBlock {
return this.offsetInBytes;
}
- public abstract toHexString(): string;
+ public setOffset(offsetInBytes: number) {
+ this.offsetInBytes = offsetInBytes;
+ }
+
+ public abstract toBuffer(): Buffer;
}
export class PayloadCalldataBlock extends CalldataBlock {
private payload: Buffer;
- constructor(name: string, signature: string, offsetInBytes: number, relocatable: boolean, payload: Buffer) {
+ constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) {
const headerSizeInBytes = 0;
const bodySizeInBytes = payload.byteLength;
- super(name, signature, offsetInBytes, headerSizeInBytes, bodySizeInBytes, relocatable);
+ super(name, signature, /*offsetInBytes,*/ headerSizeInBytes, bodySizeInBytes, relocatable);
this.payload = payload;
}
- public toHexString(): string {
- const payloadHex = ethUtil.bufferToHex(this.payload);
- return payloadHex;
+ public toBuffer(): Buffer {
+ return this.payload;
}
}
@@ -70,15 +81,15 @@ export class DependentCalldataBlock extends CalldataBlock {
private parent: CalldataBlock;
private dependency: CalldataBlock;
- constructor(name: string, signature: string, offsetInBytes: number, relocatable: boolean, parent: CalldataBlock, dependency: CalldataBlock) {
+ constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, parent: CalldataBlock, dependency: CalldataBlock) {
const headerSizeInBytes = 0;
const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES;
- super(name, signature, offsetInBytes, headerSizeInBytes, bodySizeInBytes, relocatable);
+ super(name, signature, /*offsetInBytes,*/ headerSizeInBytes, bodySizeInBytes, relocatable);
this.parent = parent;
this.dependency = dependency;
}
- public toHexString(): string {
+ public toBuffer(): Buffer {
const dependencyOffset = this.dependency.getOffsetInBytes();
const parentOffset = this.parent.getOffsetInBytes();
const parentHeaderSize = this.parent.getHeaderSizeInBytes();
@@ -86,8 +97,11 @@ export class DependentCalldataBlock extends CalldataBlock {
const pointerBuf = new Buffer(`0x${pointer.toString(16)}`);
const evmWordWidthInBytes = 32;
const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes);
- const pointerHex = ethUtil.bufferToHex(pointerBufPadded);
- return pointerHex;
+ return pointerBufPadded;
+ }
+
+ public getDependency(): CalldataBlock {
+ return this.dependency;
}
}
@@ -96,51 +110,97 @@ export class MemberCalldataBlock extends CalldataBlock {
private header: Buffer | undefined;
private members: CalldataBlock[];
- constructor(name: string, signature: string, offsetInBytes: number, relocatable: boolean, members: CalldataBlock[], header?: Buffer) {
- const headerSizeInBytes = (header === undefined) ? 0 : header.byteLength;
+ constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean) {
+ super(name, signature, /*offsetInBytes,*/ 0, 0, relocatable);
+ this.members = [];
+ this.header = undefined;
+ }
+
+ public setMembers(members: CalldataBlock[]) {
let bodySizeInBytes = 0;
- _.each(members, (member: Memblock) => {
+ _.each(members, (member: CalldataBlock) => {
bodySizeInBytes += member.getSizeInBytes();
});
-
- super(name, signature, offsetInBytes, headerSizeInBytes, bodySizeInBytes, relocatable);
this.members = members;
+ this.setBodySize(bodySizeInBytes);
+ }
+
+ public setHeader(header: Buffer) {
+ this.setHeaderSize(header.byteLength);
this.header = header;
}
- public toHexString(): string {
- let valueBuffers: Buffer[] = [];
- if (this.header !== undefined) valueBuffers.push(this.header);
- _.each(this.members, (member: CalldataBlock) => {
- const memberHexString = member.toHexString();
- const memberBuf = ethUtil.toBuffer(memberHexString);
- valueBuffers.push(memberBuf);
- });
- const combinedValueBufs = Buffer.concat(valueBuffers);
- const combinedValuesAsHex = ethUtil.bufferToHex(combinedValueBufs);
- return combinedValuesAsHex;
+ public toBuffer(): Buffer {
+ if (this.header !== undefined) return this.header;
+ return new Buffer('');
+ }
+
+ public getMembers(): CalldataBlock[] {
+ return this.members;
+ }
+}
+
+class Queue<T> {
+ private store: T[] = [];
+ push(val: T) {
+ this.store.push(val);
+ }
+ pop(): T | undefined {
+ return this.store.shift();
}
}
export class Calldata {
private selector: string;
private sizeInBytes: number;
- private blocks: CalldataBlock[];
+ private root: CalldataBlock | undefined;
constructor() {
this.selector = '0x';
this.sizeInBytes = 0;
- this.blocks = [];
+ this.root = undefined;
}
public toHexString(): string {
- let calldataString = `${this.selector}`;
- _.each(this.blocks, (block: CalldataBlock) => {
- const blockAsHexString = block.toHexString();
- const blockAsHexWithoutPrefix = ethUtil.stripHexPrefix(blockAsHexString);
- calldataString += blockAsHexWithoutPrefix;
- });
- return calldataString;
+ let selectorBuffer = ethUtil.toBuffer(this.selector);
+ if (this.root === undefined) {
+ throw new Error('expected root');
+ }
+
+ const blockQueue = new Queue<CalldataBlock>();
+ blockQueue.push(this.root);
+
+ // Assign locations in breadth-first manner
+ let block: CalldataBlock | undefined;
+ let offset = 0;
+ while ((block = blockQueue.pop()) !== undefined) {
+ block.setOffset(offset);
+ if (block instanceof DependentCalldataBlock) {
+ blockQueue.push(block.getDependency());
+ } else if (block instanceof MemberCalldataBlock) {
+ _.each(block.getMembers(), (member: CalldataBlock) => {
+ blockQueue.push(member);
+ });
+ }
+ }
+
+ // Fetch values using same technique
+ const valueBufs: Buffer[] = [selectorBuffer];
+ while ((block = blockQueue.pop()) !== undefined) {
+ valueBufs.push(block.toBuffer());
+
+ if (block instanceof DependentCalldataBlock) {
+ blockQueue.push(block.getDependency());
+ } else if (block instanceof MemberCalldataBlock) {
+ _.each(block.getMembers(), (member: CalldataBlock) => {
+ blockQueue.push(member);
+ });
+ }
+ }
+
+ const combinedBuffers = Buffer.concat(valueBufs);
+ const hexValue = ethUtil.bufferToHex(combinedBuffers);
+ return hexValue;
}
public getSelectorHex(): string {
@@ -155,8 +215,8 @@ export class Calldata {
return "";
}
- public pushBlock(block: CalldataBlock) {
- this.blocks.push(block);
+ public setRoot(block: CalldataBlock) {
+ this.root = block;
this.sizeInBytes += block.getSizeInBytes();
}
diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts
index f74621085..1a4610f7c 100644
--- a/packages/order-utils/test/abi/data_type.ts
+++ b/packages/order-utils/test/abi/data_type.ts
@@ -1,4 +1,4 @@
-import { Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock } from "./calldata";
+import { Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata";
import { MethodAbi, DataItem } from 'ethereum-types';
import { BigNumber } from '@0x/utils';
import ethUtil = require('ethereumjs-util');
@@ -17,7 +17,7 @@ export abstract class DataType {
return this.dataItem;
}
- protected abstract createCalldataBlock(): CalldataBlock;
+ public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock;
public abstract encode(value: any, calldata: Calldata): void;
public abstract getSignature(): string;
public abstract isStatic(): boolean;
@@ -31,19 +31,27 @@ export abstract class PayloadDataType extends DataType {
this.hasConstantSize = hasConstantSize;
}
- protected generateCalldataBlock(payload: Buffer, calldata: Calldata): void {
+ public generateCalldataBlocks(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock {
+ const encodedValue = this.encodeValue(value);
const name = this.getDataItem().name;
const signature = this.getSignature();
- const offsetInBytes = calldata.getSizeInBytes();
+ // const offsetInBytes = calldata.getSizeInBytes();
const relocatable = false;
- const block = new PayloadCalldataBlock(name, signature, offsetInBytes, relocatable, payload);
- calldata.pushBlock(block);
+ const block = new PayloadCalldataBlock(name, signature, /*offsetInBytes,*/ relocatable, encodedValue);
+ return block;
+ }
+
+ public encode(value: any, calldata: Calldata): void {
+ const block = this.generateCalldataBlock(value);
+ calldata.setRoot(block);
}
public isStatic(): boolean {
// If a payload has a constant size then it's static
return this.hasConstantSize;
}
+
+ public abstract encodeValue(value: any): Buffer;
}
export abstract class DependentDataType extends DataType {
@@ -56,20 +64,21 @@ export abstract class DependentDataType extends DataType {
this.parent = parent;
}
- protected generateCalldataBlock(offsetInBytes: number, calldata: Calldata): CalldataBlock {
+ public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock {
+ if (parentBlock === undefined) {
+ throw new Error(`DependentDataType requires a parent block to generate its block`);
+ }
+ const dependencyBlock = this.dependency.generateCalldataBlock(value);
const name = this.getDataItem().name;
const signature = this.getSignature();
const relocatable = false;
- const parentBlock = calldata.lookupBlockByName(this.parent.getDataItem().name);
- const dependencyBlock = calldata.lookupBlockByName(this.parent.getDataItem().name);
- const block = new DependentCalldataBlock(name, signature, offsetInBytes, relocatable, dependencyBlock, parentBlock);
- calldata.pushBlock(block);
+ const block = new DependentCalldataBlock(name, signature, /*offsetInBytes,*/ relocatable, dependencyBlock, parentBlock);
+ return block;
}
public encode(value: any, calldata: Calldata = new Calldata()): void {
- const offsetInBytes = calldata.reserveSpace(DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES);
- this.dependency.encode(value, calldata);
- this.generateCalldataBlock(offsetInBytes, calldata);
+ const block = this.generateCalldataBlock(value);
+ calldata.setRoot(block);
}
public isStatic(): boolean {
@@ -96,7 +105,7 @@ export abstract class MemberDataType extends DataType {
this.isArray = isArray;
this.arrayLength = arrayLength;
if (isArray && arrayLength !== undefined) {
- [this.members, this.memberMap] = MemberDataType.createMembersWithLength(arrayLength);
+ [this.members, this.memberMap] = MemberDataType.createMembersWithLength(dataItem, arrayLength);
} else if (!isArray) {
[this.members, this.memberMap] = MemberDataType.createMembersWithKeys(dataItem);
}
@@ -129,7 +138,7 @@ export abstract class MemberDataType extends DataType {
const range = _.range(length);
_.each(range, (idx: number) => {
const childDataItem = {
- type: this.type,
+ type: dataItem.type,
name: `${dataItem.name}[${idx.toString(10)}]`,
} as DataItem;
const components = dataItem.components;
@@ -144,57 +153,60 @@ export abstract class MemberDataType extends DataType {
return [members, memberMap];
}
- protected encodeFromArray(value: any[], calldata: Calldata) {
+ protected generateCalldataBlockFromArray(value: any[]): MemberCalldataBlock {
// Sanity check length
- const valueLength = new BigNumber(value.length);
- if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) {
+ if (this.arrayLength !== undefined && value.length !== this.arrayLength) {
throw new Error(
`Expected array of ${JSON.stringify(
- this.length,
- )} elements, but got array of length ${JSON.stringify(valueLength)}`,
+ this.arrayLength,
+ )} elements, but got array of length ${JSON.stringify(value.length)}`,
);
}
- // Assign values to children
- for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) {
- const idxNumber = idx.toNumber();
- this.members[idxNumber].assignValue(value[idxNumber]);
+ let members = this.members;
+ if (this.arrayLength === undefined) {
+ [members,] = MemberDataType.createMembersWithLength(this.getDataItem(), value.length);
}
+
+ const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), false);
+ const memberBlocks: CalldataBlock[] = [];
+ _.each(members, (member: DataType) => {
+ const block = member.generateCalldataBlock(value, methodBlock);
+ memberBlocks.push(block);
+ });
+ methodBlock.setMembers(memberBlocks);
+ return methodBlock;
}
- protected encodeFromObject(obj: object, calldata: Calldata) {
+ protected generateCalldataBlockFromObject(obj: object): MemberCalldataBlock {
+ const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), false);
+ const memberBlocks: CalldataBlock[] = [];
let childMap = _.cloneDeep(this.memberMap);
_.forOwn(obj, (value: any, key: string) => {
if (key in childMap === false) {
throw new Error(`Could not assign tuple to object: unrecognized key '${key}'`);
}
- this.members[this.childMap[key]].assignValue(value);
+ const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock);
+ memberBlocks.push(block);
delete childMap[key];
});
if (Object.keys(childMap).length !== 0) {
throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`);
}
+
+ methodBlock.setMembers(memberBlocks);
+ return methodBlock;
}
- public encode(value: any[] | object, calldata = new Calldata()) {
- if (value instanceof Array) {
- this.encodeFromArray(value, calldata);
- } else if (typeof value === 'object') {
- this.encodeFromObject(value, encodeFromObject);
- } else {
- throw new Error(`Unexpected type for ${value}`);
- }
+ public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock {
+ const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value) : this.generateCalldataBlockFromObject(value, calldata);
+ return block;
}
- protected generateCalldataBlock(offsetInBytes: number, calldata: Calldata): CalldataBlock {
- const name = this.getDataItem().name;
- const signature = this.getSignature();
- const relocatable = false;
- const parentBlock = calldata.lookupBlockByName(this.parent.getDataItem().name);
- const dependencyBlock = calldata.lookupBlockByName(this.parent.getDataItem().name);
- const block = new DependentCalldataBlock(name, signature, offsetInBytes, relocatable, dependencyBlock, parentBlock);
- calldata.pushBlock(block);
+ public encode(value: any, calldata: Calldata = new Calldata()): void {
+ const block = this.generateCalldataBlock(value);
+ calldata.setRoot(block);
}
protected computeSignatureOfMembers(): string {
diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts
index 4e42a992a..bdbaf5b3c 100644
--- a/packages/order-utils/test/abi/evm_data_types.ts
+++ b/packages/order-utils/test/abi/evm_data_types.ts
@@ -4,6 +4,8 @@ import { MethodAbi, DataItem } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
+import { Calldata } from './calldata';
+
import { BigNumber } from '@0x/utils';
export interface DataTypeStaticInterface {
@@ -29,7 +31,7 @@ export class Address extends PayloadDataType {
return type === 'address';
}
- public static encodeValue(value: boolean): Buffer {
+ public encodeValue(value: boolean): Buffer {
const evmWordWidth = 32;
const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth);
return encodedValueBuf;
@@ -54,7 +56,7 @@ export class Bool extends PayloadDataType {
return type === 'bool';
}
- public static encodeValue(value: boolean): Buffer {
+ public encodeValue(value: boolean): Buffer {
const evmWordWidth = 32;
const encodedValue = value === true ? '0x1' : '0x0';
const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth);
@@ -81,7 +83,7 @@ abstract class Number extends PayloadDataType {
}
}
- public static encodeValue(value: BigNumber): Buffer {
+ public encodeValue(value: BigNumber): Buffer {
if (value.greaterThan(this.getMaxValue())) {
throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`;
} else if (value.lessThan(this.getMinValue())) {
@@ -204,7 +206,7 @@ export class Byte extends PayloadDataType {
return `bytes${this.width}`;
}
- public static encodeValue(value: string | Buffer): Buffer {
+ public encodeValue(value: string | Buffer): Buffer {
// Convert value into a buffer and do bounds checking
const valueBuf = ethUtil.toBuffer(value);
if (valueBuf.byteLength > this.width) {
@@ -275,7 +277,7 @@ export class SolString extends PayloadDataType {
}
}
- public static encodeValue(value: string): Buffer {
+ public encodeValue(value: string): Buffer {
const wordsForValue = Math.ceil(value.length / 32);
const paddedBytesForValue = wordsForValue * 32;
const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue);
@@ -398,6 +400,11 @@ export class Method extends MemberDataType {
return selector;
}
+ public encode(value: any[] | object, calldata = new Calldata()) {
+ calldata.setSelector(this.methodSelector);
+ super.encode(value, calldata);
+ }
+
public getSignature(): string {
return this.methodSignature;
}