github.com/dim4egster/coreth@v0.10.2/consensus/dummy/consensus.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package dummy 5 6 import ( 7 "bytes" 8 "errors" 9 "fmt" 10 "math/big" 11 "time" 12 13 "github.com/dim4egster/coreth/consensus" 14 "github.com/dim4egster/coreth/core/state" 15 "github.com/dim4egster/coreth/core/types" 16 "github.com/dim4egster/coreth/params" 17 "github.com/dim4egster/coreth/rpc" 18 "github.com/dim4egster/coreth/trie" 19 "github.com/ethereum/go-ethereum/common" 20 ) 21 22 var ( 23 allowedFutureBlockTime = 10 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks 24 25 errInvalidBlockTime = errors.New("timestamp less than parent's") 26 errUnclesUnsupported = errors.New("uncles unsupported") 27 errBlockGasCostNil = errors.New("block gas cost is nil") 28 errBlockGasCostTooLarge = errors.New("block gas cost is not uint64") 29 errBaseFeeNil = errors.New("base fee is nil") 30 errExtDataGasUsedNil = errors.New("extDataGasUsed is nil") 31 errExtDataGasUsedTooLarge = errors.New("extDataGasUsed is not uint64") 32 ) 33 34 type Mode uint 35 36 const ( 37 ModeSkipHeader Mode = 1 // Skip over header verification 38 ModeSkipBlockFee Mode = 2 // Skip block fee verification 39 ) 40 41 type ( 42 OnFinalizeAndAssembleCallbackType = func(header *types.Header, state *state.StateDB, txs []*types.Transaction) (extraData []byte, blockFeeContribution *big.Int, extDataGasUsed *big.Int, err error) 43 OnAPIsCallbackType = func(consensus.ChainHeaderReader) []rpc.API 44 OnExtraStateChangeType = func(block *types.Block, statedb *state.StateDB) (blockFeeContribution *big.Int, extDataGasUsed *big.Int, err error) 45 46 ConsensusCallbacks struct { 47 OnFinalizeAndAssemble OnFinalizeAndAssembleCallbackType 48 OnExtraStateChange OnExtraStateChangeType 49 } 50 51 DummyEngine struct { 52 cb *ConsensusCallbacks 53 consensusMode Mode 54 } 55 ) 56 57 func NewDummyEngine(cb *ConsensusCallbacks) *DummyEngine { 58 return &DummyEngine{ 59 cb: cb, 60 } 61 } 62 63 func NewETHFaker() *DummyEngine { 64 return &DummyEngine{ 65 cb: new(ConsensusCallbacks), 66 consensusMode: ModeSkipBlockFee, 67 } 68 } 69 70 func NewComplexETHFaker(cb *ConsensusCallbacks) *DummyEngine { 71 return &DummyEngine{ 72 cb: cb, 73 consensusMode: ModeSkipBlockFee, 74 } 75 } 76 77 func NewFaker() *DummyEngine { 78 return NewDummyEngine(new(ConsensusCallbacks)) 79 } 80 81 func NewFullFaker() *DummyEngine { 82 return &DummyEngine{ 83 cb: new(ConsensusCallbacks), 84 consensusMode: ModeSkipHeader, 85 } 86 } 87 88 func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header) error { 89 timestamp := new(big.Int).SetUint64(header.Time) 90 91 // Verify that the gas limit is <= 2^63-1 92 if header.GasLimit > params.MaxGasLimit { 93 return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit) 94 } 95 // Verify that the gasUsed is <= gasLimit 96 if header.GasUsed > header.GasLimit { 97 return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) 98 } 99 if config.IsApricotPhase1(timestamp) { 100 if header.GasLimit != params.ApricotPhase1GasLimit { 101 return fmt.Errorf("expected gas limit to be %d, but found %d", params.ApricotPhase1GasLimit, header.GasLimit) 102 } 103 } else { 104 // Verify that the gas limit remains within allowed bounds 105 diff := int64(parent.GasLimit) - int64(header.GasLimit) 106 if diff < 0 { 107 diff *= -1 108 } 109 limit := parent.GasLimit / params.GasLimitBoundDivisor 110 111 if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit { 112 return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit) 113 } 114 } 115 116 if !config.IsApricotPhase3(timestamp) { 117 // Verify BaseFee is not present before AP3 118 if header.BaseFee != nil { 119 return fmt.Errorf("invalid baseFee before fork: have %d, want <nil>", header.BaseFee) 120 } 121 } else { 122 // Verify baseFee and rollupWindow encoding as part of header verification 123 // starting in AP3 124 expectedRollupWindowBytes, expectedBaseFee, err := CalcBaseFee(config, parent, header.Time) 125 if err != nil { 126 return fmt.Errorf("failed to calculate base fee: %w", err) 127 } 128 if !bytes.Equal(expectedRollupWindowBytes, header.Extra) { 129 return fmt.Errorf("expected rollup window bytes: %x, found %x", expectedRollupWindowBytes, header.Extra) 130 } 131 if header.BaseFee == nil { 132 return errors.New("expected baseFee to be non-nil") 133 } 134 if bfLen := header.BaseFee.BitLen(); bfLen > 256 { 135 return fmt.Errorf("too large base fee: bitlen %d", bfLen) 136 } 137 if header.BaseFee.Cmp(expectedBaseFee) != 0 { 138 return fmt.Errorf("expected base fee (%d), found (%d)", expectedBaseFee, header.BaseFee) 139 } 140 } 141 142 // Verify BlockGasCost, ExtDataGasUsed not present before AP4 143 if !config.IsApricotPhase4(timestamp) { 144 if header.BlockGasCost != nil { 145 return fmt.Errorf("invalid blockGasCost before fork: have %d, want <nil>", header.BlockGasCost) 146 } 147 if header.ExtDataGasUsed != nil { 148 return fmt.Errorf("invalid extDataGasUsed before fork: have %d, want <nil>", header.ExtDataGasUsed) 149 } 150 return nil 151 } 152 153 // Enforce BlockGasCost constraints 154 blockGasCostStep := ApricotPhase4BlockGasCostStep 155 if config.IsApricotPhase5(timestamp) { 156 blockGasCostStep = ApricotPhase5BlockGasCostStep 157 } 158 expectedBlockGasCost := calcBlockGasCost( 159 ApricotPhase4TargetBlockRate, 160 ApricotPhase4MinBlockGasCost, 161 ApricotPhase4MaxBlockGasCost, 162 blockGasCostStep, 163 parent.BlockGasCost, 164 parent.Time, header.Time, 165 ) 166 if header.BlockGasCost == nil { 167 return errBlockGasCostNil 168 } 169 if !header.BlockGasCost.IsUint64() { 170 return errBlockGasCostTooLarge 171 } 172 if header.BlockGasCost.Cmp(expectedBlockGasCost) != 0 { 173 return fmt.Errorf("invalid block gas cost: have %d, want %d", header.BlockGasCost, expectedBlockGasCost) 174 } 175 // ExtDataGasUsed correctness is checked during block validation 176 // (when the validator has access to the block contents) 177 if header.ExtDataGasUsed == nil { 178 return errExtDataGasUsedNil 179 } 180 if !header.ExtDataGasUsed.IsUint64() { 181 return errExtDataGasUsedTooLarge 182 } 183 return nil 184 } 185 186 // modified from consensus.go 187 func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, uncle bool) error { 188 var ( 189 config = chain.Config() 190 timestamp = new(big.Int).SetUint64(header.Time) 191 ) 192 // Ensure that we do not verify an uncle 193 if uncle { 194 return errUnclesUnsupported 195 } 196 // Ensure that the header's extra-data section is of a reasonable size 197 if !config.IsApricotPhase3(timestamp) { 198 if uint64(len(header.Extra)) > params.MaximumExtraDataSize { 199 return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize) 200 } 201 } else { 202 if uint64(len(header.Extra)) != params.ApricotPhase3ExtraDataSize { 203 return fmt.Errorf("expected extra-data field to be: %d, but found %d", params.ApricotPhase3ExtraDataSize, len(header.Extra)) 204 } 205 } 206 // Ensure gas-related header fields are correct 207 if err := self.verifyHeaderGasFields(config, header, parent); err != nil { 208 return err 209 } 210 // Verify the header's timestamp 211 if header.Time > uint64(time.Now().Add(allowedFutureBlockTime).Unix()) { 212 return consensus.ErrFutureBlock 213 } 214 //if header.Time <= parent.Time { 215 if header.Time < parent.Time { 216 return errInvalidBlockTime 217 } 218 // Verify that the block number is parent's +1 219 if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 { 220 return consensus.ErrInvalidNumber 221 } 222 return nil 223 } 224 225 func (self *DummyEngine) Author(header *types.Header) (common.Address, error) { 226 return header.Coinbase, nil 227 } 228 229 func (self *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error { 230 // If we're running a full engine faking, accept any input as valid 231 if self.consensusMode == ModeSkipHeader { 232 return nil 233 } 234 // Short circuit if the header is known, or it's parent not 235 number := header.Number.Uint64() 236 if chain.GetHeader(header.Hash(), number) != nil { 237 return nil 238 } 239 parent := chain.GetHeader(header.ParentHash, number-1) 240 if parent == nil { 241 return consensus.ErrUnknownAncestor 242 } 243 // Sanity checks passed, do a proper verification 244 return self.verifyHeader(chain, header, parent, false) 245 } 246 247 func (self *DummyEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { 248 if len(block.Uncles()) > 0 { 249 return errUnclesUnsupported 250 } 251 return nil 252 } 253 254 func (self *DummyEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { 255 header.Difficulty = big.NewInt(1) 256 return nil 257 } 258 259 func (self *DummyEngine) verifyBlockFee( 260 baseFee *big.Int, 261 requiredBlockGasCost *big.Int, 262 txs []*types.Transaction, 263 receipts []*types.Receipt, 264 extraStateChangeContribution *big.Int, 265 ) error { 266 if self.consensusMode == ModeSkipBlockFee { 267 return nil 268 } 269 if baseFee == nil || baseFee.Sign() <= 0 { 270 return fmt.Errorf("invalid base fee (%d) in apricot phase 4", baseFee) 271 } 272 if requiredBlockGasCost == nil || !requiredBlockGasCost.IsUint64() { 273 return fmt.Errorf("invalid block gas cost (%d) in apricot phase 4", requiredBlockGasCost) 274 } 275 276 var ( 277 gasUsed = new(big.Int) 278 blockFeeContribution = new(big.Int) 279 totalBlockFee = new(big.Int) 280 ) 281 282 // Add in the external contribution 283 if extraStateChangeContribution != nil { 284 if extraStateChangeContribution.Cmp(common.Big0) < 0 { 285 return fmt.Errorf("invalid extra state change contribution: %d", extraStateChangeContribution) 286 } 287 totalBlockFee.Add(totalBlockFee, extraStateChangeContribution) 288 } 289 290 // Calculate the total excess (denominated in AVAX) over the base fee that was paid towards the block fee 291 for i, receipt := range receipts { 292 // Each transaction contributes the excess over the baseFee towards the totalBlockFee 293 // This should be equivalent to the sum of the "priority fees" within EIP-1559. 294 txFeePremium, err := txs[i].EffectiveGasTip(baseFee) 295 if err != nil { 296 return err 297 } 298 // Multiply the [txFeePremium] by the gasUsed in the transaction since this gives the total AVAX that was paid 299 // above the amount required if the transaction had simply paid the minimum base fee for the block. 300 // 301 // Ex. LegacyTx paying a gas price of 100 gwei for 1M gas in a block with a base fee of 10 gwei. 302 // Total Fee = 100 gwei * 1M gas 303 // Minimum Fee = 10 gwei * 1M gas (minimum fee that would have been accepted for this transaction) 304 // Fee Premium = 90 gwei 305 // Total Overpaid = 90 gwei * 1M gas 306 blockFeeContribution.Mul(txFeePremium, gasUsed.SetUint64(receipt.GasUsed)) 307 totalBlockFee.Add(totalBlockFee, blockFeeContribution) 308 } 309 // Calculate how much gas the [totalBlockFee] would purchase at the price level 310 // set by the base fee of this block. 311 blockGas := new(big.Int).Div(totalBlockFee, baseFee) 312 313 // Require that the amount of gas purchased by the effective tips within the block, [blockGas], 314 // covers at least [requiredBlockGasCost]. 315 // 316 // NOTE: To determine the [requiredBlockFee], multiply [requiredBlockGasCost] 317 // by [baseFee]. 318 if blockGas.Cmp(requiredBlockGasCost) < 0 { 319 return fmt.Errorf( 320 "insufficient gas (%d) to cover the block cost (%d) at base fee (%d) (total block fee: %d)", 321 blockGas, requiredBlockGasCost, baseFee, totalBlockFee, 322 ) 323 } 324 return nil 325 } 326 327 func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types.Block, parent *types.Header, state *state.StateDB, receipts []*types.Receipt) error { 328 // Perform extra state change while finalizing the block 329 var ( 330 contribution, extDataGasUsed *big.Int 331 err error 332 ) 333 if self.cb.OnExtraStateChange != nil { 334 contribution, extDataGasUsed, err = self.cb.OnExtraStateChange(block, state) 335 if err != nil { 336 return err 337 } 338 } 339 if chain.Config().IsApricotPhase4(new(big.Int).SetUint64(block.Time())) { 340 // Validate extDataGasUsed and BlockGasCost match expectations 341 // 342 // NOTE: This is a duplicate check of what is already performed in 343 // blockValidator but is done here for symmetry with FinalizeAndAssemble. 344 if extDataGasUsed == nil { 345 extDataGasUsed = new(big.Int).Set(common.Big0) 346 } 347 if blockExtDataGasUsed := block.ExtDataGasUsed(); blockExtDataGasUsed == nil || !blockExtDataGasUsed.IsUint64() || blockExtDataGasUsed.Cmp(extDataGasUsed) != 0 { 348 return fmt.Errorf("invalid extDataGasUsed: have %d, want %d", blockExtDataGasUsed, extDataGasUsed) 349 } 350 blockGasCostStep := ApricotPhase4BlockGasCostStep 351 if chain.Config().IsApricotPhase5(new(big.Int).SetUint64(block.Time())) { 352 blockGasCostStep = ApricotPhase5BlockGasCostStep 353 } 354 // Calculate the expected blockGasCost for this block. 355 // Note: this is a deterministic transtion that defines an exact block fee for this block. 356 blockGasCost := calcBlockGasCost( 357 ApricotPhase4TargetBlockRate, 358 ApricotPhase4MinBlockGasCost, 359 ApricotPhase4MaxBlockGasCost, 360 blockGasCostStep, 361 parent.BlockGasCost, 362 parent.Time, block.Time(), 363 ) 364 // Verify the BlockGasCost set in the header matches the calculated value. 365 if blockBlockGasCost := block.BlockGasCost(); blockBlockGasCost == nil || !blockBlockGasCost.IsUint64() || blockBlockGasCost.Cmp(blockGasCost) != 0 { 366 return fmt.Errorf("invalid blockGasCost: have %d, want %d", blockBlockGasCost, blockGasCost) 367 } 368 // Verify the block fee was paid. 369 if err := self.verifyBlockFee( 370 block.BaseFee(), 371 block.BlockGasCost(), 372 block.Transactions(), 373 receipts, 374 contribution, 375 ); err != nil { 376 return err 377 } 378 } 379 380 return nil 381 } 382 383 func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, state *state.StateDB, txs []*types.Transaction, 384 uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { 385 var ( 386 contribution, extDataGasUsed *big.Int 387 extraData []byte 388 err error 389 ) 390 if self.cb.OnFinalizeAndAssemble != nil { 391 extraData, contribution, extDataGasUsed, err = self.cb.OnFinalizeAndAssemble(header, state, txs) 392 if err != nil { 393 return nil, err 394 } 395 } 396 if chain.Config().IsApricotPhase4(new(big.Int).SetUint64(header.Time)) { 397 header.ExtDataGasUsed = extDataGasUsed 398 if header.ExtDataGasUsed == nil { 399 header.ExtDataGasUsed = new(big.Int).Set(common.Big0) 400 } 401 blockGasCostStep := ApricotPhase4BlockGasCostStep 402 if chain.Config().IsApricotPhase5(new(big.Int).SetUint64(header.Time)) { 403 blockGasCostStep = ApricotPhase5BlockGasCostStep 404 } 405 // Calculate the required block gas cost for this block. 406 header.BlockGasCost = calcBlockGasCost( 407 ApricotPhase4TargetBlockRate, 408 ApricotPhase4MinBlockGasCost, 409 ApricotPhase4MaxBlockGasCost, 410 blockGasCostStep, 411 parent.BlockGasCost, 412 parent.Time, header.Time, 413 ) 414 // Verify that this block covers the block fee. 415 if err := self.verifyBlockFee( 416 header.BaseFee, 417 header.BlockGasCost, 418 txs, 419 receipts, 420 contribution, 421 ); err != nil { 422 return nil, err 423 } 424 } 425 // commit the final state root 426 header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) 427 428 // Header seems complete, assemble into a block and return 429 return types.NewBlock( 430 header, txs, uncles, receipts, new(trie.Trie), extraData, 431 chain.Config().IsApricotPhase1(new(big.Int).SetUint64(header.Time)), 432 ), nil 433 } 434 435 func (self *DummyEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { 436 return big.NewInt(1) 437 } 438 439 func (self *DummyEngine) Close() error { 440 return nil 441 }