Skip to content

Commit

Permalink
feat: support multiple btc chain config (#2870)
Browse files Browse the repository at this point in the history
* support multiple btc chain config

* keep BitcoinConfig to be backward compatible

* clean accidentially included changelog entry during merge; improve unit test

* use go-mask to mask sensitive information in zetaclient config file

* fix e2etest failure by renaming btc node user: smoketest --> e2etest

* change e2etest --> smoketest; change Signet chain id as 18333
  • Loading branch information
ws4charlie authored Sep 18, 2024
1 parent 3bf36b9 commit 8d17118
Show file tree
Hide file tree
Showing 17 changed files with 218 additions and 88 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* [2784](https://github.com/zeta-chain/node/pull/2784) - staking precompiled contract
* [2795](https://github.com/zeta-chain/node/pull/2795) - support restricted address in Solana
* [2861](https://github.com/zeta-chain/node/pull/2861) - emit events from staking precompile
* [2870](https://github.com/zeta-chain/node/pull/2870) - support for multiple Bitcoin chains in the zetaclient
* [2883](https://github.com/zeta-chain/node/pull/2883) - add chain static information for btc signet testnet

### Refactor
Expand Down
14 changes: 9 additions & 5 deletions cmd/zetaclientd/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,17 +169,21 @@ func debugCmd(_ *cobra.Command, args []string) error {
fmt.Println("CoinType not detected")
}
fmt.Println("CoinType : ", coinType)
} else if chain.IsUTXO() {
} else if chain.IsBitcoin() {
btcObserver := btcobserver.Observer{}
btcObserver.WithZetacoreClient(client)
btcObserver.WithChain(*chainProto)
btcConfig, found := cfg.GetBTCConfig(chainID)
if !found {
return fmt.Errorf("unable to find config for BTC chain %d", chainID)
}
connCfg := &rpcclient.ConnConfig{
Host: cfg.BitcoinConfig.RPCHost,
User: cfg.BitcoinConfig.RPCUsername,
Pass: cfg.BitcoinConfig.RPCPassword,
Host: btcConfig.RPCHost,
User: btcConfig.RPCUsername,
Pass: btcConfig.RPCPassword,
HTTPPostMode: true,
DisableTLS: true,
Params: cfg.BitcoinConfig.RPCParams,
Params: btcConfig.RPCParams,
}

btcClient, err := rpcclient.New(connCfg, nil)
Expand Down
4 changes: 2 additions & 2 deletions cmd/zetaclientd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func start(_ *cobra.Command, _ []string) error {
return err
}

startLogger.Info().Msgf("Config is updated from zetacore %s", maskCfg(cfg))
startLogger.Info().Msgf("Config is updated from zetacore\n %s", cfg.StringMasked())

go zetacoreClient.UpdateAppContextWorker(ctx, appContext)

Expand Down Expand Up @@ -230,7 +230,7 @@ func start(_ *cobra.Command, _ []string) error {
return err
}

btcChains := appContext.FilterChains(zctx.Chain.IsUTXO)
btcChains := appContext.FilterChains(zctx.Chain.IsBitcoin)
switch {
case len(btcChains) == 0:
return errors.New("no BTC chains found")
Expand Down
41 changes: 0 additions & 41 deletions cmd/zetaclientd/start_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"fmt"
"net"
"net/url"
"strings"
"time"

Expand Down Expand Up @@ -52,43 +51,3 @@ func validatePeer(seedPeer string) error {

return nil
}

// maskCfg sensitive fields are masked, currently only the EVM endpoints and bitcoin credentials,
//
// other fields can be added.
func maskCfg(cfg config.Config) string {
maskedCfg := cfg

maskedCfg.BitcoinConfig = config.BTCConfig{
RPCUsername: cfg.BitcoinConfig.RPCUsername,
RPCPassword: cfg.BitcoinConfig.RPCPassword,
RPCHost: cfg.BitcoinConfig.RPCHost,
RPCParams: cfg.BitcoinConfig.RPCParams,
}
maskedCfg.EVMChainConfigs = map[int64]config.EVMConfig{}
for key, val := range cfg.EVMChainConfigs {
maskedCfg.EVMChainConfigs[key] = config.EVMConfig{
Chain: val.Chain,
Endpoint: val.Endpoint,
}
}

// Mask Sensitive data
for _, chain := range maskedCfg.EVMChainConfigs {
if chain.Endpoint == "" {
continue
}
endpointURL, err := url.Parse(chain.Endpoint)
if err != nil {
continue
}
chain.Endpoint = endpointURL.Hostname()
}

// mask endpoints
maskedCfg.BitcoinConfig.RPCUsername = ""
maskedCfg.BitcoinConfig.RPCPassword = ""
maskedCfg.SolanaConfig.Endpoint = ""

return maskedCfg.String()
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ require (

require (
github.com/oasisprotocol/curve25519-voi v0.0.0-20220328075252-7dd334e3daae // indirect
github.com/showa-93/go-mask v0.6.2 // indirect
github.com/snksoft/crc v1.1.0 // indirect
github.com/tonkeeper/tongo v1.9.3 // indirect
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1433,6 +1433,8 @@ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/showa-93/go-mask v0.6.2 h1:sJEUQRpbxUoMTfBKey5K9hCg+eSx5KIAZFT7pa1LXbM=
github.com/showa-93/go-mask v0.6.2/go.mod h1:aswIj007gm0EPAzOGES9ACy1jDm3QT08/LPSClMp410=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
Expand Down
2 changes: 1 addition & 1 deletion pkg/chains/chains.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ var (

BitcoinSignetTestnet = Chain{
ChainName: ChainName_btc_signet_testnet,
ChainId: 18334,
ChainId: 18333,
Network: Network_btc,
NetworkType: NetworkType_testnet,
Vm: Vm_no_vm,
Expand Down
13 changes: 10 additions & 3 deletions zetaclient/config/config_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ const (
func New(setDefaults bool) Config {
cfg := Config{
EVMChainConfigs: make(map[int64]EVMConfig),
BitcoinConfig: BTCConfig{},
BTCChainConfigs: make(map[int64]BTCConfig),

mu: &sync.RWMutex{},
}

if setDefaults {
cfg.BitcoinConfig = bitcoinConfigRegnet()
cfg.SolanaConfig = solanaConfigLocalnet()
cfg.EVMChainConfigs = evmChainsConfigs()
cfg.BTCChainConfigs = btcChainsConfigs()
cfg.SolanaConfig = solanaConfigLocalnet()
}

return cfg
Expand Down Expand Up @@ -80,3 +80,10 @@ func evmChainsConfigs() map[int64]EVMConfig {
},
}
}

// btcChainsConfigs contains BTC chain configs
func btcChainsConfigs() map[int64]BTCConfig {
return map[int64]BTCConfig{
chains.BitcoinRegtest.ChainId: bitcoinConfigRegnet(),
}
}
59 changes: 43 additions & 16 deletions zetaclient/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"strings"
"sync"

"github.com/showa-93/go-mask"

"github.com/zeta-chain/node/pkg/chains"
)

Expand Down Expand Up @@ -38,23 +40,23 @@ type ClientConfiguration struct {
// EVMConfig is the config for EVM chain
type EVMConfig struct {
Chain chains.Chain
Endpoint string
Endpoint string `mask:"filled"`
RPCAlertLatency int64
}

// BTCConfig is the config for Bitcoin chain
type BTCConfig struct {
// the following are rpcclient ConnConfig fields
RPCUsername string
RPCPassword string
RPCHost string
RPCUsername string `mask:"filled"`
RPCPassword string `mask:"filled"`
RPCHost string `mask:"filled"`
RPCParams string // "regtest", "mainnet", "testnet3" , "signet"
RPCAlertLatency int64
}

// SolanaConfig is the config for Solana chain
type SolanaConfig struct {
Endpoint string
Endpoint string `mask:"filled"`
RPCAlertLatency int64
}

Expand Down Expand Up @@ -91,8 +93,10 @@ type Config struct {

// chain configs
EVMChainConfigs map[int64]EVMConfig `json:"EVMChainConfigs"`
BitcoinConfig BTCConfig `json:"BitcoinConfig"`
SolanaConfig SolanaConfig `json:"SolanaConfig"`
BTCChainConfigs map[int64]BTCConfig `json:"BTCChainConfigs"`
// Deprecated: the 'BitcoinConfig' will be removed once the 'BTCChainConfigs' is fully adopted
BitcoinConfig BTCConfig `json:"BitcoinConfig"`
SolanaConfig SolanaConfig `json:"SolanaConfig"`

// compliance config
ComplianceConfig ComplianceConfig `json:"ComplianceConfig"`
Expand All @@ -104,8 +108,9 @@ type Config struct {
func (c Config) GetEVMConfig(chainID int64) (EVMConfig, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
evmCfg, found := c.EVMChainConfigs[chainID]
return evmCfg, found

evmCfg := c.EVMChainConfigs[chainID]
return evmCfg, !evmCfg.Empty()
}

// GetAllEVMConfigs returns a map of all EVM configs
Expand All @@ -121,12 +126,19 @@ func (c Config) GetAllEVMConfigs() map[int64]EVMConfig {
return copied
}

// GetBTCConfig returns the BTC config
func (c Config) GetBTCConfig() (BTCConfig, bool) {
// GetBTCConfig returns the BTC config for the given chain ID
func (c Config) GetBTCConfig(chainID int64) (BTCConfig, bool) {
c.mu.RLock()
defer c.mu.RUnlock()

return c.BitcoinConfig, c.BitcoinConfig != (BTCConfig{})
// we prefer 'BTCChainConfigs' over 'BitcoinConfig' but still fallback to be backward compatible
// this will allow new 'zetaclientd' binary to work with old config file
btcCfg, found := c.BTCChainConfigs[chainID]
if !found || btcCfg.Empty() {
btcCfg = c.BitcoinConfig
}

return btcCfg, !btcCfg.Empty()
}

// GetSolanaConfig returns the Solana config
Expand All @@ -137,9 +149,20 @@ func (c Config) GetSolanaConfig() (SolanaConfig, bool) {
return c.SolanaConfig, c.SolanaConfig != (SolanaConfig{})
}

// String returns the string representation of the config
func (c Config) String() string {
s, err := json.MarshalIndent(c, "", "\t")
// StringMasked returns the string representation of the config with sensitive fields masked.
// Currently only the endpoints and bitcoin credentials are masked.
func (c Config) StringMasked() string {
// create a masker
masker := mask.NewMasker()
masker.RegisterMaskStringFunc(mask.MaskTypeFilled, masker.MaskFilledString)

// mask the config
masked, err := masker.Mask(c)
if err != nil {
return ""
}

s, err := json.MarshalIndent(masked, "", "\t")
if err != nil {
return ""
}
Expand Down Expand Up @@ -178,5 +201,9 @@ func (c Config) GetRelayerKeyPath() string {
}

func (c EVMConfig) Empty() bool {
return c.Endpoint == "" && c.Chain.IsEmpty()
return c.Endpoint == "" || c.Chain.IsEmpty()
}

func (c BTCConfig) Empty() bool {
return c.RPCHost == ""
}
Loading

0 comments on commit 8d17118

Please sign in to comment.