sdk/vaa: add verify function to vaa

This commit is contained in:
Paul Noel 2022-10-21 17:32:24 +00:00 committed by Evan Gray
parent 847dad8d4c
commit b0f424b4d2
6 changed files with 44 additions and 39 deletions

View File

@ -107,7 +107,7 @@ func (p *Processor) handleCleanup(ctx context.Context) {
}
hasSigs := len(s.signatures)
wantSigs := CalculateQuorum(len(gs.Keys))
wantSigs := vaa.CalculateQuorum(len(gs.Keys))
quorum := hasSigs >= wantSigs
var chain vaa.ChainID
@ -211,7 +211,7 @@ func (p *Processor) handleCleanup(ctx context.Context) {
// network reached consensus without us. We don't know the correct guardian
// set, so we simply use the most recent one.
hasSigs := len(s.signatures)
wantSigs := CalculateQuorum(len(p.gs.Keys))
wantSigs := vaa.CalculateQuorum(len(p.gs.Keys))
p.logger.Info("expiring unsubmitted nil observation",
zap.String("digest", hash),

View File

@ -192,7 +192,7 @@ func (p *Processor) handleObservation(ctx context.Context, m *gossipv1.SignedObs
// We have made this observation on chain!
// 2/3+ majority required for VAA to be valid - wait until we have quorum to submit VAA.
quorum := CalculateQuorum(len(gs.Keys))
quorum := vaa.CalculateQuorum(len(gs.Keys))
p.logger.Info("aggregation state for observation",
zap.String("digest", hash),
@ -247,36 +247,8 @@ func (p *Processor) handleInboundSignedVAAWithQuorum(ctx context.Context, m *gos
return
}
// Check if VAA doesn't have any signatures
if len(v.Signatures) == 0 {
p.logger.Warn("received SignedVAAWithQuorum message with no VAA signatures",
zap.String("digest", hash),
zap.Any("message", m),
zap.Any("vaa", v),
)
return
}
// Verify VAA has enough signatures for quorum
quorum := CalculateQuorum(len(p.gs.Keys))
if len(v.Signatures) < quorum {
p.logger.Warn("received SignedVAAWithQuorum message without quorum",
zap.String("digest", hash),
zap.Any("message", m),
zap.Any("vaa", v),
zap.Int("wanted_sigs", quorum),
zap.Int("got_sigs", len(v.Signatures)),
)
return
}
// Verify VAA signatures to prevent a DoS attack on our local store.
if !v.VerifySignatures(p.gs.Keys) {
p.logger.Warn("received SignedVAAWithQuorum message with invalid VAA signatures",
zap.String("digest", hash),
zap.Any("message", m),
zap.Any("vaa", v),
)
if err := v.Verify(p.gs.Keys); err != nil {
p.logger.Warn("dropping SignedVAAWithQuorum message because it failed verification: " + err.Error())
return
}

View File

@ -75,13 +75,13 @@ func TestHandleInboundSignedVAAWithQuorum(t *testing.T) {
{label: "GuardianSetNoKeys", keyOrder: []*ecdsa.PrivateKey{}, indexOrder: []uint8{}, addrs: []ethcommon.Address{},
errString: "dropping SignedVAAWithQuorum message since we have a guardian set without keys"},
{label: "VAANoSignatures", keyOrder: []*ecdsa.PrivateKey{}, indexOrder: []uint8{0}, addrs: []ethcommon.Address{goodAddr1},
errString: "received SignedVAAWithQuorum message with no VAA signatures"},
errString: "dropping SignedVAAWithQuorum message because it failed verification: VAA was not signed"},
{label: "VAAInvalidSignatures", keyOrder: []*ecdsa.PrivateKey{badPrivateKey1}, indexOrder: []uint8{0}, addrs: []ethcommon.Address{goodAddr1},
errString: "received SignedVAAWithQuorum message with invalid VAA signatures"},
errString: "dropping SignedVAAWithQuorum message because it failed verification: VAA had bad signatures"},
{label: "DuplicateGoodSignaturesNonMonotonic", keyOrder: []*ecdsa.PrivateKey{goodPrivateKey1, goodPrivateKey1, goodPrivateKey1, goodPrivateKey1}, indexOrder: []uint8{0, 0, 0, 0}, addrs: []ethcommon.Address{goodAddr1},
errString: "received SignedVAAWithQuorum message with invalid VAA signatures"},
errString: "dropping SignedVAAWithQuorum message because it failed verification: VAA had bad signatures"},
{label: "DuplicateGoodSignaturesMonotonic", keyOrder: []*ecdsa.PrivateKey{goodPrivateKey1, goodPrivateKey1, goodPrivateKey1, goodPrivateKey1}, indexOrder: []uint8{0, 1, 2, 3}, addrs: []ethcommon.Address{goodAddr1},
errString: "received SignedVAAWithQuorum message with invalid VAA signatures"},
errString: "dropping SignedVAAWithQuorum message because it failed verification: VAA had bad signatures"},
}
for _, tc := range tests {

View File

@ -5,6 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/wormhole-foundation/wormhole/sdk/vaa"
)
func TestCalculateQuorum(t *testing.T) {
@ -31,7 +32,7 @@ func TestCalculateQuorum(t *testing.T) {
}
for _, tc := range tests {
t.Run(fmt.Sprint(tc.have), func(t *testing.T) {
assert.Equal(t, tc.want, CalculateQuorum(tc.have))
assert.Equal(t, tc.want, vaa.CalculateQuorum(tc.have))
})
}
}

View File

@ -1,4 +1,4 @@
package processor
package vaa
// CalculateQuorum returns the minimum number of guardians that need to sign a VAA for a given guardian set.
//

View File

@ -5,6 +5,7 @@ import (
"crypto/ecdsa"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"math/big"
@ -444,6 +445,37 @@ func (v *VAA) VerifySignatures(addresses []common.Address) bool {
return true
}
// Verify is a function on the VAA that takes a complete set of guardian keys as input and attempts certain checks with respect to this guardian.
// Verify will return nil if the VAA passes checks. Otherwise, Verify will return an error containing the text of the first check to fail.
// NOTE: Verify will not work correctly if a subset of the guardian set keys is passed in. The complete guardian set must be passed in.
// Verify does the following checks:
// - If the guardian does not have or know its own guardian set keys, then the VAA cannot be verified.
// - Quorum is calculated on the guardian set passed in and checks if the VAA has enough signatures.
// - The signatures in the VAA is verified against the guardian set keys.
func (v *VAA) Verify(addresses []common.Address) error {
if addresses == nil {
return errors.New("No addresses were provided")
}
// Check if VAA doesn't have any signatures
if len(v.Signatures) == 0 {
return errors.New("VAA was not signed")
}
// Verify VAA has enough signatures for quorum
quorum := CalculateQuorum(len(addresses))
if len(v.Signatures) < quorum {
return errors.New("VAA did not have a quorum")
}
// Verify VAA signatures to prevent a DoS attack on our local store.
if !v.VerifySignatures(addresses) {
return errors.New("VAA had bad signatures")
}
return nil
}
// Marshal returns the binary representation of the VAA
func (v *VAA) Marshal() ([]byte, error) {
buf := new(bytes.Buffer)