Handle NULL input for all data types

This commit is contained in:
Greg Hysen
2019-02-05 17:28:31 -08:00
parent b5eb47f609
commit 2326dcdb12
12 changed files with 85 additions and 6 deletions

View File

@@ -47,7 +47,7 @@ export abstract class DataType {
const hasSelector = !_.isUndefined(selector);
const rawCalldata = new RawCalldata(calldata, hasSelector);
const rules_ = _.isUndefined(rules) ? constants.DEFAULT_DECODING_RULES : rules;
const value = this.generateValue(rawCalldata, rules_);
const value = rawCalldata.getSizeInBytes() > 0 ? this.generateValue(rawCalldata, rules_) : this.getDefaultValue(rules_);
return value;
}
@@ -71,6 +71,7 @@ export abstract class DataType {
public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock;
public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any;
public abstract getDefaultValue(rules?: DecodingRules): any;
public abstract getSignatureType(): string;
public abstract isStatic(): boolean;
}

View File

@@ -97,6 +97,27 @@ export abstract class AbstractSetDataType extends DataType {
return isStatic;
}
public getDefaultValue(rules?: DecodingRules): any[] | object {
let defaultValue: any[] | object;
if (this._isArray && _.isUndefined(this._arrayLength)) {
defaultValue = [];
} else if (!_.isUndefined(rules) && rules.shouldConvertStructsToObjects && !this._isArray) {
defaultValue = {};
_.each(this._memberIndexByName, (idx: number, key: string) => {
const member = this._members[idx];
const memberValue = member.getDefaultValue();
(defaultValue as { [key: string]: any })[key] = memberValue;
});
} else {
defaultValue = [];
_.each(this._members, (member: DataType, idx: number) => {
const memberValue = member.getDefaultValue();
(defaultValue as any[]).push(memberValue);
});
}
return defaultValue;
}
protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): SetCalldataBlock {
// Sanity check: if the set has a defined length then `value` must have the same length.
if (!_.isUndefined(this._arrayLength) && value.length !== this._arrayLength) {

View File

@@ -79,4 +79,9 @@ export class RawCalldata {
public getSelector(): string {
return this._selector;
}
public getSizeInBytes(): number {
const sizeInBytes = this._value.byteLength;
return sizeInBytes;
}
}

View File

@@ -12,6 +12,7 @@ export class AddressDataType extends AbstractBlobDataType {
private static readonly _ADDRESS_SIZE_IN_BYTES = 20;
private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES =
constants.EVM_WORD_WIDTH_IN_BYTES - AddressDataType._ADDRESS_SIZE_IN_BYTES;
private static readonly _DEFAULT_VALUE = '0x0000000000000000000000000000000000000000';
public static matchType(type: string): boolean {
return type === SolidityTypes.Address;
@@ -43,6 +44,10 @@ export class AddressDataType extends AbstractBlobDataType {
return valueLowercase;
}
public getDefaultValue(): string {
return AddressDataType._DEFAULT_VALUE;
}
public getSignatureType(): string {
return SolidityTypes.Address;
}

View File

@@ -10,6 +10,7 @@ import { constants } from '../utils/constants';
export class BoolDataType extends AbstractBlobDataType {
private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true;
private static readonly _DEFAULT_VALUE: boolean =false;
public static matchType(type: string): boolean {
return type === SolidityTypes.Bool;
@@ -47,6 +48,10 @@ export class BoolDataType extends AbstractBlobDataType {
return value;
}
public getDefaultValue(): boolean {
return BoolDataType._DEFAULT_VALUE;
}
public getSignatureType(): string {
return SolidityTypes.Bool;
}

View File

@@ -9,6 +9,7 @@ import { constants } from '../utils/constants';
export class DynamicBytesDataType extends AbstractBlobDataType {
private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false;
private static readonly _DEFAULT_VALUE = "0x";
public static matchType(type: string): boolean {
return type === SolidityTypes.Bytes;
@@ -65,6 +66,10 @@ export class DynamicBytesDataType extends AbstractBlobDataType {
return value;
}
public getDefaultValue(): string {
return DynamicBytesDataType._DEFAULT_VALUE;
}
public getSignatureType(): string {
return SolidityTypes.Bytes;
}

View File

@@ -15,6 +15,7 @@ export class IntDataType extends AbstractBlobDataType {
private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true;
private static readonly _MAX_WIDTH: number = 256;
private static readonly _DEFAULT_WIDTH: number = IntDataType._MAX_WIDTH;
private static readonly _DEFAULT_VALUE = new BigNumber(0);
private readonly _width: number;
private readonly _minValue: BigNumber;
private readonly _maxValue: BigNumber;
@@ -50,13 +51,20 @@ export class IntDataType extends AbstractBlobDataType {
public decodeValue(calldata: RawCalldata): BigNumber | number {
const valueBuf = calldata.popWord();
const value = EncoderMath.safeDecodeNumericValue(valueBuf, this._minValue, this._maxValue);
const numberOfBytesInUint8 = 8;
if (this._width === numberOfBytesInUint8) {
if (this._width === constants.NUMBER_OF_BYTES_IN_UINT8) {
return value.toNumber();
}
return value;
}
public getDefaultValue(): BigNumber | number {
const defaultValue = IntDataType._DEFAULT_VALUE;
if (this._width === constants.NUMBER_OF_BYTES_IN_UINT8) {
return defaultValue.toNumber();
}
return defaultValue;
}
public getSignatureType(): string {
return `${SolidityTypes.Int}${this._width}`;
}

View File

@@ -18,4 +18,9 @@ export class PointerDataType extends AbstractPointerDataType {
public getSignature(isDetailed?: boolean): string {
return this._destination.getSignature(isDetailed);
}
public getDefaultValue(): any {
const defaultValue = this._destination.getDefaultValue();
return defaultValue;
}
}

View File

@@ -58,6 +58,13 @@ export class StaticBytesDataType extends AbstractBlobDataType {
return value;
}
public getDefaultValue(): string {
const valueBufPadded = constants.EMPTY_EVM_WORD_BUFFER;
const valueBuf = valueBufPadded.slice(0, this._width);
const value = ethUtil.bufferToHex(valueBuf);
return value;
}
private _sanityCheckValue(value: string | Buffer): void {
if (typeof value === 'string') {
if (!_.startsWith(value, '0x')) {

View File

@@ -9,6 +9,7 @@ import { constants } from '../utils/constants';
export class StringDataType extends AbstractBlobDataType {
private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false;
private static readonly _DEFAULT_VALUE = "";
public static matchType(type: string): boolean {
return type === SolidityTypes.String;
@@ -52,6 +53,10 @@ export class StringDataType extends AbstractBlobDataType {
return value;
}
public getDefaultValue(): string {
return StringDataType._DEFAULT_VALUE;
}
public getSignatureType(): string {
return SolidityTypes.String;
}

View File

@@ -16,6 +16,7 @@ export class UIntDataType extends AbstractBlobDataType {
private static readonly _MAX_WIDTH: number = 256;
private static readonly _DEFAULT_WIDTH: number = UIntDataType._MAX_WIDTH;
private static readonly _MIN_VALUE = new BigNumber(0);
private static readonly _DEFAULT_VALUE = new BigNumber(0);
private readonly _width: number;
private readonly _maxValue: BigNumber;
@@ -49,13 +50,20 @@ export class UIntDataType extends AbstractBlobDataType {
public decodeValue(calldata: RawCalldata): BigNumber | number {
const valueBuf = calldata.popWord();
const value = EncoderMath.safeDecodeNumericValue(valueBuf, UIntDataType._MIN_VALUE, this._maxValue);
const numberOfBytesInUint8 = 8;
if (this._width === numberOfBytesInUint8) {
if (this._width === constants.NUMBER_OF_BYTES_IN_INT8) {
return value.toNumber();
}
return value;
}
public getDefaultValue(): BigNumber | number {
const defaultValue = UIntDataType._DEFAULT_VALUE;
if (this._width === constants.NUMBER_OF_BYTES_IN_INT8) {
return defaultValue.toNumber();
}
return defaultValue;
}
public getSignatureType(): string {
return `${SolidityTypes.Uint}${this._width}`;
}

View File

@@ -14,4 +14,8 @@ export const constants = {
DEFAULT_DECODING_RULES: { shouldConvertStructsToObjects: true } as DecodingRules,
DEFAULT_ENCODING_RULES: { shouldOptimize: true, shouldAnnotate: false } as EncodingRules,
/* tslint:enable no-object-literal-type-assertion */
};
EMPTY_EVM_WORD_STRING: '0x0000000000000000000000000000000000000000000000000000000000000000',
EMPTY_EVM_WORD_BUFFER: new Buffer('0x0000000000000000000000000000000000000000000000000000000000000000'),
NUMBER_OF_BYTES_IN_UINT8: 8,
NUMBER_OF_BYTES_IN_INT8: 8,
}