130 lines
3.9 KiB
Go
130 lines
3.9 KiB
Go
package processor
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"time"
|
|
|
|
"github.com/mr-tron/base58"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
|
|
ethCommon "github.com/ethereum/go-ethereum/common"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zapcore"
|
|
|
|
"github.com/certusone/wormhole/node/pkg/common"
|
|
"github.com/wormhole-foundation/wormhole/sdk/vaa"
|
|
)
|
|
|
|
var (
|
|
// SECURITY: source_chain/target_chain are untrusted uint8 values. An attacker could cause a maximum of 255**2 label
|
|
// pairs to be created, which is acceptable.
|
|
|
|
messagesObservedTotal = promauto.NewCounterVec(
|
|
prometheus.CounterOpts{
|
|
Name: "wormhole_message_observations_total",
|
|
Help: "Total number of messages observed",
|
|
},
|
|
[]string{"emitter_chain"})
|
|
)
|
|
|
|
// handleMessage processes a message received from a chain and instantiates our deterministic copy of the VAA. An
|
|
// event may be received multiple times and must be handled in an idempotent fashion.
|
|
func (p *Processor) handleMessage(k *common.MessagePublication) {
|
|
if p.gs == nil {
|
|
p.logger.Warn("dropping observation since we haven't initialized our guardian set yet",
|
|
zap.String("message_id", k.MessageIDString()),
|
|
zap.Uint32("nonce", k.Nonce),
|
|
zap.Stringer("txhash", k.TxHash),
|
|
zap.Time("timestamp", k.Timestamp),
|
|
)
|
|
return
|
|
}
|
|
|
|
messagesObservedTotal.WithLabelValues(k.EmitterChain.String()).Inc()
|
|
|
|
// All nodes will create the exact same VAA and sign its digest.
|
|
// Consensus is established on this digest.
|
|
|
|
v := &VAA{
|
|
VAA: vaa.VAA{
|
|
Version: vaa.SupportedVAAVersion,
|
|
GuardianSetIndex: p.gs.Index,
|
|
Signatures: nil,
|
|
Timestamp: k.Timestamp,
|
|
Nonce: k.Nonce,
|
|
EmitterChain: k.EmitterChain,
|
|
EmitterAddress: k.EmitterAddress,
|
|
Payload: k.Payload,
|
|
Sequence: k.Sequence,
|
|
ConsistencyLevel: k.ConsistencyLevel,
|
|
},
|
|
Unreliable: k.Unreliable,
|
|
Reobservation: k.IsReobservation,
|
|
}
|
|
|
|
// Generate digest of the unsigned VAA.
|
|
digest := v.SigningDigest()
|
|
hash := hex.EncodeToString(digest.Bytes())
|
|
|
|
// Sign the digest using the node's GuardianSigner
|
|
signature, err := p.guardianSigner.Sign(digest.Bytes())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
shouldPublishImmediately := p.shouldPublishImmediately(&v.VAA)
|
|
|
|
if p.logger.Core().Enabled(zapcore.DebugLevel) {
|
|
p.logger.Debug("observed and signed confirmed message publication",
|
|
zap.String("message_id", k.MessageIDString()),
|
|
zap.Stringer("txhash", k.TxHash),
|
|
zap.String("txhash_b58", base58.Encode(k.TxHash.Bytes())),
|
|
zap.String("hash", hash),
|
|
zap.Uint32("nonce", k.Nonce),
|
|
zap.Time("timestamp", k.Timestamp),
|
|
zap.Uint8("consistency_level", k.ConsistencyLevel),
|
|
zap.String("signature", hex.EncodeToString(signature)),
|
|
zap.Bool("shouldPublishImmediately", shouldPublishImmediately),
|
|
zap.Bool("isReobservation", k.IsReobservation),
|
|
)
|
|
}
|
|
|
|
// Broadcast the signature.
|
|
ourObs, msg := p.broadcastSignature(v.MessageID(), k.TxHash.Bytes(), digest, signature, shouldPublishImmediately)
|
|
|
|
// Indicate that we observed this one.
|
|
observationsReceivedTotal.Inc()
|
|
observationsReceivedByGuardianAddressTotal.WithLabelValues(p.ourAddr.Hex()).Inc()
|
|
|
|
// Get / create our state entry.
|
|
s := p.state.signatures[hash]
|
|
if s == nil {
|
|
s = &state{
|
|
firstObserved: time.Now(),
|
|
nextRetry: time.Now().Add(nextRetryDuration(0)),
|
|
signatures: map[ethCommon.Address][]byte{},
|
|
source: "loopback",
|
|
}
|
|
|
|
p.state.signatures[hash] = s
|
|
}
|
|
|
|
// Update our state.
|
|
s.ourObservation = v
|
|
s.txHash = k.TxHash.Bytes()
|
|
s.source = v.GetEmitterChain().String()
|
|
s.gs = p.gs // guaranteed to match ourObservation - there's no concurrent access to p.gs
|
|
s.signatures[p.ourAddr] = signature
|
|
s.ourObs = ourObs
|
|
s.ourMsg = msg
|
|
|
|
// Fast path for our own signature.
|
|
if !s.submitted {
|
|
start := time.Now()
|
|
p.checkForQuorum(ourObs, s, s.gs, hash)
|
|
timeToHandleObservation.Observe(float64(time.Since(start).Microseconds()))
|
|
}
|
|
}
|