sdk: add CalculateQuorum documentation and fuzz testing (#2996)

* sdk: Add fuzz testing and documentation for CalculateQuorum

* Use testify assertions instead of stdlib

* sdk: move unit and fuzz run into one line

* sdk: use better testify function for zero check

* sdk: drop unnecessary format string assertions

* sdk: remove obviously ignored test corpus items
This commit is contained in:
Jonathan Claudius 2023-05-30 16:46:51 -04:00 committed by GitHub
parent 49a183a147
commit 703fbe32c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 3 deletions

View File

@ -262,7 +262,7 @@ jobs:
git diff --name-only --exit-code && echo "✅ Generated proto matches committed proto" || (echo "❌ Generated proto differs from committed proto, run \`make proto -B\` and commit the result" >&2 && exit 1)
make test
# Verify go sdk unit tests
# Verify go sdk unit/fuzz tests
sdk_vaa:
runs-on: ubuntu-20.04
steps:
@ -270,8 +270,8 @@ jobs:
- uses: actions/setup-go@v2
with:
go-version: "1.20.4"
- run: cd sdk/vaa && go test
- run: cd sdk/vaa && go test && go test -v -fuzz FuzzCalculateQuorum -run FuzzCalculateQuorum -fuzztime 15s
# Run Go linters
node-lint:
# The linter is slow enough that we want to run it on the self-hosted runner

View File

@ -5,8 +5,18 @@ package vaa
// The canonical source is the calculation in the contracts (solana/bridge/src/processor.rs and
// ethereum/contracts/Wormhole.sol), and this needs to match the implementation in the contracts.
func CalculateQuorum(numGuardians int) int {
// A safety check to avoid caller from ever supplying a negative
// number, because we're dealing with signed integers
if numGuardians < 0 {
panic("Invalid numGuardians is less than zero")
}
// The goal here is to acheive a 2/3 quorum, but since we're
// dividing on int, we need to +1 to avoid the rounding down
// effect of integer division
//
// For example sake, 5 / 2 == 2, but really that's not an
// effective 2/3 quorum, so we add 1 for safety to get to 3
//
return ((numGuardians * 2) / 3) + 1
}

View File

@ -55,3 +55,39 @@ func TestCalculateQuorum(t *testing.T) {
})
}
}
func FuzzCalculateQuorum(f *testing.F) {
// Add examples to our fuzz corpus
f.Add(1)
f.Add(2)
f.Add(4)
f.Add(8)
f.Add(16)
f.Add(32)
f.Add(64)
f.Add(128)
f.Fuzz(func(t *testing.T, numGuardians int) {
// These are known cases, which the implementation will panic on and/or we have explicit
// unit-test coverage of above, so we can safely ignore it in our fuzz testing
if numGuardians <= 0 {
t.Skip()
}
// Let's determine how many guardians are needed for quorum
num := CalculateQuorum(numGuardians)
// Let's always be sure that there are enough guardians to maintain quorum
assert.LessOrEqual(t, num, numGuardians, "fuzz violation: quorum cannot be acheived because we require more guardians than we have")
// Let's always be sure that num is never zero
assert.NotZero(t, num, "fuzz violation: no guardians are required to acheive quorum")
var floorFloat float64 = 0.66666666666666666
numGuardiansFloat := float64(numGuardians)
numFloat := float64(num)
actualFloat := numFloat / numGuardiansFloat
// Let's always make sure that the int division does not violate the floor of our float division
assert.Greater(t, actualFloat, floorFloat, "fuzz violation: quorum has dropped below 2/3rds threshold")
})
}