gitlab.com/flarenetwork/coreth@v0.1.1/params/config.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2016 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package params 28 29 import ( 30 "errors" 31 "fmt" 32 "math/big" 33 34 "github.com/ethereum/go-ethereum/common" 35 ) 36 37 // Avalanche ChainIDs 38 var ( 39 // AvalancheMainnetChainID ... 40 AvalancheMainnetChainID = big.NewInt(43114) 41 // AvalancheFujiChainID ... 42 AvalancheFujiChainID = big.NewInt(43113) 43 // AvalancheLocalChainID ... 44 AvalancheLocalChainID = big.NewInt(43112) 45 46 errNonGenesisForkByHeight = errors.New("coreth only supports forking by height at the genesis block") 47 ) 48 49 var ( 50 // AvalancheMainnetChainConfig is the configuration for Avalanche Main Network 51 AvalancheMainnetChainConfig = &ChainConfig{ 52 ChainID: AvalancheMainnetChainID, 53 HomesteadBlock: big.NewInt(0), 54 DAOForkBlock: big.NewInt(0), 55 DAOForkSupport: true, 56 EIP150Block: big.NewInt(0), 57 EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), 58 EIP155Block: big.NewInt(0), 59 EIP158Block: big.NewInt(0), 60 ByzantiumBlock: big.NewInt(0), 61 ConstantinopleBlock: big.NewInt(0), 62 PetersburgBlock: big.NewInt(0), 63 IstanbulBlock: big.NewInt(0), 64 MuirGlacierBlock: big.NewInt(0), 65 ApricotPhase1BlockTimestamp: big.NewInt(1617199200), // 10am EST 3/31/2021 66 ApricotPhase2BlockTimestamp: big.NewInt(1620644400), // 10am EST 5/10/2021 67 ApricotPhase3BlockTimestamp: big.NewInt(1629813600), // 10am EST 8/24/2021 68 } 69 70 // AvalancheFujiChainConfig is the configuration for the Fuji Test Network 71 AvalancheFujiChainConfig = &ChainConfig{ 72 ChainID: AvalancheFujiChainID, 73 HomesteadBlock: big.NewInt(0), 74 DAOForkBlock: big.NewInt(0), 75 DAOForkSupport: true, 76 EIP150Block: big.NewInt(0), 77 EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), 78 EIP155Block: big.NewInt(0), 79 EIP158Block: big.NewInt(0), 80 ByzantiumBlock: big.NewInt(0), 81 ConstantinopleBlock: big.NewInt(0), 82 PetersburgBlock: big.NewInt(0), 83 IstanbulBlock: big.NewInt(0), 84 MuirGlacierBlock: big.NewInt(0), 85 ApricotPhase1BlockTimestamp: big.NewInt(1616767200), // 10am EST 3/26/2021 86 ApricotPhase2BlockTimestamp: big.NewInt(1620223200), // 10am EST 5/5/2021 87 ApricotPhase3BlockTimestamp: big.NewInt(1629140400), // 3pm EST 8/16/2021 88 } 89 90 // AvalancheLocalChainConfig is the configuration for the Avalanche Local Network 91 AvalancheLocalChainConfig = &ChainConfig{ 92 ChainID: AvalancheLocalChainID, 93 HomesteadBlock: big.NewInt(0), 94 DAOForkBlock: big.NewInt(0), 95 DAOForkSupport: true, 96 EIP150Block: big.NewInt(0), 97 EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), 98 EIP155Block: big.NewInt(0), 99 EIP158Block: big.NewInt(0), 100 ByzantiumBlock: big.NewInt(0), 101 ConstantinopleBlock: big.NewInt(0), 102 PetersburgBlock: big.NewInt(0), 103 IstanbulBlock: big.NewInt(0), 104 MuirGlacierBlock: big.NewInt(0), 105 ApricotPhase1BlockTimestamp: big.NewInt(0), 106 ApricotPhase2BlockTimestamp: big.NewInt(0), 107 ApricotPhase3BlockTimestamp: big.NewInt(0), 108 } 109 110 TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)} 111 AllAvalancheUpgradesConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)} 112 TestRules = TestChainConfig.AvalancheRules(new(big.Int), new(big.Int)) 113 ) 114 115 // ChainConfig is the core config which determines the blockchain settings. 116 // 117 // ChainConfig is stored in the database on a per block basis. This means 118 // that any network, identified by its genesis block, can have its own 119 // set of configuration options. 120 type ChainConfig struct { 121 ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection 122 123 HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // Homestead switch block (nil = no fork, 0 = already homestead) 124 125 DAOForkBlock *big.Int `json:"daoForkBlock,omitempty"` // TheDAO hard-fork switch block (nil = no fork) 126 DAOForkSupport bool `json:"daoForkSupport,omitempty"` // Whether the nodes supports or opposes the DAO hard-fork 127 128 // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150) 129 EIP150Block *big.Int `json:"eip150Block,omitempty"` // EIP150 HF block (nil = no fork) 130 EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // EIP150 HF hash (needed for header only clients as only gas pricing changed) 131 132 EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block 133 EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block 134 135 ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) 136 ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) 137 PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople) 138 IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) 139 MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) 140 141 // Avalanche Network Upgrades 142 ApricotPhase1BlockTimestamp *big.Int `json:"apricotPhase1BlockTimestamp,omitempty"` // Apricot Phase 1 Block Timestamp (nil = no fork, 0 = already activated) 143 // Apricot Phase 2 Block Timestamp (nil = no fork, 0 = already activated) 144 // Apricot Phase 2 includes a modified version of the Berlin Hard Fork from Ethereum 145 ApricotPhase2BlockTimestamp *big.Int `json:"apricotPhase2BlockTimestamp,omitempty"` 146 // Apricot Phase 3 introduces dynamic fees and batching of atomic transactions (nil = no fork, 0 = already activated) 147 ApricotPhase3BlockTimestamp *big.Int `json:"apricotPhase3BlockTimestamp,omitempty"` 148 } 149 150 // String implements the fmt.Stringer interface. 151 func (c *ChainConfig) String() string { 152 return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Apricot Phase 1: %v, Apricot Phase 2: %v, Apricot Phase 3: %v, Engine: Dummy Consensus Engine}", 153 c.ChainID, 154 c.HomesteadBlock, 155 c.DAOForkBlock, 156 c.DAOForkSupport, 157 c.EIP150Block, 158 c.EIP155Block, 159 c.EIP158Block, 160 c.ByzantiumBlock, 161 c.ConstantinopleBlock, 162 c.PetersburgBlock, 163 c.IstanbulBlock, 164 c.MuirGlacierBlock, 165 c.ApricotPhase1BlockTimestamp, 166 c.ApricotPhase2BlockTimestamp, 167 c.ApricotPhase3BlockTimestamp, 168 ) 169 } 170 171 // IsHomestead returns whether num is either equal to the homestead block or greater. 172 func (c *ChainConfig) IsHomestead(num *big.Int) bool { 173 return isForked(c.HomesteadBlock, num) 174 } 175 176 // IsDAOFork returns whether num is either equal to the DAO fork block or greater. 177 func (c *ChainConfig) IsDAOFork(num *big.Int) bool { 178 return isForked(c.DAOForkBlock, num) 179 } 180 181 // IsEIP150 returns whether num is either equal to the EIP150 fork block or greater. 182 func (c *ChainConfig) IsEIP150(num *big.Int) bool { 183 return isForked(c.EIP150Block, num) 184 } 185 186 // IsEIP155 returns whether num is either equal to the EIP155 fork block or greater. 187 func (c *ChainConfig) IsEIP155(num *big.Int) bool { 188 return isForked(c.EIP155Block, num) 189 } 190 191 // IsEIP158 returns whether num is either equal to the EIP158 fork block or greater. 192 func (c *ChainConfig) IsEIP158(num *big.Int) bool { 193 return isForked(c.EIP158Block, num) 194 } 195 196 // IsByzantium returns whether num is either equal to the Byzantium fork block or greater. 197 func (c *ChainConfig) IsByzantium(num *big.Int) bool { 198 return isForked(c.ByzantiumBlock, num) 199 } 200 201 // IsConstantinople returns whether num is either equal to the Constantinople fork block or greater. 202 func (c *ChainConfig) IsConstantinople(num *big.Int) bool { 203 return isForked(c.ConstantinopleBlock, num) 204 } 205 206 // IsMuirGlacier returns whether num is either equal to the Muir Glacier (EIP-2384) fork block or greater. 207 func (c *ChainConfig) IsMuirGlacier(num *big.Int) bool { 208 return isForked(c.MuirGlacierBlock, num) 209 } 210 211 // IsPetersburg returns whether num is either 212 // - equal to or greater than the PetersburgBlock fork block, 213 // - OR is nil, and Constantinople is active 214 func (c *ChainConfig) IsPetersburg(num *big.Int) bool { 215 return isForked(c.PetersburgBlock, num) || c.PetersburgBlock == nil && isForked(c.ConstantinopleBlock, num) 216 } 217 218 // IsIstanbul returns whether num is either equal to the Istanbul fork block or greater. 219 func (c *ChainConfig) IsIstanbul(num *big.Int) bool { 220 return isForked(c.IstanbulBlock, num) 221 } 222 223 // Avalanche Upgrades: 224 225 // IsApricotPhase1 returns whether [blockTimestamp] represents a block 226 // with a timestamp after the Apricot Phase 1 upgrade time. 227 func (c *ChainConfig) IsApricotPhase1(blockTimestamp *big.Int) bool { 228 return isForked(c.ApricotPhase1BlockTimestamp, blockTimestamp) 229 } 230 231 // IsApricotPhase2 returns whether [blockTimestamp] represents a block 232 // with a timestamp after the Apricot Phase 2 upgrade time. 233 func (c *ChainConfig) IsApricotPhase2(blockTimestamp *big.Int) bool { 234 return isForked(c.ApricotPhase2BlockTimestamp, blockTimestamp) 235 } 236 237 // IsApricotPhase3 returns whether [blockTimestamp] represents a block 238 // with a timestamp after the Apricot Phase 3 upgrade time. 239 func (c *ChainConfig) IsApricotPhase3(blockTimestamp *big.Int) bool { 240 return isForked(c.ApricotPhase3BlockTimestamp, blockTimestamp) 241 } 242 243 // CheckCompatible checks whether scheduled fork transitions have been imported 244 // with a mismatching chain configuration. 245 func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError { 246 bhead := new(big.Int).SetUint64(height) 247 248 // Iterate checkCompatible to find the lowest conflict. 249 var lasterr *ConfigCompatError 250 for { 251 err := c.checkCompatible(newcfg, bhead) 252 if err == nil || (lasterr != nil && err.RewindTo == lasterr.RewindTo) { 253 break 254 } 255 lasterr = err 256 bhead.SetUint64(err.RewindTo) 257 } 258 return lasterr 259 } 260 261 // CheckConfigForkOrder checks that we don't "skip" any forks, geth isn't pluggable enough 262 // to guarantee that forks can be implemented in a different order than on official networks 263 func (c *ChainConfig) CheckConfigForkOrder() error { 264 type fork struct { 265 name string 266 block *big.Int 267 optional bool // if true, the fork may be nil and next fork is still allowed 268 } 269 var lastFork fork 270 for _, cur := range []fork{ 271 {name: "homesteadBlock", block: c.HomesteadBlock}, 272 {name: "daoForkBlock", block: c.DAOForkBlock, optional: true}, 273 {name: "eip150Block", block: c.EIP150Block}, 274 {name: "eip155Block", block: c.EIP155Block}, 275 {name: "eip158Block", block: c.EIP158Block}, 276 {name: "byzantiumBlock", block: c.ByzantiumBlock}, 277 {name: "constantinopleBlock", block: c.ConstantinopleBlock}, 278 {name: "petersburgBlock", block: c.PetersburgBlock}, 279 {name: "istanbulBlock", block: c.IstanbulBlock}, 280 {name: "muirGlacierBlock", block: c.MuirGlacierBlock, optional: true}, 281 } { 282 if cur.block != nil && common.Big0.Cmp(cur.block) != 0 { 283 return errNonGenesisForkByHeight 284 } 285 if lastFork.name != "" { 286 // Next one must be higher number 287 if lastFork.block == nil && cur.block != nil { 288 return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at %v", 289 lastFork.name, cur.name, cur.block) 290 } 291 if lastFork.block != nil && cur.block != nil { 292 if lastFork.block.Cmp(cur.block) > 0 { 293 return fmt.Errorf("unsupported fork ordering: %v enabled at %v, but %v enabled at %v", 294 lastFork.name, lastFork.block, cur.name, cur.block) 295 } 296 } 297 } 298 // If it was optional and not set, then ignore it 299 if !cur.optional || cur.block != nil { 300 lastFork = cur 301 } 302 } 303 304 // Note: ApricotPhase1 and ApricotPhase2 override the rules set by block number 305 // hard forks. In Avalanche, hard forks must take place via block timestamps instead 306 // of block numbers since blocks are produced asynchronously. Therefore, we do not 307 // check that the block timestamps for Apricot Phase1 and Phase2 in the same way as for 308 // the block number forks since it would not be a meaningful comparison. 309 // Instead, we check only that Apricot Phases are enabled in order. 310 lastFork = fork{} 311 for _, cur := range []fork{ 312 {name: "apricotPhase1BlockTimestamp", block: c.ApricotPhase1BlockTimestamp}, 313 {name: "apricotPhase2BlockTimestamp", block: c.ApricotPhase2BlockTimestamp}, 314 {name: "apricotPhase3BlockTimestamp", block: c.ApricotPhase3BlockTimestamp}, 315 } { 316 if lastFork.name != "" { 317 // Next one must be higher number 318 if lastFork.block == nil && cur.block != nil { 319 return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at %v", 320 lastFork.name, cur.name, cur.block) 321 } 322 if lastFork.block != nil && cur.block != nil { 323 if lastFork.block.Cmp(cur.block) > 0 { 324 return fmt.Errorf("unsupported fork ordering: %v enabled at %v, but %v enabled at %v", 325 lastFork.name, lastFork.block, cur.name, cur.block) 326 } 327 } 328 } 329 // If it was optional and not set, then ignore it 330 if !cur.optional || cur.block != nil { 331 lastFork = cur 332 } 333 } 334 // TODO(aaronbuchwald) check that avalanche block timestamps are at least possible with the other rule set changes 335 // additional change: require that block number hard forks are either 0 or nil since they should not 336 // be enabled at a specific block number. 337 338 return nil 339 } 340 341 func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError { 342 if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, head) { 343 return newCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock) 344 } 345 if isForkIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, head) { 346 return newCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock) 347 } 348 if c.IsDAOFork(head) && c.DAOForkSupport != newcfg.DAOForkSupport { 349 return newCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock) 350 } 351 if isForkIncompatible(c.EIP150Block, newcfg.EIP150Block, head) { 352 return newCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block) 353 } 354 if isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, head) { 355 return newCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block) 356 } 357 if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, head) { 358 return newCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block) 359 } 360 if c.IsEIP158(head) && !configNumEqual(c.ChainID, newcfg.ChainID) { 361 return newCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block) 362 } 363 if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, head) { 364 return newCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock) 365 } 366 if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) { 367 return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock) 368 } 369 if isForkIncompatible(c.PetersburgBlock, newcfg.PetersburgBlock, head) { 370 // the only case where we allow Petersburg to be set in the past is if it is equal to Constantinople 371 // mainly to satisfy fork ordering requirements which state that Petersburg fork be set if Constantinople fork is set 372 if isForkIncompatible(c.ConstantinopleBlock, newcfg.PetersburgBlock, head) { 373 return newCompatError("Petersburg fork block", c.PetersburgBlock, newcfg.PetersburgBlock) 374 } 375 } 376 if isForkIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, head) { 377 return newCompatError("Istanbul fork block", c.IstanbulBlock, newcfg.IstanbulBlock) 378 } 379 if isForkIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, head) { 380 return newCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock) 381 } 382 // TODO(aaronbuchwald) ensure that Avalanche Blocktimestamps are not modified 383 return nil 384 } 385 386 // isForkIncompatible returns true if a fork scheduled at s1 cannot be rescheduled to 387 // block s2 because head is already past the fork. 388 func isForkIncompatible(s1, s2, head *big.Int) bool { 389 return (isForked(s1, head) || isForked(s2, head)) && !configNumEqual(s1, s2) 390 } 391 392 // isForked returns whether a fork scheduled at block s is active at the given head block. 393 func isForked(s, head *big.Int) bool { 394 if s == nil || head == nil { 395 return false 396 } 397 return s.Cmp(head) <= 0 398 } 399 400 func configNumEqual(x, y *big.Int) bool { 401 if x == nil { 402 return y == nil 403 } 404 if y == nil { 405 return x == nil 406 } 407 return x.Cmp(y) == 0 408 } 409 410 // ConfigCompatError is raised if the locally-stored blockchain is initialised with a 411 // ChainConfig that would alter the past. 412 type ConfigCompatError struct { 413 What string 414 // block numbers of the stored and new configurations 415 StoredConfig, NewConfig *big.Int 416 // the block number to which the local chain must be rewound to correct the error 417 RewindTo uint64 418 } 419 420 func newCompatError(what string, storedblock, newblock *big.Int) *ConfigCompatError { 421 var rew *big.Int 422 switch { 423 case storedblock == nil: 424 rew = newblock 425 case newblock == nil || storedblock.Cmp(newblock) < 0: 426 rew = storedblock 427 default: 428 rew = newblock 429 } 430 err := &ConfigCompatError{what, storedblock, newblock, 0} 431 if rew != nil && rew.Sign() > 0 { 432 err.RewindTo = rew.Uint64() - 1 433 } 434 return err 435 } 436 437 func (err *ConfigCompatError) Error() string { 438 return fmt.Sprintf("mismatching %s in database (have %d, want %d, rewindto %d)", err.What, err.StoredConfig, err.NewConfig, err.RewindTo) 439 } 440 441 // Rules wraps ChainConfig and is merely syntactic sugar or can be used for functions 442 // that do not have or require information about the block. 443 // 444 // Rules is a one time interface meaning that it shouldn't be used in between transition 445 // phases. 446 type Rules struct { 447 ChainID *big.Int 448 IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool 449 IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool 450 451 // Rules for Avalanche releases 452 IsApricotPhase1 bool 453 IsApricotPhase2 bool 454 IsApricotPhase3 bool 455 } 456 457 // Rules ensures c's ChainID is not nil. 458 func (c *ChainConfig) rules(num *big.Int) Rules { 459 chainID := c.ChainID 460 if chainID == nil { 461 chainID = new(big.Int) 462 } 463 return Rules{ 464 ChainID: new(big.Int).Set(chainID), 465 IsHomestead: c.IsHomestead(num), 466 IsEIP150: c.IsEIP150(num), 467 IsEIP155: c.IsEIP155(num), 468 IsEIP158: c.IsEIP158(num), 469 IsByzantium: c.IsByzantium(num), 470 IsConstantinople: c.IsConstantinople(num), 471 IsPetersburg: c.IsPetersburg(num), 472 IsIstanbul: c.IsIstanbul(num), 473 } 474 } 475 476 // AvalancheRules returns the Avalanche modified rules to support Avalanche 477 // network upgrades 478 func (c *ChainConfig) AvalancheRules(blockNum, blockTimestamp *big.Int) Rules { 479 rules := c.rules(blockNum) 480 481 rules.IsApricotPhase1 = c.IsApricotPhase1(blockTimestamp) 482 rules.IsApricotPhase2 = c.IsApricotPhase2(blockTimestamp) 483 rules.IsApricotPhase3 = c.IsApricotPhase3(blockTimestamp) 484 return rules 485 }