diff --git a/bridge/cmd/guardiand/processor.go b/bridge/cmd/guardiand/processor.go index f99676487..332272e3e 100644 --- a/bridge/cmd/guardiand/processor.go +++ b/bridge/cmd/guardiand/processor.go @@ -5,7 +5,6 @@ import ( "crypto/ecdsa" "encoding/hex" "fmt" - "math" "strings" "time" @@ -218,7 +217,7 @@ func vaaConsensusProcessor(lockC chan *common.ChainLock, setC chan *common.Guard } // 2/3+ majority required for VAA to be valid - wait until we have quorum to submit VAA. - quorum := int(math.Ceil((float64(len(gs.Keys)) / 3) * 2)) + quorum := CalculateQuorum(len(gs.Keys)) logger.Info("aggregation state for VAA", zap.String("digest", hash), @@ -303,7 +302,7 @@ func checkDevModeGuardianSetUpdate(ctx context.Context, vaaC chan *vaa.VAA, gs * defer cancel() tx, err := devnet.SubmitVAA(timeout, *ethRPC, v) if err != nil { - return fmt.Errorf("failed to submit devnet guardian set change: %v") + return fmt.Errorf("failed to submit devnet guardian set change: %v", err) } logger.Info("devnet guardian set change submitted to Ethereum", zap.Any("tx", tx), zap.Any("vaa", v)) diff --git a/bridge/cmd/guardiand/quorum.go b/bridge/cmd/guardiand/quorum.go new file mode 100644 index 000000000..98db37c4c --- /dev/null +++ b/bridge/cmd/guardiand/quorum.go @@ -0,0 +1,9 @@ +package main + +// CalculateQuorum returns the minimum number of have that needs to sign a VAA for a given guardian set. +// +// 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 { + return ((numGuardians*10/3)*2)/10 + 1 +} diff --git a/bridge/cmd/guardiand/quorum_test.go b/bridge/cmd/guardiand/quorum_test.go new file mode 100644 index 000000000..11ca691d1 --- /dev/null +++ b/bridge/cmd/guardiand/quorum_test.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "testing" +) + +func TestCalculateQuorum(t *testing.T) { + tests := []struct { + have int + want int + }{ + {have: 1, want: 1}, + {have: 2, want: 2}, + {have: 3, want: 3}, + {have: 4, want: 3}, + {have: 5, want: 4}, + {have: 6, want: 5}, + {have: 7, want: 5}, + {have: 8, want: 6}, + {have: 9, want: 7}, + {have: 10, want: 7}, + {have: 11, want: 8}, + {have: 12, want: 9}, + {have: 20, want: 14}, + {have: 25, want: 17}, + {have: 100, want: 67}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%d have", tt.have), func(t *testing.T) { + if got := CalculateQuorum(tt.have); got != tt.want { + t.Errorf("CalculateQuorum(%d) = %v, want %v", tt.have, got, tt.want) + } + }) + } +}