node: introduce SignedHeartbeat
Bug: certusone/wormhole#267 Change-Id: Ia34fa053240d7b340287cc4cc1e15556d3ff2893
This commit is contained in:
parent
b02d782f1c
commit
2ebf473531
|
@ -404,7 +404,7 @@ func runBridge(cmd *cobra.Command, args []string) {
|
||||||
// Run supervisor.
|
// Run supervisor.
|
||||||
supervisor.New(rootCtx, logger, func(ctx context.Context) error {
|
supervisor.New(rootCtx, logger, func(ctx context.Context) error {
|
||||||
if err := supervisor.Run(ctx, "p2p", p2p.Run(
|
if err := supervisor.Run(ctx, "p2p", p2p.Run(
|
||||||
obsvC, sendC, rawHeartbeatListeners, priv, *p2pPort, *p2pNetworkID, *p2pBootstrap, *nodeName, rootCtxCancel)); err != nil {
|
obsvC, sendC, rawHeartbeatListeners, priv, gk, *p2pPort, *p2pNetworkID, *p2pBootstrap, *nodeName, rootCtxCancel)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,11 @@ package p2p
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/certusone/wormhole/bridge/pkg/version"
|
"github.com/certusone/wormhole/bridge/pkg/version"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -48,10 +51,17 @@ var (
|
||||||
}, []string{"type"})
|
}, []string{"type"})
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run(obsvC chan *gossipv1.SignedObservation,
|
var heartbeatMessagePrefix = []byte("heartbeat|")
|
||||||
sendC chan []byte,
|
|
||||||
rawHeartbeatListeners *publicrpc.RawHeartbeatConns,
|
func heartbeatDigest(b []byte) common.Hash {
|
||||||
|
return ethcrypto.Keccak256Hash(append(heartbeatMessagePrefix, b...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(
|
||||||
|
obsvC chan *gossipv1.SignedObservation,
|
||||||
|
sendC chan []byte, rawHeartbeatListeners *publicrpc.RawHeartbeatConns,
|
||||||
priv crypto.PrivKey,
|
priv crypto.PrivKey,
|
||||||
|
gk *ecdsa.PrivateKey,
|
||||||
port uint,
|
port uint,
|
||||||
networkID string,
|
networkID string,
|
||||||
bootstrapPeers string,
|
bootstrapPeers string,
|
||||||
|
@ -190,24 +200,43 @@ func Run(obsvC chan *gossipv1.SignedObservation,
|
||||||
networks = append(networks, v)
|
networks = append(networks, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := gossipv1.GossipMessage{Message: &gossipv1.GossipMessage_Heartbeat{
|
heartbeat := &gossipv1.Heartbeat{
|
||||||
Heartbeat: &gossipv1.Heartbeat{
|
|
||||||
NodeName: nodeName,
|
NodeName: nodeName,
|
||||||
Counter: ctr,
|
Counter: ctr,
|
||||||
Timestamp: time.Now().UnixNano(),
|
Timestamp: time.Now().UnixNano(),
|
||||||
Networks: networks,
|
Networks: networks,
|
||||||
Version: version.Version(),
|
Version: version.Version(),
|
||||||
GuardianAddr: DefaultRegistry.guardianAddress,
|
GuardianAddr: DefaultRegistry.guardianAddress,
|
||||||
}}}
|
}
|
||||||
|
|
||||||
rawHeartbeatListeners.PublishHeartbeat(msg.GetHeartbeat())
|
rawHeartbeatListeners.PublishHeartbeat(heartbeat)
|
||||||
|
|
||||||
b, err := proto.Marshal(&msg)
|
b, err := proto.Marshal(heartbeat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultRegistry.mu.Unlock()
|
DefaultRegistry.mu.Unlock()
|
||||||
|
|
||||||
|
// Sign the heartbeat using our node's guardian key.
|
||||||
|
digest := heartbeatDigest(b)
|
||||||
|
sig, err := ethcrypto.Sign(digest.Bytes(), gk)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := gossipv1.GossipMessage{Message: &gossipv1.GossipMessage_SignedHeartbeat{
|
||||||
|
SignedHeartbeat: &gossipv1.SignedHeartbeat{
|
||||||
|
Heartbeat: b,
|
||||||
|
Signature: sig,
|
||||||
|
GuardianAddr: ethcrypto.PubkeyToAddress(gk.PublicKey).Bytes(),
|
||||||
|
}}}
|
||||||
|
|
||||||
|
b, err = proto.Marshal(&msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = th.Publish(ctx, b)
|
err = th.Publish(ctx, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn("failed to publish heartbeat message", zap.Error(err))
|
logger.Warn("failed to publish heartbeat message", zap.Error(err))
|
||||||
|
@ -263,12 +292,31 @@ func Run(obsvC chan *gossipv1.SignedObservation,
|
||||||
zap.String("from", envelope.GetFrom().String()))
|
zap.String("from", envelope.GetFrom().String()))
|
||||||
|
|
||||||
switch m := msg.Message.(type) {
|
switch m := msg.Message.(type) {
|
||||||
|
// TODO(leo): remove Heartbeat support after upgrade
|
||||||
case *gossipv1.GossipMessage_Heartbeat:
|
case *gossipv1.GossipMessage_Heartbeat:
|
||||||
logger.Debug("heartbeat received",
|
logger.Debug("unsigned heartbeat received",
|
||||||
zap.Any("value", m.Heartbeat),
|
zap.Any("value", m.Heartbeat),
|
||||||
zap.String("from", envelope.GetFrom().String()))
|
zap.String("from", envelope.GetFrom().String()))
|
||||||
rawHeartbeatListeners.PublishHeartbeat(msg.GetHeartbeat())
|
rawHeartbeatListeners.PublishHeartbeat(msg.GetHeartbeat())
|
||||||
p2pMessagesReceived.WithLabelValues("heartbeat").Inc()
|
p2pMessagesReceived.WithLabelValues("heartbeat").Inc()
|
||||||
|
case *gossipv1.GossipMessage_SignedHeartbeat:
|
||||||
|
s := m.SignedHeartbeat
|
||||||
|
if heartbeat, err := processSignedHeartbeat(s); err != nil {
|
||||||
|
p2pMessagesReceived.WithLabelValues("invalid_heartbeat").Inc()
|
||||||
|
|
||||||
|
logger.Warn("invalid signed heartbeat received",
|
||||||
|
zap.Error(err),
|
||||||
|
zap.Any("payload", msg.Message),
|
||||||
|
zap.Any("value", s),
|
||||||
|
zap.Binary("raw", envelope.Data),
|
||||||
|
zap.String("from", envelope.GetFrom().String()))
|
||||||
|
} else {
|
||||||
|
p2pMessagesReceived.WithLabelValues("valid_heartbeat").Inc()
|
||||||
|
logger.Debug("valid signed heartbeat received",
|
||||||
|
zap.Any("value", heartbeat),
|
||||||
|
zap.String("from", envelope.GetFrom().String()))
|
||||||
|
rawHeartbeatListeners.PublishHeartbeat(heartbeat)
|
||||||
|
}
|
||||||
case *gossipv1.GossipMessage_SignedObservation:
|
case *gossipv1.GossipMessage_SignedObservation:
|
||||||
obsvC <- m.SignedObservation
|
obsvC <- m.SignedObservation
|
||||||
p2pMessagesReceived.WithLabelValues("observation").Inc()
|
p2pMessagesReceived.WithLabelValues("observation").Inc()
|
||||||
|
@ -282,3 +330,15 @@ func Run(obsvC chan *gossipv1.SignedObservation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func processSignedHeartbeat(s *gossipv1.SignedHeartbeat) (*gossipv1.Heartbeat, error) {
|
||||||
|
// TODO: verify signature here
|
||||||
|
|
||||||
|
var h gossipv1.Heartbeat
|
||||||
|
err := proto.Unmarshal(s.Heartbeat, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal heartbeat: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &h, nil
|
||||||
|
}
|
||||||
|
|
|
@ -6,12 +6,27 @@ option go_package = "github.com/certusone/wormhole/bridge/pkg/proto/gossip/v1;go
|
||||||
|
|
||||||
message GossipMessage {
|
message GossipMessage {
|
||||||
oneof message {
|
oneof message {
|
||||||
|
// Deprecated: use SignedHeartbeat.
|
||||||
Heartbeat heartbeat = 1;
|
Heartbeat heartbeat = 1;
|
||||||
SignedObservation signed_observation = 2;
|
SignedObservation signed_observation = 2;
|
||||||
|
SignedHeartbeat signed_heartbeat = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2P gossip heartbeats for network introspection purposes. ALL FIELDS ARE UNTRUSTED.
|
message SignedHeartbeat {
|
||||||
|
// Serialized Heartbeat message.
|
||||||
|
bytes heartbeat = 1;
|
||||||
|
|
||||||
|
// ECDSA signature using the node's guardian public key.
|
||||||
|
bytes signature = 2;
|
||||||
|
|
||||||
|
// Guardian address that signed this payload (truncated Eth address).
|
||||||
|
// This is already contained in Heartbeat, however, we want to verify
|
||||||
|
// the payload before we deserialize it.
|
||||||
|
bytes guardian_addr = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// P2P gossip heartbeats for network introspection purposes.
|
||||||
message Heartbeat {
|
message Heartbeat {
|
||||||
// The node's arbitrarily chosen, untrusted nodeName.
|
// The node's arbitrarily chosen, untrusted nodeName.
|
||||||
string node_name = 1;
|
string node_name = 1;
|
||||||
|
@ -35,8 +50,6 @@ message Heartbeat {
|
||||||
|
|
||||||
// Human-readable representation of the guardian key's address.
|
// Human-readable representation of the guardian key's address.
|
||||||
string guardian_addr = 6;
|
string guardian_addr = 6;
|
||||||
|
|
||||||
// TODO: include signed statement of gk public key?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A SignedObservation is a signed statement by a given guardian node
|
// A SignedObservation is a signed statement by a given guardian node
|
||||||
|
|
Loading…
Reference in New Issue