node: calculate digests using Vaa type or using message prefix
This commit is contained in:
parent
64fb51d68d
commit
d4e0445785
|
@ -320,7 +320,7 @@ func (s *nodePrivilegedService) InjectGovernanceVAA(ctx context.Context, req *no
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate digest of the unsigned VAA.
|
// Generate digest of the unsigned VAA.
|
||||||
digest := v.SigningMsg()
|
digest := v.SigningDigest()
|
||||||
|
|
||||||
s.logger.Info("governance VAA constructed",
|
s.logger.Info("governance VAA constructed",
|
||||||
zap.Any("vaa", v),
|
zap.Any("vaa", v),
|
||||||
|
|
|
@ -68,7 +68,7 @@ func runGovernanceVAAVerify(cmd *cobra.Command, args []string) {
|
||||||
log.Fatalf("invalid update: %v", err)
|
log.Fatalf("invalid update: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
digest := v.SigningMsg().Bytes()
|
digest := v.SigningDigest().Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -331,7 +331,10 @@ func SubmitObservationsToContract(
|
||||||
return nil, fmt.Errorf("acct: failed to marshal accountant observation request: %w", err)
|
return nil, fmt.Errorf("acct: failed to marshal accountant observation request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
digest := vaa.SigningMsg(append(submitObservationPrefix, bytes...))
|
digest, err := vaa.MessageSigningDigest(submitObservationPrefix, bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("acct: failed to sign accountant Observation request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
sigBytes, err := ethCrypto.Sign(digest.Bytes(), gk)
|
sigBytes, err := ethCrypto.Sign(digest.Bytes(), gk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -152,6 +152,6 @@ func (msg *MessagePublication) CreateVAA(gsIndex uint32) *vaa.VAA {
|
||||||
|
|
||||||
func (msg *MessagePublication) CreateDigest() string {
|
func (msg *MessagePublication) CreateDigest() string {
|
||||||
v := msg.CreateVAA(0) // The guardian set index is not part of the digest, so we can pass in zero.
|
v := msg.CreateVAA(0) // The guardian set index is not part of the digest, so we can pass in zero.
|
||||||
db := v.SigningMsg()
|
db := v.SigningDigest()
|
||||||
return hex.EncodeToString(db.Bytes())
|
return hex.EncodeToString(db.Bytes())
|
||||||
}
|
}
|
||||||
|
|
|
@ -680,6 +680,6 @@ func (tk tokenKey) String() string {
|
||||||
|
|
||||||
func (gov *ChainGovernor) HashFromMsg(msg *common.MessagePublication) string {
|
func (gov *ChainGovernor) HashFromMsg(msg *common.MessagePublication) string {
|
||||||
v := msg.CreateVAA(0) // We can pass zero in as the guardian set index because it is not part of the digest.
|
v := msg.CreateVAA(0) // We can pass zero in as the guardian set index because it is not part of the digest.
|
||||||
digest := v.SigningMsg()
|
digest := v.SigningDigest()
|
||||||
return hex.EncodeToString(digest.Bytes())
|
return hex.EncodeToString(digest.Bytes())
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ func (d *DiscordNotifier) LookupGroupID(groupName string) (string, error) {
|
||||||
type Observation interface {
|
type Observation interface {
|
||||||
GetEmitterChain() vaa.ChainID
|
GetEmitterChain() vaa.ChainID
|
||||||
MessageID() string
|
MessageID() string
|
||||||
SigningMsg() common.Hash
|
SigningDigest() common.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DiscordNotifier) MissingSignaturesOnObservation(o Observation, hasSigs, wantSigs int, quorum bool, missing []string) error {
|
func (d *DiscordNotifier) MissingSignaturesOnObservation(o Observation, hasSigs, wantSigs int, quorum bool, missing []string) error {
|
||||||
|
@ -145,7 +145,7 @@ func (d *DiscordNotifier) MissingSignaturesOnObservation(o Observation, hasSigs,
|
||||||
Title: "Message with missing signatures",
|
Title: "Message with missing signatures",
|
||||||
Fields: []discord.EmbedField{
|
Fields: []discord.EmbedField{
|
||||||
{Name: "Message ID", Value: wrapCode(o.MessageID()), Inline: true},
|
{Name: "Message ID", Value: wrapCode(o.MessageID()), Inline: true},
|
||||||
{Name: "Digest", Value: wrapCode(hex.EncodeToString(o.SigningMsg().Bytes())), Inline: true},
|
{Name: "Digest", Value: wrapCode(hex.EncodeToString(o.SigningDigest().Bytes())), Inline: true},
|
||||||
{Name: "Quorum", Value: quorumText, Inline: true},
|
{Name: "Quorum", Value: quorumText, Inline: true},
|
||||||
{Name: "Source Chain", Value: caser.String(o.GetEmitterChain().String()), Inline: false},
|
{Name: "Source Chain", Value: caser.String(o.GetEmitterChain().String()), Inline: false},
|
||||||
{Name: "Missing Guardians", Value: missingText.String(), Inline: false},
|
{Name: "Missing Guardians", Value: missingText.String(), Inline: false},
|
||||||
|
|
|
@ -28,7 +28,7 @@ func (p *Processor) broadcastSignature(
|
||||||
signature []byte,
|
signature []byte,
|
||||||
txhash []byte,
|
txhash []byte,
|
||||||
) {
|
) {
|
||||||
digest := o.SigningMsg()
|
digest := o.SigningDigest()
|
||||||
obsv := gossipv1.SignedObservation{
|
obsv := gossipv1.SignedObservation{
|
||||||
Addr: crypto.PubkeyToAddress(p.gk.PublicKey).Bytes(),
|
Addr: crypto.PubkeyToAddress(p.gk.PublicKey).Bytes(),
|
||||||
Hash: digest.Bytes(),
|
Hash: digest.Bytes(),
|
||||||
|
|
|
@ -25,7 +25,7 @@ var (
|
||||||
// handleInjection processes a pre-populated VAA injected locally.
|
// handleInjection processes a pre-populated VAA injected locally.
|
||||||
func (p *Processor) handleInjection(ctx context.Context, v *vaa.VAA) {
|
func (p *Processor) handleInjection(ctx context.Context, v *vaa.VAA) {
|
||||||
// Generate digest of the unsigned VAA.
|
// Generate digest of the unsigned VAA.
|
||||||
digest := v.SigningMsg()
|
digest := v.SigningDigest()
|
||||||
|
|
||||||
// The internal originator is responsible for logging the full VAA, just log the digest here.
|
// The internal originator is responsible for logging the full VAA, just log the digest here.
|
||||||
supervisor.Logger(ctx).Info("signing injected VAA",
|
supervisor.Logger(ctx).Info("signing injected VAA",
|
||||||
|
|
|
@ -129,7 +129,7 @@ func (p *Processor) handleMessage(ctx context.Context, k *common.MessagePublicat
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate digest of the unsigned VAA.
|
// Generate digest of the unsigned VAA.
|
||||||
digest := v.SigningMsg()
|
digest := v.SigningDigest()
|
||||||
|
|
||||||
// Sign the digest using our node's guardian key.
|
// Sign the digest using our node's guardian key.
|
||||||
s, err := crypto.Sign(digest.Bytes(), p.gk)
|
s, err := crypto.Sign(digest.Bytes(), p.gk)
|
||||||
|
|
|
@ -227,7 +227,7 @@ func (p *Processor) handleInboundSignedVAAWithQuorum(ctx context.Context, m *gos
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate digest for logging
|
// Calculate digest for logging
|
||||||
digest := v.SigningMsg()
|
digest := v.SigningDigest()
|
||||||
hash := hex.EncodeToString(digest.Bytes())
|
hash := hex.EncodeToString(digest.Bytes())
|
||||||
|
|
||||||
if p.gs == nil {
|
if p.gs == nil {
|
||||||
|
|
|
@ -30,9 +30,9 @@ type (
|
||||||
GetEmitterChain() vaa.ChainID
|
GetEmitterChain() vaa.ChainID
|
||||||
// MessageID returns a human-readable emitter_chain/emitter_address/sequence tuple.
|
// MessageID returns a human-readable emitter_chain/emitter_address/sequence tuple.
|
||||||
MessageID() string
|
MessageID() string
|
||||||
// SigningMsg returns the hash of the signing body of the observation. This is used
|
// SigningDigest returns the hash of the hash signing body of the observation. This is used
|
||||||
// for signature generation and verification.
|
// for signature generation and verification.
|
||||||
SigningMsg() ethcommon.Hash
|
SigningDigest() ethcommon.Hash
|
||||||
// IsReliable returns whether this message is considered reliable meaning it can be reobserved.
|
// IsReliable returns whether this message is considered reliable meaning it can be reobserved.
|
||||||
IsReliable() bool
|
IsReliable() bool
|
||||||
// HandleQuorum finishes processing the observation once a quorum of signatures have
|
// HandleQuorum finishes processing the observation once a quorum of signatures have
|
||||||
|
|
|
@ -617,7 +617,7 @@ func UnmarshalBatch(data []byte) (*BatchVAA, error) {
|
||||||
|
|
||||||
// check for malformed data - verify that the hash of the observation matches what was supplied
|
// check for malformed data - verify that the hash of the observation matches what was supplied
|
||||||
// the guardian has no interest in or use for observations after the batch has been signed, but still check
|
// the guardian has no interest in or use for observations after the batch has been signed, but still check
|
||||||
obsHash := headless.SigningMsg()
|
obsHash := headless.SigningDigest()
|
||||||
if obsHash != v.Hashes[obsvIndex] {
|
if obsHash != v.Hashes[obsvIndex] {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"BatchVAA Observation %v does not match supplied hash", obsvIndex)
|
"BatchVAA Observation %v does not match supplied hash", obsvIndex)
|
||||||
|
@ -652,21 +652,42 @@ func (v *BatchVAA) signingBody() []byte {
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SigningMsg returns the hash of the signing body.
|
func doubleKeccak(bz []byte) common.Hash {
|
||||||
func SigningMsg(data []byte) common.Hash {
|
|
||||||
// In order to save space in the solana signature verification instruction, we hash twice so we only need to pass in
|
// In order to save space in the solana signature verification instruction, we hash twice so we only need to pass in
|
||||||
// the first hash (32 bytes) vs the full body data.
|
// the first hash (32 bytes) vs the full body data.
|
||||||
return crypto.Keccak256Hash(crypto.Keccak256Hash(data).Bytes())
|
return crypto.Keccak256Hash(crypto.Keccak256Hash(bz).Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// SigningMsg returns the hash of the signing body. This is used for signature generation and verification
|
// This is a temporary method to produce a vaa signing digest on raw bytes.
|
||||||
func (v *VAA) SigningMsg() common.Hash {
|
// It is error prone and we should use `v.SigningDigest()` instead.
|
||||||
return SigningMsg(v.signingBody())
|
// whenever possible.
|
||||||
|
// This will be removed in a subsequent release.
|
||||||
|
func DeprecatedSigningDigest(bz []byte) common.Hash {
|
||||||
|
return doubleKeccak(bz)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SigningMsg returns the hash of the signing body. This is used for signature generation and verification
|
// MessageSigningDigest returns the hash of the data prepended with it's signing prefix.
|
||||||
func (v *BatchVAA) SigningMsg() common.Hash {
|
// This is intending to be used for signing messages of different types from VAA's.
|
||||||
return SigningMsg(v.signingBody())
|
// The message prefix help protect from message collisions.
|
||||||
|
func MessageSigningDigest(prefix []byte, data []byte) (common.Hash, error) {
|
||||||
|
if len(prefix) < 32 {
|
||||||
|
// Prefixes must be at least 32 bytes
|
||||||
|
// https://github.com/wormhole-foundation/wormhole/blob/main/whitepapers/0009_guardian_key.md
|
||||||
|
return common.Hash([32]byte{}), errors.New("prefix must be atleast 32 bytes")
|
||||||
|
}
|
||||||
|
return crypto.Keccak256Hash(prefix[:], data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SigningDigest returns the hash of the vaa hash to be signed directly.
|
||||||
|
// This is used for signature generation and verification
|
||||||
|
func (v *VAA) SigningDigest() common.Hash {
|
||||||
|
return doubleKeccak(v.signingBody())
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchSigningDigest returns the hash of the batch vaa hash to be signed directly.
|
||||||
|
// This is used for signature generation and verification
|
||||||
|
func (v *BatchVAA) SigningDigest() common.Hash {
|
||||||
|
return doubleKeccak(v.signingBody())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObsvHashArray creates an array of hashes of Observation.
|
// ObsvHashArray creates an array of hashes of Observation.
|
||||||
|
@ -675,7 +696,7 @@ func (v *BatchVAA) ObsvHashArray() []common.Hash {
|
||||||
hashes := make([]common.Hash, len(v.Observations))
|
hashes := make([]common.Hash, len(v.Observations))
|
||||||
for _, msg := range v.Observations {
|
for _, msg := range v.Observations {
|
||||||
obsIndex := msg.Index
|
obsIndex := msg.Index
|
||||||
hashes[obsIndex] = msg.Observation.SigningMsg()
|
hashes[obsIndex] = msg.Observation.SigningDigest()
|
||||||
}
|
}
|
||||||
|
|
||||||
return hashes
|
return hashes
|
||||||
|
@ -683,9 +704,10 @@ func (v *BatchVAA) ObsvHashArray() []common.Hash {
|
||||||
|
|
||||||
// Verify Signature checks that the provided address matches the address that created the signature for the provided digest
|
// Verify Signature checks that the provided address matches the address that created the signature for the provided digest
|
||||||
// Digest should be the output of SigningMsg(data).Bytes()
|
// Digest should be the output of SigningMsg(data).Bytes()
|
||||||
func VerifySignature(digest []byte, signature *Signature, address common.Address) bool {
|
// Should not be public as other message types should be verified using a message prefix.
|
||||||
|
func verifySignature(vaa_digest []byte, signature *Signature, address common.Address) bool {
|
||||||
// retrieve the address that signed the data
|
// retrieve the address that signed the data
|
||||||
pubKey, err := crypto.Ecrecover(digest, signature.Signature[:])
|
pubKey, err := crypto.Ecrecover(vaa_digest, signature.Signature[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -696,7 +718,8 @@ func VerifySignature(digest []byte, signature *Signature, address common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
// Digest should be the output of SigningMsg(data).Bytes()
|
// Digest should be the output of SigningMsg(data).Bytes()
|
||||||
func VerifySignatures(digest []byte, signatures []*Signature, addresses []common.Address) bool {
|
// Should not be public as other message types should be verified using a message prefix.
|
||||||
|
func verifySignatures(vaa_digest []byte, signatures []*Signature, addresses []common.Address) bool {
|
||||||
if len(addresses) < len(signatures) {
|
if len(addresses) < len(signatures) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -717,7 +740,7 @@ func VerifySignatures(digest []byte, signatures []*Signature, addresses []common
|
||||||
|
|
||||||
// verify this signature
|
// verify this signature
|
||||||
addr := addresses[sig.Index]
|
addr := addresses[sig.Index]
|
||||||
ok := VerifySignature(digest, sig, addr)
|
ok := verifySignature(vaa_digest, sig, addr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -734,16 +757,34 @@ func VerifySignatures(digest []byte, signatures []*Signature, addresses []common
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Operating on bytes directly is error prone. We should use `vaa.VerifyingSignatures()` whenever possible.
|
||||||
|
// This function will be removed in a subsequent release.
|
||||||
|
func DeprecatedVerifySignatures(vaaBody []byte, signatures []*Signature, addresses []common.Address) bool {
|
||||||
|
vaaDigest := doubleKeccak(vaaBody)
|
||||||
|
return verifySignatures(vaaDigest[:], signatures, addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifyMessageSignature(prefix []byte, messageBody []byte, signatures *Signature, addresses common.Address) bool {
|
||||||
|
if len(prefix) < 32 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
msgDigest, err := MessageSigningDigest(prefix, messageBody)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return verifySignature(msgDigest[:], signatures, addresses)
|
||||||
|
}
|
||||||
|
|
||||||
// VerifySignatures verifies the signature of the VAA given the signer addresses.
|
// VerifySignatures verifies the signature of the VAA given the signer addresses.
|
||||||
// Returns true if the signatures were verified successfully.
|
// Returns true if the signatures were verified successfully.
|
||||||
func (v *VAA) VerifySignatures(addresses []common.Address) bool {
|
func (v *VAA) VerifySignatures(addresses []common.Address) bool {
|
||||||
return VerifySignatures(v.SigningMsg().Bytes(), v.Signatures, addresses)
|
return verifySignatures(v.SigningDigest().Bytes(), v.Signatures, addresses)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifySignatures verifies the signature of the BatchVAA given the signer addresses.
|
// VerifySignatures verifies the signature of the BatchVAA given the signer addresses.
|
||||||
// Returns true if the signatures were verified successfully.
|
// Returns true if the signatures were verified successfully.
|
||||||
func (v *BatchVAA) VerifySignatures(addresses []common.Address) bool {
|
func (v *BatchVAA) VerifySignatures(addresses []common.Address) bool {
|
||||||
return VerifySignatures(v.SigningMsg().Bytes(), v.Signatures, addresses)
|
return verifySignatures(v.SigningDigest().Bytes(), v.Signatures, addresses)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal returns the binary representation of the BatchVAA
|
// Marshal returns the binary representation of the BatchVAA
|
||||||
|
@ -798,7 +839,7 @@ func (v *BatchVAA) serializeBody() []byte {
|
||||||
// - The signatures in the VAA is verified against the guardian set keys.
|
// - The signatures in the VAA is verified against the guardian set keys.
|
||||||
func (v *VAA) Verify(addresses []common.Address) error {
|
func (v *VAA) Verify(addresses []common.Address) error {
|
||||||
if addresses == nil {
|
if addresses == nil {
|
||||||
return errors.New("No addresses were provided")
|
return errors.New("no addresses were provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if VAA doesn't have any signatures
|
// Check if VAA doesn't have any signatures
|
||||||
|
@ -906,12 +947,12 @@ func (v *BatchVAA) GetTransactionID() common.Hash {
|
||||||
|
|
||||||
// HexDigest returns the hex-encoded digest.
|
// HexDigest returns the hex-encoded digest.
|
||||||
func (v *VAA) HexDigest() string {
|
func (v *VAA) HexDigest() string {
|
||||||
return hex.EncodeToString(v.SigningMsg().Bytes())
|
return hex.EncodeToString(v.SigningDigest().Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// HexDigest returns the hex-encoded digest.
|
// HexDigest returns the hex-encoded digest.
|
||||||
func (b *BatchVAA) HexDigest() string {
|
func (b *BatchVAA) HexDigest() string {
|
||||||
return hex.EncodeToString(b.SigningMsg().Bytes())
|
return hex.EncodeToString(b.SigningDigest().Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -932,7 +973,7 @@ func (v *VAA) serializeBody() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VAA) AddSignature(key *ecdsa.PrivateKey, index uint8) {
|
func (v *VAA) AddSignature(key *ecdsa.PrivateKey, index uint8) {
|
||||||
sig, err := crypto.Sign(v.SigningMsg().Bytes(), key)
|
sig, err := crypto.Sign(v.SigningDigest().Bytes(), key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -948,7 +989,7 @@ func (v *VAA) AddSignature(key *ecdsa.PrivateKey, index uint8) {
|
||||||
// creates signature of BatchVAA.Hashes and adds it to BatchVAA.Signatures.
|
// creates signature of BatchVAA.Hashes and adds it to BatchVAA.Signatures.
|
||||||
func (v *BatchVAA) AddSignature(key *ecdsa.PrivateKey, index uint8) {
|
func (v *BatchVAA) AddSignature(key *ecdsa.PrivateKey, index uint8) {
|
||||||
|
|
||||||
sig, err := crypto.Sign(v.SigningMsg().Bytes(), key)
|
sig, err := crypto.Sign(v.SigningDigest().Bytes(), key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -328,7 +328,7 @@ func TestSigningBody(t *testing.T) {
|
||||||
func TestSigningMsg(t *testing.T) {
|
func TestSigningMsg(t *testing.T) {
|
||||||
vaa := getVaa()
|
vaa := getVaa()
|
||||||
expected := common.HexToHash("4fae136bb1fd782fe1b5180ba735cdc83bcece3f9b7fd0e5e35300a61c8acd8f")
|
expected := common.HexToHash("4fae136bb1fd782fe1b5180ba735cdc83bcece3f9b7fd0e5e35300a61c8acd8f")
|
||||||
assert.Equal(t, vaa.SigningMsg(), expected)
|
assert.Equal(t, vaa.SigningDigest(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMessageID(t *testing.T) {
|
func TestMessageID(t *testing.T) {
|
||||||
|
|
|
@ -65,7 +65,7 @@ func TestVerifySignature(t *testing.T) {
|
||||||
Payload: []byte("abcd"),
|
Payload: []byte("abcd"),
|
||||||
}
|
}
|
||||||
|
|
||||||
data := v.SigningMsg()
|
data := v.SigningDigest()
|
||||||
|
|
||||||
key, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
key, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package keeper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
|
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
|
||||||
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
|
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
|
||||||
|
@ -17,18 +18,35 @@ func NewCustomQueryHandler(keeper Keeper) *wasmkeeper.QueryPlugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
type WormholeQuery struct {
|
type WormholeQuery struct {
|
||||||
VerifyQuorum *verifyQuorumParams `json:"verify_quorum,omitempty"`
|
// This is deprecated and will be removed in a subsequent release
|
||||||
VerifySignature *verifySignatureParams `json:"verify_signature,omitempty"`
|
// because it uses an error-prone verification interface.
|
||||||
|
VerifyQuorum *verifyQuorumParams `json:"verify_quorum,omitempty"`
|
||||||
|
|
||||||
|
// Verify the signatures on a VAA.
|
||||||
|
// Successor to `VerifyQuorum` as the verification uses a safe interface.
|
||||||
|
VerifyVaa *verifyVaaParams `json:"verify_vaa,omitempty"`
|
||||||
|
|
||||||
|
// Verify the signatures on a message with a given message prefix.
|
||||||
|
// The caller should take care not to allow outside sources to choose the prefix.
|
||||||
|
VerifyMessageSignature *verifyMessageSignatureParams `json:"verify_message_signature,omitempty"`
|
||||||
|
|
||||||
|
// Calculate the minimum number of participants required in quorum for the latest guardian set.
|
||||||
CalculateQuorum *calculateQuorumParams `json:"calculate_quorum,omitempty"`
|
CalculateQuorum *calculateQuorumParams `json:"calculate_quorum,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deprecated
|
||||||
type verifyQuorumParams struct {
|
type verifyQuorumParams struct {
|
||||||
Data []byte `json:"data"`
|
Data []byte `json:"data"`
|
||||||
GuardianSetIndex uint32 `json:"guardian_set_index"`
|
GuardianSetIndex uint32 `json:"guardian_set_index"`
|
||||||
Signatures []*vaa.Signature `json:"signatures"`
|
Signatures []*vaa.Signature `json:"signatures"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type verifySignatureParams struct {
|
type verifyVaaParams struct {
|
||||||
|
Vaa []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type verifyMessageSignatureParams struct {
|
||||||
|
Prefix []byte `json:"prefix"`
|
||||||
Data []byte `json:"data"`
|
Data []byte `json:"data"`
|
||||||
GuardianSetIndex uint32 `json:"guardian_set_index"`
|
GuardianSetIndex uint32 `json:"guardian_set_index"`
|
||||||
Signature *vaa.Signature `json:"signature"`
|
Signature *vaa.Signature `json:"signature"`
|
||||||
|
@ -45,20 +63,37 @@ func WormholeQuerier(keeper Keeper) func(ctx sdk.Context, data json.RawMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||||
}
|
}
|
||||||
|
fmt.Println("-- WE GOT QUERIED")
|
||||||
|
|
||||||
if wormholeQuery.VerifyQuorum != nil {
|
if wormholeQuery.VerifyQuorum != nil {
|
||||||
// handle the verify quorum query
|
// verify vaa using deprecated method
|
||||||
digest := vaa.SigningMsg(wormholeQuery.VerifyQuorum.Data).Bytes()
|
err := keeper.DeprecatedVerifyVaa(ctx, wormholeQuery.VerifyQuorum.Data, wormholeQuery.VerifyQuorum.GuardianSetIndex, wormholeQuery.VerifyQuorum.Signatures)
|
||||||
err := keeper.VerifyQuorum(ctx, digest, wormholeQuery.VerifyQuorum.GuardianSetIndex, wormholeQuery.VerifyQuorum.Signatures)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return []byte("{}"), nil
|
return []byte("{}"), nil
|
||||||
}
|
}
|
||||||
if wormholeQuery.VerifySignature != nil {
|
if wormholeQuery.VerifyVaa != nil {
|
||||||
// handle the verify signature query
|
// verify vaa using recommended method
|
||||||
digest := vaa.SigningMsg(wormholeQuery.VerifySignature.Data).Bytes()
|
v, err := vaa.Unmarshal(wormholeQuery.VerifyVaa.Vaa)
|
||||||
err := keeper.VerifySignature(ctx, digest, wormholeQuery.VerifySignature.GuardianSetIndex, wormholeQuery.VerifySignature.Signature)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = keeper.VerifyVAA(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte("{}"), nil
|
||||||
|
}
|
||||||
|
if wormholeQuery.VerifyMessageSignature != nil {
|
||||||
|
// handle the verify message signature query
|
||||||
|
err := keeper.VerifyMessageSignature(
|
||||||
|
ctx,
|
||||||
|
wormholeQuery.VerifyMessageSignature.Prefix,
|
||||||
|
wormholeQuery.VerifyMessageSignature.Data,
|
||||||
|
wormholeQuery.VerifyMessageSignature.GuardianSetIndex,
|
||||||
|
wormholeQuery.VerifyMessageSignature.Signature,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ func (k Keeper) CalculateQuorum(ctx sdk.Context, guardianSetIndex uint32) (int,
|
||||||
return CalculateQuorum(len(guardianSet.Keys)), &guardianSet, nil
|
return CalculateQuorum(len(guardianSet.Keys)), &guardianSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) VerifySignature(ctx sdk.Context, data []byte, guardianSetIndex uint32, signature *vaa.Signature) error {
|
func (k Keeper) VerifyMessageSignature(ctx sdk.Context, prefix []byte, data []byte, guardianSetIndex uint32, signature *vaa.Signature) error {
|
||||||
// Calculate quorum and retrieve guardian set
|
// Calculate quorum and retrieve guardian set
|
||||||
_, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex)
|
_, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -53,7 +53,7 @@ func (k Keeper) VerifySignature(ctx sdk.Context, data []byte, guardianSetIndex u
|
||||||
return types.ErrGuardianIndexOutOfBounds
|
return types.ErrGuardianIndexOutOfBounds
|
||||||
}
|
}
|
||||||
|
|
||||||
ok := vaa.VerifySignature(data, signature, addresses[signature.Index])
|
ok := vaa.VerifyMessageSignature(prefix, data, signature, addresses[signature.Index])
|
||||||
if !ok {
|
if !ok {
|
||||||
return types.ErrSignaturesInvalid
|
return types.ErrSignaturesInvalid
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ func (k Keeper) VerifySignature(ctx sdk.Context, data []byte, guardianSetIndex u
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) VerifyQuorum(ctx sdk.Context, data []byte, guardianSetIndex uint32, signatures []*vaa.Signature) error {
|
func (k Keeper) DeprecatedVerifyVaa(ctx sdk.Context, vaaBody []byte, guardianSetIndex uint32, signatures []*vaa.Signature) error {
|
||||||
// Calculate quorum and retrieve guardian set
|
// Calculate quorum and retrieve guardian set
|
||||||
quorum, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex)
|
quorum, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -72,7 +72,7 @@ func (k Keeper) VerifyQuorum(ctx sdk.Context, data []byte, guardianSetIndex uint
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify signatures
|
// Verify signatures
|
||||||
ok := vaa.VerifySignatures(data, signatures, guardianSet.KeysAsAddresses())
|
ok := vaa.DeprecatedVerifySignatures(vaaBody, signatures, guardianSet.KeysAsAddresses())
|
||||||
if !ok {
|
if !ok {
|
||||||
return types.ErrSignaturesInvalid
|
return types.ErrSignaturesInvalid
|
||||||
}
|
}
|
||||||
|
@ -80,8 +80,23 @@ func (k Keeper) VerifyQuorum(ctx sdk.Context, data []byte, guardianSetIndex uint
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) VerifyVAA(ctx sdk.Context, vaa *vaa.VAA) error {
|
func (k Keeper) VerifyVAA(ctx sdk.Context, v *vaa.VAA) error {
|
||||||
return k.VerifyQuorum(ctx, vaa.SigningMsg().Bytes(), vaa.GuardianSetIndex, vaa.Signatures)
|
// Calculate quorum and retrieve guardian set
|
||||||
|
quorum, guardianSet, err := k.CalculateQuorum(ctx, v.GuardianSetIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(v.Signatures) < quorum {
|
||||||
|
return types.ErrNoQuorum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify signatures
|
||||||
|
ok := v.VerifySignatures(guardianSet.KeysAsAddresses())
|
||||||
|
if !ok {
|
||||||
|
return types.ErrSignaturesInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify a governance VAA:
|
// Verify a governance VAA:
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
keepertest "github.com/wormhole-foundation/wormchain/testutil/keeper"
|
keepertest "github.com/wormhole-foundation/wormchain/testutil/keeper"
|
||||||
"github.com/wormhole-foundation/wormchain/x/wormhole/keeper"
|
"github.com/wormhole-foundation/wormchain/x/wormhole/keeper"
|
||||||
"github.com/wormhole-foundation/wormchain/x/wormhole/types"
|
"github.com/wormhole-foundation/wormchain/x/wormhole/types"
|
||||||
|
@ -110,8 +111,11 @@ func sign(data common.Hash, key *ecdsa.PrivateKey, index uint8) *vaa.Signature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVerifySignature(t *testing.T) {
|
func TestVerifyMessageSignature(t *testing.T) {
|
||||||
payload := vaa.SigningMsg([]byte{97, 97, 97, 97, 97, 97})
|
prefix := [32]byte{}
|
||||||
|
payload := []byte{97, 97, 97, 97, 97, 97}
|
||||||
|
digest, err := vaa.MessageSigningDigest(prefix[:], payload)
|
||||||
|
require.NoError(t, err)
|
||||||
privKey1, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
privKey1, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
privKey2, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
privKey2, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
|
||||||
|
@ -154,13 +158,85 @@ func TestVerifySignature(t *testing.T) {
|
||||||
keeper.AppendGuardianSet(ctx, tc.guardianSet)
|
keeper.AppendGuardianSet(ctx, tc.guardianSet)
|
||||||
|
|
||||||
// build the signature
|
// build the signature
|
||||||
signature := sign(payload, tc.signer, 0)
|
signature := sign(digest, tc.signer, 0)
|
||||||
if tc.setSigIndex {
|
if tc.setSigIndex {
|
||||||
signature.Index = tc.sigIndex
|
signature.Index = tc.sigIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify the signature
|
// verify the signature
|
||||||
err := keeper.VerifySignature(ctx, payload.Bytes(), tc.guardianSet.Index, signature)
|
err := keeper.VerifyMessageSignature(ctx, prefix[:], payload, tc.guardianSet.Index, signature)
|
||||||
|
|
||||||
|
if tc.willError == true {
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
assert.Equal(t, err, tc.err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyVaaSignature(t *testing.T) {
|
||||||
|
v := &vaa.VAA{
|
||||||
|
Version: 1,
|
||||||
|
GuardianSetIndex: 0,
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
Nonce: 1,
|
||||||
|
Sequence: 1,
|
||||||
|
Payload: []byte{97, 97, 97, 97, 97, 97},
|
||||||
|
}
|
||||||
|
digest := v.SigningDigest()
|
||||||
|
privKey1, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
privKey2, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
|
||||||
|
addr1 := crypto.PubkeyToAddress(privKey1.PublicKey)
|
||||||
|
|
||||||
|
addrsBytes := [][]byte{}
|
||||||
|
addrsBytes = append(addrsBytes, addr1.Bytes())
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
label string
|
||||||
|
guardianSet types.GuardianSet
|
||||||
|
signer *ecdsa.PrivateKey
|
||||||
|
setSigIndex bool
|
||||||
|
sigIndex uint8
|
||||||
|
willError bool
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
|
||||||
|
{label: "ValidSigner",
|
||||||
|
guardianSet: types.GuardianSet{Index: 0, Keys: addrsBytes, ExpirationTime: 0},
|
||||||
|
signer: privKey1,
|
||||||
|
willError: false},
|
||||||
|
{label: "IndexOutOfBounds",
|
||||||
|
guardianSet: types.GuardianSet{Index: 0, Keys: addrsBytes, ExpirationTime: 0},
|
||||||
|
signer: privKey1,
|
||||||
|
setSigIndex: true,
|
||||||
|
sigIndex: 1,
|
||||||
|
willError: true,
|
||||||
|
// this out of bounds issue will trigger invalid signature from sdk.
|
||||||
|
err: types.ErrSignaturesInvalid},
|
||||||
|
{label: "InvalidSigner",
|
||||||
|
guardianSet: types.GuardianSet{Index: 0, Keys: addrsBytes, ExpirationTime: 0},
|
||||||
|
signer: privKey2,
|
||||||
|
willError: true,
|
||||||
|
err: types.ErrSignaturesInvalid},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.label, func(t *testing.T) {
|
||||||
|
keeper, ctx := keepertest.WormholeKeeper(t)
|
||||||
|
keeper.AppendGuardianSet(ctx, tc.guardianSet)
|
||||||
|
|
||||||
|
// build the signature
|
||||||
|
signature := sign(digest, tc.signer, 0)
|
||||||
|
if tc.setSigIndex {
|
||||||
|
signature.Index = tc.sigIndex
|
||||||
|
}
|
||||||
|
v.Signatures = append(v.Signatures, signature)
|
||||||
|
|
||||||
|
// verify the signature
|
||||||
|
err := keeper.VerifyVAA(ctx, v)
|
||||||
|
|
||||||
if tc.willError == true {
|
if tc.willError == true {
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
Loading…
Reference in New Issue