Merge pull request #2832 from nspcc-dev/consensus-config-section

Consensus config section
This commit is contained in:
Roman Khimov 2022-12-07 20:18:23 +07:00 committed by GitHub
commit 531ab4bd6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 189 additions and 71 deletions

View file

@ -135,3 +135,13 @@ time-related settings to `Duration` format).
Removal of deprecated P2P related application settings is scheduled for May-June Removal of deprecated P2P related application settings is scheduled for May-June
2023 (~0.103.0 release). 2023 (~0.103.0 release).
## Direct UnlockWallet consensus configuration
Top-level UnlockWallet section in ApplicationConfiguration was used as an
implicit consensus service configuration, now this setting (with Enabled flag)
is moved into a section of its own (Consensus). Old configurations are still
supported, but this support will eventually be removed.
Removal of this compatibility code is scheduled for May-June 2023 (~0.103.0
release).

View file

@ -376,8 +376,8 @@ func mkOracle(config config.OracleConfiguration, magic netmode.Magic, chain *cor
return orc, nil return orc, nil
} }
func mkConsensus(config config.Wallet, tpb time.Duration, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (consensus.Service, error) { func mkConsensus(config config.Consensus, tpb time.Duration, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (consensus.Service, error) {
if len(config.Path) == 0 { if !config.Enabled {
return nil, nil return nil, nil
} }
srv, err := consensus.NewService(consensus.Config{ srv, err := consensus.NewService(consensus.Config{
@ -387,7 +387,7 @@ func mkConsensus(config config.Wallet, tpb time.Duration, chain *core.Blockchain
ProtocolConfiguration: chain.GetConfig(), ProtocolConfiguration: chain.GetConfig(),
RequestTx: serv.RequestTx, RequestTx: serv.RequestTx,
StopTxFlow: serv.StopTxFlow, StopTxFlow: serv.StopTxFlow,
Wallet: &config, Wallet: config.UnlockWallet,
TimePerBlock: tpb, TimePerBlock: tpb,
}) })
if err != nil { if err != nil {
@ -476,7 +476,7 @@ func startServer(ctx *cli.Context) error {
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
dbftSrv, err := mkConsensus(cfg.ApplicationConfiguration.UnlockWallet, serverConfig.TimePerBlock, chain, serv, log) dbftSrv, err := mkConsensus(cfg.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
@ -609,7 +609,7 @@ Main:
serv.DelConsensusService(dbftSrv) serv.DelConsensusService(dbftSrv)
dbftSrv.Shutdown() dbftSrv.Shutdown()
} }
dbftSrv, err = mkConsensus(cfgnew.ApplicationConfiguration.UnlockWallet, serverConfig.TimePerBlock, chain, serv, log) dbftSrv, err = mkConsensus(cfgnew.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log)
if err != nil { if err != nil {
log.Error("failed to create consensus service", zap.Error(err)) log.Error("failed to create consensus service", zap.Error(err))
break // Whatever happens, I'll leave it all to chance. break // Whatever happens, I'll leave it all to chance.

View file

@ -58,6 +58,11 @@ ApplicationConfiguration:
AttemptConnPeers: 20 AttemptConnPeers: 20
MinPeers: 5 MinPeers: 5
Relay: true Relay: true
Consensus:
Enabled: false
UnlockWallet:
Path: "/cn_wallet.json"
Password: "pass"
Oracle: Oracle:
Enabled: false Enabled: false
AllowedContentTypes: AllowedContentTypes:

View file

@ -70,6 +70,11 @@ ApplicationConfiguration:
AttemptConnPeers: 20 AttemptConnPeers: 20
MinPeers: 10 MinPeers: 10
Relay: true Relay: true
Consensus:
Enabled: false
UnlockWallet:
Path: "/cn_wallet.json"
Password: "pass"
Oracle: Oracle:
Enabled: false Enabled: false
AllowedContentTypes: AllowedContentTypes:

View file

@ -81,6 +81,8 @@ ApplicationConfiguration:
Enabled: false Enabled: false
Addresses: Addresses:
- ":20014" - ":20014"
Consensus:
Enabled: true
UnlockWallet: UnlockWallet:
Path: "/wallet4.json" Path: "/wallet4.json"
Password: "four" Password: "four"

View file

@ -80,6 +80,8 @@ ApplicationConfiguration:
Pprof: Pprof:
Enabled: false Enabled: false
Port: 20011 Port: 20011
Consensus:
Enabled: true
UnlockWallet: UnlockWallet:
Path: "/wallet1.json" Path: "/wallet1.json"
Password: "one" Password: "one"

View file

@ -72,6 +72,8 @@ ApplicationConfiguration:
Enabled: false Enabled: false
Addresses: Addresses:
- ":20011" - ":20011"
Consensus:
Enabled: true
UnlockWallet: UnlockWallet:
Path: "/wallet1.json" Path: "/wallet1.json"
Password: "one" Password: "one"

View file

@ -81,6 +81,8 @@ ApplicationConfiguration:
Enabled: false Enabled: false
Addresses: Addresses:
- ":20013" - ":20013"
Consensus:
Enabled: true
UnlockWallet: UnlockWallet:
Path: "/wallet3.json" Path: "/wallet3.json"
Password: "three" Password: "three"

View file

@ -81,6 +81,8 @@ ApplicationConfiguration:
Enabled: false Enabled: false
Addresses: Addresses:
- ":20012" - ":20012"
Consensus:
Enabled: true
UnlockWallet: UnlockWallet:
Path: "/wallet2.json" Path: "/wallet2.json"
Password: "two" Password: "two"

View file

@ -49,6 +49,11 @@ ApplicationConfiguration:
AttemptConnPeers: 5 AttemptConnPeers: 5
MinPeers: 3 MinPeers: 3
Relay: true Relay: true
Consensus:
Enabled: false
UnlockWallet:
Path: "/cn_wallet.json"
Password: "pass"
P2PNotary: P2PNotary:
Enabled: false Enabled: false
UnlockWallet: UnlockWallet:

View file

@ -58,6 +58,11 @@ ApplicationConfiguration:
AttemptConnPeers: 20 AttemptConnPeers: 20
MinPeers: 5 MinPeers: 5
Relay: true Relay: true
Consensus:
Enabled: false
UnlockWallet:
Path: "/cn_wallet.json"
Password: "pass"
Oracle: Oracle:
Enabled: false Enabled: false
AllowedContentTypes: AllowedContentTypes:

View file

@ -73,6 +73,11 @@ ApplicationConfiguration:
AttemptConnPeers: 20 AttemptConnPeers: 20
MinPeers: 10 MinPeers: 10
Relay: true Relay: true
Consensus:
Enabled: false
UnlockWallet:
Path: "/cn_wallet.json"
Password: "pass"
Oracle: Oracle:
Enabled: false Enabled: false
AllowedContentTypes: AllowedContentTypes:

View file

@ -44,6 +44,8 @@ ApplicationConfiguration:
MaxPeers: 10 MaxPeers: 10
AttemptConnPeers: 5 AttemptConnPeers: 5
Relay: true Relay: true
Consensus:
Enabled: true
UnlockWallet: UnlockWallet:
Path: "../testdata/wallet1_solo.json" Path: "../testdata/wallet1_solo.json"
Password: "one" Password: "one"

View file

@ -48,6 +48,8 @@ neo-go binary).
Add the following subsection to `ApplicationConfiguration` section of your Add the following subsection to `ApplicationConfiguration` section of your
configuration file (`protocol.mainnet.yml` or `protocol.testnet.yml`): configuration file (`protocol.mainnet.yml` or `protocol.testnet.yml`):
``` ```
Consensus:
Enabled: true
UnlockWallet: UnlockWallet:
Path: "wallet.json" Path: "wallet.json"
Password: "welcometotherealworld" Password: "welcometotherealworld"
@ -71,6 +73,15 @@ your expectations. Details on various configuration options are provided in the
[node configuration documentation](node-configuration.md), CLI commands are [node configuration documentation](node-configuration.md), CLI commands are
provided in the [CLI documentation](cli.md). provided in the [CLI documentation](cli.md).
Consensus service can also run in watch-only mode when the node will
receive/process/log dBFT messages generated by other nodes, but won't be able
to generate any. It's mostly useful for debugging/monitoring. To enable this
mode just drop the `UnlockWallet` section from the configuration like this:
```
Consensus:
Enabled: true
```
### Registration ### Registration
To register as a candidate, use neo-go as CLI command with an external RPC To register as a candidate, use neo-go as CLI command with an external RPC
@ -174,8 +185,10 @@ place the corresponding config named `protocol.privnet.yml` there.
2. Edit configuration file for every node. 2. Edit configuration file for every node.
Examples can be found at `config/protocol.privnet.docker.one.yml` (`two`, `three` etc.). Examples can be found at `config/protocol.privnet.docker.one.yml` (`two`, `three` etc.).
1. Add `UnlockWallet` section with `Path` and `Password` strings for NEP-6 1. Add `Consensus` section with `Enabled: true` field and an
wallet path and the password for the account to be used for the consensus node. `UnlockWallet` subsection with `Path` and `Password` strings for NEP-6
wallet path and the password for the account to be used for the
consensus node.
2. Make sure that your `MinPeers` setting is equal to 2. Make sure that your `MinPeers` setting is equal to
the number of nodes participating in consensus. the number of nodes participating in consensus.
This requirement is needed for nodes to correctly This requirement is needed for nodes to correctly

View file

@ -37,9 +37,10 @@ node-related settings described in the table below.
| Prometheus | [Metrics Services Configuration](#Metrics-Services-Configuration) | | Configuration for Prometheus (monitoring system). See the [Metrics Services Configuration](#Metrics-Services-Configuration) section for details | | Prometheus | [Metrics Services Configuration](#Metrics-Services-Configuration) | | Configuration for Prometheus (monitoring system). See the [Metrics Services Configuration](#Metrics-Services-Configuration) section for details |
| ProtoTickInterval | `int64` | `5` | Duration in seconds between protocol ticks with each connected peer. Warning: this field is deprecated and moved to `P2P` section. | | ProtoTickInterval | `int64` | `5` | Duration in seconds between protocol ticks with each connected peer. Warning: this field is deprecated and moved to `P2P` section. |
| Relay | `bool` | `true` | Determines whether the server is forwarding its inventory. | | Relay | `bool` | `true` | Determines whether the server is forwarding its inventory. |
| Consensus | [Consensus Configuration](#Consensus-Configuration) | | Describes consensus (dBFT) configuration. See the [Consensus Configuration](#Consensus-Configuration) for details. |
| RPC | [RPC Configuration](#RPC-Configuration) | | Describes [RPC subsystem](rpc.md) configuration. See the [RPC Configuration](#RPC-Configuration) for details. | | RPC | [RPC Configuration](#RPC-Configuration) | | Describes [RPC subsystem](rpc.md) configuration. See the [RPC Configuration](#RPC-Configuration) for details. |
| StateRoot | [State Root Configuration](#State-Root-Configuration) | | State root module configuration. See the [State Root Configuration](#State-Root-Configuration) section for details. | | StateRoot | [State Root Configuration](#State-Root-Configuration) | | State root module configuration. See the [State Root Configuration](#State-Root-Configuration) section for details. |
| UnlockWallet | [Unlock Wallet Configuration](#Unlock-Wallet-Configuration) | | Node wallet configuration used for consensus (dBFT) operation. See the [Unlock Wallet Configuration](#Unlock-Wallet-Configuration) section for details. | | UnlockWallet | [Unlock Wallet Configuration](#Unlock-Wallet-Configuration) | | Node wallet configuration used for consensus (dBFT) operation. See the [Unlock Wallet Configuration](#Unlock-Wallet-Configuration) section for details. This section is deprecated and replaced by Consensus, it only exists for compatibility with old configuration files, but will be removed in future node versions. |
### P2P Configuration ### P2P Configuration
@ -293,6 +294,26 @@ where:
[Unlock Wallet Configuration](#Unlock-Wallet-Configuration) section for [Unlock Wallet Configuration](#Unlock-Wallet-Configuration) section for
structure details. structure details.
### Consensus Configuration
`Consensus` configuration section describes configuration for dBFT node
module and has the following structure:
```
Consensus:
Enabled: false
UnlockWallet:
Path: "/consensus_node_wallet.json"
Password: "pass"
```
where:
- `Enabled` denotes whether dBFT module is active.
- `UnlockWallet` is a consensus node wallet configuration, see the
[Unlock Wallet Configuration](#Unlock-Wallet-Configuration) section for
structure details.
Please, refer to the [consensus node documentation](./consensus.md) for more
details on consensus node setup.
### Unlock Wallet Configuration ### Unlock Wallet Configuration
`UnlockWallet` configuration section contains wallet settings and has the following `UnlockWallet` configuration section contains wallet settings and has the following

View file

@ -158,7 +158,7 @@ func NewTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockch
ProtocolConfiguration: chain.GetConfig(), ProtocolConfiguration: chain.GetConfig(),
RequestTx: netSrv.RequestTx, RequestTx: netSrv.RequestTx,
StopTxFlow: netSrv.StopTxFlow, StopTxFlow: netSrv.StopTxFlow,
Wallet: &cfg.ApplicationConfiguration.UnlockWallet, Wallet: cfg.ApplicationConfiguration.Consensus.UnlockWallet,
TimePerBlock: serverConfig.TimePerBlock, TimePerBlock: serverConfig.TimePerBlock,
}) })
require.NoError(t, err) require.NoError(t, err)

View file

@ -43,6 +43,7 @@ type ApplicationConfiguration struct {
// Deprecated: this option is moved to the P2P section. // Deprecated: this option is moved to the P2P section.
ProtoTickInterval int64 `yaml:"ProtoTickInterval"` ProtoTickInterval int64 `yaml:"ProtoTickInterval"`
Relay bool `yaml:"Relay"` Relay bool `yaml:"Relay"`
Consensus Consensus `yaml:"Consensus"`
RPC RPC `yaml:"RPC"` RPC RPC `yaml:"RPC"`
UnlockWallet Wallet `yaml:"UnlockWallet"` UnlockWallet Wallet `yaml:"UnlockWallet"`
Oracle OracleConfiguration `yaml:"Oracle"` Oracle OracleConfiguration `yaml:"Oracle"`

View file

@ -42,7 +42,8 @@ func Load(path string, netMode netmode.Magic) (Config, error) {
return LoadFile(configPath) return LoadFile(configPath)
} }
// LoadFile loads config from the provided path. // LoadFile loads config from the provided path. It also applies backwards compatibility
// fixups if necessary.
func LoadFile(configPath string) (Config, error) { func LoadFile(configPath string) (Config, error) {
if _, err := os.Stat(configPath); os.IsNotExist(err) { if _, err := os.Stat(configPath); os.IsNotExist(err) {
return Config{}, fmt.Errorf("config '%s' doesn't exist", configPath) return Config{}, fmt.Errorf("config '%s' doesn't exist", configPath)
@ -72,6 +73,11 @@ func LoadFile(configPath string) (Config, error) {
return Config{}, fmt.Errorf("failed to unmarshal config YAML: %w", err) return Config{}, fmt.Errorf("failed to unmarshal config YAML: %w", err)
} }
if len(config.ApplicationConfiguration.UnlockWallet.Path) > 0 && len(config.ApplicationConfiguration.Consensus.UnlockWallet.Path) == 0 {
config.ApplicationConfiguration.Consensus.UnlockWallet = config.ApplicationConfiguration.UnlockWallet
config.ApplicationConfiguration.Consensus.Enabled = true
}
err = config.ProtocolConfiguration.Validate() err = config.ProtocolConfiguration.Validate()
if err != nil { if err != nil {
return Config{}, err return Config{}, err

4
pkg/config/consensus.go Normal file
View file

@ -0,0 +1,4 @@
package config
// Consensus contains consensus service configuration.
type Consensus InternalService

View file

@ -0,0 +1,8 @@
package config
// InternalService stores configuration for internal services that don't have
// any network configuration, but use a wallet and can be enabled/disabled.
type InternalService struct {
Enabled bool `yaml:"Enabled"`
UnlockWallet Wallet `yaml:"UnlockWallet"`
}

View file

@ -1,7 +1,4 @@
package config package config
// P2PNotary stores configuration for Notary node service. // P2PNotary stores configuration for Notary node service.
type P2PNotary struct { type P2PNotary InternalService
Enabled bool `yaml:"Enabled"`
UnlockWallet Wallet `yaml:"UnlockWallet"`
}

View file

@ -1,7 +1,4 @@
package config package config
// StateRoot contains state root service configuration. // StateRoot contains state root service configuration.
type StateRoot struct { type StateRoot InternalService
Enabled bool `yaml:"Enabled"`
UnlockWallet Wallet `yaml:"UnlockWallet"`
}

View file

@ -124,8 +124,9 @@ type Config struct {
StopTxFlow func() StopTxFlow func()
// TimePerBlock is minimal time that should pass before the next block is accepted. // TimePerBlock is minimal time that should pass before the next block is accepted.
TimePerBlock time.Duration TimePerBlock time.Duration
// Wallet is a local-node wallet configuration. // Wallet is a local-node wallet configuration. If the path is empty, then
Wallet *config.Wallet // no wallet will be initialized and the service will be in watch-only mode.
Wallet config.Wallet
} }
// NewService returns a new consensus.Service instance. // NewService returns a new consensus.Service instance.
@ -154,6 +155,7 @@ func NewService(cfg Config) (Service, error) {
var err error var err error
if len(cfg.Wallet.Path) > 0 {
if srv.wallet, err = wallet.NewWalletFromFile(cfg.Wallet.Path); err != nil { if srv.wallet, err = wallet.NewWalletFromFile(cfg.Wallet.Path); err != nil {
return nil, err return nil, err
} }
@ -170,6 +172,7 @@ func NewService(cfg Config) (Service, error) {
if !ok { if !ok {
return nil, errors.New("no account with provided password was found") return nil, errors.New("no account with provided password was found")
} }
}
srv.dbft = dbft.New( srv.dbft = dbft.New(
dbft.WithLogger(srv.log), dbft.WithLogger(srv.log),
@ -282,8 +285,10 @@ func (s *service) Shutdown() {
s.log.Info("stopping consensus service") s.log.Info("stopping consensus service")
close(s.quit) close(s.quit)
<-s.finished <-s.finished
if s.wallet != nil {
s.wallet.Close() s.wallet.Close()
} }
}
} }
func (s *service) eventLoop() { func (s *service) eventLoop() {
@ -377,6 +382,7 @@ func (s *service) validatePayload(p *Payload) bool {
} }
func (s *service) getKeyPair(pubs []crypto.PublicKey) (int, crypto.PrivateKey, crypto.PublicKey) { func (s *service) getKeyPair(pubs []crypto.PublicKey) (int, crypto.PrivateKey, crypto.PublicKey) {
if s.wallet != nil {
for i := range pubs { for i := range pubs {
sh := pubs[i].(*publicKey).GetScriptHash() sh := pubs[i].(*publicKey).GetScriptHash()
acc := s.wallet.GetAccount(sh) acc := s.wallet.GetAccount(sh)
@ -394,7 +400,7 @@ func (s *service) getKeyPair(pubs []crypto.PublicKey) (int, crypto.PrivateKey, c
return i, &privateKey{PrivateKey: acc.PrivateKey()}, &publicKey{PublicKey: acc.PublicKey()} return i, &privateKey{PrivateKey: acc.PrivateKey()}, &publicKey{PublicKey: acc.PublicKey()}
} }
}
return -1, nil, nil return -1, nil, nil
} }

View file

@ -44,6 +44,24 @@ func TestNewService(t *testing.T) {
require.Equal(t, tx, txx[0]) require.Equal(t, tx, txx[0])
} }
func TestNewWatchingService(t *testing.T) {
bc := newTestChain(t, false)
srv, err := NewService(Config{
Logger: zaptest.NewLogger(t),
Broadcast: func(*npayload.Extensible) {},
Chain: bc,
ProtocolConfiguration: bc.GetConfig(),
RequestTx: func(...util.Uint256) {},
StopTxFlow: func() {},
TimePerBlock: bc.GetConfig().TimePerBlock,
// No wallet provided.
})
require.NoError(t, err)
require.NotPanics(t, srv.Start)
require.NotPanics(t, srv.Shutdown)
}
func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint32) (*service, *wallet.Account) { func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint32) (*service, *wallet.Account) {
acc, err := wallet.NewAccountFromWIF(testchain.WIF(testchain.IDToOrder(0))) acc, err := wallet.NewAccountFromWIF(testchain.WIF(testchain.IDToOrder(0)))
require.NoError(t, err) require.NoError(t, err)
@ -481,7 +499,7 @@ func newTestServiceWithChain(t *testing.T, bc *core.Blockchain) *service {
RequestTx: func(...util.Uint256) {}, RequestTx: func(...util.Uint256) {},
StopTxFlow: func() {}, StopTxFlow: func() {},
TimePerBlock: bc.GetConfig().TimePerBlock, TimePerBlock: bc.GetConfig().TimePerBlock,
Wallet: &config.Wallet{ Wallet: config.Wallet{
Path: "./testdata/wallet1.json", Path: "./testdata/wallet1.json",
Password: "one", Password: "one",
}, },