wormhole/wormchain/x/wormhole/keeper/vaa.go

142 lines
3.9 KiB
Go

package keeper
import (
"bytes"
"encoding/binary"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/wormhole-foundation/wormchain/x/wormhole/types"
"github.com/wormhole-foundation/wormhole/sdk/vaa"
)
func ParseVAA(data []byte) (*vaa.VAA, error) {
v, err := vaa.Unmarshal(data)
if err != nil {
return nil, err
}
return v, nil
}
// CalculateQuorum returns the minimum number of guardians that need 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*2)/3 + 1
}
// Calculate Quorum retrieves the guardian set for the given index, verifies that it is a valid set, and then calculates the needed quorum.
func (k Keeper) CalculateQuorum(ctx sdk.Context, guardianSetIndex uint32) (int, *types.GuardianSet, error) {
guardianSet, exists := k.GetGuardianSet(ctx, guardianSetIndex)
if !exists {
return 0, nil, types.ErrGuardianSetNotFound
}
if 0 < guardianSet.ExpirationTime && guardianSet.ExpirationTime < uint64(ctx.BlockTime().Unix()) {
return 0, nil, types.ErrGuardianSetExpired
}
return CalculateQuorum(len(guardianSet.Keys)), &guardianSet, nil
}
func (k Keeper) VerifySignature(ctx sdk.Context, data []byte, guardianSetIndex uint32, signature *vaa.Signature) error {
// Calculate quorum and retrieve guardian set
_, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex)
if err != nil {
return err
}
// verify signature
addresses := guardianSet.KeysAsAddresses()
if int(signature.Index) >= len(addresses) {
return types.ErrGuardianIndexOutOfBounds
}
ok := vaa.VerifySignature(data, signature, addresses[signature.Index])
if !ok {
return types.ErrSignaturesInvalid
}
return nil
}
func (k Keeper) VerifyQuorum(ctx sdk.Context, data []byte, guardianSetIndex uint32, signatures []*vaa.Signature) error {
// Calculate quorum and retrieve guardian set
quorum, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex)
if err != nil {
return err
}
if len(signatures) < quorum {
return types.ErrNoQuorum
}
// Verify signatures
ok := vaa.VerifySignatures(data, signatures, guardianSet.KeysAsAddresses())
if !ok {
return types.ErrSignaturesInvalid
}
return nil
}
func (k Keeper) VerifyVAA(ctx sdk.Context, vaa *vaa.VAA) error {
return k.VerifyQuorum(ctx, vaa.SigningMsg().Bytes(), vaa.GuardianSetIndex, vaa.Signatures)
}
// Verify a governance VAA:
// - Check signatures
// - Replay protection
// - Check the source chain and address is governance
// - Check the governance payload is for wormchain and the specified module
// - return the parsed action and governance payload
func (k Keeper) VerifyGovernanceVAA(ctx sdk.Context, v *vaa.VAA, module [32]byte) (action byte, payload []byte, err error) {
if err = k.VerifyVAA(ctx, v); err != nil {
return
}
_, known := k.GetReplayProtection(ctx, v.HexDigest())
if known {
err = types.ErrVAAAlreadyExecuted
return
}
// Prevent replay
k.SetReplayProtection(ctx, types.ReplayProtection{Index: v.HexDigest()})
config, ok := k.GetConfig(ctx)
if !ok {
err = types.ErrNoConfig
return
}
if !bytes.Equal(v.EmitterAddress[:], config.GovernanceEmitter) {
err = types.ErrInvalidGovernanceEmitter
return
}
if v.EmitterChain != vaa.ChainID(config.GovernanceChain) {
err = types.ErrInvalidGovernanceEmitter
return
}
if len(v.Payload) < 35 {
err = types.ErrGovernanceHeaderTooShort
return
}
// Check governance header
if !bytes.Equal(v.Payload[:32], module[:]) {
err = types.ErrUnknownGovernanceModule
return
}
// Decode header
action = v.Payload[32]
chain := binary.BigEndian.Uint16(v.Payload[33:35])
payload = v.Payload[35:]
if chain != 0 && chain != uint16(config.ChainId) {
err = types.ErrInvalidGovernanceTargetChain
return
}
return
}