mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-25 13:47:19 +00:00
config: add Consensus subsection for dBFT config, fix #2677
Old UnlockWallet is still supported and works just fine.
This commit is contained in:
parent
730849a1cd
commit
883c6c5286
10 changed files with 126 additions and 47 deletions
12
ROADMAP.md
12
ROADMAP.md
|
@ -134,4 +134,14 @@ need is to move specified settings under the `P2P` section and convert
|
||||||
time-related settings to `Duration` format).
|
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).
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -48,9 +48,11 @@ 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`):
|
||||||
```
|
```
|
||||||
UnlockWallet:
|
Consensus:
|
||||||
Path: "wallet.json"
|
Enabled: true
|
||||||
Password: "welcometotherealworld"
|
UnlockWallet:
|
||||||
|
Path: "wallet.json"
|
||||||
|
Password: "welcometotherealworld"
|
||||||
```
|
```
|
||||||
where `wallet.json` is a path to your NEP-6 wallet and `welcometotherealworld`
|
where `wallet.json` is a path to your NEP-6 wallet and `welcometotherealworld`
|
||||||
is a password to your CN key. Run the node in a regular way after that:
|
is a password to your CN key. Run the node in a regular way after that:
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -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
4
pkg/config/consensus.go
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
// Consensus contains consensus service configuration.
|
||||||
|
type Consensus InternalService
|
|
@ -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,21 +155,23 @@ func NewService(cfg Config) (Service, error) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if srv.wallet, err = wallet.NewWalletFromFile(cfg.Wallet.Path); err != nil {
|
if len(cfg.Wallet.Path) > 0 {
|
||||||
return nil, err
|
if srv.wallet, err = wallet.NewWalletFromFile(cfg.Wallet.Path); err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
// Check that the wallet password is correct for at least one account.
|
|
||||||
var ok bool
|
// Check that the wallet password is correct for at least one account.
|
||||||
for _, acc := range srv.wallet.Accounts {
|
var ok bool
|
||||||
err := acc.Decrypt(srv.Config.Wallet.Password, srv.wallet.Scrypt)
|
for _, acc := range srv.wallet.Accounts {
|
||||||
if err == nil {
|
err := acc.Decrypt(srv.Config.Wallet.Password, srv.wallet.Scrypt)
|
||||||
ok = true
|
if err == nil {
|
||||||
break
|
ok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("no account with provided password was found")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("no account with provided password was found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.dbft = dbft.New(
|
srv.dbft = dbft.New(
|
||||||
|
@ -282,7 +285,9 @@ 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
|
||||||
s.wallet.Close()
|
if s.wallet != nil {
|
||||||
|
s.wallet.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,24 +382,25 @@ 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) {
|
||||||
for i := range pubs {
|
if s.wallet != nil {
|
||||||
sh := pubs[i].(*publicKey).GetScriptHash()
|
for i := range pubs {
|
||||||
acc := s.wallet.GetAccount(sh)
|
sh := pubs[i].(*publicKey).GetScriptHash()
|
||||||
if acc == nil {
|
acc := s.wallet.GetAccount(sh)
|
||||||
continue
|
if acc == nil {
|
||||||
}
|
continue
|
||||||
|
|
||||||
if !acc.CanSign() {
|
|
||||||
err := acc.Decrypt(s.Config.Wallet.Password, s.wallet.Scrypt)
|
|
||||||
if err != nil {
|
|
||||||
s.log.Fatal("can't unlock account", zap.String("address", address.Uint160ToString(sh)))
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !acc.CanSign() {
|
||||||
|
err := acc.Decrypt(s.Config.Wallet.Password, s.wallet.Scrypt)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Fatal("can't unlock account", zap.String("address", address.Uint160ToString(sh)))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue