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 }