-
Notifications
You must be signed in to change notification settings - Fork 25
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
WIP: tx fuzz testing #311
base: main
Are you sure you want to change the base?
WIP: tx fuzz testing #311
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,11 @@ | ||
// +build gofuzzbeta | ||
|
||
package keeper | ||
|
||
import ( | ||
"encoding/binary" | ||
"fmt" | ||
"math" | ||
"math/big" | ||
"testing" | ||
|
||
|
@@ -79,6 +84,102 @@ func TestAddToOutgoingPool(t *testing.T) { | |
assert.Equal(t, exp, got) | ||
} | ||
|
||
func createSeedUint64ByteArrayWithValue(numElts int, value uint64) []byte { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fuzzing currently doesn't support typed arrays, only byte arrays. These methods make it easy to "seed" the fuzzer with example data to work off of |
||
numBytes := numElts * 64 | ||
arr := make([]byte, numBytes) | ||
for i := 0; i < numElts; i++ { | ||
binary.PutUvarint(arr[64*i:64*(i+1)], value) | ||
} | ||
return arr | ||
} | ||
|
||
func deserializeByteArrayToUint64Array(bytes []byte, numElts int) []uint64 { | ||
uints := make([]uint64, numElts) | ||
|
||
for j := 0; j < numElts; j++ { | ||
uints[j] = binary.BigEndian.Uint64(bytes[64*j : 64*(j+1)]) | ||
} | ||
return uints | ||
} | ||
|
||
func FuzzAddToOutgoingPool(f *testing.F) { | ||
numInputs := 6 | ||
ones := createSeedUint64ByteArrayWithValue(numInputs, uint64(1)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Create some example uint64 arrays. I wanted to start out with fees all equal to 1, and amounts all equal to 100 |
||
oneHundreds := createSeedUint64ByteArrayWithValue(numInputs, uint64(100)) | ||
|
||
f.Add(ones, oneHundreds, "0000000000000000000000000000000000000000") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Add function creates a single point of seed input into the fuzzer. I don't fully understand how it will use the seed data, but I'm guessing it just speeds up the time it takes to randomly generate acceptable inputs |
||
f.Fuzz(func(t *testing.T, feez []byte, amountz []byte, contractAddr string) { | ||
if types.ValidateEthAddress(contractAddr) != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Plenty of the input strings were too short, if the fuzzer generates a bad string then we just skip with t.Skip() |
||
t.Skip() | ||
} | ||
fees := deserializeByteArrayToUint64Array(feez, numInputs) | ||
amounts := deserializeByteArrayToUint64Array(amountz, numInputs) | ||
for j := 0; j < numInputs; j++ { | ||
fees[j] = binary.BigEndian.Uint64(feez[64*j : 64*(j+1)]) | ||
amounts[j] = binary.BigEndian.Uint64(amountz[64*j : 64*(j+1)]) | ||
} | ||
|
||
input := CreateTestEnv(t) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From this point onward this test is basically a copy of the TestAddToOutgoingPool test above it |
||
ctx := input.Context | ||
var ( | ||
mySender, _ = sdk.AccAddressFromBech32("cosmos1ahx7f8wyertuus9r20284ej0asrs085case3kn") | ||
myReceiver = "0xd041c41EA1bf0F006ADBb6d2c9ef9D425dE5eaD7" | ||
myTokenContractAddr = "0x" + contractAddr | ||
) | ||
|
||
// mint some voucher first | ||
var balance uint64 = math.MaxUint64 | ||
allVouchers := sdk.Coins{types.NewERC20Token(balance, myTokenContractAddr).GravityCoin()} | ||
err := input.BankKeeper.MintCoins(ctx, types.ModuleName, allVouchers) | ||
if err != nil { | ||
t.Skip() | ||
} | ||
|
||
// set senders balance | ||
input.AccountKeeper.NewAccountWithAddress(ctx, mySender) | ||
err = input.BankKeeper.SetBalances(ctx, mySender, allVouchers) | ||
if err != nil { | ||
t.Skip() | ||
} | ||
|
||
transacts := make([]*types.OutgoingTransferTx, len(fees)) | ||
// create transactions | ||
for i, _ := range fees { | ||
txAmt := amounts[i] | ||
txFee := fees[i] | ||
if uint64(txAmt+txFee) >= balance { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the account doesn't have enough balance (starts with uint64 max) to cover this transaction, we modify the amount and fee to make the transaction work out. Later on we check that there are a correct number of transactions created so I didn't want to just skip. |
||
txAmt = uint64(balance / 2) | ||
txFee = uint64(txAmt / 2) | ||
} | ||
amount := types.NewERC20Token(txAmt, myTokenContractAddr).GravityCoin() | ||
fee := types.NewERC20Token(txFee, myTokenContractAddr).GravityCoin() | ||
transacts[i] = &types.OutgoingTransferTx{ | ||
Id: uint64(i + 1), | ||
Sender: mySender.String(), | ||
DestAddress: myReceiver, | ||
Erc20Token: types.NewSDKIntERC20Token(amount.Amount, amount.Denom), | ||
Erc20Fee: types.NewSDKIntERC20Token(fee.Amount, fee.Denom), | ||
} | ||
r, err := input.GravityKeeper.AddToOutgoingPool(ctx, mySender, myReceiver, amount, fee) | ||
balance = balance - uint64(txAmt+txFee) | ||
require.NoError(t, err) | ||
t.Logf("___ response: %#v", r) | ||
// Should create: | ||
// 1: amount 100, fee 2 | ||
// 2: amount 101, fee 3 | ||
// 3: amount 102, fee 2 | ||
// 4: amount 103, fee 1 | ||
|
||
} | ||
|
||
got := input.GravityKeeper.GetUnbatchedTransactionsByContract(ctx, myTokenContractAddr) | ||
if len(got) != len(fees) { | ||
t.Fatal(fmt.Errorf("generated transactions do not match ones received\nexpected: %v\nreceived: %v", transacts, got)) | ||
} | ||
}) | ||
|
||
} | ||
|
||
func TestTotalBatchFeeInPool(t *testing.T) { | ||
input := CreateTestEnv(t) | ||
ctx := input.Context | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
go test fuzz v1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file was generated automatically when I ran There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should probably be git-ignored |
||
[]byte("\x01") | ||
[]byte("d") | ||
string("0") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation mentions that in order to get access to the beta fuzz feature you need to install gotip and use that in place of the go command: