2020-03-25 15:30:21 +00:00
package config
import (
2022-01-19 23:59:40 +00:00
"errors"
"fmt"
2022-01-21 02:33:06 +00:00
"sort"
2022-01-19 23:59:40 +00:00
2020-06-14 07:34:50 +00:00
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
2022-01-19 23:59:40 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
2021-07-20 12:54:56 +00:00
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
2020-03-25 15:30:21 +00:00
)
// ProtocolConfiguration represents the protocol config.
type (
ProtocolConfiguration struct {
2022-01-21 02:33:06 +00:00
// CommitteeHistory stores committee size change history (height: size).
CommitteeHistory map [ uint32 ] int ` yaml:"CommitteeHistory" `
2022-01-29 08:28:29 +00:00
// GarbageCollectionPeriod sets the number of blocks to wait before
// starting the next MPT garbage collection cycle when RemoveUntraceableBlocks
// option is used.
GarbageCollectionPeriod uint32 ` yaml:"GarbageCollectionPeriod" `
Magic netmode . Magic ` yaml:"Magic" `
MemPoolSize int ` yaml:"MemPoolSize" `
2021-07-20 12:54:56 +00:00
2022-05-06 12:19:17 +00:00
// Hardforks is a map of hardfork names that enables version-specific application
// logic dependent on the specified height.
Hardforks map [ string ] uint32 ` yaml:"Hardforks" `
2021-07-20 12:54:56 +00:00
// InitialGASSupply is the amount of GAS generated in the genesis block.
InitialGASSupply fixedn . Fixed8 ` yaml:"InitialGASSupply" `
2020-11-27 10:55:48 +00:00
// P2PNotaryRequestPayloadPoolSize specifies the memory pool size for P2PNotaryRequestPayloads.
// It is valid only if P2PSigExtensions are enabled.
P2PNotaryRequestPayloadPoolSize int ` yaml:"P2PNotaryRequestPayloadPoolSize" `
2022-04-20 18:30:09 +00:00
// KeepOnlyLatestState specifies if MPT should only store the latest state.
2020-10-21 13:58:41 +00:00
// If true, DB size will be smaller, but older roots won't be accessible.
// This value should remain the same for the same database.
KeepOnlyLatestState bool ` yaml:"KeepOnlyLatestState" `
2022-01-29 08:28:29 +00:00
// RemoveUntraceableBlocks specifies if old data should be removed.
2020-11-24 09:07:58 +00:00
RemoveUntraceableBlocks bool ` yaml:"RemoveUntraceableBlocks" `
2021-03-15 10:00:04 +00:00
// MaxBlockSize is the maximum block size in bytes.
MaxBlockSize uint32 ` yaml:"MaxBlockSize" `
2021-03-15 10:51:07 +00:00
// MaxBlockSystemFee is the maximum overall system fee per block.
MaxBlockSystemFee int64 ` yaml:"MaxBlockSystemFee" `
2020-11-05 19:01:12 +00:00
// MaxTraceableBlocks is the length of the chain accessible to smart contracts.
MaxTraceableBlocks uint32 ` yaml:"MaxTraceableBlocks" `
2021-02-17 15:22:57 +00:00
// MaxTransactionsPerBlock is the maximum amount of transactions per block.
MaxTransactionsPerBlock uint16 ` yaml:"MaxTransactionsPerBlock" `
2021-05-17 08:07:08 +00:00
// MaxValidUntilBlockIncrement is the upper increment size of blockchain height in blocks
// exceeding that a transaction should fail validation. It is set to estimated daily number
// of blocks with 15s interval.
MaxValidUntilBlockIncrement uint32 ` yaml:"MaxValidUntilBlockIncrement" `
2022-04-20 18:30:09 +00:00
// NativeUpdateHistories is a list of histories of native contracts updates.
2021-03-11 11:39:51 +00:00
NativeUpdateHistories map [ string ] [ ] uint32 ` yaml:"NativeActivations" `
2020-11-27 10:55:48 +00:00
// P2PSigExtensions enables additional signature-related logic.
2020-10-14 16:07:16 +00:00
P2PSigExtensions bool ` yaml:"P2PSigExtensions" `
2021-06-15 08:09:35 +00:00
// P2PStateExchangeExtensions enables additional P2P MPT state data exchange logic.
P2PStateExchangeExtensions bool ` yaml:"P2PStateExchangeExtensions" `
2020-10-15 10:06:22 +00:00
// ReservedAttributes allows to have reserved attributes range for experimental or private purposes.
ReservedAttributes bool ` yaml:"ReservedAttributes" `
2020-03-25 15:30:21 +00:00
// SaveStorageBatch enables storage batch saving before every persist.
2020-08-03 14:07:24 +00:00
SaveStorageBatch bool ` yaml:"SaveStorageBatch" `
SecondsPerBlock int ` yaml:"SecondsPerBlock" `
SeedList [ ] string ` yaml:"SeedList" `
StandbyCommittee [ ] string ` yaml:"StandbyCommittee" `
2020-11-17 12:57:50 +00:00
// StateRooInHeader enables storing state root in block header.
StateRootInHeader bool ` yaml:"StateRootInHeader" `
2021-06-15 08:09:35 +00:00
// 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" `
ValidatorsCount int ` yaml:"ValidatorsCount" `
2022-01-21 02:33:06 +00:00
// Validators stores history of changes to consensus node number (height: number).
ValidatorsHistory map [ uint32 ] int ` yaml:"ValidatorsHistory" `
2020-03-25 15:30:21 +00:00
// Whether to verify received blocks.
VerifyBlocks bool ` yaml:"VerifyBlocks" `
2022-04-20 18:30:09 +00:00
// Whether to verify transactions in the received blocks.
2020-03-25 15:30:21 +00:00
VerifyTransactions bool ` yaml:"VerifyTransactions" `
}
)
2022-01-19 23:59:40 +00:00
2022-01-21 02:33:06 +00:00
// heightNumber is an auxiliary structure for configuration checks.
type heightNumber struct {
h uint32
n int
}
2022-01-19 23:59:40 +00:00
// Validate checks ProtocolConfiguration for internal consistency and returns
2022-04-20 18:30:09 +00:00
// an error if anything inappropriate found. Other methods can rely on protocol
2022-01-19 23:59:40 +00:00
// validity after this.
func ( p * ProtocolConfiguration ) Validate ( ) error {
2022-01-21 02:33:06 +00:00
var err error
2022-07-14 11:13:38 +00:00
if p . P2PStateExchangeExtensions && p . KeepOnlyLatestState && ! p . RemoveUntraceableBlocks {
return fmt . Errorf ( "P2PStateExchangeExtensions can be supprted either on MPT-complete node (KeepOnlyLatestState=false) or on light GC-enabled node (KeepOnlyLatestState=true, RemoveUntraceableBlocks=true)" )
2022-02-08 18:47:17 +00:00
}
2022-01-19 23:59:40 +00:00
for name := range p . NativeUpdateHistories {
if ! nativenames . IsValid ( name ) {
return fmt . Errorf ( "NativeActivations configuration section contains unexpected native contract name: %s" , name )
}
}
2022-05-06 12:19:17 +00:00
for name := range p . Hardforks {
if ! IsHardforkValid ( name ) {
return fmt . Errorf ( "Hardforks configuration section contains unexpected hardfork: %s" , name )
}
}
2022-01-21 02:33:06 +00:00
if p . ValidatorsCount != 0 && len ( p . ValidatorsHistory ) != 0 {
return errors . New ( "configuration should either have ValidatorsCount or ValidatorsHistory, not both" )
}
if len ( p . StandbyCommittee ) < p . ValidatorsCount {
return errors . New ( "validators count can't exceed the size of StandbyCommittee" )
}
var arr = make ( [ ] heightNumber , 0 , len ( p . CommitteeHistory ) )
for h , n := range p . CommitteeHistory {
if int ( n ) > len ( p . StandbyCommittee ) {
return fmt . Errorf ( "too small StandbyCommittee for required number of committee members at %d" , h )
}
arr = append ( arr , heightNumber { h , n } )
}
if len ( arr ) != 0 {
err = sortCheckZero ( arr , "CommitteeHistory" )
if err != nil {
return err
}
for i , hn := range arr [ 1 : ] {
if int64 ( hn . h ) % int64 ( hn . n ) != 0 || int64 ( hn . h ) % int64 ( arr [ i ] . n ) != 0 {
return fmt . Errorf ( "invalid CommitteeHistory: bad %d height for %d and %d committee" , hn . h , hn . n , arr [ i ] . n )
}
}
}
arr = arr [ : 0 ]
for h , n := range p . ValidatorsHistory {
if int ( n ) > len ( p . StandbyCommittee ) {
return fmt . Errorf ( "too small StandbyCommittee for required number of validators at %d" , h )
}
arr = append ( arr , heightNumber { h , n } )
}
if len ( arr ) != 0 {
err = sortCheckZero ( arr , "ValidatorsHistory" )
if err != nil {
return err
}
for _ , hn := range arr {
if int64 ( hn . n ) > int64 ( p . GetCommitteeSize ( hn . h ) ) {
return fmt . Errorf ( "requested number of validators is too big: %d at %d" , hn . n , hn . h )
}
if int64 ( hn . h ) % int64 ( p . GetCommitteeSize ( hn . h ) ) != 0 {
return fmt . Errorf ( "validators number change is not aligned with committee change at %d" , hn . h )
}
}
}
2022-01-19 23:59:40 +00:00
return nil
}
2022-01-21 02:33:06 +00:00
// sortCheckZero sorts heightNumber array and checks for zero height presence.
func sortCheckZero ( arr [ ] heightNumber , field string ) error {
sort . Slice ( arr , func ( i , j int ) bool {
return arr [ i ] . h < arr [ j ] . h
} )
if arr [ 0 ] . h != 0 {
return fmt . Errorf ( "invalid %s: no height 0 specified" , field )
}
return nil
}
// GetCommitteeSize returns the committee size for the given height. It implies
// valid configuration file.
func ( p * ProtocolConfiguration ) GetCommitteeSize ( height uint32 ) int {
if len ( p . CommitteeHistory ) == 0 {
return len ( p . StandbyCommittee )
}
return getBestFromMap ( p . CommitteeHistory , height )
}
func getBestFromMap ( dict map [ uint32 ] int , height uint32 ) int {
var res int
var bestH = uint32 ( 0 )
for h , n := range dict {
if h >= bestH && h <= height {
res = n
bestH = h
}
}
return res
}
// GetNumOfCNs returns the number of validators for the given height.
// It implies valid configuration file.
func ( p * ProtocolConfiguration ) GetNumOfCNs ( height uint32 ) int {
if len ( p . ValidatorsHistory ) == 0 {
return p . ValidatorsCount
}
return getBestFromMap ( p . ValidatorsHistory , height )
}
// ShouldUpdateCommitteeAt answers the question of whether the committee
// should be updated at the given height.
func ( p * ProtocolConfiguration ) ShouldUpdateCommitteeAt ( height uint32 ) bool {
return height % uint32 ( p . GetCommitteeSize ( height ) ) == 0
}