From 1c3b2b7141138f8da82197dfbdbdbfd76e7780b7 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 29 Aug 2018 11:39:52 -0700 Subject: Updated readBytes4 to match spec + added unit tests. These are 3.5/3.6 from audit --- .../protocol/AssetProxyOwner/AssetProxyOwner.sol | 13 +++++++++-- .../src/2.0.0/utils/LibBytes/LibBytes.sol | 7 +++++- packages/contracts/test/libraries/lib_bytes.ts | 26 +++++++++++++++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol b/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol index 8b7333646..e661f2427 100644 --- a/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol +++ b/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol @@ -105,11 +105,20 @@ contract AssetProxyOwner is uint256 index ) internal + pure returns (bytes4 result) { - require(b.length >= index + 4); + require( + b.length >= index + 4, + "GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED" + ); + + // Arrays are prefixed by a 32 byte length field + index += 32; + + // Read the bytes4 from array memory assembly { - result := mload(add(b, 32)) + result := mload(add(b, index)) // Solidity does not require us to clean the trailing bytes. // We do it anyway result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000) diff --git a/packages/contracts/src/2.0.0/utils/LibBytes/LibBytes.sol b/packages/contracts/src/2.0.0/utils/LibBytes/LibBytes.sol index 504e950a8..93873cbcc 100644 --- a/packages/contracts/src/2.0.0/utils/LibBytes/LibBytes.sol +++ b/packages/contracts/src/2.0.0/utils/LibBytes/LibBytes.sol @@ -467,8 +467,13 @@ library LibBytes { b.length >= index + 4, "GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED" ); + + // Arrays are prefixed by a 32 byte length field + index += 32; + + // Read the bytes4 from array memory assembly { - result := mload(add(b, 32)) + result := mload(add(b, index)) // Solidity does not require us to clean the trailing bytes. // We do it anyway result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000) diff --git a/packages/contracts/test/libraries/lib_bytes.ts b/packages/contracts/test/libraries/lib_bytes.ts index 1c497a226..6fda9dc97 100644 --- a/packages/contracts/test/libraries/lib_bytes.ts +++ b/packages/contracts/test/libraries/lib_bytes.ts @@ -41,6 +41,8 @@ describe('LibBytes', () => { const testBytes32B = '0x534877abd8443578526845cdfef020047528759477fedef87346527659aced32'; const testUint256 = new BigNumber(testBytes32, 16); const testUint256B = new BigNumber(testBytes32B, 16); + const testBytes4 = '0xabcdef12'; + const testBytes4B = new BigNumber(testBytes4, 16); let shortData: string; let shortTestBytes: string; let shortTestBytesAsBuffer: Buffer; @@ -462,8 +464,9 @@ describe('LibBytes', () => { // AssertionError: expected promise to be rejected with an error including 'revert' but it was fulfilled with '0x08c379a0' it('should revert if byte array has a length < 4', async () => { const byteArrayLessThan4Bytes = '0x010101'; + const offset = new BigNumber(0); return expectContractCallFailed( - libBytes.publicReadBytes4.callAsync(byteArrayLessThan4Bytes, new BigNumber(0)), + libBytes.publicReadBytes4.callAsync(byteArrayLessThan4Bytes, offset), RevertReason.LibBytesGreaterOrEqualTo4LengthRequired, ); }); @@ -472,6 +475,27 @@ describe('LibBytes', () => { const expectedFirst4Bytes = byteArrayLongerThan32Bytes.slice(0, 10); expect(first4Bytes).to.equal(expectedFirst4Bytes); }); + it('should successfully read bytes4 when the bytes4 takes up the whole array', async () => { + const testBytes4Offset = new BigNumber(0); + const bytes4 = await libBytes.publicReadBytes4.callAsync(testBytes4, testBytes4Offset); + return expect(bytes4).to.be.equal(testBytes4); + }); + it('should successfully read bytes4 when it is offset in the array)', async () => { + const bytes4ByteArrayBuffer = ethUtil.toBuffer(testBytes4); + const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef'); + const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, bytes4ByteArrayBuffer]); + const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer); + const testBytes4Offset = new BigNumber(prefixByteArrayBuffer.byteLength); + const bytes4 = await libBytes.publicReadBytes4.callAsync(combinedByteArray, testBytes4Offset); + return expect(bytes4).to.be.equal(testBytes4); + }); + it('should fail if the length between the offset and end of the byte array is too short to hold a bytes4', async () => { + const badOffset = new BigNumber(ethUtil.toBuffer(testBytes4).byteLength); + return expectContractCallFailed( + libBytes.publicReadBytes4.callAsync(testBytes4, badOffset), + RevertReason.LibBytesGreaterOrEqualTo4LengthRequired, + ); + }); }); describe('readBytesWithLength', () => { -- cgit v1.2.3