bridge: add network heights and guardian address to heartbeat message

This commit is contained in:
Leo 2021-02-03 13:01:51 +01:00
parent aacff406e4
commit 9c1d6ee00c
6 changed files with 107 additions and 10 deletions

View File

@ -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())

View File

@ -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()

View File

@ -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 {

View File

@ -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 &registry{
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()
}

View File

@ -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)))

View File

@ -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