config: add TimePerBlock to replace SecondsPerBlock

It's more generic and convenient than MillisecondsPerBlock. This setting is
made in backwards-compatible fashion, but it'll override SecondsPerBlock if
both are used. Configurations are specifically not changed here, it's
important to check compatibility.

Fixes #2675.
This commit is contained in:
Roman Khimov 2022-12-02 19:10:45 +03:00
parent f6a9969fa8
commit c2adbf768b
13 changed files with 70 additions and 28 deletions

View file

@ -92,3 +92,13 @@ used instead (that also have a counter).
It's not a frequently used thing and it's easy to replace it, so removal of
old counters is scheduled for January-February 2023 (~0.100.X release).
## SecondsPerBlock protocol configuration
With 0.100.0 version SecondsPerBlock protocol configuration setting was
deprecated and replaced by a bit more generic and precise TimePerBlock
(allowing for subsecond time). An informational message is printed on node
startup to inform about this, it's very easy to deal with this configuration
change, just replace one line.
Removal of SecondsPerBlock is scheduled for May-June 2023 (~0.103.0 release).

View file

@ -197,7 +197,7 @@ where:
enable `SessionBackedByMPT`, see `SessionBackedByMPT` documentation for more
details.
- `SessionExpirationTime` is a lifetime of iterator session in seconds. It is set
to `SecondsPerBlock` seconds by default and is relevant only if `SessionEnabled`
to `TimePerBlock` seconds by default and is relevant only if `SessionEnabled`
is set to `true`.
- `SessionBackedByMPT` is a flag forcing JSON-RPC server into using MPT-backed
storage for delayed iterator traversal. If `true`, then iterator resources got
@ -275,11 +275,12 @@ protocol-related settings described in the table below.
| RemoveUntraceableBlocks | `bool`| `false` | Denotes whether old blocks should be removed from cache and database. If enabled, then only the last `MaxTraceableBlocks` are stored and accessible to smart contracts. Old MPT data is also deleted in accordance with `GarbageCollectionPeriod` setting. If enabled along with `P2PStateExchangeExtensions`, then old blocks and MPT states will be removed up to the second latest state synchronisation point (see `StateSyncInterval`). |
| ReservedAttributes | `bool` | `false` | Allows to have reserved attributes range for experimental or private purposes. |
| SaveStorageBatch | `bool` | `false` | Enables storage batch saving before every persist. It is similar to StorageDump plugin for C# node. |
| SecondsPerBlock | `int` | `15` | Minimal time that should pass before next block is accepted. |
| SecondsPerBlock | `int` | `15` | Minimal time that should pass before next block is accepted. Deprecated: please use TimePerBlock setting (which overrides anything set here), SecondsPerBlock will be removed in future versions. |
| SeedList | `[]string` | [] | List of initial nodes addresses used to establish connectivity. |
| StandbyCommittee | `[]string` | [] | List of public keys of standby committee validators are chosen from. |
| StateRootInHeader | `bool` | `false` | Enables storing state root in block header. | Experimental protocol extension! |
| StateSyncInterval | `int` | `40000` | The number of blocks between state heights available for MPT state data synchronization. | `P2PStateExchangeExtensions` should be enabled to use this setting. |
| TimePerBlock | `Duration` | `15s` | Minimal (and targeted for) time interval between blocks. Must be an integer number of milliseconds. |
| ValidatorsCount | `int` | `0` | Number of validators set for the whole network lifetime, can't be set if `ValidatorsHistory` setting is used. |
| ValidatorsHistory | map[uint32]int | none | Number of consensus nodes to use after given height (see `CommitteeHistory` also). Heights where the change occurs must be divisible by the number of committee members at that height. Can't be used with `ValidatorsCount` not equal to zero. |
| VerifyBlocks | `bool` | `false` | Denotes whether to verify the received blocks. |

View file

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"sort"
"time"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
@ -59,6 +60,9 @@ type (
ReservedAttributes bool `yaml:"ReservedAttributes"`
// SaveStorageBatch enables storage batch saving before every persist.
SaveStorageBatch bool `yaml:"SaveStorageBatch"`
// SecondsPerBlock is the time interval (in seconds) between blocks that consensus nodes work with.
//
// Deprecated: replaced by TimePerBlock, to be removed in future versions.
SecondsPerBlock int `yaml:"SecondsPerBlock"`
SeedList []string `yaml:"SeedList"`
StandbyCommittee []string `yaml:"StandbyCommittee"`
@ -67,6 +71,9 @@ type (
// StateSyncInterval is the number of blocks between state heights available for MPT state data synchronization.
// It is valid only if P2PStateExchangeExtensions are enabled.
StateSyncInterval int `yaml:"StateSyncInterval"`
// TimePerBlock is the time interval between blocks that consensus nodes work with.
// It must be an integer number of milliseconds.
TimePerBlock time.Duration `yaml:"TimePerBlock"`
ValidatorsCount int `yaml:"ValidatorsCount"`
// Validators stores history of changes to consensus node number (height: number).
ValidatorsHistory map[uint32]int `yaml:"ValidatorsHistory"`
@ -92,6 +99,9 @@ func (p *ProtocolConfiguration) Validate() error {
if p.P2PStateExchangeExtensions && p.KeepOnlyLatestState && !p.RemoveUntraceableBlocks {
return fmt.Errorf("P2PStateExchangeExtensions can be enabled either on MPT-complete node (KeepOnlyLatestState=false) or on light GC-enabled node (RemoveUntraceableBlocks=true)")
}
if p.TimePerBlock%time.Millisecond != 0 {
return errors.New("TimePerBlock must be an integer number of milliseconds")
}
for name := range p.NativeUpdateHistories {
if !nativenames.IsValid(name) {
return fmt.Errorf("NativeActivations configuration section contains unexpected native contract name: %s", name)
@ -219,6 +229,7 @@ func (p *ProtocolConfiguration) Equals(o *ProtocolConfiguration) bool {
p.SecondsPerBlock != o.SecondsPerBlock ||
p.StateRootInHeader != o.StateRootInHeader ||
p.StateSyncInterval != o.StateSyncInterval ||
p.TimePerBlock != o.TimePerBlock ||
p.ValidatorsCount != o.ValidatorsCount ||
p.VerifyBlocks != o.VerifyBlocks ||
p.VerifyTransactions != o.VerifyTransactions ||

View file

@ -3,6 +3,7 @@ package config
import (
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/require"
)
@ -17,6 +18,14 @@ func TestProtocolConfigurationValidation(t *testing.T) {
P2PStateExchangeExtensions: true,
}
require.Error(t, p.Validate())
p = &ProtocolConfiguration{
StandbyCommittee: []string{
"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
},
ValidatorsCount: 1,
TimePerBlock: time.Microsecond,
}
require.Error(t, p.Validate())
p = &ProtocolConfiguration{
ValidatorsCount: 1,
}

View file

@ -480,7 +480,7 @@ func newTestServiceWithChain(t *testing.T, bc *core.Blockchain) *service {
ProtocolConfiguration: bc.GetConfig(),
RequestTx: func(...util.Uint256) {},
StopTxFlow: func() {},
TimePerBlock: time.Duration(bc.GetConfig().SecondsPerBlock) * time.Second,
TimePerBlock: bc.GetConfig().TimePerBlock,
Wallet: &config.Wallet{
Path: "./testdata/wallet1.json",
Password: "one",

View file

@ -55,7 +55,7 @@ const (
defaultMaxBlockSystemFee = 900000000000
defaultMaxTraceableBlocks = 2102400 // 1 year of 15s blocks
defaultMaxTransactionsPerBlock = 512
defaultSecondsPerBlock = 15
defaultTimePerBlock = 15 * time.Second
// HeaderVerificationGasLimit is the maximum amount of GAS for block header verification.
HeaderVerificationGasLimit = 3_00000000 // 3 GAS
defaultStateSyncInterval = 40000
@ -252,15 +252,21 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L
log.Info("MaxTransactionsPerBlock is not set or wrong, using default value",
zap.Uint16("MaxTransactionsPerBlock", cfg.MaxTransactionsPerBlock))
}
if cfg.SecondsPerBlock == 0 {
cfg.SecondsPerBlock = defaultSecondsPerBlock
log.Info("SecondsPerBlock is not set or wrong, using default value",
zap.Int("SecondsPerBlock", cfg.SecondsPerBlock))
if cfg.TimePerBlock <= 0 {
if cfg.SecondsPerBlock > 0 { //nolint:staticcheck // SA1019: cfg.SecondsPerBlock is deprecated
cfg.TimePerBlock = time.Duration(cfg.SecondsPerBlock) * time.Second //nolint:staticcheck // SA1019: cfg.SecondsPerBlock is deprecated
log.Info("TimePerBlock is not set, using deprecated SecondsPerBlock setting, consider updating your config",
zap.Duration("TimePerBlock", cfg.TimePerBlock))
} else {
cfg.TimePerBlock = defaultTimePerBlock
log.Info("TimePerBlock is not set or wrong, using default value",
zap.Duration("TimePerBlock", cfg.TimePerBlock))
}
}
if cfg.MaxValidUntilBlockIncrement == 0 {
const secondsPerDay = int(24 * time.Hour / time.Second)
const timePerDay = 24 * time.Hour
cfg.MaxValidUntilBlockIncrement = uint32(secondsPerDay / cfg.SecondsPerBlock)
cfg.MaxValidUntilBlockIncrement = uint32(timePerDay / cfg.TimePerBlock)
log.Info("MaxValidUntilBlockIncrement is not set or wrong, using default value",
zap.Uint32("MaxValidUntilBlockIncrement", cfg.MaxValidUntilBlockIncrement))
}
@ -2650,7 +2656,7 @@ func (bc *Blockchain) getFakeNextBlock(nextBlockHeight uint32) (*block.Block, er
if err != nil {
return nil, err
}
b.Timestamp = hdr.Timestamp + uint64(bc.config.SecondsPerBlock*int(time.Second/time.Millisecond))
b.Timestamp = hdr.Timestamp + uint64(bc.config.TimePerBlock/time.Millisecond)
return b, nil
}

View file

@ -4,6 +4,7 @@ import (
"encoding/hex"
"sort"
"testing"
"time"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
@ -22,9 +23,9 @@ const (
// We don't need a lot of traceable blocks for tests.
MaxTraceableBlocks = 1000
// SecondsPerBlock is the default SecondsPerBlock setting used for test chains.
// TimePerBlock is the default TimePerBlock setting used for test chains (1s).
// Usually blocks are created by tests bypassing this setting.
SecondsPerBlock = 1
TimePerBlock = time.Second
)
const singleValidatorWIF = "KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY"
@ -117,7 +118,7 @@ func init() {
// NewSingle creates a new blockchain instance with a single validator and
// setups cleanup functions. The configuration used is with netmode.UnitTestNet
// magic and SecondsPerBlock/MaxTraceableBlocks options defined by constants in
// magic and TimePerBlock/MaxTraceableBlocks options defined by constants in
// this package. MemoryStore is used as the backend storage, so all of the chain
// contents is always in RAM. The Signer returned is the validator (and the committee at
// the same time).
@ -140,7 +141,7 @@ func NewSingleWithCustomConfigAndStore(t testing.TB, f func(cfg *config.Protocol
protoCfg := config.ProtocolConfiguration{
Magic: netmode.UnitTestNet,
MaxTraceableBlocks: MaxTraceableBlocks,
SecondsPerBlock: SecondsPerBlock,
TimePerBlock: TimePerBlock,
StandbyCommittee: []string{hex.EncodeToString(committeeAcc.PublicKey().Bytes())},
ValidatorsCount: 1,
VerifyBlocks: true,
@ -196,7 +197,7 @@ func NewMultiWithCustomConfigAndStoreNoCheck(t testing.TB, f func(*config.Protoc
protoCfg := config.ProtocolConfiguration{
Magic: netmode.UnitTestNet,
MaxTraceableBlocks: MaxTraceableBlocks,
SecondsPerBlock: SecondsPerBlock,
TimePerBlock: TimePerBlock,
StandbyCommittee: standByCommittee,
ValidatorsCount: 4,
VerifyBlocks: true,

View file

@ -89,6 +89,10 @@ type (
func NewServerConfig(cfg config.Config) ServerConfig {
appConfig := cfg.ApplicationConfiguration
protoConfig := cfg.ProtocolConfiguration
timePerBlock := protoConfig.TimePerBlock
if timePerBlock == 0 && protoConfig.SecondsPerBlock > 0 { //nolint:staticcheck // SA1019: protoConfig.SecondsPerBlock is deprecated
timePerBlock = time.Duration(protoConfig.SecondsPerBlock) * time.Second //nolint:staticcheck // SA1019: protoConfig.SecondsPerBlock is deprecated
}
return ServerConfig{
UserAgent: cfg.GenerateUserAgent(),
@ -105,7 +109,7 @@ func NewServerConfig(cfg config.Config) ServerConfig {
MaxPeers: appConfig.MaxPeers,
AttemptConnPeers: appConfig.AttemptConnPeers,
MinPeers: appConfig.MinPeers,
TimePerBlock: time.Duration(protoConfig.SecondsPerBlock) * time.Second,
TimePerBlock: timePerBlock,
OracleCfg: appConfig.Oracle,
P2PNotaryCfg: appConfig.P2PNotary,
StateRootCfg: appConfig.StateRoot,

View file

@ -205,7 +205,7 @@ func (p *TCPPeer) handleQueues() {
var p2pSkipCounter uint32
const p2pSkipDivisor = 4
var writeTimeout = time.Duration(p.server.config.SecondsPerBlock) * time.Second
var writeTimeout = p.server.TimePerBlock
for {
var msg []byte

View file

@ -1994,7 +1994,7 @@ func TestClient_Wait(t *testing.T) {
select {
case <-rcvr:
break waitloop
case <-time.NewTimer(time.Duration(chain.GetConfig().SecondsPerBlock) * time.Second).C:
case <-time.NewTimer(chain.GetConfig().TimePerBlock).C:
t.Fatal("transaction failed to be awaited")
}
}
@ -2070,7 +2070,7 @@ func TestWSClient_Wait(t *testing.T) {
require.Equal(t, vmstate.Halt, aer.VMState)
}
break waitloop
case <-time.NewTimer(time.Duration(chain.GetConfig().SecondsPerBlock) * time.Second).C:
case <-time.NewTimer(chain.GetConfig().TimePerBlock).C:
t.Fatalf("transaction from block %d failed to be awaited: deadline exceeded", b.Index)
}
}
@ -2232,7 +2232,7 @@ waitloop:
require.Equal(t, trigger.Application, aer.Trigger)
require.Equal(t, vmstate.Halt, aer.VMState)
break waitloop
case <-time.NewTimer(time.Duration(chain.GetConfig().SecondsPerBlock) * time.Second).C:
case <-time.NewTimer(chain.GetConfig().TimePerBlock).C:
t.Fatal("transaction failed to be awaited")
}
}

View file

@ -268,8 +268,8 @@ func New(chain Ledger, conf config.RPC, coreServer *network.Server,
protoCfg := chain.GetConfig()
if conf.SessionEnabled {
if conf.SessionExpirationTime <= 0 {
conf.SessionExpirationTime = protoCfg.SecondsPerBlock
log.Info("SessionExpirationTime is not set or wrong, setting default value", zap.Int("SessionExpirationTime", protoCfg.SecondsPerBlock))
conf.SessionExpirationTime = int(protoCfg.TimePerBlock / time.Second)
log.Info("SessionExpirationTime is not set or wrong, setting default value", zap.Int("SessionExpirationTime", conf.SessionExpirationTime))
}
if conf.SessionPoolSize <= 0 {
conf.SessionPoolSize = defaultSessionPoolSize
@ -706,7 +706,7 @@ func (s *Server) getVersion(_ params.Params) (interface{}, *neorpc.Error) {
Protocol: result.Protocol{
AddressVersion: address.NEO3Prefix,
Network: cfg.Magic,
MillisecondsPerBlock: cfg.SecondsPerBlock * 1000,
MillisecondsPerBlock: int(cfg.TimePerBlock / time.Millisecond),
MaxTraceableBlocks: cfg.MaxTraceableBlocks,
MaxValidUntilBlockIncrement: cfg.MaxValidUntilBlockIncrement,
MaxTransactionsPerBlock: cfg.MaxTransactionsPerBlock,

View file

@ -872,7 +872,7 @@ var rpcTestCases = map[string][]rpcTestCase{
cfg := e.chain.GetConfig()
require.EqualValues(t, address.NEO3Prefix, resp.Protocol.AddressVersion)
require.EqualValues(t, cfg.Magic, resp.Protocol.Network)
require.EqualValues(t, cfg.SecondsPerBlock*1000, resp.Protocol.MillisecondsPerBlock)
require.EqualValues(t, cfg.TimePerBlock/time.Millisecond, resp.Protocol.MillisecondsPerBlock)
require.EqualValues(t, cfg.MaxTraceableBlocks, resp.Protocol.MaxTraceableBlocks)
require.EqualValues(t, cfg.MaxValidUntilBlockIncrement, resp.Protocol.MaxValidUntilBlockIncrement)
require.EqualValues(t, cfg.MaxTransactionsPerBlock, resp.Protocol.MaxTransactionsPerBlock)

View file

@ -87,7 +87,7 @@ func New(cfg config.StateRoot, sm *stateroot.Module, log *zap.Logger, bc Ledger,
blockCh: make(chan *block.Block),
stopCh: make(chan struct{}),
done: make(chan struct{}),
timePerBlock: time.Duration(bcConf.SecondsPerBlock) * time.Second,
timePerBlock: bcConf.TimePerBlock,
maxRetries: voteValidEndInc,
relayExtensible: cb,
}