Allow events to use compact bytes ABI coded data for Solidity 0.4 external events (#891, #992).

This commit is contained in:
Richard Moore 2020-09-07 19:35:37 -04:00
parent 97acaa1129
commit 4e394fc680
No known key found for this signature in database
GPG Key ID: 665176BE8E9DC651
5 changed files with 21 additions and 23 deletions

@ -83,8 +83,8 @@ export class AbiCoder {
_getWordSize(): number { return 32; } _getWordSize(): number { return 32; }
_getReader(data: Uint8Array): Reader { _getReader(data: Uint8Array, allowLoose?: boolean): Reader {
return new Reader(data, this._getWordSize(), this.coerceFunc); return new Reader(data, this._getWordSize(), this.coerceFunc, allowLoose);
} }
_getWriter(): Writer { _getWriter(): Writer {
@ -107,10 +107,10 @@ export class AbiCoder {
return writer.data; return writer.data;
} }
decode(types: Array<string | ParamType>, data: BytesLike): Result { decode(types: Array<string | ParamType>, data: BytesLike, loose?: boolean): Result {
const coders: Array<Coder> = types.map((type) => this._getCoder(ParamType.from(type))); const coders: Array<Coder> = types.map((type) => this._getCoder(ParamType.from(type)));
const coder = new TupleCoder(coders, "_"); const coder = new TupleCoder(coders, "_");
return coder.decode(this._getReader(arrayify(data))); return coder.decode(this._getReader(arrayify(data), loose));
} }
} }

@ -131,16 +131,18 @@ export class Writer {
export class Reader { export class Reader {
readonly wordSize: number; readonly wordSize: number;
readonly allowLoose: boolean;
readonly _data: Uint8Array; readonly _data: Uint8Array;
readonly _coerceFunc: CoerceFunc; readonly _coerceFunc: CoerceFunc;
_offset: number; _offset: number;
constructor(data: BytesLike, wordSize?: number, coerceFunc?: CoerceFunc) { constructor(data: BytesLike, wordSize?: number, coerceFunc?: CoerceFunc, allowLoose?: boolean) {
defineReadOnly(this, "_data", arrayify(data)); defineReadOnly(this, "_data", arrayify(data));
defineReadOnly(this, "wordSize", wordSize || 32); defineReadOnly(this, "wordSize", wordSize || 32);
defineReadOnly(this, "_coerceFunc", coerceFunc); defineReadOnly(this, "_coerceFunc", coerceFunc);
defineReadOnly(this, "allowLoose", allowLoose);
this._offset = 0; this._offset = 0;
} }
@ -160,23 +162,27 @@ export class Reader {
return Reader.coerce(name, value); return Reader.coerce(name, value);
} }
_peekBytes(offset: number, length: number): Uint8Array { _peekBytes(offset: number, length: number, loose?: boolean): Uint8Array {
let alignedLength = Math.ceil(length / this.wordSize) * this.wordSize; let alignedLength = Math.ceil(length / this.wordSize) * this.wordSize;
if (this._offset + alignedLength > this._data.length) { if (this._offset + alignedLength > this._data.length) {
if (this.allowLoose && loose && this._offset + length <= this._data.length) {
alignedLength = length;
} else {
logger.throwError("data out-of-bounds", Logger.errors.BUFFER_OVERRUN, { logger.throwError("data out-of-bounds", Logger.errors.BUFFER_OVERRUN, {
length: this._data.length, length: this._data.length,
offset: this._offset + alignedLength offset: this._offset + alignedLength
}); });
} }
}
return this._data.slice(this._offset, this._offset + alignedLength) return this._data.slice(this._offset, this._offset + alignedLength)
} }
subReader(offset: number): Reader { subReader(offset: number): Reader {
return new Reader(this._data.slice(this._offset + offset), this.wordSize, this._coerceFunc); return new Reader(this._data.slice(this._offset + offset), this.wordSize, this._coerceFunc, this.allowLoose);
} }
readBytes(length: number): Uint8Array { readBytes(length: number, loose?: boolean): Uint8Array {
let bytes = this._peekBytes(0, length); let bytes = this._peekBytes(0, length, !!loose);
this._offset += bytes.length; this._offset += bytes.length;
// @TODO: Make sure the length..end bytes are all 0? // @TODO: Make sure the length..end bytes are all 0?
return bytes.slice(0, length); return bytes.slice(0, length);

@ -86,9 +86,6 @@ export function unpack(reader: Reader, coders: Array<Coder>): Result {
// A reader anchored to this base // A reader anchored to this base
let baseReader = reader.subReader(0); let baseReader = reader.subReader(0);
// The amount of dynamic data read; to consume later to synchronize
let dynamicLength = 0;
coders.forEach((coder) => { coders.forEach((coder) => {
let value: any = null; let value: any = null;
@ -105,7 +102,6 @@ export function unpack(reader: Reader, coders: Array<Coder>): Result {
value.name = coder.localName; value.name = coder.localName;
value.type = coder.type; value.type = coder.type;
} }
dynamicLength += offsetReader.consumed;
} else { } else {
try { try {
@ -125,10 +121,6 @@ export function unpack(reader: Reader, coders: Array<Coder>): Result {
} }
}); });
// @TODO: get rid of this an see if it still works?
// Consume the dynamic components in the main reader
reader.readBytes(dynamicLength);
// We only output named properties for uniquely named coders // We only output named properties for uniquely named coders
const uniqueNames = coders.reduce((accum, coder) => { const uniqueNames = coders.reduce((accum, coder) => {
const name = coder.localName; const name = coder.localName;

@ -17,7 +17,7 @@ export class DynamicBytesCoder extends Coder {
} }
decode(reader: Reader): any { decode(reader: Reader): any {
return reader.readBytes(reader.readValue().toNumber()); return reader.readBytes(reader.readValue().toNumber(), true);
} }
} }

@ -475,7 +475,7 @@ export class Interface {
}); });
let resultIndexed = (topics != null) ? this._abiCoder.decode(indexed, concat(topics)): null; let resultIndexed = (topics != null) ? this._abiCoder.decode(indexed, concat(topics)): null;
let resultNonIndexed = this._abiCoder.decode(nonIndexed, data); let resultNonIndexed = this._abiCoder.decode(nonIndexed, data, true);
let result: (Array<any> & { [ key: string ]: any }) = [ ]; let result: (Array<any> & { [ key: string ]: any }) = [ ];
let nonIndexedIndex = 0, indexedIndex = 0; let nonIndexedIndex = 0, indexedIndex = 0;