bridge: add network heights and guardian address to heartbeat message
This commit is contained in:
parent
aacff406e4
commit
9c1d6ee00c
|
@ -320,8 +320,11 @@ func runBridge(cmd *cobra.Command, args []string) {
|
|||
logger.Fatal("failed to load guardian key", zap.Error(err))
|
||||
}
|
||||
|
||||
guardianAddr := ethcrypto.PubkeyToAddress(gk.PublicKey).String()
|
||||
logger.Info("Loaded guardian key", zap.String(
|
||||
"address", ethcrypto.PubkeyToAddress(gk.PublicKey).String()))
|
||||
"address", guardianAddr))
|
||||
|
||||
p2p.DefaultRegistry.SetGuardianAddress(guardianAddr)
|
||||
|
||||
// Node's main lifecycle context.
|
||||
rootCtx, rootCtxCancel = context.WithCancel(context.Background())
|
||||
|
|
|
@ -3,6 +3,8 @@ package ethereum
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/certusone/wormhole/bridge/pkg/p2p"
|
||||
gossipv1 "github.com/certusone/wormhole/bridge/pkg/proto/gossip/v1"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -89,6 +91,11 @@ func NewEthBridgeWatcher(url string, bridge eth_common.Address, minConfirmations
|
|||
}
|
||||
|
||||
func (e *EthBridgeWatcher) Run(ctx context.Context) error {
|
||||
// Initialize gossip metrics (we want to broadcast the address even if we're not yet syncing)
|
||||
p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDEthereum, &gossipv1.Heartbeat_Network{
|
||||
BridgeAddress: e.bridge.Hex(),
|
||||
})
|
||||
|
||||
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
defer cancel()
|
||||
c, err := ethclient.DialContext(timeout, e.url)
|
||||
|
@ -244,6 +251,10 @@ func (e *EthBridgeWatcher) Run(ctx context.Context) error {
|
|||
logger.Info("processing new header", zap.Stringer("block", ev.Number))
|
||||
currentEthHeight.Set(float64(ev.Number.Int64()))
|
||||
readiness.SetReady(common.ReadinessEthSyncing)
|
||||
p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDEthereum, &gossipv1.Heartbeat_Network{
|
||||
Height: ev.Number.Int64(),
|
||||
BridgeAddress: e.bridge.Hex(),
|
||||
})
|
||||
|
||||
e.pendingLocksGuard.Lock()
|
||||
|
||||
|
|
|
@ -187,18 +187,27 @@ func Run(obsvC chan *gossipv1.SignedObservation,
|
|||
case <-ctx.Done():
|
||||
return
|
||||
case <-tick.C:
|
||||
DefaultRegistry.mu.Lock()
|
||||
networks := make([]*gossipv1.Heartbeat_Network, 0, len(DefaultRegistry.networkStats))
|
||||
for _, v := range DefaultRegistry.networkStats {
|
||||
networks = append(networks, v)
|
||||
}
|
||||
|
||||
msg := gossipv1.GossipMessage{Message: &gossipv1.GossipMessage_Heartbeat{
|
||||
Heartbeat: &gossipv1.Heartbeat{
|
||||
NodeName: nodeName,
|
||||
Counter: ctr,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Version: version.Version(),
|
||||
NodeName: nodeName,
|
||||
Counter: ctr,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Networks: networks,
|
||||
Version: version.Version(),
|
||||
GuardianAddr: DefaultRegistry.guardianAddress,
|
||||
}}}
|
||||
|
||||
b, err := proto.Marshal(&msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
DefaultRegistry.mu.Unlock()
|
||||
|
||||
err = th.Publish(ctx, b)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package p2p
|
||||
|
||||
import (
|
||||
gossipv1 "github.com/certusone/wormhole/bridge/pkg/proto/gossip/v1"
|
||||
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// The p2p package implements a simple global metrics registry singleton for node status values transmitted on-chain.
|
||||
|
||||
type registry struct {
|
||||
mu sync.Mutex
|
||||
|
||||
// Mapping of chain IDs to network status messages.
|
||||
networkStats map[vaa.ChainID]*gossipv1.Heartbeat_Network
|
||||
|
||||
// Value of Heartbeat.guardian_addr.
|
||||
guardianAddress string
|
||||
}
|
||||
|
||||
func NewRegistry() *registry {
|
||||
return ®istry{
|
||||
networkStats: map[vaa.ChainID]*gossipv1.Heartbeat_Network{},
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultRegistry = NewRegistry()
|
||||
)
|
||||
|
||||
// SetGuardianAddress stores the node's guardian address to broadcast in Heartbeat messages.
|
||||
// This should be called once during startup, when the guardian key is loaded.
|
||||
func (r *registry) SetGuardianAddress(addr string) {
|
||||
r.mu.Lock()
|
||||
r.guardianAddress = addr
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetNetworkStats sets the current network status to be broadcast in Heartbeat messages.
|
||||
// The "Id" field is automatically set to the specified chain ID.
|
||||
func (r *registry) SetNetworkStats(chain vaa.ChainID, data *gossipv1.Heartbeat_Network) {
|
||||
r.mu.Lock()
|
||||
data.Id = uint32(chain)
|
||||
r.networkStats[chain] = data
|
||||
r.mu.Unlock()
|
||||
}
|
|
@ -6,11 +6,14 @@ import (
|
|||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/certusone/wormhole/bridge/pkg/common"
|
||||
"github.com/certusone/wormhole/bridge/pkg/p2p"
|
||||
gossipv1 "github.com/certusone/wormhole/bridge/pkg/proto/gossip/v1"
|
||||
"github.com/certusone/wormhole/bridge/pkg/supervisor"
|
||||
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
||||
"github.com/dfuse-io/solana-go"
|
||||
"github.com/dfuse-io/solana-go/rpc"
|
||||
eth_common "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/mr-tron/base58"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.uber.org/zap"
|
||||
"math/big"
|
||||
|
@ -65,6 +68,12 @@ func NewSolanaWatcher(wsUrl, rpcUrl string, bridgeAddress solana.PublicKey, lock
|
|||
}
|
||||
|
||||
func (s *SolanaWatcher) Run(ctx context.Context) error {
|
||||
// Initialize gossip metrics (we want to broadcast the address even if we're not yet syncing)
|
||||
bridgeAddr := base58.Encode(s.bridge[:])
|
||||
p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDSolana, &gossipv1.Heartbeat_Network{
|
||||
BridgeAddress: bridgeAddr,
|
||||
})
|
||||
|
||||
rpcClient := rpc.NewClient(s.rpcUrl)
|
||||
logger := supervisor.Logger(ctx)
|
||||
errC := make(chan error)
|
||||
|
@ -91,6 +100,10 @@ func (s *SolanaWatcher) Run(ctx context.Context) error {
|
|||
return
|
||||
}
|
||||
currentSolanaHeight.Set(float64(slot))
|
||||
p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDSolana, &gossipv1.Heartbeat_Network{
|
||||
Height: int64(slot),
|
||||
BridgeAddress: bridgeAddr,
|
||||
})
|
||||
|
||||
logger.Info("current Solana height", zap.Uint64("slot", uint64(slot)))
|
||||
|
||||
|
|
|
@ -11,26 +11,41 @@ message GossipMessage {
|
|||
}
|
||||
}
|
||||
|
||||
// P2P gossip heartbeats for network introspection purposes.
|
||||
// P2P gossip heartbeats for network introspection purposes. ALL FIELDS ARE UNTRUSTED.
|
||||
message Heartbeat {
|
||||
// The node's arbitrarily chosen, untrusted nodeName.
|
||||
string node_name = 1;
|
||||
// A monotonic counter that resets to zero on startup.
|
||||
int64 counter = 2;
|
||||
// UNIX wall time.
|
||||
int64 timestamp = 3; // TODO: use this
|
||||
int64 timestamp = 3;
|
||||
|
||||
// Consensus heights on connected networks
|
||||
message Network {// TODO: use this
|
||||
message Network {
|
||||
// Canonical chain ID.
|
||||
uint32 id = 1;
|
||||
// Consensus height of the node.
|
||||
int64 height = 2;
|
||||
// Chain-specific human-readable representation of the bridge contract address.
|
||||
string bridge_address = 3;
|
||||
|
||||
// Fee payer account for this network, if present. Some networks like Ethereum do not use fee payer accounts.
|
||||
message FeePayer {
|
||||
// The account's on-chain balance.
|
||||
int64 balance = 1;
|
||||
// Chain-specific human-readable representation of the fee payer account's address.
|
||||
string address = 2;
|
||||
}
|
||||
FeePayer fee_payer = 4;
|
||||
}
|
||||
repeated Network networks = 4;
|
||||
|
||||
// Human-readable representation of the current bridge node release.
|
||||
string version = 5;
|
||||
|
||||
// TODO: include statement of gk public key?
|
||||
// Human-readable representation of the guardian key's address.
|
||||
string guardian_addr = 6;
|
||||
|
||||
// TODO: include signed statement of gk public key?
|
||||
}
|
||||
|
||||
// A SignedObservation is a signed statement by a given guardian node
|
||||
|
|
Loading…
Reference in New Issue