@0x:contracts-utils
Added RichErrors to LibBytes
This commit is contained in:
parent
c788db785b
commit
d3db2dcfbb
@ -13,7 +13,7 @@ import {
|
|||||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||||
import { transactionHashUtils } from '@0x/order-utils';
|
import { transactionHashUtils } from '@0x/order-utils';
|
||||||
import { EIP712DomainWithDefaultSchema, RevertReason, SignatureType, SignedOrder } from '@0x/types';
|
import { EIP712DomainWithDefaultSchema, RevertReason, SignatureType, SignedOrder } from '@0x/types';
|
||||||
import { BigNumber, providerUtils } from '@0x/utils';
|
import { BigNumber, LibBytesRevertErrors, providerUtils } from '@0x/utils';
|
||||||
import * as chai from 'chai';
|
import * as chai from 'chai';
|
||||||
import * as ethUtil from 'ethereumjs-util';
|
import * as ethUtil from 'ethereumjs-util';
|
||||||
|
|
||||||
@ -206,10 +206,12 @@ describe('Mixins tests', () => {
|
|||||||
});
|
});
|
||||||
it('should revert if data is less than 4 bytes long', async () => {
|
it('should revert if data is less than 4 bytes long', async () => {
|
||||||
const data = '0x010203';
|
const data = '0x010203';
|
||||||
await expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
mixins.decodeOrdersFromFillData.callAsync(data),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo4LengthRequired,
|
new BigNumber(3), // the length of data
|
||||||
|
new BigNumber(4),
|
||||||
);
|
);
|
||||||
|
return expect(mixins.decodeOrdersFromFillData.callAsync(data)).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "./LibBytesRichErrors.sol";
|
||||||
|
|
||||||
|
|
||||||
library LibBytes {
|
library LibBytes {
|
||||||
|
|
||||||
@ -38,7 +40,7 @@ library LibBytes {
|
|||||||
}
|
}
|
||||||
return memoryAddress;
|
return memoryAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Gets the memory address for the contents of a byte array.
|
/// @dev Gets the memory address for the contents of a byte array.
|
||||||
/// @param input Byte array to lookup.
|
/// @param input Byte array to lookup.
|
||||||
/// @return memoryAddress Memory address of the contents of the byte array.
|
/// @return memoryAddress Memory address of the contents of the byte array.
|
||||||
@ -121,7 +123,7 @@ library LibBytes {
|
|||||||
source := add(source, 32)
|
source := add(source, 32)
|
||||||
dest := add(dest, 32)
|
dest := add(dest, 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the last 32 bytes
|
// Write the last 32 bytes
|
||||||
mstore(dEnd, last)
|
mstore(dEnd, last)
|
||||||
}
|
}
|
||||||
@ -152,7 +154,7 @@ library LibBytes {
|
|||||||
sEnd := sub(sEnd, 32)
|
sEnd := sub(sEnd, 32)
|
||||||
dEnd := sub(dEnd, 32)
|
dEnd := sub(dEnd, 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the first 32 bytes
|
// Write the first 32 bytes
|
||||||
mstore(dest, first)
|
mstore(dest, first)
|
||||||
}
|
}
|
||||||
@ -174,15 +176,23 @@ library LibBytes {
|
|||||||
pure
|
pure
|
||||||
returns (bytes memory result)
|
returns (bytes memory result)
|
||||||
{
|
{
|
||||||
require(
|
// Ensure that the from and to positions are valid positions for a slice within
|
||||||
from <= to,
|
// the byte array that is being used.
|
||||||
"FROM_LESS_THAN_TO_REQUIRED"
|
if (from > to) {
|
||||||
);
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
require(
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
|
||||||
to <= b.length,
|
from,
|
||||||
"TO_LESS_THAN_LENGTH_REQUIRED"
|
to
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
if (to > b.length) {
|
||||||
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
|
||||||
|
to,
|
||||||
|
b.length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new bytes structure and copy contents
|
// Create a new bytes structure and copy contents
|
||||||
result = new bytes(to - from);
|
result = new bytes(to - from);
|
||||||
memCopy(
|
memCopy(
|
||||||
@ -192,7 +202,7 @@ library LibBytes {
|
|||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Returns a slice from a byte array without preserving the input.
|
/// @dev Returns a slice from a byte array without preserving the input.
|
||||||
/// @param b The byte array to take a slice from. Will be destroyed in the process.
|
/// @param b The byte array to take a slice from. Will be destroyed in the process.
|
||||||
/// @param from The starting index for the slice (inclusive).
|
/// @param from The starting index for the slice (inclusive).
|
||||||
@ -208,15 +218,23 @@ library LibBytes {
|
|||||||
pure
|
pure
|
||||||
returns (bytes memory result)
|
returns (bytes memory result)
|
||||||
{
|
{
|
||||||
require(
|
// Ensure that the from and to positions are valid positions for a slice within
|
||||||
from <= to,
|
// the byte array that is being used.
|
||||||
"FROM_LESS_THAN_TO_REQUIRED"
|
if (from > to) {
|
||||||
);
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
require(
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
|
||||||
to <= b.length,
|
from,
|
||||||
"TO_LESS_THAN_LENGTH_REQUIRED"
|
to
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
if (to > b.length) {
|
||||||
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
|
||||||
|
to,
|
||||||
|
b.length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new bytes structure around [from, to) in-place.
|
// Create a new bytes structure around [from, to) in-place.
|
||||||
assembly {
|
assembly {
|
||||||
result := add(b, from)
|
result := add(b, from)
|
||||||
@ -233,10 +251,13 @@ library LibBytes {
|
|||||||
pure
|
pure
|
||||||
returns (bytes1 result)
|
returns (bytes1 result)
|
||||||
{
|
{
|
||||||
require(
|
if (b.length == 0) {
|
||||||
b.length > 0,
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
"GREATER_THAN_ZERO_LENGTH_REQUIRED"
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
|
||||||
);
|
b.length,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Store last byte.
|
// Store last byte.
|
||||||
result = b[b.length - 1];
|
result = b[b.length - 1];
|
||||||
@ -257,10 +278,13 @@ library LibBytes {
|
|||||||
pure
|
pure
|
||||||
returns (address result)
|
returns (address result)
|
||||||
{
|
{
|
||||||
require(
|
if (b.length < 20) {
|
||||||
b.length >= 20,
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED"
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
);
|
b.length,
|
||||||
|
20 // 20 is length of address
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Store last 20 bytes.
|
// Store last 20 bytes.
|
||||||
result = readAddress(b, b.length - 20);
|
result = readAddress(b, b.length - 20);
|
||||||
@ -303,10 +327,13 @@ library LibBytes {
|
|||||||
pure
|
pure
|
||||||
returns (address result)
|
returns (address result)
|
||||||
{
|
{
|
||||||
require(
|
if (b.length < index + 20) {
|
||||||
b.length >= index + 20, // 20 is length of address
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED"
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
);
|
b.length,
|
||||||
|
index + 20 // 20 is length of address
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Add offset to index:
|
// Add offset to index:
|
||||||
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
|
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
|
||||||
@ -335,10 +362,13 @@ library LibBytes {
|
|||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
{
|
{
|
||||||
require(
|
if (b.length < index + 20) {
|
||||||
b.length >= index + 20, // 20 is length of address
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED"
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
);
|
b.length,
|
||||||
|
index + 20 // 20 is length of address
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Add offset to index:
|
// Add offset to index:
|
||||||
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
|
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
|
||||||
@ -359,7 +389,7 @@ library LibBytes {
|
|||||||
mload(add(b, index)),
|
mload(add(b, index)),
|
||||||
0xffffffffffffffffffffffff0000000000000000000000000000000000000000
|
0xffffffffffffffffffffffff0000000000000000000000000000000000000000
|
||||||
)
|
)
|
||||||
|
|
||||||
// Make sure input address is clean.
|
// Make sure input address is clean.
|
||||||
// (Solidity does not guarantee this)
|
// (Solidity does not guarantee this)
|
||||||
input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)
|
input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)
|
||||||
@ -381,10 +411,13 @@ library LibBytes {
|
|||||||
pure
|
pure
|
||||||
returns (bytes32 result)
|
returns (bytes32 result)
|
||||||
{
|
{
|
||||||
require(
|
if (b.length < index + 32) {
|
||||||
b.length >= index + 32,
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED"
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
);
|
b.length,
|
||||||
|
index + 32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Arrays are prefixed by a 256 bit length parameter
|
// Arrays are prefixed by a 256 bit length parameter
|
||||||
index += 32;
|
index += 32;
|
||||||
@ -408,10 +441,13 @@ library LibBytes {
|
|||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
{
|
{
|
||||||
require(
|
if (b.length < index + 32) {
|
||||||
b.length >= index + 32,
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED"
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
);
|
b.length,
|
||||||
|
index + 32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Arrays are prefixed by a 256 bit length parameter
|
// Arrays are prefixed by a 256 bit length parameter
|
||||||
index += 32;
|
index += 32;
|
||||||
@ -465,10 +501,13 @@ library LibBytes {
|
|||||||
pure
|
pure
|
||||||
returns (bytes4 result)
|
returns (bytes4 result)
|
||||||
{
|
{
|
||||||
require(
|
if (b.length < index + 4) {
|
||||||
b.length >= index + 4,
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
"GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED"
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
|
||||||
);
|
b.length,
|
||||||
|
index + 4
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Arrays are prefixed by a 32 byte length field
|
// Arrays are prefixed by a 32 byte length field
|
||||||
index += 32;
|
index += 32;
|
||||||
@ -503,11 +542,15 @@ library LibBytes {
|
|||||||
|
|
||||||
// Assert length of <b> is valid, given
|
// Assert length of <b> is valid, given
|
||||||
// length of nested bytes
|
// length of nested bytes
|
||||||
require(
|
if (b.length < index + nestedBytesLength) {
|
||||||
b.length >= index + nestedBytesLength,
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
"GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED"
|
LibBytesRichErrors
|
||||||
);
|
.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
||||||
|
b.length,
|
||||||
|
index + nestedBytesLength
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Return a pointer to the byte array as it exists inside `b`
|
// Return a pointer to the byte array as it exists inside `b`
|
||||||
assembly {
|
assembly {
|
||||||
result := add(b, index)
|
result := add(b, index)
|
||||||
@ -529,10 +572,14 @@ library LibBytes {
|
|||||||
{
|
{
|
||||||
// Assert length of <b> is valid, given
|
// Assert length of <b> is valid, given
|
||||||
// length of input
|
// length of input
|
||||||
require(
|
if (b.length < index + 32 + input.length) {
|
||||||
b.length >= index + 32 + input.length, // 32 bytes to store length
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
"GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED"
|
LibBytesRichErrors
|
||||||
);
|
.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
||||||
|
b.length,
|
||||||
|
index + 32 + input.length // 32 bytes to store length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Copy <input> into <b>
|
// Copy <input> into <b>
|
||||||
memCopy(
|
memCopy(
|
||||||
@ -554,10 +601,14 @@ library LibBytes {
|
|||||||
{
|
{
|
||||||
uint256 sourceLen = source.length;
|
uint256 sourceLen = source.length;
|
||||||
// Dest length must be >= source length, or some bytes would not be copied.
|
// Dest length must be >= source length, or some bytes would not be copied.
|
||||||
require(
|
if (dest.length < sourceLen) {
|
||||||
dest.length >= sourceLen,
|
LibBytesRichErrors.InvalidByteOperationErrorRevert(
|
||||||
"GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED"
|
LibBytesRichErrors
|
||||||
);
|
.InvalidByteOperationErrorCodes.DestinationLengthGreaterThanOrEqualSourceLengthRequired,
|
||||||
|
dest.length,
|
||||||
|
sourceLen
|
||||||
|
);
|
||||||
|
}
|
||||||
memCopy(
|
memCopy(
|
||||||
dest.contentAddress(),
|
dest.contentAddress(),
|
||||||
source.contentAddress(),
|
source.contentAddress(),
|
||||||
|
59
contracts/utils/contracts/src/LibBytesRichErrors.sol
Normal file
59
contracts/utils/contracts/src/LibBytesRichErrors.sol
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "./LibRichErrors.sol";
|
||||||
|
|
||||||
|
|
||||||
|
library LibBytesRichErrors {
|
||||||
|
|
||||||
|
using LibRichErrors for *;
|
||||||
|
|
||||||
|
enum InvalidByteOperationErrorCodes {
|
||||||
|
FromLessThanOrEqualsToRequired,
|
||||||
|
ToLessThanOrEqualsLengthRequired,
|
||||||
|
LengthGreaterThanZeroRequired,
|
||||||
|
LengthGreaterThanOrEqualsFourRequired,
|
||||||
|
LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
|
LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
|
LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
||||||
|
DestinationLengthGreaterThanOrEqualSourceLengthRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytes4(keccak256("InvalidByteOperationError(uint8,uint256,uint256)"))
|
||||||
|
bytes4 internal constant INVALID_BYTE_OPERATION_ERROR_SELECTOR =
|
||||||
|
0x28006595;
|
||||||
|
|
||||||
|
// solhint-disable func-name-mixedcase
|
||||||
|
function InvalidByteOperationErrorRevert(
|
||||||
|
InvalidByteOperationErrorCodes errorCode,
|
||||||
|
uint256 endpoint,
|
||||||
|
uint256 required
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
{
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
INVALID_BYTE_OPERATION_ERROR_SELECTOR,
|
||||||
|
errorCode,
|
||||||
|
endpoint,
|
||||||
|
required
|
||||||
|
)._rrevert();
|
||||||
|
}
|
||||||
|
}
|
58
contracts/utils/contracts/src/LibRichErrors.sol
Normal file
58
contracts/utils/contracts/src/LibRichErrors.sol
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
library LibRichErrors {
|
||||||
|
|
||||||
|
// bytes4(keccak256("Error(string)"))
|
||||||
|
bytes4 internal constant STANDARD_ERROR_SELECTOR =
|
||||||
|
0x08c379a0;
|
||||||
|
|
||||||
|
// solhint-disable func-name-mixedcase
|
||||||
|
/// @dev ABI encode a standard, string revert error payload.
|
||||||
|
/// This is the same payload that would be included by a `revert(string)`
|
||||||
|
/// solidity statement. It has the function signature `Error(string)`.
|
||||||
|
/// @param message The error string.
|
||||||
|
/// @return The ABI encoded error.
|
||||||
|
function StandardError(
|
||||||
|
string memory message
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
STANDARD_ERROR_SELECTOR,
|
||||||
|
bytes(message)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// solhint-enable func-name-mixedcase
|
||||||
|
|
||||||
|
/// @dev Reverts an encoded rich revert reason `errorData`.
|
||||||
|
/// @param errorData ABI encoded error data.
|
||||||
|
function _rrevert(bytes memory errorData)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
{
|
||||||
|
assembly {
|
||||||
|
revert(add(errorData, 0x20), mload(errorData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,9 +22,20 @@ import "../src/LibBytes.sol";
|
|||||||
|
|
||||||
|
|
||||||
contract TestLibBytes {
|
contract TestLibBytes {
|
||||||
|
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
|
||||||
|
/// @dev Pops the last byte off of a byte array by modifying its length.
|
||||||
|
/// @param b Byte array that will be modified.
|
||||||
|
/// @return The byte that was popped off.
|
||||||
|
function publicPopLastByteStateful(bytes memory b)
|
||||||
|
public
|
||||||
|
returns (bytes memory, bytes1 result)
|
||||||
|
{
|
||||||
|
result = b.popLastByte();
|
||||||
|
return (b, result);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Pops the last byte off of a byte array by modifying its length.
|
/// @dev Pops the last byte off of a byte array by modifying its length.
|
||||||
/// @param b Byte array that will be modified.
|
/// @param b Byte array that will be modified.
|
||||||
/// @return The byte that was popped off.
|
/// @return The byte that was popped off.
|
||||||
@ -61,7 +72,7 @@ contract TestLibBytes {
|
|||||||
equal = lhs.equals(rhs);
|
equal = lhs.equals(rhs);
|
||||||
return equal;
|
return equal;
|
||||||
}
|
}
|
||||||
|
|
||||||
function publicEqualsPop1(bytes memory lhs, bytes memory rhs)
|
function publicEqualsPop1(bytes memory lhs, bytes memory rhs)
|
||||||
public
|
public
|
||||||
pure
|
pure
|
||||||
@ -236,7 +247,7 @@ contract TestLibBytes {
|
|||||||
b.writeBytesWithLength(index, input);
|
b.writeBytesWithLength(index, input);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Copies a block of memory from one location to another.
|
/// @dev Copies a block of memory from one location to another.
|
||||||
/// @param mem Memory contents we want to apply memCopy to
|
/// @param mem Memory contents we want to apply memCopy to
|
||||||
/// @param dest Destination offset into <mem>.
|
/// @param dest Destination offset into <mem>.
|
||||||
|
@ -1,16 +1,7 @@
|
|||||||
import {
|
import { chaiSetup, constants, provider, txDefaults, typeEncodingUtils, web3Wrapper } from '@0x/contracts-test-utils';
|
||||||
chaiSetup,
|
|
||||||
constants,
|
|
||||||
expectContractCallFailedAsync,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
typeEncodingUtils,
|
|
||||||
web3Wrapper,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
||||||
import { RevertReason } from '@0x/types';
|
import { BigNumber, LibBytesRevertErrors } from '@0x/utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
import BN = require('bn.js');
|
import BN = require('bn.js');
|
||||||
import * as chai from 'chai';
|
import * as chai from 'chai';
|
||||||
import ethUtil = require('ethereumjs-util');
|
import ethUtil = require('ethereumjs-util');
|
||||||
@ -106,10 +97,12 @@ describe('LibBytes', () => {
|
|||||||
|
|
||||||
describe('popLastByte', () => {
|
describe('popLastByte', () => {
|
||||||
it('should revert if length is 0', async () => {
|
it('should revert if length is 0', async () => {
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicPopLastByte.callAsync(constants.NULL_BYTES),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
|
||||||
RevertReason.LibBytesGreaterThanZeroLengthRequired,
|
constants.ZERO_AMOUNT,
|
||||||
|
constants.ZERO_AMOUNT,
|
||||||
);
|
);
|
||||||
|
return expect(libBytes.publicPopLastByte.callAsync(constants.NULL_BYTES)).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should pop the last byte from the input and return it when array holds more than 1 byte', async () => {
|
it('should pop the last byte from the input and return it when array holds more than 1 byte', async () => {
|
||||||
const [newBytes, poppedByte] = await libBytes.publicPopLastByte.callAsync(byteArrayLongerThan32Bytes);
|
const [newBytes, poppedByte] = await libBytes.publicPopLastByte.callAsync(byteArrayLongerThan32Bytes);
|
||||||
@ -128,9 +121,14 @@ describe('LibBytes', () => {
|
|||||||
|
|
||||||
describe('popLast20Bytes', () => {
|
describe('popLast20Bytes', () => {
|
||||||
it('should revert if length is less than 20', async () => {
|
it('should revert if length is less than 20', async () => {
|
||||||
return expectContractCallFailedAsync(
|
const byteLen = (byteArrayShorterThan20Bytes.length - 2) / 2;
|
||||||
libBytes.publicPopLast20Bytes.callAsync(byteArrayShorterThan20Bytes),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
|
new BigNumber(byteLen), // length of byteArrayShorterThan20Byte
|
||||||
|
new BigNumber(20),
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicPopLast20Bytes.callAsync(byteArrayShorterThan20Bytes)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should pop the last 20 bytes from the input and return it when array holds more than 20 bytes', async () => {
|
it('should pop the last 20 bytes from the input and return it when array holds more than 20 bytes', async () => {
|
||||||
@ -203,10 +201,16 @@ describe('LibBytes', () => {
|
|||||||
|
|
||||||
describe('deepCopyBytes', () => {
|
describe('deepCopyBytes', () => {
|
||||||
it('should revert if dest is shorter than source', async () => {
|
it('should revert if dest is shorter than source', async () => {
|
||||||
return expectContractCallFailedAsync(
|
const endpointByteLen = (byteArrayShorterThan20Bytes.length - 2) / 2;
|
||||||
libBytes.publicDeepCopyBytes.callAsync(byteArrayShorterThan32Bytes, byteArrayLongerThan32Bytes),
|
const requiredByteLen = (byteArrayLongerThan32Bytes.length - 2) / 2;
|
||||||
RevertReason.LibBytesGreaterOrEqualToSourceBytesLengthRequired,
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.DestinationLengthGreaterThanOrEqualSourceLengthRequired,
|
||||||
|
new BigNumber(endpointByteLen), // length of byteArrayShorterThan20Byte
|
||||||
|
new BigNumber(requiredByteLen), // length of byteArrayShorterThan20Byte
|
||||||
);
|
);
|
||||||
|
return expect(
|
||||||
|
libBytes.publicDeepCopyBytes.callAsync(byteArrayShorterThan32Bytes, byteArrayLongerThan32Bytes),
|
||||||
|
).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should overwrite dest with source if source and dest have equal length', async () => {
|
it('should overwrite dest with source if source and dest have equal length', async () => {
|
||||||
const zeroedByteArrayLongerThan32Bytes = `0x${_.repeat('0', byteArrayLongerThan32Bytes.length - 2)}`;
|
const zeroedByteArrayLongerThan32Bytes = `0x${_.repeat('0', byteArrayLongerThan32Bytes.length - 2)}`;
|
||||||
@ -256,18 +260,22 @@ describe('LibBytes', () => {
|
|||||||
it('should fail if the byte array is too short to hold an address', async () => {
|
it('should fail if the byte array is too short to hold an address', async () => {
|
||||||
const shortByteArray = '0xabcdef';
|
const shortByteArray = '0xabcdef';
|
||||||
const offset = new BigNumber(0);
|
const offset = new BigNumber(0);
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicReadAddress.callAsync(shortByteArray, offset),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired,
|
new BigNumber(3),
|
||||||
|
new BigNumber(20),
|
||||||
);
|
);
|
||||||
|
return expect(libBytes.publicReadAddress.callAsync(shortByteArray, offset)).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => {
|
it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => {
|
||||||
const byteArray = testAddress;
|
const byteArray = testAddress;
|
||||||
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
|
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicReadAddress.callAsync(byteArray, badOffset),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired,
|
new BigNumber(20),
|
||||||
|
new BigNumber(40),
|
||||||
);
|
);
|
||||||
|
return expect(libBytes.publicReadAddress.callAsync(byteArray, badOffset)).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -300,17 +308,26 @@ describe('LibBytes', () => {
|
|||||||
});
|
});
|
||||||
it('should fail if the byte array is too short to hold an address', async () => {
|
it('should fail if the byte array is too short to hold an address', async () => {
|
||||||
const offset = new BigNumber(0);
|
const offset = new BigNumber(0);
|
||||||
return expectContractCallFailedAsync(
|
const byteLen = ethUtil.toBuffer(byteArrayShorterThan20Bytes).byteLength;
|
||||||
libBytes.publicWriteAddress.callAsync(byteArrayShorterThan20Bytes, offset, testAddress),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
|
new BigNumber(byteLen),
|
||||||
|
new BigNumber(20),
|
||||||
);
|
);
|
||||||
|
return expect(
|
||||||
|
libBytes.publicWriteAddress.callAsync(byteArrayShorterThan20Bytes, offset, testAddress),
|
||||||
|
).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => {
|
it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => {
|
||||||
const byteArray = byteArrayLongerThan32Bytes;
|
const byteArray = byteArrayLongerThan32Bytes;
|
||||||
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
|
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicWriteAddress.callAsync(byteArray, badOffset, testAddress),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired,
|
badOffset,
|
||||||
|
badOffset.plus(new BigNumber(20)),
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicWriteAddress.callAsync(byteArray, badOffset, testAddress)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -332,17 +349,24 @@ describe('LibBytes', () => {
|
|||||||
});
|
});
|
||||||
it('should fail if the byte array is too short to hold a bytes32', async () => {
|
it('should fail if the byte array is too short to hold a bytes32', async () => {
|
||||||
const offset = new BigNumber(0);
|
const offset = new BigNumber(0);
|
||||||
return expectContractCallFailedAsync(
|
const byteLen = new BigNumber((byteArrayShorterThan32Bytes.length - 2) / 2);
|
||||||
libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
|
byteLen,
|
||||||
|
new BigNumber(32),
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => {
|
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => {
|
||||||
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength);
|
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength);
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicReadBytes32.callAsync(testBytes32, badOffset),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
badOffset,
|
||||||
|
badOffset.plus(new BigNumber(32)),
|
||||||
);
|
);
|
||||||
|
return expect(libBytes.publicReadBytes32.callAsync(testBytes32, badOffset)).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -375,17 +399,26 @@ describe('LibBytes', () => {
|
|||||||
});
|
});
|
||||||
it('should fail if the byte array is too short to hold a bytes32', async () => {
|
it('should fail if the byte array is too short to hold a bytes32', async () => {
|
||||||
const offset = new BigNumber(0);
|
const offset = new BigNumber(0);
|
||||||
return expectContractCallFailedAsync(
|
const byteLen = new BigNumber((byteArrayShorterThan32Bytes.length - 2) / 2);
|
||||||
libBytes.publicWriteBytes32.callAsync(byteArrayShorterThan32Bytes, offset, testBytes32),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
|
byteLen,
|
||||||
|
new BigNumber(32),
|
||||||
);
|
);
|
||||||
|
return expect(
|
||||||
|
libBytes.publicWriteBytes32.callAsync(byteArrayShorterThan32Bytes, offset, testBytes32),
|
||||||
|
).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => {
|
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => {
|
||||||
const byteArray = byteArrayLongerThan32Bytes;
|
const byteArray = byteArrayLongerThan32Bytes;
|
||||||
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
|
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicWriteBytes32.callAsync(byteArray, badOffset, testBytes32),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
badOffset,
|
||||||
|
badOffset.plus(new BigNumber(32)),
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicWriteBytes32.callAsync(byteArray, badOffset, testBytes32)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -411,9 +444,14 @@ describe('LibBytes', () => {
|
|||||||
});
|
});
|
||||||
it('should fail if the byte array is too short to hold a uint256', async () => {
|
it('should fail if the byte array is too short to hold a uint256', async () => {
|
||||||
const offset = new BigNumber(0);
|
const offset = new BigNumber(0);
|
||||||
return expectContractCallFailedAsync(
|
const byteLen = new BigNumber((byteArrayShorterThan32Bytes.length - 2) / 2);
|
||||||
libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
|
byteLen,
|
||||||
|
new BigNumber(32),
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => {
|
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => {
|
||||||
@ -421,10 +459,12 @@ describe('LibBytes', () => {
|
|||||||
const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256);
|
const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256);
|
||||||
const byteArray = ethUtil.bufferToHex(testUint256AsBuffer);
|
const byteArray = ethUtil.bufferToHex(testUint256AsBuffer);
|
||||||
const badOffset = new BigNumber(testUint256AsBuffer.byteLength);
|
const badOffset = new BigNumber(testUint256AsBuffer.byteLength);
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicReadUint256.callAsync(byteArray, badOffset),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
badOffset,
|
||||||
|
badOffset.plus(new BigNumber(32)),
|
||||||
);
|
);
|
||||||
|
return expect(libBytes.publicReadUint256.callAsync(byteArray, badOffset)).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -461,17 +501,26 @@ describe('LibBytes', () => {
|
|||||||
});
|
});
|
||||||
it('should fail if the byte array is too short to hold a uint256', async () => {
|
it('should fail if the byte array is too short to hold a uint256', async () => {
|
||||||
const offset = new BigNumber(0);
|
const offset = new BigNumber(0);
|
||||||
return expectContractCallFailedAsync(
|
const byteLen = new BigNumber((byteArrayShorterThan32Bytes.length - 2) / 2);
|
||||||
libBytes.publicWriteUint256.callAsync(byteArrayShorterThan32Bytes, offset, testUint256),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
|
byteLen,
|
||||||
|
new BigNumber(32),
|
||||||
);
|
);
|
||||||
|
return expect(
|
||||||
|
libBytes.publicWriteUint256.callAsync(byteArrayShorterThan32Bytes, offset, testUint256),
|
||||||
|
).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => {
|
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => {
|
||||||
const byteArray = byteArrayLongerThan32Bytes;
|
const byteArray = byteArrayLongerThan32Bytes;
|
||||||
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
|
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicWriteUint256.callAsync(byteArray, badOffset, testUint256),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
badOffset,
|
||||||
|
badOffset.plus(new BigNumber(32)),
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicWriteUint256.callAsync(byteArray, badOffset, testUint256)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -480,10 +529,15 @@ describe('LibBytes', () => {
|
|||||||
// AssertionError: expected promise to be rejected with an error including 'revert' but it was fulfilled with '0x08c379a0'
|
// 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 () => {
|
it('should revert if byte array has a length < 4', async () => {
|
||||||
const byteArrayLessThan4Bytes = '0x010101';
|
const byteArrayLessThan4Bytes = '0x010101';
|
||||||
|
const byteLen = (byteArrayLessThan4Bytes.length - 2) / 2;
|
||||||
const offset = new BigNumber(0);
|
const offset = new BigNumber(0);
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicReadBytes4.callAsync(byteArrayLessThan4Bytes, offset),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo4LengthRequired,
|
new BigNumber(byteLen), // length of byteArrayLessThan4Bytes
|
||||||
|
new BigNumber(4),
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicReadBytes4.callAsync(byteArrayLessThan4Bytes, offset)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should return the first 4 bytes of a byte array of arbitrary length', async () => {
|
it('should return the first 4 bytes of a byte array of arbitrary length', async () => {
|
||||||
@ -507,10 +561,13 @@ describe('LibBytes', () => {
|
|||||||
});
|
});
|
||||||
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes4', async () => {
|
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);
|
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes4).byteLength);
|
||||||
return expectContractCallFailedAsync(
|
const byteLen = new BigNumber((testBytes4.length - 2) / 2);
|
||||||
libBytes.publicReadBytes4.callAsync(testBytes4, badOffset),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualTo4LengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
|
||||||
|
byteLen,
|
||||||
|
badOffset.plus(new BigNumber(4)),
|
||||||
);
|
);
|
||||||
|
return expect(libBytes.publicReadBytes4.callAsync(testBytes4, badOffset)).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -557,30 +614,48 @@ describe('LibBytes', () => {
|
|||||||
it('should fail if the byte array is too short to hold the length of a nested byte array', async () => {
|
it('should fail if the byte array is too short to hold the length of a nested byte array', async () => {
|
||||||
// The length of the nested array is 32 bytes. By storing less than 32 bytes, a length cannot be read.
|
// The length of the nested array is 32 bytes. By storing less than 32 bytes, a length cannot be read.
|
||||||
const offset = new BigNumber(0);
|
const offset = new BigNumber(0);
|
||||||
return expectContractCallFailedAsync(
|
const byteLen = new BigNumber((byteArrayShorterThan32Bytes.length - 2) / 2);
|
||||||
libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, offset),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
|
byteLen,
|
||||||
|
new BigNumber(32),
|
||||||
);
|
);
|
||||||
|
return expect(
|
||||||
|
libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, offset),
|
||||||
|
).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should fail if we store a nested byte array length, without a nested byte array', async () => {
|
it('should fail if we store a nested byte array length, without a nested byte array', async () => {
|
||||||
const offset = new BigNumber(0);
|
const offset = new BigNumber(0);
|
||||||
return expectContractCallFailedAsync(
|
const byteLen = new BigNumber((testBytes32.length - 2) / 2);
|
||||||
libBytes.publicReadBytesWithLength.callAsync(testBytes32, offset),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
||||||
|
byteLen,
|
||||||
|
(new BigNumber(testBytes32)).plus(32),
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicReadBytesWithLength.callAsync(testBytes32, offset)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => {
|
it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => {
|
||||||
const badOffset = new BigNumber(ethUtil.toBuffer(byteArrayShorterThan32Bytes).byteLength);
|
const badOffset = new BigNumber(ethUtil.toBuffer(byteArrayShorterThan32Bytes).byteLength);
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, badOffset),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
badOffset,
|
||||||
|
badOffset.plus(new BigNumber(32)),
|
||||||
);
|
);
|
||||||
|
return expect(
|
||||||
|
libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, badOffset),
|
||||||
|
).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should fail if the length between the offset and end of the byte array is too short to hold the nested byte array', async () => {
|
it('should fail if the length between the offset and end of the byte array is too short to hold the nested byte array', async () => {
|
||||||
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength);
|
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength);
|
||||||
return expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicReadBytesWithLength.callAsync(testBytes32, badOffset),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
|
badOffset,
|
||||||
|
badOffset.plus(new BigNumber(32)),
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicReadBytesWithLength.callAsync(testBytes32, badOffset)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -690,18 +765,28 @@ describe('LibBytes', () => {
|
|||||||
it('should fail if the byte array is too short to hold the length of a nested byte array', async () => {
|
it('should fail if the byte array is too short to hold the length of a nested byte array', async () => {
|
||||||
const offset = new BigNumber(0);
|
const offset = new BigNumber(0);
|
||||||
const emptyByteArray = ethUtil.bufferToHex(new Buffer(1));
|
const emptyByteArray = ethUtil.bufferToHex(new Buffer(1));
|
||||||
return expectContractCallFailedAsync(
|
const inputLen = new BigNumber((longData.length - 2) / 2);
|
||||||
libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, offset, longData),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
||||||
|
new BigNumber(1),
|
||||||
|
new BigNumber(32).plus(inputLen),
|
||||||
);
|
);
|
||||||
|
return expect(
|
||||||
|
libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, offset, longData),
|
||||||
|
).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => {
|
it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => {
|
||||||
const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength));
|
const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength));
|
||||||
const badOffset = new BigNumber(ethUtil.toBuffer(shortTestBytesAsBuffer).byteLength);
|
const badOffset = new BigNumber(ethUtil.toBuffer(shortTestBytesAsBuffer).byteLength);
|
||||||
return expectContractCallFailedAsync(
|
const inputLen = new BigNumber((shortData.length - 2) / 2);
|
||||||
libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, badOffset, shortData),
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired,
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
||||||
|
badOffset,
|
||||||
|
badOffset.plus(new BigNumber(32)).plus(inputLen),
|
||||||
);
|
);
|
||||||
|
return expect(
|
||||||
|
libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, badOffset, shortData),
|
||||||
|
).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -875,9 +960,13 @@ describe('LibBytes', () => {
|
|||||||
it('should revert if from > to', async () => {
|
it('should revert if from > to', async () => {
|
||||||
const from = new BigNumber(1);
|
const from = new BigNumber(1);
|
||||||
const to = new BigNumber(0);
|
const to = new BigNumber(0);
|
||||||
expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicSlice.callAsync(byteArrayLongerThan32Bytes, from, to),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
|
||||||
RevertReason.FromLessThanToRequired,
|
from,
|
||||||
|
to,
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicSlice.callAsync(byteArrayLongerThan32Bytes, from, to)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should return a byte array of length 0 if from == to', async () => {
|
it('should return a byte array of length 0 if from == to', async () => {
|
||||||
@ -896,12 +985,16 @@ describe('LibBytes', () => {
|
|||||||
expect(result).to.eq(constants.NULL_BYTES);
|
expect(result).to.eq(constants.NULL_BYTES);
|
||||||
});
|
});
|
||||||
it('should revert if to > input.length', async () => {
|
it('should revert if to > input.length', async () => {
|
||||||
const byteLen = (byteArrayLongerThan32Bytes.length - 2) / 2;
|
const byteLen: number = (byteArrayLongerThan32Bytes.length - 2) / 2;
|
||||||
const from = new BigNumber(0);
|
const from = new BigNumber(0);
|
||||||
const to = new BigNumber(byteLen).plus(1);
|
const to = new BigNumber(byteLen).plus(1);
|
||||||
expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicSlice.callAsync(byteArrayLongerThan32Bytes, from, to),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
|
||||||
RevertReason.ToLessThanLengthRequired,
|
to,
|
||||||
|
new BigNumber(byteLen),
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicSlice.callAsync(byteArrayLongerThan32Bytes, from, to)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should slice a section of the input', async () => {
|
it('should slice a section of the input', async () => {
|
||||||
@ -926,9 +1019,13 @@ describe('LibBytes', () => {
|
|||||||
it('should revert if from > to', async () => {
|
it('should revert if from > to', async () => {
|
||||||
const from = new BigNumber(1);
|
const from = new BigNumber(1);
|
||||||
const to = new BigNumber(0);
|
const to = new BigNumber(0);
|
||||||
expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicSliceDestructive.callAsync(byteArrayLongerThan32Bytes, from, to),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
|
||||||
RevertReason.FromLessThanToRequired,
|
from,
|
||||||
|
to,
|
||||||
|
);
|
||||||
|
return expect(libBytes.publicSlice.callAsync(byteArrayLongerThan32Bytes, from, to)).to.revertWith(
|
||||||
|
expectedError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should return a byte array of length 0 if from == to', async () => {
|
it('should return a byte array of length 0 if from == to', async () => {
|
||||||
@ -948,10 +1045,14 @@ describe('LibBytes', () => {
|
|||||||
const byteLen = (byteArrayLongerThan32Bytes.length - 2) / 2;
|
const byteLen = (byteArrayLongerThan32Bytes.length - 2) / 2;
|
||||||
const from = new BigNumber(0);
|
const from = new BigNumber(0);
|
||||||
const to = new BigNumber(byteLen).plus(1);
|
const to = new BigNumber(byteLen).plus(1);
|
||||||
expectContractCallFailedAsync(
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
libBytes.publicSliceDestructive.callAsync(byteArrayLongerThan32Bytes, from, to),
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
|
||||||
RevertReason.ToLessThanLengthRequired,
|
to,
|
||||||
|
new BigNumber(byteLen),
|
||||||
);
|
);
|
||||||
|
return expect(
|
||||||
|
libBytes.publicSliceDestructive.callAsync(byteArrayLongerThan32Bytes, from, to),
|
||||||
|
).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should slice a section of the input', async () => {
|
it('should slice a section of the input', async () => {
|
||||||
const from = new BigNumber(1);
|
const from = new BigNumber(1);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import * as LibBytesRevertErrors from './lib_bytes_revert_errors';
|
||||||
import * as OwnableRevertErrors from './ownable_revert_errors';
|
import * as OwnableRevertErrors from './ownable_revert_errors';
|
||||||
import * as SafeMathRevertErrors from './safe_math_revert_errors';
|
import * as SafeMathRevertErrors from './safe_math_revert_errors';
|
||||||
|
|
||||||
@ -28,4 +29,4 @@ export {
|
|||||||
AnyRevertError,
|
AnyRevertError,
|
||||||
} from './revert_error';
|
} from './revert_error';
|
||||||
|
|
||||||
export { OwnableRevertErrors, SafeMathRevertErrors };
|
export { LibBytesRevertErrors, OwnableRevertErrors, SafeMathRevertErrors };
|
||||||
|
30
packages/utils/src/lib_bytes_revert_errors.ts
Normal file
30
packages/utils/src/lib_bytes_revert_errors.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { BigNumber } from './configured_bignumber';
|
||||||
|
import { RevertError } from './revert_error';
|
||||||
|
|
||||||
|
export enum InvalidByteOperationErrorCodes {
|
||||||
|
FromLessThanOrEqualsToRequired,
|
||||||
|
ToLessThanOrEqualsLengthRequired,
|
||||||
|
LengthGreaterThanZeroRequired,
|
||||||
|
LengthGreaterThanOrEqualsFourRequired,
|
||||||
|
LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
|
LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
|
LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
||||||
|
DestinationLengthGreaterThanOrEqualSourceLengthRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidByteOperationError extends RevertError {
|
||||||
|
constructor(error?: InvalidByteOperationErrorCodes, endpoint?: BigNumber, required?: BigNumber) {
|
||||||
|
super(
|
||||||
|
'InvalidByteOperationError',
|
||||||
|
'InvalidByteOperationError(uint8 error, uint256 endpoint, uint256 required)',
|
||||||
|
{
|
||||||
|
error,
|
||||||
|
endpoint,
|
||||||
|
required,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the InvalidByteOperationError type
|
||||||
|
RevertError.registerType(InvalidByteOperationError);
|
Loading…
x
Reference in New Issue
Block a user