node: remove unused legacy code (#2441)

* node: remove discord notifier

Change-Id: If309974f73be35bddb3004e62ff9a0e1d06efab8

* node: remove legacy devnet features

Change-Id: I6f9dac21cb44846f638996c3aa5eb8376794cd78
This commit is contained in:
Hendrik Hofstadt 2023-03-03 16:14:39 +01:00 committed by GitHub
parent 26da76077e
commit 36c34bec1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 13 additions and 425 deletions

View File

@ -151,7 +151,6 @@ def build_node_yaml():
container = obj["spec"]["template"]["spec"]["containers"][0]
if container["name"] != "guardiand":
fail("container 0 is not guardiand")
container["command"] += ["--devNumGuardians", str(num_guardians)]
if guardiand_debug:
container["command"] = command_with_dlv(container["command"])

View File

@ -27,7 +27,6 @@ import (
"github.com/benbjohnson/clock"
"github.com/certusone/wormhole/node/pkg/db"
"github.com/certusone/wormhole/node/pkg/notify/discord"
"github.com/certusone/wormhole/node/pkg/telemetry"
"github.com/certusone/wormhole/node/pkg/version"
"github.com/gagliardetto/solana-go/rpc"
@ -182,10 +181,9 @@ var (
publicRpcLogDetailStr *string
publicRpcLogToTelemetry *bool
unsafeDevMode *bool
testnetMode *bool
devNumGuardians *uint
nodeName *string
unsafeDevMode *bool
testnetMode *bool
nodeName *string
publicRPC *string
publicWeb *string
@ -198,9 +196,6 @@ var (
telemetryKey *string
discordToken *string
discordChannel *string
bigTablePersistenceEnabled *bool
bigTableGCPProject *string
bigTableInstanceName *string
@ -336,7 +331,6 @@ func init() {
unsafeDevMode = NodeCmd.Flags().Bool("unsafeDevMode", false, "Launch node in unsafe, deterministic devnet mode")
testnetMode = NodeCmd.Flags().Bool("testnetMode", false, "Launch node in testnet mode (enables testnet-only features)")
devNumGuardians = NodeCmd.Flags().Uint("devNumGuardians", 5, "Number of devnet guardians to include in guardian set")
nodeName = NodeCmd.Flags().String("nodeName", "", "Node name to announce in gossip heartbeats")
publicRPC = NodeCmd.Flags().String("publicRPC", "", "Listen address for public gRPC interface")
@ -354,9 +348,6 @@ func init() {
telemetryKey = NodeCmd.Flags().String("telemetryKey", "",
"Telemetry write key")
discordToken = NodeCmd.Flags().String("discordToken", "", "Discord bot token (optional)")
discordChannel = NodeCmd.Flags().String("discordChannel", "", "Discord channel name (optional)")
bigTablePersistenceEnabled = NodeCmd.Flags().Bool("bigTablePersistenceEnabled", false, "Turn on forwarding events to BigTable")
bigTableGCPProject = NodeCmd.Flags().String("bigTableGCPProject", "", "Google Cloud project ID for storing events")
bigTableInstanceName = NodeCmd.Flags().String("bigTableInstanceName", "", "BigTable instance name for storing events")
@ -921,14 +912,6 @@ func runNode(cmd *cobra.Command, args []string) {
}(chainMsgC[chainId], chainId)
}
var notifier *discord.DiscordNotifier
if *discordToken != "" {
notifier, err = discord.NewDiscordNotifier(*discordToken, *discordChannel, logger)
if err != nil {
logger.Error("failed to initialize Discord bot", zap.Error(err))
}
}
// Load p2p private key
var priv crypto.PrivKey
if *unsafeDevMode {
@ -1434,12 +1417,7 @@ func runNode(cmd *cobra.Command, args []string) {
signedInReadC,
gk,
gst,
*unsafeDevMode,
*devNumGuardians,
*ethRPC,
*wormchainLCD,
attestationEvents,
notifier,
gov,
acct,
acctReadC,

View File

@ -1,59 +0,0 @@
package main
import (
"encoding/hex"
"flag"
"github.com/certusone/wormhole/node/pkg/notify/discord"
"github.com/wormhole-foundation/wormhole/sdk/vaa"
"go.uber.org/zap"
)
var (
botToken = flag.String("botToken", "", "Discord bot token")
)
func init() {
flag.Parse()
if *botToken == "" {
panic("please provide bot token")
}
}
const (
exampleVaaBytes = `01000000010d01b074d1f0e483942e2e222121749b94d82696cb2692f455b6efa5ee5ffe7644382ec92c69a01e2815d07e86ea79cba64d0db0797cd7fea7184f1b6386470f15c40002e327ba5b53500f73f33dc5d499e3483eb97b69e5c7c338a57f01eff7884f74443e10b0f5e895fd92392448662ceb788e00bcbd4af54129ca1386a34c94e4a91e00033076b5dbcf0826cf245848cb0d66aa556bd63de37a02dbc282b8b8559057071b675844eff803a201ac40d4b4c203f51c56b6a7879831507d052ab5df5a62c5f40004973bd450a72d74960b3adb7345fc2bf66e57ebf60e31599999ee45c2ee31d8656812047289e4ff72dcbad211acf96008b019dad22d26d90c923509769cb1c12601061a7f9cb619addccdda4f79493945506ea6622ddf07be15b9012a5eb694b330a465b23a7eb6ff20715d5b36f73af372ab27a6015cd37b60b833c8574ea84dcfdb000732cd1559a554908d77b6e6ee539de392236ab2f2274554ff4e59761927cc2ce71b41a7f72dd5b91fe41a04361e71c4589b659c48652d7fea135d926ef50fe6e90109de5789414b8dd2eacd3eb1bbf29842aa1c55fc1f8449e0da61cb63ea161c0d9c52a796e79b365cf9bda8fac18a322de54c3e4f32039f26a222b0a7aa374e9d08010a0e43548171d384415d9d1c931c3950e2cfd4416b944cd144ca283b243e765e8a35bc3f3c8aab91f121dd15bc0a337fb0b5938f273aacbb1693f7f010d9e6ea88000c1ccd493f9512f3c1a8042a0b568f389c6e457c61a52aafd5b8f3915d63ab270745e1b6adfae19a005699dcdb4885e95d5bc72d8de8f7219d47d6af3882dfdc9c000d5359bf248f08afb1fd3ecce0b014c4eae7fc51f0dffb5f38536cce2b11becce80150c44d051281f350d4d47666c7b161d9da341a938872aeaa0f4cf21d52229a000e2b206ab8f5bcc8833716631626ecfd5b2b287c47c967025b22e03706eb5dba8f7e7c8c59f12167650d7ce871938c9053ddcb826db823951f88e811dfdc43d1fe001065bf71105ce76db70c75542d5dec9b45df756a021190165ee8a41b1ed9410665318e7b9fc9d68411084e40f67c4fe717f4949a480e6006ea09e29710492356aa0012ac1c80da05f99eaf5edae8daaf40b1161bd6733d3b4ca9764f7ca980c31e475a450d151404708ed465b2d25577d1f9ce1662c735b14d0e9978068964786570f100615c4f9700005e650001ec7372995d5cc8732397fb0ad35c0121e0eaa90d26f828a534cab54391b3a4f500000000000005d4200100000000000000000000000000000000000000000000000000000005883f8260000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800020000000000000000000000009200cd14071a98cda2ab3a87f94973aa44cbbf1600020000000000000000000000000000000000000000000000000000000000000000`
)
func main() {
b, err := hex.DecodeString(exampleVaaBytes)
if err != nil {
panic(err)
}
v, err := vaa.Unmarshal(b)
if err != nil {
panic(err)
}
logger, err := zap.NewDevelopment()
if err != nil {
panic(err)
}
d, err := discord.NewDiscordNotifier(
*botToken, "alerts", logger)
if err != nil {
logger.Fatal("failed to initialize notifier", zap.Error(err))
}
if err := d.MissingSignaturesOnObservation(v, 14, 13, true, []string{
"Certus One", "Not Certus One"}); err != nil {
logger.Fatal("failed to send test message", zap.Error(err))
}
if err := d.MissingSignaturesOnObservation(v, 14, 13, true, []string{
"Certus One"}); err != nil {
logger.Fatal("failed to send test message", zap.Error(err))
}
}

View File

@ -7,20 +7,10 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/mr-tron/base58"
hdwallet "github.com/miguelmota/go-ethereum-hdwallet"
"github.com/wormhole-foundation/wormhole/sdk/vaa"
)
var (
// Ganache RPC URL
GanacheRPCURL = "ws://localhost:8545"
// Address of the first account, which is used as the default client account.
GanacheClientDefaultAccountAddress = common.HexToAddress("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1")
// Contracts (deployed by "truffle migrate" on a deterministic devnet)
GanacheWormholeContractAddress = common.HexToAddress("0xC89Ce4735882C9F0f0FE26686c53074E09B0D550")
)
@ -48,17 +38,3 @@ func Wallet() *hdwallet.Wallet {
}
return wallet
}
// Base58ToEthAddress converts a Solana base58 address to a 32-byte vaa.Address.
// Panics if the input data is invalid - intended for use on constants.
func MustBase58ToEthAddress(address string) (res vaa.Address) {
b, err := base58.Decode(address)
if err != nil {
panic(err)
}
if n := copy(res[:], b); n != 32 {
panic("invalid length")
}
return
}

View File

@ -1,90 +0,0 @@
package devnet
import (
"context"
"fmt"
"time"
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors/ethabi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"go.uber.org/zap"
"github.com/certusone/wormhole/node/pkg/supervisor"
"github.com/wormhole-foundation/wormhole/sdk/vaa"
)
// DevnetGuardianSetVSS returns a VAA signed by guardian-0 that adds all n validators.
func DevnetGuardianSetVSS(n uint) *vaa.VAA {
pubkeys := make([]common.Address, n)
for n := range pubkeys {
key := InsecureDeterministicEcdsaKeyByIndex(crypto.S256(), uint64(n))
pubkeys[n] = crypto.PubkeyToAddress(key.PublicKey)
}
v := &vaa.VAA{
Version: 1,
GuardianSetIndex: 0,
Timestamp: time.Unix(5000, 0),
EmitterChain: 3,
EmitterAddress: vaa.Address{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
},
Payload: vaa.BodyGuardianSetUpdate{
Keys: pubkeys,
NewIndex: 1,
}.Serialize(),
}
// The devnet is initialized with a single guardian (ethereum/migrations/1_initial_migration.js).
key0 := InsecureDeterministicEcdsaKeyByIndex(crypto.S256(), 0)
v.AddSignature(key0, 0)
return v
}
// SubmitVAA submits a VAA to the devnet chain using well-known accounts and contract addresses.
func SubmitVAA(ctx context.Context, rpcURL string, vaa *vaa.VAA) (*types.Transaction, error) {
c, err := ethclient.DialContext(ctx, rpcURL)
if err != nil {
return nil, fmt.Errorf("dialing eth client failed: %w", err)
}
kt := GetKeyedTransactor(ctx)
contract, err := ethabi.NewAbi(GanacheWormholeContractAddress, c)
if err != nil {
panic(err)
}
b, err := vaa.Marshal()
if err != nil {
panic(err)
}
supervisor.Logger(ctx).Info("submitted VAA to Ethereum devnet", zap.Binary("binary", b))
tx, err := contract.SubmitNewGuardianSet(kt, b)
if err != nil {
return nil, err
}
return tx, nil
}
// GetKeyedTransactor returns a transaction signer with the deterministic devnet key.
func GetKeyedTransactor(ctx context.Context) *bind.TransactOpts {
key, err := Wallet().PrivateKey(DeriveAccount(0))
if err != nil {
panic(err)
}
kt := bind.NewKeyedTransactor(key) // nolint
kt.Context = ctx
return kt
}

View File

@ -1,160 +0,0 @@
package discord
import (
"bytes"
"encoding/hex"
"fmt"
"sync"
"github.com/diamondburned/arikawa/v3/api"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/ethereum/go-ethereum/common"
"github.com/wormhole-foundation/wormhole/sdk/vaa"
"go.uber.org/zap"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
type DiscordNotifier struct {
c *api.Client
chans []discord.Channel
logger *zap.Logger
groupToIDMu sync.RWMutex
groupToID map[string]string
}
// NewDiscordNotifier returns and initializes a new Discord notifier.
//
// During initialization, a list of all guilds and channels is fetched.
// Newly added guilds and channels won't be detected at runtime.
func NewDiscordNotifier(botToken string, channelName string, logger *zap.Logger) (*DiscordNotifier, error) {
c := api.NewClient("Bot " + botToken)
chans := make([]discord.Channel, 0)
guilds, err := c.Guilds(0)
if err != nil {
return nil, fmt.Errorf("failed to retrieve guilds: %w", err)
}
for _, guild := range guilds {
gcn, err := c.Channels(guild.ID)
if err != nil {
return nil, fmt.Errorf("failed to retrieve channels for %s: %w", guild.ID, err)
}
for _, cn := range gcn {
if cn.Name == channelName {
chans = append(chans, cn)
}
}
}
logger.Info("notification channels", zap.Any("channels", chans))
return &DiscordNotifier{
c: c,
chans: chans,
logger: logger,
groupToID: make(map[string]string),
}, nil
}
func wrapCode(in string) string {
return fmt.Sprintf("`%s`", in)
}
func (d *DiscordNotifier) LookupGroupID(groupName string) (string, error) {
d.groupToIDMu.RLock()
if id, ok := d.groupToID[groupName]; ok {
d.groupToIDMu.RUnlock()
return id, nil
}
d.groupToIDMu.RUnlock()
guilds, err := d.c.Guilds(0)
if err != nil {
return "", fmt.Errorf("failed to retrieve guilds: %w", err)
}
for _, guild := range guilds {
gcn, err := d.c.Roles(guild.ID)
if err != nil {
return "", fmt.Errorf("failed to retrieve roles for %s: %w", guild.ID, err)
}
for _, cn := range gcn {
if cn.Name == groupName {
m := cn.ID.String()
d.groupToIDMu.Lock()
d.groupToID[groupName] = m
d.groupToIDMu.Unlock()
return m, nil
}
}
}
return "", fmt.Errorf("failed to find group %s", groupName)
}
// Observation defines the same interface as processor.Observation but redefined
// here to avoid circular dependencies.
type Observation interface {
GetEmitterChain() vaa.ChainID
MessageID() string
SigningDigest() common.Hash
}
func (d *DiscordNotifier) MissingSignaturesOnObservation(o Observation, hasSigs, wantSigs int, quorum bool, missing []string) error {
if len(missing) == 0 {
panic("no missing nodes specified")
}
var quorumText string
if quorum {
quorumText = fmt.Sprintf("✔️ yes (%d/%d)", hasSigs, wantSigs)
} else {
quorumText = fmt.Sprintf("🚨️ **NO** (%d/%d)", hasSigs, wantSigs)
}
var messageText string
if !quorum {
messageText = "**NO QUORUM** - Wormhole likely failed to achieve consensus on this message @here"
}
missingText := &bytes.Buffer{}
for _, m := range missing {
groupID, err := d.LookupGroupID(m)
if err != nil {
d.logger.Error("failed to lookup group id", zap.Error(err), zap.String("name", m))
groupID = m
} else {
groupID = fmt.Sprintf("<@&%s>", groupID)
}
if _, err := fmt.Fprintf(missingText, "- %s\n", groupID); err != nil {
panic(err)
}
}
for _, cn := range d.chans {
caser := cases.Title(language.English)
if _, err := d.c.SendMessage(cn.ID, messageText,
discord.Embed{
Title: "Message with missing signatures",
Fields: []discord.EmbedField{
{Name: "Message ID", Value: wrapCode(o.MessageID()), Inline: true},
{Name: "Digest", Value: wrapCode(hex.EncodeToString(o.SigningDigest().Bytes())), Inline: true},
{Name: "Quorum", Value: quorumText, Inline: true},
{Name: "Source Chain", Value: caser.String(o.GetEmitterChain().String()), Inline: false},
{Name: "Missing Guardians", Value: missingText.String(), Inline: false},
},
},
); err != nil {
return err
}
}
return nil
}

View File

@ -2,12 +2,10 @@ package processor
import (
"context"
"encoding/hex"
"time"
"github.com/certusone/wormhole/node/pkg/common"
"github.com/certusone/wormhole/node/pkg/db"
"github.com/certusone/wormhole/node/pkg/notify/discord"
gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
@ -113,38 +111,6 @@ func (p *Processor) handleCleanup(ctx context.Context) {
var chain vaa.ChainID
if s.ourObservation != nil {
chain = s.ourObservation.GetEmitterChain()
// If a notifier is configured, send a notification for any missing signatures.
//
// Only send a notification if we have a observation. Otherwise, bogus observations
// could cause invalid alerts.
if p.notifier != nil && hasSigs < len(gs.Keys) {
p.logger.Info("sending miss notification", zap.String("digest", hash))
// Find names of missing validators
missing := make([]string, 0, len(gs.Keys))
for _, k := range gs.Keys {
if s.signatures[k] == nil {
name := hex.EncodeToString(k.Bytes())
h := p.gst.LastHeartbeat(k)
// Pick first node if there are multiple peers.
for _, hb := range h {
name = hb.NodeName
break
}
missing = append(missing, name)
}
}
// Send notification for individual message when quorum has failed or
// more than one node is missing.
if !quorum || len(missing) > 1 {
go func(o discord.Observation, hasSigs, wantSigs int, quorum bool, missing []string) {
if err := p.notifier.MissingSignaturesOnObservation(o, hasSigs, wantSigs, quorum, missing); err != nil {
p.logger.Error("failed to send notification", zap.Error(err))
}
}(s.ourObservation, hasSigs, wantSigs, quorum, missing)
}
}
}
p.logger.Info("observation considered settled",

View File

@ -6,8 +6,6 @@ import (
"fmt"
"time"
"github.com/certusone/wormhole/node/pkg/notify/discord"
"github.com/certusone/wormhole/node/pkg/db"
"github.com/certusone/wormhole/node/pkg/governor"
@ -102,13 +100,6 @@ type Processor struct {
// gk is the node's guardian private key
gk *ecdsa.PrivateKey
// devnetMode specified whether to submit transactions to the hardcoded Ethereum devnet
devnetMode bool
devnetNumGuardians uint
devnetEthRPC string
wormchainLCD string
attestationEvents *reporter.AttestationEventReporter
logger *zap.Logger
@ -130,7 +121,6 @@ type Processor struct {
// cleanup triggers periodic state cleanup
cleanup *time.Ticker
notifier *discord.DiscordNotifier
governor *governor.ChainGovernor
acct *accountant.Accountant
acctReadC <-chan *common.MessagePublication
@ -149,38 +139,26 @@ func NewProcessor(
signedInC <-chan *gossipv1.SignedVAAWithQuorum,
gk *ecdsa.PrivateKey,
gst *common.GuardianSetState,
devnetMode bool,
devnetNumGuardians uint,
devnetEthRPC string,
wormchainLCD string,
attestationEvents *reporter.AttestationEventReporter,
notifier *discord.DiscordNotifier,
g *governor.ChainGovernor,
acct *accountant.Accountant,
acctReadC <-chan *common.MessagePublication,
) *Processor {
return &Processor{
msgC: msgC,
setC: setC,
gossipSendC: gossipSendC,
obsvC: obsvC,
obsvReqSendC: obsvReqSendC,
signedInC: signedInC,
injectC: injectC,
gk: gk,
gst: gst,
devnetMode: devnetMode,
devnetNumGuardians: devnetNumGuardians,
devnetEthRPC: devnetEthRPC,
db: db,
wormchainLCD: wormchainLCD,
msgC: msgC,
setC: setC,
gossipSendC: gossipSendC,
obsvC: obsvC,
obsvReqSendC: obsvReqSendC,
signedInC: signedInC,
injectC: injectC,
gk: gk,
gst: gst,
db: db,
attestationEvents: attestationEvents,
notifier: notifier,
logger: supervisor.Logger(ctx),
state: &aggregationState{observationMap{}},
ourAddr: crypto.PubkeyToAddress(gk.PublicKey),