github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/config/protocol_config.go (about)

     1  package config
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  	"time"
     8  
     9  	"github.com/nspcc-dev/neo-go/pkg/config/netmode"
    10  	"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
    11  )
    12  
    13  // ProtocolConfiguration represents the protocol config.
    14  type (
    15  	ProtocolConfiguration struct {
    16  		// CommitteeHistory stores committee size change history (height: size).
    17  		CommitteeHistory map[uint32]uint32 `yaml:"CommitteeHistory"`
    18  		// Genesis stores genesis-related settings including a set of NeoGo
    19  		// extensions that should be included into genesis block or be enabled
    20  		// at the moment of native contracts initialization.
    21  		Genesis Genesis `yaml:"Genesis"`
    22  
    23  		Magic       netmode.Magic `yaml:"Magic"`
    24  		MemPoolSize int           `yaml:"MemPoolSize"`
    25  
    26  		// Hardforks is a map of hardfork names that enables version-specific application
    27  		// logic dependent on the specified height.
    28  		Hardforks map[string]uint32 `yaml:"Hardforks"`
    29  		// InitialGASSupply is the amount of GAS generated in the genesis block.
    30  		InitialGASSupply fixedn.Fixed8 `yaml:"InitialGASSupply"`
    31  		// P2PNotaryRequestPayloadPoolSize specifies the memory pool size for P2PNotaryRequestPayloads.
    32  		// It is valid only if P2PSigExtensions are enabled.
    33  		P2PNotaryRequestPayloadPoolSize int `yaml:"P2PNotaryRequestPayloadPoolSize"`
    34  		// MaxBlockSize is the maximum block size in bytes.
    35  		MaxBlockSize uint32 `yaml:"MaxBlockSize"`
    36  		// MaxBlockSystemFee is the maximum overall system fee per block.
    37  		MaxBlockSystemFee int64 `yaml:"MaxBlockSystemFee"`
    38  		// MaxTraceableBlocks is the length of the chain accessible to smart contracts.
    39  		MaxTraceableBlocks uint32 `yaml:"MaxTraceableBlocks"`
    40  		// MaxTransactionsPerBlock is the maximum amount of transactions per block.
    41  		MaxTransactionsPerBlock uint16 `yaml:"MaxTransactionsPerBlock"`
    42  		// MaxValidUntilBlockIncrement is the upper increment size of blockchain height in blocks
    43  		// exceeding that a transaction should fail validation. It is set to estimated daily number
    44  		// of blocks with 15s interval.
    45  		MaxValidUntilBlockIncrement uint32 `yaml:"MaxValidUntilBlockIncrement"`
    46  		// P2PSigExtensions enables additional signature-related logic.
    47  		P2PSigExtensions bool `yaml:"P2PSigExtensions"`
    48  		// P2PStateExchangeExtensions enables additional P2P MPT state data exchange logic.
    49  		P2PStateExchangeExtensions bool `yaml:"P2PStateExchangeExtensions"`
    50  		// ReservedAttributes allows to have reserved attributes range for experimental or private purposes.
    51  		ReservedAttributes bool `yaml:"ReservedAttributes"`
    52  
    53  		SeedList         []string `yaml:"SeedList"`
    54  		StandbyCommittee []string `yaml:"StandbyCommittee"`
    55  		// StateRooInHeader enables storing state root in block header.
    56  		StateRootInHeader bool `yaml:"StateRootInHeader"`
    57  		// StateSyncInterval is the number of blocks between state heights available for MPT state data synchronization.
    58  		// It is valid only if P2PStateExchangeExtensions are enabled.
    59  		StateSyncInterval int `yaml:"StateSyncInterval"`
    60  		// TimePerBlock is the time interval between blocks that consensus nodes work with.
    61  		// It must be an integer number of milliseconds.
    62  		TimePerBlock    time.Duration `yaml:"TimePerBlock"`
    63  		ValidatorsCount uint32        `yaml:"ValidatorsCount"`
    64  		// Validators stores history of changes to consensus node number (height: number).
    65  		ValidatorsHistory map[uint32]uint32 `yaml:"ValidatorsHistory"`
    66  		// Whether to verify transactions in the received blocks.
    67  		VerifyTransactions bool `yaml:"VerifyTransactions"`
    68  	}
    69  )
    70  
    71  // heightNumber is an auxiliary structure for configuration checks.
    72  type heightNumber struct {
    73  	h uint32
    74  	n uint32
    75  }
    76  
    77  // Validate checks ProtocolConfiguration for internal consistency and returns
    78  // an error if anything inappropriate found. Other methods can rely on protocol
    79  // validity after this.
    80  func (p *ProtocolConfiguration) Validate() error {
    81  	var err error
    82  
    83  	if p.TimePerBlock%time.Millisecond != 0 {
    84  		return errors.New("TimePerBlock must be an integer number of milliseconds")
    85  	}
    86  	for name := range p.Hardforks {
    87  		if !IsHardforkValid(name) {
    88  			return fmt.Errorf("Hardforks configuration section contains unexpected hardfork: %s", name)
    89  		}
    90  	}
    91  	var (
    92  		prev             uint32
    93  		shouldBeDisabled bool
    94  	)
    95  	for _, cfgHf := range Hardforks {
    96  		h := p.Hardforks[cfgHf.String()]
    97  		if h != 0 && shouldBeDisabled {
    98  			return fmt.Errorf("missing previous hardfork configuration with %s present", cfgHf.String())
    99  		}
   100  		if h != 0 && h < prev {
   101  			return fmt.Errorf("hardfork %s has inconsistent enabling height %d (lower than the previouse one)", cfgHf.String(), h)
   102  		}
   103  		if h != 0 {
   104  			prev = h
   105  		} else if prev != 0 {
   106  			shouldBeDisabled = true
   107  		}
   108  	}
   109  	if p.ValidatorsCount != 0 && len(p.ValidatorsHistory) != 0 || p.ValidatorsCount == 0 && len(p.ValidatorsHistory) == 0 {
   110  		return errors.New("configuration should either have one of ValidatorsCount or ValidatorsHistory, not both")
   111  	}
   112  
   113  	if len(p.StandbyCommittee) == 0 {
   114  		return errors.New("configuration should include StandbyCommittee")
   115  	}
   116  	if len(p.StandbyCommittee) < int(p.ValidatorsCount) {
   117  		return errors.New("validators count can't exceed the size of StandbyCommittee")
   118  	}
   119  	var arr = make([]heightNumber, 0, len(p.CommitteeHistory))
   120  	for h, n := range p.CommitteeHistory {
   121  		if n == 0 {
   122  			return fmt.Errorf("invalid CommitteeHistory: bad members count (%d) for height %d", n, h)
   123  		}
   124  		if int(n) > len(p.StandbyCommittee) {
   125  			return fmt.Errorf("too small StandbyCommittee for required number of committee members at %d", h)
   126  		}
   127  		arr = append(arr, heightNumber{h, n})
   128  	}
   129  	if len(arr) != 0 {
   130  		err = sortCheckZero(arr, "CommitteeHistory")
   131  		if err != nil {
   132  			return err
   133  		}
   134  		for i, hn := range arr[1:] {
   135  			if int64(hn.h)%int64(hn.n) != 0 || int64(hn.h)%int64(arr[i].n) != 0 {
   136  				return fmt.Errorf("invalid CommitteeHistory: bad %d height for %d and %d committee", hn.h, hn.n, arr[i].n)
   137  			}
   138  		}
   139  	}
   140  	arr = arr[:0]
   141  	for h, n := range p.ValidatorsHistory {
   142  		if n == 0 {
   143  			return fmt.Errorf("invalid ValidatorsHistory: bad members count (%d) for height %d", n, h)
   144  		}
   145  		if int(n) > len(p.StandbyCommittee) {
   146  			return fmt.Errorf("too small StandbyCommittee for required number of validators at %d", h)
   147  		}
   148  		arr = append(arr, heightNumber{h, n})
   149  	}
   150  	if len(arr) != 0 {
   151  		err = sortCheckZero(arr, "ValidatorsHistory")
   152  		if err != nil {
   153  			return err
   154  		}
   155  		for _, hn := range arr {
   156  			if int64(hn.n) > int64(p.GetCommitteeSize(hn.h)) {
   157  				return fmt.Errorf("requested number of validators is too big: %d at %d", hn.n, hn.h)
   158  			}
   159  			if int64(hn.h)%int64(p.GetCommitteeSize(hn.h)) != 0 {
   160  				return fmt.Errorf("validators number change is not aligned with committee change at %d", hn.h)
   161  			}
   162  		}
   163  	}
   164  	return nil
   165  }
   166  
   167  // sortCheckZero sorts heightNumber array and checks for zero height presence.
   168  func sortCheckZero(arr []heightNumber, field string) error {
   169  	sort.Slice(arr, func(i, j int) bool {
   170  		return arr[i].h < arr[j].h
   171  	})
   172  	if arr[0].h != 0 {
   173  		return fmt.Errorf("invalid %s: no height 0 specified", field)
   174  	}
   175  	return nil
   176  }
   177  
   178  // GetCommitteeSize returns the committee size for the given height. It implies
   179  // valid configuration file.
   180  func (p *ProtocolConfiguration) GetCommitteeSize(height uint32) int {
   181  	if len(p.CommitteeHistory) == 0 {
   182  		return len(p.StandbyCommittee)
   183  	}
   184  	return int(getBestFromMap(p.CommitteeHistory, height))
   185  }
   186  
   187  func getBestFromMap(dict map[uint32]uint32, height uint32) uint32 {
   188  	var res uint32
   189  	var bestH = uint32(0)
   190  	for h, n := range dict {
   191  		if h >= bestH && h <= height {
   192  			res = n
   193  			bestH = h
   194  		}
   195  	}
   196  	return res
   197  }
   198  
   199  // GetNumOfCNs returns the number of validators for the given height.
   200  // It implies valid configuration file.
   201  func (p *ProtocolConfiguration) GetNumOfCNs(height uint32) int {
   202  	if len(p.ValidatorsHistory) == 0 {
   203  		return int(p.ValidatorsCount)
   204  	}
   205  	return int(getBestFromMap(p.ValidatorsHistory, height))
   206  }
   207  
   208  // ShouldUpdateCommitteeAt answers the question of whether the committee
   209  // should be updated at the given height.
   210  func (p *ProtocolConfiguration) ShouldUpdateCommitteeAt(height uint32) bool {
   211  	return height%uint32(p.GetCommitteeSize(height)) == 0
   212  }
   213  
   214  // Equals allows to compare two ProtocolConfiguration instances, returns true if
   215  // they're equal.
   216  func (p *ProtocolConfiguration) Equals(o *ProtocolConfiguration) bool {
   217  	if p.InitialGASSupply != o.InitialGASSupply ||
   218  		p.Magic != o.Magic ||
   219  		p.MaxBlockSize != o.MaxBlockSize ||
   220  		p.MaxBlockSystemFee != o.MaxBlockSystemFee ||
   221  		p.MaxTraceableBlocks != o.MaxTraceableBlocks ||
   222  		p.MaxTransactionsPerBlock != o.MaxTransactionsPerBlock ||
   223  		p.MaxValidUntilBlockIncrement != o.MaxValidUntilBlockIncrement ||
   224  		p.MemPoolSize != o.MemPoolSize ||
   225  		p.P2PNotaryRequestPayloadPoolSize != o.P2PNotaryRequestPayloadPoolSize ||
   226  		p.P2PSigExtensions != o.P2PSigExtensions ||
   227  		p.P2PStateExchangeExtensions != o.P2PStateExchangeExtensions ||
   228  		p.ReservedAttributes != o.ReservedAttributes ||
   229  		p.StateRootInHeader != o.StateRootInHeader ||
   230  		p.StateSyncInterval != o.StateSyncInterval ||
   231  		p.TimePerBlock != o.TimePerBlock ||
   232  		p.ValidatorsCount != o.ValidatorsCount ||
   233  		p.VerifyTransactions != o.VerifyTransactions ||
   234  		len(p.CommitteeHistory) != len(o.CommitteeHistory) ||
   235  		len(p.Hardforks) != len(o.Hardforks) ||
   236  		len(p.SeedList) != len(o.SeedList) ||
   237  		len(p.StandbyCommittee) != len(o.StandbyCommittee) ||
   238  		len(p.ValidatorsHistory) != len(o.ValidatorsHistory) {
   239  		return false
   240  	}
   241  	for k, v := range p.CommitteeHistory {
   242  		vo, ok := o.CommitteeHistory[k]
   243  		if !ok || v != vo {
   244  			return false
   245  		}
   246  	}
   247  	for k, v := range p.Hardforks {
   248  		vo, ok := o.Hardforks[k]
   249  		if !ok || v != vo {
   250  			return false
   251  		}
   252  	}
   253  	for i := range p.SeedList {
   254  		if p.SeedList[i] != o.SeedList[i] {
   255  			return false
   256  		}
   257  	}
   258  	for i := range p.StandbyCommittee {
   259  		if p.StandbyCommittee[i] != o.StandbyCommittee[i] {
   260  			return false
   261  		}
   262  	}
   263  	for k, v := range p.ValidatorsHistory {
   264  		vo, ok := o.ValidatorsHistory[k]
   265  		if !ok || v != vo {
   266  			return false
   267  		}
   268  	}
   269  	return true
   270  }