sdk/vaa: add verify function to vaa
This commit is contained in:
parent
847dad8d4c
commit
b0f424b4d2
|
@ -107,7 +107,7 @@ func (p *Processor) handleCleanup(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSigs := len(s.signatures)
|
hasSigs := len(s.signatures)
|
||||||
wantSigs := CalculateQuorum(len(gs.Keys))
|
wantSigs := vaa.CalculateQuorum(len(gs.Keys))
|
||||||
quorum := hasSigs >= wantSigs
|
quorum := hasSigs >= wantSigs
|
||||||
|
|
||||||
var chain vaa.ChainID
|
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
|
// network reached consensus without us. We don't know the correct guardian
|
||||||
// set, so we simply use the most recent one.
|
// set, so we simply use the most recent one.
|
||||||
hasSigs := len(s.signatures)
|
hasSigs := len(s.signatures)
|
||||||
wantSigs := CalculateQuorum(len(p.gs.Keys))
|
wantSigs := vaa.CalculateQuorum(len(p.gs.Keys))
|
||||||
|
|
||||||
p.logger.Info("expiring unsubmitted nil observation",
|
p.logger.Info("expiring unsubmitted nil observation",
|
||||||
zap.String("digest", hash),
|
zap.String("digest", hash),
|
||||||
|
|
|
@ -192,7 +192,7 @@ func (p *Processor) handleObservation(ctx context.Context, m *gossipv1.SignedObs
|
||||||
// We have made this observation on chain!
|
// We have made this observation on chain!
|
||||||
|
|
||||||
// 2/3+ majority required for VAA to be valid - wait until we have quorum to submit VAA.
|
// 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",
|
p.logger.Info("aggregation state for observation",
|
||||||
zap.String("digest", hash),
|
zap.String("digest", hash),
|
||||||
|
@ -247,36 +247,8 @@ func (p *Processor) handleInboundSignedVAAWithQuorum(ctx context.Context, m *gos
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if VAA doesn't have any signatures
|
if err := v.Verify(p.gs.Keys); err != nil {
|
||||||
if len(v.Signatures) == 0 {
|
p.logger.Warn("dropping SignedVAAWithQuorum message because it failed verification: " + err.Error())
|
||||||
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),
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,13 +75,13 @@ func TestHandleInboundSignedVAAWithQuorum(t *testing.T) {
|
||||||
{label: "GuardianSetNoKeys", keyOrder: []*ecdsa.PrivateKey{}, indexOrder: []uint8{}, addrs: []ethcommon.Address{},
|
{label: "GuardianSetNoKeys", keyOrder: []*ecdsa.PrivateKey{}, indexOrder: []uint8{}, addrs: []ethcommon.Address{},
|
||||||
errString: "dropping SignedVAAWithQuorum message since we have a guardian set without keys"},
|
errString: "dropping SignedVAAWithQuorum message since we have a guardian set without keys"},
|
||||||
{label: "VAANoSignatures", keyOrder: []*ecdsa.PrivateKey{}, indexOrder: []uint8{0}, addrs: []ethcommon.Address{goodAddr1},
|
{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},
|
{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},
|
{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},
|
{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 {
|
for _, tc := range tests {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/wormhole-foundation/wormhole/sdk/vaa"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCalculateQuorum(t *testing.T) {
|
func TestCalculateQuorum(t *testing.T) {
|
||||||
|
@ -31,7 +32,7 @@ func TestCalculateQuorum(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
t.Run(fmt.Sprint(tc.have), func(t *testing.T) {
|
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))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
// CalculateQuorum returns the minimum number of guardians that need to sign a VAA for a given guardian set.
|
||||||
//
|
//
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
@ -444,6 +445,37 @@ func (v *VAA) VerifySignatures(addresses []common.Address) bool {
|
||||||
return true
|
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
|
// Marshal returns the binary representation of the VAA
|
||||||
func (v *VAA) Marshal() ([]byte, error) {
|
func (v *VAA) Marshal() ([]byte, error) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
|
Loading…
Reference in New Issue