142 lines
3.9 KiB
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
|
|
}
|