Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix flaky test in randomized ABI encoding test #3346

Merged
merged 5 commits into from
Dec 23, 2021
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 76 additions & 35 deletions data/abi/abi_encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,58 @@ import (
"github.com/stretchr/testify/require"
)

const (
UintStepLength = 8
UintBegin = 8
UintEnd = 512
UintRandomTestPoints = 1000
UintTestCaseCount = 200
UfixedPrecision = 160
UfixedRandomTestPoints = 20
TupleMaxLength = 10
ByteTestCaseCount = 1 << 8
BoolTestCaseCount = 2
AddressTestCaseCount = 300
StringTestCaseCount = 10
StringTestCaseSpecLenCount = 5
TakeNum = 10
TupleTestCaseCount = 100
)

/*
The set of parameters ensure that the error of byte length >= 2^16 is eliminated.

i. Consider uint512[] with length 10, the ABI encoding length is: 64 x 10 + 2
(2 is introduced from dynamic array length encoding)
The motivation here is that, forall ABI type that is non-array/non-tuple like,
uint512 gives the longest byte length in ABI encoding
(utf-8 string's byte length is at most 42, address byte length is at most 32)

ii. Consider a tuple of length 10, with all elements uint512[] of length 10.
The ABI encoding length is: 10 x 2 + 10 x 642 == 6440
(2 is for tuple index to keep track of dynamic type encoding)

iii. Consider a tuple of length 10, with all elements of tuples mentioned in (ii).
The ABI encoding length is: 10 x 2 + 10 x 6440 == 64420
This is the end of the generation of nested-tuple test case,
no more layers of random tuples will be produced.

This gives an upper bound for the produced ABI encoding byte length in this test script,
and noticing that length 64420 mentioned in (iii) is less than 2^16 == 65536.
Assuming that ABI implementation is correct, then the flaky test should not happen again.
*/

func TestEncodeValid(t *testing.T) {
partitiontest.PartitionTest(t)

// encoding test for uint type, iterating through all uint sizes
// randomly pick 1000 valid uint values and check if encoded value match with expected
for intSize := 8; intSize <= 512; intSize += 8 {
for intSize := UintBegin; intSize <= UintEnd; intSize += UintStepLength {
upperLimit := big.NewInt(0).Lsh(big.NewInt(1), uint(intSize))
uintType, err := makeUintType(intSize)
require.NoError(t, err, "make uint type fail")

for i := 0; i < 1000; i++ {
for i := 0; i < UintRandomTestPoints; i++ {
randomInt, err := rand.Int(rand.Reader, upperLimit)
require.NoError(t, err, "cryptographic random int init fail")

Expand All @@ -64,17 +105,17 @@ func TestEncodeValid(t *testing.T) {
// encoding test for ufixed, iterating through all the valid ufixed bitSize and precision
// randomly generate 10 big int values for ufixed numerator and check if encoded value match with expected
// also check if ufixed can fit max numerator (2^bitSize - 1) under specific byte bitSize
for size := 8; size <= 512; size += 8 {
for size := UintBegin; size <= UintEnd; size += UintStepLength {
upperLimit := big.NewInt(0).Lsh(big.NewInt(1), uint(size))
largest := big.NewInt(0).Add(
upperLimit,
big.NewInt(1).Neg(big.NewInt(1)),
)
for precision := 1; precision <= 160; precision++ {
for precision := 1; precision <= UfixedPrecision; precision++ {
typeUfixed, err := makeUfixedType(size, precision)
require.NoError(t, err, "make ufixed type fail")

for i := 0; i < 10; i++ {
for i := 0; i < UfixedRandomTestPoints; i++ {
randomInt, err := rand.Int(rand.Reader, upperLimit)
require.NoError(t, err, "cryptographic random int init fail")

Expand All @@ -96,13 +137,13 @@ func TestEncodeValid(t *testing.T) {

// encoding test for address, since address is 32 byte, it can be considered as 256 bit uint
// randomly generate 1000 uint256 and make address values, check if encoded value match with expected
upperLimit := big.NewInt(0).Lsh(big.NewInt(1), 256)
for i := 0; i < 1000; i++ {
upperLimit := big.NewInt(0).Lsh(big.NewInt(1), addressByteSize<<3)
for i := 0; i < UintRandomTestPoints; i++ {
randomAddrInt, err := rand.Int(rand.Reader, upperLimit)
require.NoError(t, err, "cryptographic random int init fail")

rand256Bytes := randomAddrInt.Bytes()
addrBytesExpected := make([]byte, 32-len(rand256Bytes))
addrBytesExpected := make([]byte, addressByteSize-len(rand256Bytes))
addrBytesExpected = append(addrBytesExpected, rand256Bytes...)

addrBytesActual, err := addressType.Encode(addrBytesExpected)
Expand All @@ -111,7 +152,7 @@ func TestEncodeValid(t *testing.T) {
}

// encoding test for bool values
for i := 0; i < 2; i++ {
for i := 0; i < BoolTestCaseCount; i++ {
boolEncode, err := boolType.Encode(i == 1)
require.NoError(t, err, "bool encode fail")
expected := []byte{0x00}
Expand All @@ -122,7 +163,7 @@ func TestEncodeValid(t *testing.T) {
}

// encoding test for byte values
for i := 0; i < (1 << 8); i++ {
for i := 0; i < ByteTestCaseCount; i++ {
byteEncode, err := byteType.Encode(byte(i))
require.NoError(t, err, "byte encode fail")
expected := []byte{byte(i)}
Expand All @@ -133,8 +174,8 @@ func TestEncodeValid(t *testing.T) {
// we use `gobberish` to generate random utf-8 symbols
// randomly generate utf-8 str from length 1 to 100, each length draw 10 random strs
// check if encoded ABI str match with expected value
for length := 1; length <= 100; length++ {
for i := 0; i < 10; i++ {
for length := 1; length <= StringTestCaseCount; length++ {
for i := 0; i < StringTestCaseSpecLenCount; i++ {
// generate utf8 strings from `gobberish` at some length
utf8Str := gobberish.GenerateString(length)
// since string is just type alias of `byte[]`, we need to store number of bytes in encoding
Expand Down Expand Up @@ -843,20 +884,20 @@ func categorySelfRoundTripTest(t *testing.T, category []testUnit) {
}

func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
(*pool)[Uint] = make([]testUnit, 200*64)
(*pool)[Ufixed] = make([]testUnit, 160*64)
(*pool)[Uint] = make([]testUnit, UintTestCaseCount*UintEnd/UintStepLength)
(*pool)[Ufixed] = make([]testUnit, UfixedPrecision*UintEnd/UintStepLength)

uintIndex := 0
ufixedIndex := 0

for bitSize := 8; bitSize <= 512; bitSize += 8 {
for bitSize := UintBegin; bitSize <= UintEnd; bitSize += UintStepLength {
max := new(big.Int).Lsh(big.NewInt(1), uint(bitSize))

uintT, err := makeUintType(bitSize)
require.NoError(t, err, "make uint type failure")
uintTstr := uintT.String()

for j := 0; j < 200; j++ {
for j := 0; j < UintTestCaseCount; j++ {
randVal, err := rand.Int(rand.Reader, max)
require.NoError(t, err, "generate random uint, should be no error")

Expand All @@ -867,7 +908,7 @@ func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
uintIndex++
}

for precision := 1; precision <= 160; precision++ {
for precision := 1; precision <= UfixedPrecision; precision++ {
randVal, err := rand.Int(rand.Reader, max)
require.NoError(t, err, "generate random ufixed, should be no error")

Expand All @@ -884,33 +925,33 @@ func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
categorySelfRoundTripTest(t, (*pool)[Uint])
categorySelfRoundTripTest(t, (*pool)[Ufixed])

(*pool)[Byte] = make([]testUnit, 1<<8)
for i := 0; i < (1 << 8); i++ {
(*pool)[Byte] = make([]testUnit, ByteTestCaseCount)
for i := 0; i < ByteTestCaseCount; i++ {
(*pool)[Byte][i] = testUnit{serializedType: byteType.String(), value: byte(i)}
}
categorySelfRoundTripTest(t, (*pool)[Byte])

(*pool)[Bool] = make([]testUnit, 2)
(*pool)[Bool] = make([]testUnit, BoolTestCaseCount)
(*pool)[Bool][0] = testUnit{serializedType: boolType.String(), value: false}
(*pool)[Bool][1] = testUnit{serializedType: boolType.String(), value: true}
categorySelfRoundTripTest(t, (*pool)[Bool])

maxAddress := new(big.Int).Lsh(big.NewInt(1), 256)
(*pool)[Address] = make([]testUnit, 300)
for i := 0; i < 300; i++ {
maxAddress := new(big.Int).Lsh(big.NewInt(1), addressByteSize<<3)
(*pool)[Address] = make([]testUnit, AddressTestCaseCount)
for i := 0; i < AddressTestCaseCount; i++ {
randAddrVal, err := rand.Int(rand.Reader, maxAddress)
require.NoError(t, err, "generate random value for address, should be no error")
addrBytes := randAddrVal.Bytes()
remainBytes := make([]byte, 32-len(addrBytes))
remainBytes := make([]byte, addressByteSize-len(addrBytes))
addrBytes = append(remainBytes, addrBytes...)
(*pool)[Address][i] = testUnit{serializedType: addressType.String(), value: addrBytes}
}
categorySelfRoundTripTest(t, (*pool)[Address])

(*pool)[String] = make([]testUnit, 400)
(*pool)[String] = make([]testUnit, StringTestCaseCount*StringTestCaseSpecLenCount)
stringIndex := 0
for length := 1; length <= 100; length++ {
for i := 0; i < 4; i++ {
for length := 1; length <= StringTestCaseCount; length++ {
for i := 0; i < StringTestCaseSpecLenCount; i++ {
(*pool)[String][stringIndex] = testUnit{
serializedType: stringType.String(),
value: gobberish.GenerateString(length),
Expand Down Expand Up @@ -945,21 +986,21 @@ func takeSomeFromCategoryAndGenerateArray(
}

func addArrayRandomValues(t *testing.T, pool *map[BaseType][]testUnit) {
for intIndex := 0; intIndex < len((*pool)[Uint]); intIndex += 200 {
takeSomeFromCategoryAndGenerateArray(t, Uint, intIndex, 20, pool)
for intIndex := 0; intIndex < len((*pool)[Uint]); intIndex += UintTestCaseCount {
takeSomeFromCategoryAndGenerateArray(t, Uint, intIndex, TakeNum, pool)
}
takeSomeFromCategoryAndGenerateArray(t, Byte, 0, 20, pool)
takeSomeFromCategoryAndGenerateArray(t, Address, 0, 20, pool)
takeSomeFromCategoryAndGenerateArray(t, String, 0, 20, pool)
takeSomeFromCategoryAndGenerateArray(t, Bool, 0, 20, pool)
takeSomeFromCategoryAndGenerateArray(t, Byte, 0, TakeNum, pool)
takeSomeFromCategoryAndGenerateArray(t, Address, 0, TakeNum, pool)
takeSomeFromCategoryAndGenerateArray(t, String, 0, TakeNum, pool)
takeSomeFromCategoryAndGenerateArray(t, Bool, 0, TakeNum, pool)

categorySelfRoundTripTest(t, (*pool)[ArrayStatic])
categorySelfRoundTripTest(t, (*pool)[ArrayDynamic])
}

func addTupleRandomValues(t *testing.T, slotRange BaseType, pool *map[BaseType][]testUnit) {
for i := 0; i < 100; i++ {
tupleLenBig, err := rand.Int(rand.Reader, big.NewInt(20))
for i := 0; i < TupleTestCaseCount; i++ {
tupleLenBig, err := rand.Int(rand.Reader, big.NewInt(TupleMaxLength))
require.NoError(t, err, "generate random tuple length should not return error")
tupleLen := tupleLenBig.Int64() + 1
testUnits := make([]testUnit, tupleLen)
Expand Down