Skip to content

Commit

Permalink
Evm 505: fuzz tests in nightly build (#1353)
Browse files Browse the repository at this point in the history
add slack notification for fuzz tests

Co-authored-by: Victor Castell <victor@polygon.technology>
  • Loading branch information
Nemanja0x and vcastellm authored Apr 4, 2023
1 parent b48f781 commit 99166f0
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 3 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/fuzz-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
name: Fuzz tests
on: # yamllint disable-line rule:truthy
push:
branches:
- main
- develop
workflow_dispatch:
workflow_call:
outputs:
workflow_output:
description: "Fuzz output"
value: ${{ jobs.build.outputs.fuzz_output_failure }}


jobs:
fuzz_test:
name: Polygon Edge
runs-on: ubuntu-latest
outputs:
fuzz_output_failure: ${{ steps.run_fuzz_failure.outputs.test_output }}
steps:
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.18.x

- name: Checkout Code
uses: actions/checkout@v3
with:
submodules: recursive

- name: Install Dependencies
run: ./setup-ci.sh

- name: Run Fuzz Test
run: make fuzz-test

- name: Run fuzz tests failed
if: failure()
id: run_fuzz_failure
run: echo "test_output=false" >> $GITHUB_OUTPUT

18 changes: 15 additions & 3 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,16 @@ jobs:
name: Polybft Property Tests
uses: ./.github/workflows/property-polybft.yml
needs: build


fuzz:
name: Fuzz Tests
uses: ./.github/workflows/fuzz-test.yml
needs: build

notification:
name: Nightly Notifications
runs-on: ubuntu-latest
needs: [build, test, e2e, property]
needs: [build, test, e2e, property, fuzz]
if: success() || failure()
steps:
- name: Notify Slack
Expand Down Expand Up @@ -85,6 +90,13 @@ jobs:
"text": "E2E tests ${{ needs.e2e.outputs.workflow_output == '' && ':white_check_mark:' || ':x: `failed`' }}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Fuzz tests ${{ needs.fuzz.outputs.workflow_output == '' && ':white_check_mark:' || ':x: `failed`' }}"
}
},
{
"type": "divider"
},
Expand All @@ -96,4 +108,4 @@ jobs:
}
}
]
}
}
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ generate-bsd-licenses:
test:
go test -coverprofile coverage.out -timeout 20m `go list ./... | grep -v e2e`

.PHONY: fuzz-test
fuzz-test:
./scripts/fuzzAll

.PHONY: test-e2e
test-e2e:
# We need to build the binary with the race flag enabled
Expand All @@ -57,6 +61,7 @@ test-e2e-polybft:
env EDGE_BINARY=${PWD}/artifacts/polygon-edge E2E_TESTS=true E2E_LOGS=true E2E_TESTS_TYPE=integration \
go test -v -timeout=45m ./e2e-polybft/e2e/...

.PHONY: test-property-polybft
test-property-polybft:
# We can not build with race because of a bug in boltdb dependency
go build -o artifacts/polygon-edge .
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/tinylib/msgp v1.1.2 // indirect
github.com/trailofbits/go-fuzz-utils v0.0.0-20210901195358-9657fcfd256c
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.37.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/trailofbits/go-fuzz-utils v0.0.0-20210901195358-9657fcfd256c h1:4WU+p200eLYtBsx3M5CKXvkjVdf5SC3W9nMg37y0TFI=
github.com/trailofbits/go-fuzz-utils v0.0.0-20210901195358-9657fcfd256c/go.mod h1:f3jBhpWvuZmue0HZK52GzRHJOYHYSILs/c8+K2S/J+o=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
Expand Down
18 changes: 18 additions & 0 deletions scripts/fuzzAll
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

set -e

fuzzTime=${1:-10}

files=$(grep -r --include='**_test.go' --files-with-matches 'func Fuzz' .)

for file in ${files}
do
funcs=$(grep -o 'func Fuzz\w*' $file | sed 's/func //')
for func in ${funcs}
do
echo "Fuzzing $func in $file"
parentDir=$(dirname $file)
go test $parentDir -run=$func -fuzz=$func -fuzztime=${fuzzTime}s
done
done
193 changes: 193 additions & 0 deletions state/runtime/evm/evm_fuzz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package evm

import (
"errors"
"math/big"
"testing"

"github.com/0xPolygon/polygon-edge/chain"
"github.com/0xPolygon/polygon-edge/state/runtime"
"github.com/0xPolygon/polygon-edge/types"
go_fuzz_utils "github.com/trailofbits/go-fuzz-utils"
)

var _ runtime.Host = &mockHostF{}

// mockHostF is a struct which meets the requirements of runtime.Host interface but returns naive data
type mockHostF struct {
// to use
tracer runtime.VMTracer
storage map[types.Address]map[types.Hash]types.Hash
balances map[types.Address]*big.Int
nonces map[types.Address]uint64

// to fuzz
refund uint64
blockHash types.Hash
}

func (m *mockHostF) AccountExists(addr types.Address) bool {
if _, ok := m.nonces[addr]; ok {
return true
}

return false
}

func (m *mockHostF) GetStorage(addr types.Address, key types.Hash) types.Hash {
if val, ok := m.storage[addr][key]; !ok {
return types.Hash{}
} else {
return val
}
}

func (m *mockHostF) SetStorage(
addr types.Address,
key types.Hash,
value types.Hash,
config *chain.ForksInTime,
) runtime.StorageStatus {
if _, ok := m.storage[addr]; !ok {
m.storage[addr] = make(map[types.Hash]types.Hash)
}

m.storage[addr][key] = value

return runtime.StorageModified
}

func (m *mockHostF) SetState(addr types.Address, key types.Hash, value types.Hash) {
return
}

func (m *mockHostF) GetBalance(addr types.Address) *big.Int {
if b, ok := m.balances[addr]; !ok {
m.balances[addr] = big.NewInt(0)

return m.balances[addr]
} else {
return b
}
}

func (m *mockHostF) GetCodeSize(addr types.Address) int {
return 0
}

func (m *mockHostF) GetCodeHash(addr types.Address) types.Hash {
return types.Hash{}
}

func (m *mockHostF) GetCode(addr types.Address) []byte {
return nil
}

func (m *mockHostF) Selfdestruct(addr types.Address, beneficiary types.Address) {
return
}
func (m *mockHostF) GetTxContext() runtime.TxContext {
return runtime.TxContext{}
}

func (m *mockHostF) GetBlockHash(number int64) types.Hash {
return m.blockHash
}

func (m *mockHostF) EmitLog(addr types.Address, topics []types.Hash, data []byte) {
return
}

func (m *mockHostF) Callx(c *runtime.Contract, h runtime.Host) *runtime.ExecutionResult {
return &runtime.ExecutionResult{}
}

func (m *mockHostF) Empty(addr types.Address) bool {
return true
}

func (m *mockHostF) GetNonce(addr types.Address) uint64 {
if nonce, ok := m.nonces[addr]; !ok {
m.nonces[addr] = 0

return 0
} else {
return nonce
}
}

func (m *mockHostF) Transfer(from types.Address, to types.Address, amount *big.Int) error {
f := m.GetBalance(from)
t := m.GetBalance(to)

if f.Cmp(amount) < 0 {
return errors.New("not enough balance")
}

f.Sub(f, amount)
t.Add(t, amount)

return nil
}

func (m *mockHostF) GetTracer() runtime.VMTracer {
return m.tracer
}

func (m *mockHostF) GetRefund() uint64 {
return m.refund
}

func FuzzTestEVM(f *testing.F) {
seed := []byte{
PUSH1, 0x01, PUSH1, 0x02, ADD,
PUSH1, 0x00, MSTORE8,
PUSH1, 0x01, PUSH1, 0x00, RETURN,
}

f.Add(seed)

config := &chain.ForksInTime{
Byzantium: true,
}

evm := NewEVM()

f.Fuzz(func(t *testing.T, input []byte) {
tp, err := go_fuzz_utils.NewTypeProvider(input)
if err != nil {
return
}

err = tp.SetParamsSliceBounds(1, 4*1024)
if err != nil {
return
}

refund, err := tp.GetUint64()
if err != nil {
return
}

blockHashI, err := tp.GetNBytes(types.HashLength)
if err != nil {
return
}

blockHash := types.BytesToHash(blockHashI)
host := &mockHostF{
refund: refund, blockHash: blockHash,
storage: make(map[types.Address]map[types.Hash]types.Hash),
balances: make(map[types.Address]*big.Int),
nonces: make(map[types.Address]uint64),
}

code, err := tp.GetBytes()
if err != nil {
return
}

contract := newMockContract(big.NewInt(0), 10000000, code)
evm.Run(contract, host, config)
})
}

0 comments on commit 99166f0

Please sign in to comment.