github.com/dominant-strategies/go-quai@v0.28.2/consensus/blake3pow/consensus.go (about) 1 package blake3pow 2 3 import ( 4 "errors" 5 "fmt" 6 "math/big" 7 "runtime" 8 "time" 9 10 mapset "github.com/deckarep/golang-set" 11 "github.com/dominant-strategies/go-quai/common" 12 "github.com/dominant-strategies/go-quai/consensus" 13 "github.com/dominant-strategies/go-quai/consensus/misc" 14 "github.com/dominant-strategies/go-quai/core" 15 "github.com/dominant-strategies/go-quai/core/state" 16 "github.com/dominant-strategies/go-quai/core/types" 17 "github.com/dominant-strategies/go-quai/log" 18 "github.com/dominant-strategies/go-quai/params" 19 "github.com/dominant-strategies/go-quai/trie" 20 "modernc.org/mathutil" 21 ) 22 23 // Blake3pow proof-of-work protocol constants. 24 var ( 25 maxUncles = 2 // Maximum number of uncles allowed in a single block 26 allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks 27 28 ContextTimeFactor = big10 29 ZoneBlockReward = big.NewInt(5e+18) 30 RegionBlockReward = new(big.Int).Mul(ZoneBlockReward, big3) 31 PrimeBlockReward = new(big.Int).Mul(RegionBlockReward, big3) 32 ) 33 34 // Some useful constants to avoid constant memory allocs for them. 35 var ( 36 expDiffPeriod = big.NewInt(100000) 37 big0 = big.NewInt(0) 38 big1 = big.NewInt(1) 39 big2 = big.NewInt(2) 40 big3 = big.NewInt(3) 41 big8 = big.NewInt(8) 42 big9 = big.NewInt(9) 43 big10 = big.NewInt(10) 44 big32 = big.NewInt(32) 45 bigMinus99 = big.NewInt(-99) 46 big2e256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) // 2^256 47 ) 48 49 // Various error messages to mark blocks invalid. These should be private to 50 // prevent engine specific errors from being referenced in the remainder of the 51 // codebase, inherently breaking if the engine is swapped out. Please put common 52 // error types into the consensus package. 53 var ( 54 errOlderBlockTime = errors.New("timestamp older than parent") 55 errTooManyUncles = errors.New("too many uncles") 56 errDuplicateUncle = errors.New("duplicate uncle") 57 errUncleIsAncestor = errors.New("uncle is ancestor") 58 errDanglingUncle = errors.New("uncle's parent is not ancestor") 59 errInvalidDifficulty = errors.New("non-positive difficulty") 60 errDifficultyCrossover = errors.New("sub's difficulty exceeds dom's") 61 errInvalidPoW = errors.New("invalid proof-of-work") 62 errInvalidOrder = errors.New("invalid order") 63 ) 64 65 // Author implements consensus.Engine, returning the header's coinbase as the 66 // proof-of-work verified author of the block. 67 func (blake3pow *Blake3pow) Author(header *types.Header) (common.Address, error) { 68 return header.Coinbase(), nil 69 } 70 71 // VerifyHeader checks whether a header conforms to the consensus rules of the 72 // stock Quai blake3pow engine. 73 func (blake3pow *Blake3pow) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error { 74 // If we're running a full engine faking, accept any input as valid 75 if blake3pow.config.PowMode == ModeFullFake { 76 return nil 77 } 78 // Short circuit if the header is known, or its parent not 79 number := header.NumberU64() 80 if chain.GetHeader(header.Hash(), number) != nil { 81 return nil 82 } 83 parent := chain.GetHeader(header.ParentHash(), number-1) 84 if parent == nil { 85 return consensus.ErrUnknownAncestor 86 } 87 // Sanity checks passed, do a proper verification 88 return blake3pow.verifyHeader(chain, header, parent, false, time.Now().Unix()) 89 } 90 91 // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers 92 // concurrently. The method returns a quit channel to abort the operations and 93 // a results channel to retrieve the async verifications. 94 func (blake3pow *Blake3pow) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) { 95 // If we're running a full engine faking, accept any input as valid 96 if blake3pow.config.PowMode == ModeFullFake || len(headers) == 0 { 97 abort, results := make(chan struct{}), make(chan error, len(headers)) 98 for i := 0; i < len(headers); i++ { 99 results <- nil 100 } 101 return abort, results 102 } 103 104 // Spawn as many workers as allowed threads 105 workers := runtime.GOMAXPROCS(0) 106 if len(headers) < workers { 107 workers = len(headers) 108 } 109 110 // Create a task channel and spawn the verifiers 111 var ( 112 inputs = make(chan int) 113 done = make(chan int, workers) 114 errors = make([]error, len(headers)) 115 abort = make(chan struct{}) 116 unixNow = time.Now().Unix() 117 ) 118 for i := 0; i < workers; i++ { 119 go func() { 120 for index := range inputs { 121 errors[index] = blake3pow.verifyHeaderWorker(chain, headers, index, unixNow) 122 done <- index 123 } 124 }() 125 } 126 127 errorsOut := make(chan error, len(headers)) 128 go func() { 129 defer close(inputs) 130 var ( 131 in, out = 0, 0 132 checked = make([]bool, len(headers)) 133 inputs = inputs 134 ) 135 for { 136 select { 137 case inputs <- in: 138 if in++; in == len(headers) { 139 // Reached end of headers. Stop sending to workers. 140 inputs = nil 141 } 142 case index := <-done: 143 for checked[index] = true; checked[out]; out++ { 144 errorsOut <- errors[out] 145 if out == len(headers)-1 { 146 return 147 } 148 } 149 case <-abort: 150 return 151 } 152 } 153 }() 154 return abort, errorsOut 155 } 156 157 func (blake3pow *Blake3pow) verifyHeaderWorker(chain consensus.ChainHeaderReader, headers []*types.Header, index int, unixNow int64) error { 158 var parent *types.Header 159 if index == 0 { 160 parent = chain.GetHeader(headers[0].ParentHash(), headers[0].NumberU64()-1) 161 } else if headers[index-1].Hash() == headers[index].ParentHash() { 162 parent = headers[index-1] 163 } 164 if parent == nil { 165 return consensus.ErrUnknownAncestor 166 } 167 return blake3pow.verifyHeader(chain, headers[index], parent, false, unixNow) 168 } 169 170 // VerifyUncles verifies that the given block's uncles conform to the consensus 171 // rules of the stock Quai blake3pow engine. 172 func (blake3pow *Blake3pow) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { 173 // If we're running a full engine faking, accept any input as valid 174 if blake3pow.config.PowMode == ModeFullFake { 175 return nil 176 } 177 // Verify that there are at most 2 uncles included in this block 178 if len(block.Uncles()) > maxUncles { 179 return errTooManyUncles 180 } 181 if len(block.Uncles()) == 0 { 182 return nil 183 } 184 // Gather the set of past uncles and ancestors 185 uncles, ancestors := mapset.NewSet(), make(map[common.Hash]*types.Header) 186 187 number, parent := block.NumberU64()-1, block.ParentHash() 188 for i := 0; i < 7; i++ { 189 ancestorHeader := chain.GetHeader(parent, number) 190 if ancestorHeader == nil { 191 break 192 } 193 ancestors[parent] = ancestorHeader 194 // If the ancestor doesn't have any uncles, we don't have to iterate them 195 if ancestorHeader.UncleHash() != types.EmptyUncleHash { 196 // Need to add those uncles to the banned list too 197 ancestor := chain.GetBlock(parent, number) 198 if ancestor == nil { 199 break 200 } 201 for _, uncle := range ancestor.Uncles() { 202 uncles.Add(uncle.Hash()) 203 } 204 } 205 parent, number = ancestorHeader.ParentHash(), number-1 206 } 207 ancestors[block.Hash()] = block.Header() 208 uncles.Add(block.Hash()) 209 210 // Verify each of the uncles that it's recent, but not an ancestor 211 for _, uncle := range block.Uncles() { 212 // Make sure every uncle is rewarded only once 213 hash := uncle.Hash() 214 if uncles.Contains(hash) { 215 return errDuplicateUncle 216 } 217 uncles.Add(hash) 218 219 // Make sure the uncle has a valid ancestry 220 if ancestors[hash] != nil { 221 return errUncleIsAncestor 222 } 223 if ancestors[uncle.ParentHash()] == nil || uncle.ParentHash() == block.ParentHash() { 224 return errDanglingUncle 225 } 226 if err := blake3pow.verifyHeader(chain, uncle, ancestors[uncle.ParentHash()], true, time.Now().Unix()); err != nil { 227 return err 228 } 229 } 230 return nil 231 } 232 233 // verifyHeader checks whether a header conforms to the consensus rules 234 func (blake3pow *Blake3pow) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, uncle bool, unixNow int64) error { 235 nodeCtx := common.NodeLocation.Context() 236 // Ensure that the header's extra-data section is of a reasonable size 237 if uint64(len(header.Extra())) > params.MaximumExtraDataSize { 238 return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra()), params.MaximumExtraDataSize) 239 } 240 // Verify the header's timestamp 241 if !uncle { 242 if header.Time() > uint64(unixNow+allowedFutureBlockTimeSeconds) { 243 return consensus.ErrFutureBlock 244 } 245 } 246 if header.Time() < parent.Time() { 247 return errOlderBlockTime 248 } 249 // Verify the block's difficulty based on its timestamp and parent's difficulty 250 // difficulty adjustment can only be checked in zone 251 if nodeCtx == common.ZONE_CTX { 252 expected := blake3pow.CalcDifficulty(chain, parent) 253 if expected.Cmp(header.Difficulty()) != 0 { 254 return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty(), expected) 255 } 256 } 257 // Verify the engine specific seal securing the block 258 _, order, err := blake3pow.CalcOrder(header) 259 if err != nil { 260 return err 261 } 262 if order > nodeCtx { 263 return fmt.Errorf("order of the block is greater than the context") 264 } 265 266 if !common.NodeLocation.InSameSliceAs(header.Location()) { 267 return fmt.Errorf("block location is not in the same slice as the node location") 268 } 269 270 // Verify that the parent entropy is calculated correctly on the header 271 parentEntropy := blake3pow.TotalLogS(parent) 272 if parentEntropy.Cmp(header.ParentEntropy()) != 0 { 273 return fmt.Errorf("invalid parent entropy: have %v, want %v", header.ParentEntropy(), parentEntropy) 274 } 275 276 // If not prime, verify the parentDeltaS field as well 277 if nodeCtx > common.PRIME_CTX { 278 _, parentOrder, _ := blake3pow.CalcOrder(parent) 279 // If parent was dom, deltaS is zero and otherwise should be the calc delta s on the parent 280 if parentOrder < nodeCtx { 281 if common.Big0.Cmp(header.ParentDeltaS()) != 0 { 282 return fmt.Errorf("invalid parent delta s: have %v, want %v", header.ParentDeltaS(), common.Big0) 283 } 284 } else { 285 parentDeltaS := blake3pow.DeltaLogS(parent) 286 if parentDeltaS.Cmp(header.ParentDeltaS()) != 0 { 287 return fmt.Errorf("invalid parent delta s: have %v, want %v", header.ParentDeltaS(), parentDeltaS) 288 } 289 } 290 } 291 292 if nodeCtx == common.ZONE_CTX { 293 // check if the header coinbase is in scope 294 _, err := header.Coinbase().InternalAddress() 295 if err != nil { 296 return fmt.Errorf("out-of-scope coinbase in the header") 297 } 298 // Verify that the gas limit is <= 2^63-1 299 cap := uint64(0x7fffffffffffffff) 300 if header.GasLimit() > cap { 301 return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit(), cap) 302 } 303 // Verify that the gasUsed is <= gasLimit 304 if header.GasUsed() > header.GasLimit() { 305 return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed(), header.GasLimit()) 306 } 307 // Verify the block's gas usage and verify the base fee. 308 // Verify that the gas limit remains within allowed bounds 309 expectedGasLimit := core.CalcGasLimit(parent, blake3pow.config.GasCeil) 310 if expectedGasLimit != header.GasLimit() { 311 return fmt.Errorf("invalid gasLimit: have %d, want %d", 312 header.GasLimit(), expectedGasLimit) 313 } 314 // Verify the header is not malformed 315 if header.BaseFee() == nil { 316 return fmt.Errorf("header is missing baseFee") 317 } 318 // Verify the baseFee is correct based on the parent header. 319 expectedBaseFee := misc.CalcBaseFee(chain.Config(), parent) 320 if header.BaseFee().Cmp(expectedBaseFee) != 0 { 321 return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d", 322 expectedBaseFee, header.BaseFee(), parent.BaseFee(), parent.GasUsed()) 323 } 324 } 325 // Verify that the block number is parent's +1 326 if diff := new(big.Int).Sub(header.Number(), parent.Number()); diff.Cmp(big.NewInt(1)) != 0 { 327 return consensus.ErrInvalidNumber 328 } 329 return nil 330 } 331 332 // CalcDifficulty is the difficulty adjustment algorithm. It returns 333 // the difficulty that a new block should have when created at time 334 // given the parent block's time and difficulty. 335 func (blake3pow *Blake3pow) CalcDifficulty(chain consensus.ChainHeaderReader, parent *types.Header) *big.Int { 336 nodeCtx := common.NodeLocation.Context() 337 338 if nodeCtx != common.ZONE_CTX { 339 log.Error("Cannot CalcDifficulty for", "context", nodeCtx) 340 return nil 341 } 342 343 ///// Algorithm: 344 ///// e = (DurationLimit - (parent.Time() - parentOfParent.Time())) * parent.Difficulty() 345 ///// k = Floor(BinaryLog(parent.Difficulty()))/(DurationLimit*DifficultyAdjustmentFactor*AdjustmentPeriod) 346 ///// Difficulty = Max(parent.Difficulty() + e * k, MinimumDifficulty) 347 348 if parent.Hash() == chain.Config().GenesisHash { 349 return parent.Difficulty() 350 } 351 352 parentOfParent := chain.GetHeaderByHash(parent.ParentHash()) 353 if parentOfParent == nil || parentOfParent.Hash() == chain.Config().GenesisHash { 354 return parent.Difficulty() 355 } 356 357 time := parent.Time() 358 bigTime := new(big.Int).SetUint64(time) 359 bigParentTime := new(big.Int).SetUint64(parentOfParent.Time()) 360 361 // holds intermediate values to make the algo easier to read & audit 362 x := new(big.Int) 363 x.Sub(bigTime, bigParentTime) 364 x.Sub(blake3pow.config.DurationLimit, x) 365 x.Mul(x, parent.Difficulty()) 366 k, _ := mathutil.BinaryLog(new(big.Int).Set(parent.Difficulty()), 64) 367 x.Mul(x, big.NewInt(int64(k))) 368 x.Div(x, blake3pow.config.DurationLimit) 369 x.Div(x, big.NewInt(params.DifficultyAdjustmentFactor)) 370 x.Div(x, params.DifficultyAdjustmentPeriod) 371 x.Add(x, parent.Difficulty()) 372 373 // minimum difficulty can ever be (before exponential factor) 374 if x.Cmp(blake3pow.config.MinDifficulty) < 0 { 375 x.Set(blake3pow.config.MinDifficulty) 376 } 377 return x 378 } 379 380 func (blake3pow *Blake3pow) IsDomCoincident(chain consensus.ChainHeaderReader, header *types.Header) bool { 381 _, order, err := blake3pow.CalcOrder(header) 382 if err != nil { 383 return false 384 } 385 return order < common.NodeLocation.Context() 386 } 387 388 // VerifySeal returns the PowHash and the verifySeal output 389 func (blake3pow *Blake3pow) VerifySeal(header *types.Header) (common.Hash, error) { 390 return header.Hash(), blake3pow.verifySeal(header) 391 } 392 393 // verifySeal checks whether a block satisfies the PoW difficulty requirements, 394 // either using the usual blake3pow cache for it, or alternatively using a full DAG 395 // to make remote mining fast. 396 func (blake3pow *Blake3pow) verifySeal(header *types.Header) error { 397 // If we're running a fake PoW, accept any seal as valid 398 if blake3pow.config.PowMode == ModeFake || blake3pow.config.PowMode == ModeFullFake { 399 time.Sleep(blake3pow.fakeDelay) 400 if blake3pow.fakeFail == header.Number().Uint64() { 401 return errInvalidPoW 402 } 403 return nil 404 } 405 // Ensure that we have a valid difficulty for the block 406 if header.Difficulty().Sign() <= 0 { 407 return errInvalidDifficulty 408 } 409 410 target := new(big.Int).Div(big2e256, header.Difficulty()) 411 if new(big.Int).SetBytes(header.Hash().Bytes()).Cmp(target) > 0 { 412 return errInvalidPoW 413 } 414 return nil 415 } 416 417 // Prepare implements consensus.Engine, initializing the difficulty field of a 418 // header to conform to the blake3pow protocol. The changes are done inline. 419 func (blake3pow *Blake3pow) Prepare(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header) error { 420 header.SetDifficulty(blake3pow.CalcDifficulty(chain, parent)) 421 return nil 422 } 423 424 // Finalize implements consensus.Engine, accumulating the block and uncle rewards, 425 // setting the final state on the header 426 func (blake3pow *Blake3pow) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { 427 // Accumulate any block and uncle rewards and commit the final state root 428 accumulateRewards(chain.Config(), state, header, uncles) 429 430 if common.NodeLocation.Context() == common.ZONE_CTX && header.ParentHash() == chain.Config().GenesisHash { 431 alloc := core.ReadGenesisAlloc("genallocs/gen_alloc_" + common.NodeLocation.Name() + ".json") 432 log.Info("Allocating genesis accounts", "num", len(alloc)) 433 434 for addressString, account := range alloc { 435 addr := common.HexToAddress(addressString) 436 internal, err := addr.InternalAddress() 437 if err != nil { 438 log.Error("Provided address in genesis block is out of scope") 439 } 440 state.AddBalance(internal, account.Balance) 441 state.SetCode(internal, account.Code) 442 state.SetNonce(internal, account.Nonce) 443 for key, value := range account.Storage { 444 state.SetState(internal, key, value) 445 } 446 } 447 } 448 449 header.SetRoot(state.IntermediateRoot(true)) 450 } 451 452 // FinalizeAndAssemble implements consensus.Engine, accumulating the block and 453 // uncle rewards, setting the final state and assembling the block. 454 func (blake3pow *Blake3pow) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, etxs []*types.Transaction, subManifest types.BlockManifest, receipts []*types.Receipt) (*types.Block, error) { 455 nodeCtx := common.NodeLocation.Context() 456 if nodeCtx == common.ZONE_CTX && chain.ProcessingState() { 457 // Finalize block 458 blake3pow.Finalize(chain, header, state, txs, uncles) 459 } 460 461 // Header seems complete, assemble into a block and return 462 return types.NewBlock(header, txs, uncles, etxs, subManifest, receipts, trie.NewStackTrie(nil)), nil 463 } 464 465 func (blake3pow *Blake3pow) ComputePowLight(header *types.Header) (common.Hash, common.Hash) { 466 panic("compute pow light doesnt exist for blake3") 467 } 468 469 // AccumulateRewards credits the coinbase of the given block with the mining 470 // reward. The total reward consists of the static block reward and rewards for 471 // included uncles. The coinbase of each uncle block is also rewarded. 472 func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { 473 // Select the correct block reward based on chain progression 474 blockReward := misc.CalculateReward(header) 475 476 coinbase, err := header.Coinbase().InternalAddress() 477 if err != nil { 478 log.Error("Block has out of scope coinbase, skipping block reward", "Address", header.Coinbase().String(), "Hash", header.Hash().String()) 479 return 480 } 481 482 // Accumulate the rewards for the miner and any included uncles 483 reward := new(big.Int).Set(blockReward) 484 r := new(big.Int) 485 for _, uncle := range uncles { 486 coinbase, err := uncle.Coinbase().InternalAddress() 487 if err != nil { 488 log.Error("Found uncle with out of scope coinbase, skipping reward", "Address", uncle.Coinbase().String(), "Hash", uncle.Hash().String()) 489 continue 490 } 491 r.Add(uncle.Number(), big8) 492 r.Sub(r, header.Number()) 493 r.Mul(r, blockReward) 494 r.Div(r, big8) 495 state.AddBalance(coinbase, r) 496 497 r.Div(blockReward, big32) 498 reward.Add(reward, r) 499 } 500 state.AddBalance(coinbase, reward) 501 }