From 7c733662e2669ca6682920f321c81e770605b3d5 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 13 Nov 2018 12:36:14 -0800 Subject: Annotated calldata (draft). --- packages/order-utils/test/abi/calldata.ts | 113 ++++++++++++++++++++---- packages/order-utils/test/abi/data_type.ts | 21 +++-- packages/order-utils/test/abi/evm_data_types.ts | 4 +- packages/order-utils/test/abi_encoder_test.ts | 7 +- 4 files changed, 118 insertions(+), 27 deletions(-) (limited to 'packages') diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index 0445f68ec..1173f90cc 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -9,10 +9,12 @@ export abstract class CalldataBlock { private headerSizeInBytes: number; private bodySizeInBytes: number; private relocatable: boolean; + private parentName: string; - constructor(name: string, signature: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { + constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { this.name = name; this.signature = signature; + this.parentName = parentName; this.offsetInBytes = 0; this.headerSizeInBytes = headerSizeInBytes; this.bodySizeInBytes = bodySizeInBytes; @@ -31,6 +33,10 @@ export abstract class CalldataBlock { return this.name; } + public getParentName(): string { + return this.parentName; + } + public getSignature(): string { return this.signature; } @@ -65,10 +71,10 @@ export abstract class CalldataBlock { export class PayloadCalldataBlock extends CalldataBlock { private payload: Buffer; - constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { + constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { const headerSizeInBytes = 0; const bodySizeInBytes = payload.byteLength; - super(name, signature, /*offsetInBytes,*/ headerSizeInBytes, bodySizeInBytes, relocatable); + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); this.payload = payload; } @@ -82,10 +88,10 @@ export class DependentCalldataBlock extends CalldataBlock { private parent: CalldataBlock; private dependency: CalldataBlock; - constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { + constructor(name: string, signature: string, parentName: string, relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { const headerSizeInBytes = 0; const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; - super(name, signature, /*offsetInBytes,*/ headerSizeInBytes, bodySizeInBytes, relocatable); + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); this.parent = parent; this.dependency = dependency; } @@ -112,8 +118,8 @@ export class MemberCalldataBlock extends CalldataBlock { private members: CalldataBlock[]; private contiguous: boolean; - constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, contiguous: boolean) { - super(name, signature, /*offsetInBytes,*/ 0, 0, relocatable); + constructor(name: string, signature: string, parentName: string, relocatable: boolean, contiguous: boolean) { + super(name, signature, parentName, 0, 0, relocatable); this.members = []; this.header = undefined; this.contiguous = contiguous; @@ -231,33 +237,110 @@ export class Calldata { }) }*/ - public toHexString(optimize: boolean = false): string { - let selectorBuffer = ethUtil.toBuffer(this.selector); + private generateAnnotatedHexString(): string { + let hexValue = `${this.selector}`; if (this.root === undefined) { throw new Error('expected root'); } - const offsetQueue = this.createQueue(this.root); + const valueQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; let offset = 0; - while ((block = offsetQueue.pop()) !== undefined) { - block.setOffset(offset); - offset += block.getSizeInBytes(); + const functionBlock = valueQueue.peek(); + let functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); + while ((block = valueQueue.pop()) !== undefined) { + // Set f + + // Process each block 1 word at a time + const size = block.getSizeInBytes(); + const name = block.getName(); + const parentName = block.getParentName(); + console.log('*'.repeat(50), parentName, ' vs ', name); + + //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; + //const parentOffset = name.lastIndexOf(parentName); + const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, '');//.replace(`${parentName}[`, '['); + const signature = block.getSignature(); + + // Current offset + let offsetStr = ''; + + // If this block is empty then it's a newline + let value = ''; + let nameStr = ''; + let line = ''; + if (size === 0) { + offsetStr = ' '.repeat(10); + value = ' '.repeat(74); + nameStr = `### ${prettyName.padEnd(80)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + offsetStr = `0x${offset.toString(16)}`.padEnd(10, ' '); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(0, 32))).padEnd(74); + if (block instanceof MemberCalldataBlock) { + nameStr = `### ${prettyName.padEnd(80)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + nameStr = ` ${prettyName.padEnd(80)}`; + line = `${offsetStr}${value}${nameStr}`; + } + } + + for (let j = 32; j < size; j += 32) { + offsetStr = `0x${(offset + j).toString(16)}`.padEnd(10, ' '); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + 32))).padEnd(74); + nameStr = ' '.repeat(40); + + line = `${line}\n${offsetStr}${value}${nameStr}`; + } + + // Append to hex value + hexValue = `${hexValue}\n${line}`; + offset += size; + } + + return hexValue; + } + + private generateCondensedHexString(): string { + let selectorBuffer = ethUtil.toBuffer(this.selector); + if (this.root === undefined) { + throw new Error('expected root'); } const valueQueue = this.createQueue(this.root); const valueBufs: Buffer[] = [selectorBuffer]; + let block: CalldataBlock | undefined; while ((block = valueQueue.pop()) !== undefined) { valueBufs.push(block.toBuffer()); } - // if (optimize) this.optimize(valueQueue.getStore()); - const combinedBuffers = Buffer.concat(valueBufs); const hexValue = ethUtil.bufferToHex(combinedBuffers); return hexValue; } + public toHexString(optimize: boolean = false, annotate: boolean = false): string { + if (this.root === undefined) { + throw new Error('expected root'); + } + + const offsetQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; + let offset = 0; + while ((block = offsetQueue.pop()) !== undefined) { + block.setOffset(offset); + offset += block.getSizeInBytes(); + } + + + // if (optimize) this.optimize(valueQueue.getStore()); + + const hexValue = annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); + return hexValue; + } + public getSelectorHex(): string { return this.selector; } diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 725d314f3..25f792c7a 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -38,9 +38,9 @@ export abstract class PayloadDataType extends DataType { const encodedValue = this.encodeValue(value); const name = this.getDataItem().name; const signature = this.getSignature(); - // const offsetInBytes = calldata.getSizeInBytes(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); const relocatable = false; - const block = new PayloadCalldataBlock(name, signature, /*offsetInBytes,*/ relocatable, encodedValue); + const block = new PayloadCalldataBlock(name, signature, parentName, /*offsetInBytes,*/ relocatable, encodedValue); return block; } @@ -77,11 +77,12 @@ export abstract class DependentDataType extends DataType { if (parentBlock === undefined) { throw new Error(`DependentDataType requires a parent block to generate its block`); } - const dependencyBlock = this.dependency.generateCalldataBlock(value); + const dependencyBlock = this.dependency.generateCalldataBlock(value, parentBlock); const name = this.getDataItem().name; const signature = this.getSignature(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); const relocatable = false; - const block = new DependentCalldataBlock(name, signature, /*offsetInBytes,*/ relocatable, dependencyBlock, parentBlock); + const block = new DependentCalldataBlock(name, signature, parentName, relocatable, dependencyBlock, parentBlock); return block; } @@ -179,7 +180,7 @@ export abstract class MemberDataType extends DataType { return [members, memberMap]; } - protected generateCalldataBlockFromArray(value: any[]): MemberCalldataBlock { + protected generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { // Sanity check length if (this.arrayLength !== undefined && value.length !== this.arrayLength) { throw new Error( @@ -189,7 +190,8 @@ export abstract class MemberDataType extends DataType { ); } - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), this.isStatic(), false); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); let members = this.members; if (this.isArray && this.arrayLength === undefined) { @@ -208,8 +210,9 @@ export abstract class MemberDataType extends DataType { return methodBlock; } - protected generateCalldataBlockFromObject(obj: object): MemberCalldataBlock { - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), this.isStatic(), false); + protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); const memberBlocks: CalldataBlock[] = []; let childMap = _.cloneDeep(this.memberMap); _.forOwn(obj, (value: any, key: string) => { @@ -230,7 +233,7 @@ export abstract class MemberDataType extends DataType { } public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value) : this.generateCalldataBlockFromObject(value); + const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value, parentBlock) : this.generateCalldataBlockFromObject(value, parentBlock); return block; } diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index 7b366a985..ea401247b 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -489,10 +489,10 @@ export class Method extends MemberDataType { return selector; } - public encode(value: any[] | object, calldata = new Calldata()): string { + public encode(value: any[] | object, calldata = new Calldata(), annotate: boolean = false): string { calldata.setSelector(this.methodSelector); super.encode(value, calldata); - return calldata.toHexString(); + return calldata.toHexString(false, annotate); } public decode(calldata: string, decodeStructsAsObjects: boolean = false): any[] | object { diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 137850b35..8b836f77f 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -17,6 +17,7 @@ import { assert } from '@0x/order-utils/src/assert'; import * as AbiEncoder from './abi/abi_encoder'; import * as AbiSamples from './abi_samples'; +import { Calldata } from './abi/calldata'; AbiEncoder.DataTypeFactory.setImpl(new AbiEncoder.EvmDataTypeFactoryImpl()); @@ -88,8 +89,12 @@ describe.only('ABI Encoder', () => { someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes }; - const calldata = method.encode(args); + const calldata = method.encode(args, new Calldata(), true); console.log(calldata); + + + throw new Error(`done`); + console.log('*'.repeat(40)); console.log(JSON.stringify(args)); console.log(method.getSignature()); -- cgit v1.2.3