Comments and inline documentation for dynamic bytes

This commit is contained in:
Greg Hysen
2018-11-25 17:37:14 -08:00
parent ebaf9dd275
commit acd364b71c
4 changed files with 40 additions and 29 deletions

View File

@@ -22,10 +22,6 @@ export class Bool extends PayloadDataType {
}
}
public getSignature(): string {
return 'bool';
}
public encodeValue(value: boolean): Buffer {
const encodedValue = value ? '0x1' : '0x0';
const encodedValueBuf = ethUtil.setLengthLeft(
@@ -47,4 +43,8 @@ export class Bool extends PayloadDataType {
/* tslint:enable boolean-naming */
return value;
}
public getSignature(): string {
return 'bool';
}
}

View File

@@ -17,42 +17,53 @@ export class DynamicBytes extends PayloadDataType {
public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
super(dataItem, dataTypeFactory, DynamicBytes._SIZE_KNOWN_AT_COMPILE_TIME);
if (!DynamicBytes.matchType(dataItem.type)) {
throw new Error(`Tried to instantiate DynamicBytes with bad input: ${dataItem}`);
throw new Error(`Tried to instantiate Dynamic Bytes with bad input: ${dataItem}`);
}
}
public encodeValue(value: string | Buffer): Buffer {
if (typeof value === 'string' && !value.startsWith('0x')) {
throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '${value}'`);
}
// Encoded value is of the form: <length><value>, with each field padded to be word-aligned.
// 1/3 Construct the length
const valueBuf = ethUtil.toBuffer(value);
if (value.length % 2 !== 0) {
throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`);
}
const wordsForValue = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES);
const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES;
const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedDynamicBytesForValue);
const paddedLengthBuf = ethUtil.setLengthLeft(
ethUtil.toBuffer(valueBuf.byteLength),
Constants.EVM_WORD_WIDTH_IN_BYTES,
);
const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]);
return encodedValueBuf;
const wordsToStoreValuePadded = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES);
const bytesToStoreValuePadded = wordsToStoreValuePadded * Constants.EVM_WORD_WIDTH_IN_BYTES;
const lengthBuf = ethUtil.toBuffer(valueBuf.byteLength);
const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, Constants.EVM_WORD_WIDTH_IN_BYTES);
// 2/3 Construct the value
this._sanityCheckValue(value);
const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded);
// 3/3 Combine length and value
const encodedValue = Buffer.concat([lengthBufPadded, valueBufPadded]);
return encodedValue;
}
public decodeValue(calldata: RawCalldata): string {
// Encoded value is of the form: <length><value>, with each field padded to be word-aligned.
// 1/2 Decode length
const lengthBuf = calldata.popWord();
const lengthHex = ethUtil.bufferToHex(lengthBuf);
const length = parseInt(lengthHex, Constants.HEX_BASE);
const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES);
const paddedValueBuf = calldata.popWords(wordsForValue);
const valueBuf = paddedValueBuf.slice(0, length);
const decodedValue = ethUtil.bufferToHex(valueBuf);
return decodedValue;
// 2/2 Decode value
const wordsToStoreValuePadded = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES);
const valueBufPadded = calldata.popWords(wordsToStoreValuePadded);
const valueBuf = valueBufPadded.slice(0, length);
const value = ethUtil.bufferToHex(valueBuf);
this._sanityCheckValue(value);
return value;
}
public getSignature(): string {
return 'bytes';
}
private _sanityCheckValue(value: string | Buffer): void {
if (typeof value !== 'string') {
return;
}
if (!value.startsWith('0x')) {
throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`);
} else if (value.length % 2 !== 0) {
throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`);
}
}
}

View File

@@ -32,8 +32,8 @@ export class String extends PayloadDataType {
const valueBuf = new Buffer(value);
const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded);
// 3/3 Combine length and value
const encodedValueBuf = Buffer.concat([lengthBufPadded, valueBufPadded]);
return encodedValueBuf;
const encodedValue = Buffer.concat([lengthBufPadded, valueBufPadded]);
return encodedValue;
}
public decodeValue(calldata: RawCalldata): string {

View File

@@ -1018,7 +1018,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
// Encode Args and validate result
expect(() => {
dataType.encode(args);
}).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '01'");
}).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix.");
});
it('Should throw when pass in bad hex (include a half-byte)', async () => {
// Create DataType object