github.com/cgcardona/r-subnet-evm@v0.1.5/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/ava-labs/avalanchego/utils/timer/mockable" 14 "github.com/cgcardona/r-subnet-evm/consensus" 15 "github.com/cgcardona/r-subnet-evm/core/state" 16 "github.com/cgcardona/r-subnet-evm/core/types" 17 "github.com/cgcardona/r-subnet-evm/params" 18 "github.com/cgcardona/r-subnet-evm/trie" 19 "github.com/cgcardona/r-subnet-evm/vmerrs" 20 "github.com/ethereum/go-ethereum/common" 21 ) 22 23 var ( 24 allowedFutureBlockTime = 10 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks 25 26 errInvalidBlockTime = errors.New("timestamp less than parent's") 27 errUnclesUnsupported = errors.New("uncles unsupported") 28 errBlockGasCostNil = errors.New("block gas cost is nil") 29 errBlockGasCostTooLarge = errors.New("block gas cost is not uint64") 30 errBaseFeeNil = errors.New("base fee is nil") 31 ) 32 33 type Mode struct { 34 ModeSkipHeader bool 35 ModeSkipBlockFee bool 36 ModeSkipCoinbase bool 37 } 38 39 type ( 40 DummyEngine struct { 41 clock *mockable.Clock 42 consensusMode Mode 43 } 44 ) 45 46 func NewETHFaker() *DummyEngine { 47 return &DummyEngine{ 48 clock: &mockable.Clock{}, 49 consensusMode: Mode{ModeSkipBlockFee: true}, 50 } 51 } 52 53 func NewFaker() *DummyEngine { 54 return &DummyEngine{ 55 clock: &mockable.Clock{}, 56 } 57 } 58 59 func NewFakerWithClock(clock *mockable.Clock) *DummyEngine { 60 return &DummyEngine{ 61 clock: clock, 62 } 63 } 64 65 func NewFakerWithMode(mode Mode) *DummyEngine { 66 return &DummyEngine{ 67 clock: &mockable.Clock{}, 68 consensusMode: mode, 69 } 70 } 71 72 func NewCoinbaseFaker() *DummyEngine { 73 return &DummyEngine{ 74 clock: &mockable.Clock{}, 75 consensusMode: Mode{ModeSkipCoinbase: true}, 76 } 77 } 78 79 func NewFullFaker() *DummyEngine { 80 return &DummyEngine{ 81 clock: &mockable.Clock{}, 82 consensusMode: Mode{ModeSkipHeader: true}, 83 } 84 } 85 86 // verifyCoinbase checks that the coinbase is valid for the given [header] and [parent]. 87 func (self *DummyEngine) verifyCoinbase(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error { 88 if self.consensusMode.ModeSkipCoinbase { 89 return nil 90 } 91 // get the coinbase configured at parent 92 configuredAddressAtParent, isAllowFeeRecipients, err := chain.GetCoinbaseAt(parent) 93 if err != nil { 94 return fmt.Errorf("failed to get coinbase at %v: %w", header.Hash(), err) 95 } 96 97 if isAllowFeeRecipients { 98 // if fee recipients are allowed we don't need to check the coinbase 99 return nil 100 } 101 // we fetch the configured coinbase at the parent's state 102 // to check against the coinbase in [header]. 103 if configuredAddressAtParent != header.Coinbase { 104 return fmt.Errorf("%w: %v does not match required coinbase address %v", vmerrs.ErrInvalidCoinbase, header.Coinbase, configuredAddressAtParent) 105 } 106 return nil 107 } 108 109 func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error { 110 timestamp := new(big.Int).SetUint64(header.Time) 111 112 // Verify that the gas limit is <= 2^63-1 113 if header.GasLimit > params.MaxGasLimit { 114 return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit) 115 } 116 // Verify that the gasUsed is <= gasLimit 117 if header.GasUsed > header.GasLimit { 118 return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) 119 } 120 // We verify the current block by checking the parent fee config 121 // this is because the current block cannot set the fee config for itself 122 // Fee config might depend on the state when precompile is activated 123 // but we don't know the final state while forming the block. 124 // See worker package for more details. 125 feeConfig, _, err := chain.GetFeeConfigAt(parent) 126 if err != nil { 127 return err 128 } 129 if config.IsSubnetEVM(timestamp) { 130 expectedGasLimit := feeConfig.GasLimit.Uint64() 131 if header.GasLimit != expectedGasLimit { 132 return fmt.Errorf("expected gas limit to be %d, but found %d", expectedGasLimit, header.GasLimit) 133 } 134 } else { 135 // Verify that the gas limit remains within allowed bounds 136 diff := int64(parent.GasLimit) - int64(header.GasLimit) 137 if diff < 0 { 138 diff *= -1 139 } 140 limit := parent.GasLimit / params.GasLimitBoundDivisor 141 142 if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit { 143 return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit) 144 } 145 // Verify BaseFee is not present before Subnet EVM 146 if header.BaseFee != nil { 147 return fmt.Errorf("invalid baseFee before fork: have %d, want <nil>", header.BaseFee) 148 } 149 if header.BlockGasCost != nil { 150 return fmt.Errorf("invalid blockGasCost before fork: have %d, want <nil>", header.BlockGasCost) 151 } 152 return nil 153 } 154 155 // Verify baseFee and rollupWindow encoding as part of header verification 156 // starting in Subnet EVM 157 expectedRollupWindowBytes, expectedBaseFee, err := CalcBaseFee(config, feeConfig, parent, header.Time) 158 if err != nil { 159 return fmt.Errorf("failed to calculate base fee: %w", err) 160 } 161 if !bytes.Equal(expectedRollupWindowBytes, header.Extra) { 162 return fmt.Errorf("expected rollup window bytes: %x, found %x", expectedRollupWindowBytes, header.Extra) 163 } 164 if header.BaseFee == nil { 165 return errors.New("expected baseFee to be non-nil") 166 } 167 if bfLen := header.BaseFee.BitLen(); bfLen > 256 { 168 return fmt.Errorf("too large base fee: bitlen %d", bfLen) 169 } 170 if header.BaseFee.Cmp(expectedBaseFee) != 0 { 171 return fmt.Errorf("expected base fee (%d), found (%d)", expectedBaseFee, header.BaseFee) 172 } 173 174 // Enforce BlockGasCost constraints 175 expectedBlockGasCost := calcBlockGasCost( 176 feeConfig.TargetBlockRate, 177 feeConfig.MinBlockGasCost, 178 feeConfig.MaxBlockGasCost, 179 feeConfig.BlockGasCostStep, 180 parent.BlockGasCost, 181 parent.Time, header.Time, 182 ) 183 if header.BlockGasCost == nil { 184 return errBlockGasCostNil 185 } 186 if !header.BlockGasCost.IsUint64() { 187 return errBlockGasCostTooLarge 188 } 189 if header.BlockGasCost.Cmp(expectedBlockGasCost) != 0 { 190 return fmt.Errorf("invalid block gas cost: have %d, want %d", header.BlockGasCost, expectedBlockGasCost) 191 } 192 return nil 193 } 194 195 // modified from consensus.go 196 func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, uncle bool) error { 197 var ( 198 config = chain.Config() 199 timestamp = new(big.Int).SetUint64(header.Time) 200 ) 201 // Ensure that we do not verify an uncle 202 if uncle { 203 return errUnclesUnsupported 204 } 205 // Ensure that the header's extra-data section is of a reasonable size 206 if !config.IsSubnetEVM(timestamp) { 207 if uint64(len(header.Extra)) > params.MaximumExtraDataSize { 208 return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize) 209 } 210 } else { 211 expectedExtraDataSize := params.ExtraDataSize 212 if len(header.Extra) != expectedExtraDataSize { 213 return fmt.Errorf("expected extra-data field to be: %d, but found %d", expectedExtraDataSize, len(header.Extra)) 214 } 215 } 216 // Ensure gas-related header fields are correct 217 if err := self.verifyHeaderGasFields(config, header, parent, chain); err != nil { 218 return err 219 } 220 // Ensure that coinbase is valid 221 if err := self.verifyCoinbase(config, header, parent, chain); err != nil { 222 return err 223 } 224 225 // Verify the header's timestamp 226 if header.Time > uint64(self.clock.Time().Add(allowedFutureBlockTime).Unix()) { 227 return consensus.ErrFutureBlock 228 } 229 // Verify the header's timestamp is not earlier than parent's 230 // it does include equality(==), so multiple blocks per second is ok 231 if header.Time < parent.Time { 232 return errInvalidBlockTime 233 } 234 // Verify that the block number is parent's +1 235 if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 { 236 return consensus.ErrInvalidNumber 237 } 238 return nil 239 } 240 241 func (self *DummyEngine) Author(header *types.Header) (common.Address, error) { 242 return header.Coinbase, nil 243 } 244 245 func (self *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error { 246 // If we're running a full engine faking, accept any input as valid 247 if self.consensusMode.ModeSkipHeader { 248 return nil 249 } 250 // Short circuit if the header is known, or it's parent not 251 number := header.Number.Uint64() 252 if chain.GetHeader(header.Hash(), number) != nil { 253 return nil 254 } 255 parent := chain.GetHeader(header.ParentHash, number-1) 256 if parent == nil { 257 return consensus.ErrUnknownAncestor 258 } 259 // Sanity checks passed, do a proper verification 260 return self.verifyHeader(chain, header, parent, false) 261 } 262 263 func (self *DummyEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { 264 if len(block.Uncles()) > 0 { 265 return errUnclesUnsupported 266 } 267 return nil 268 } 269 270 func (self *DummyEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { 271 header.Difficulty = big.NewInt(1) 272 return nil 273 } 274 275 func (self *DummyEngine) verifyBlockFee( 276 baseFee *big.Int, 277 requiredBlockGasCost *big.Int, 278 txs []*types.Transaction, 279 receipts []*types.Receipt, 280 ) error { 281 if self.consensusMode.ModeSkipBlockFee { 282 return nil 283 } 284 if baseFee == nil || baseFee.Sign() <= 0 { 285 return fmt.Errorf("invalid base fee (%d) in SubnetEVM", baseFee) 286 } 287 if requiredBlockGasCost == nil || !requiredBlockGasCost.IsUint64() { 288 return fmt.Errorf("invalid block gas cost (%d) in SubnetEVM", requiredBlockGasCost) 289 } 290 291 var ( 292 gasUsed = new(big.Int) 293 blockFeeContribution = new(big.Int) 294 totalBlockFee = new(big.Int) 295 ) 296 // Calculate the total excess over the base fee that was paid towards the block fee 297 for i, receipt := range receipts { 298 // Each transaction contributes the excess over the baseFee towards the totalBlockFee 299 // This should be equivalent to the sum of the "priority fees" within EIP-1559. 300 txFeePremium, err := txs[i].EffectiveGasTip(baseFee) 301 if err != nil { 302 return err 303 } 304 // Multiply the [txFeePremium] by the gasUsed in the transaction since this gives the total coin that was paid 305 // above the amount required if the transaction had simply paid the minimum base fee for the block. 306 // 307 // Ex. LegacyTx paying a gas price of 100 gwei for 1M gas in a block with a base fee of 10 gwei. 308 // Total Fee = 100 gwei * 1M gas 309 // Minimum Fee = 10 gwei * 1M gas (minimum fee that would have been accepted for this transaction) 310 // Fee Premium = 90 gwei 311 // Total Overpaid = 90 gwei * 1M gas 312 313 blockFeeContribution.Mul(txFeePremium, gasUsed.SetUint64(receipt.GasUsed)) 314 totalBlockFee.Add(totalBlockFee, blockFeeContribution) 315 } 316 // Calculate how much gas the [totalBlockFee] would purchase at the price level 317 // set by the base fee of this block. 318 blockGas := new(big.Int).Div(totalBlockFee, baseFee) 319 320 // Require that the amount of gas purchased by the effective tips within the block, [blockGas], 321 // covers at least [requiredBlockGasCost]. 322 // 323 // NOTE: To determine the [requiredBlockFee], multiply [requiredBlockGasCost] 324 // by [baseFee]. 325 if blockGas.Cmp(requiredBlockGasCost) < 0 { 326 return fmt.Errorf( 327 "insufficient gas (%d) to cover the block cost (%d) at base fee (%d) (total block fee: %d)", 328 blockGas, requiredBlockGasCost, baseFee, totalBlockFee, 329 ) 330 } 331 return nil 332 } 333 334 func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types.Block, parent *types.Header, state *state.StateDB, receipts []*types.Receipt) error { 335 if chain.Config().IsSubnetEVM(new(big.Int).SetUint64(block.Time())) { 336 // we use the parent to determine the fee config 337 // since the current block has not been finalized yet. 338 feeConfig, _, err := chain.GetFeeConfigAt(parent) 339 if err != nil { 340 return err 341 } 342 343 // Calculate the expected blockGasCost for this block. 344 // Note: this is a deterministic transtion that defines an exact block fee for this block. 345 blockGasCost := calcBlockGasCost( 346 feeConfig.TargetBlockRate, 347 feeConfig.MinBlockGasCost, 348 feeConfig.MaxBlockGasCost, 349 feeConfig.BlockGasCostStep, 350 parent.BlockGasCost, 351 parent.Time, block.Time(), 352 ) 353 // Verify the BlockGasCost set in the header matches the calculated value. 354 if blockBlockGasCost := block.BlockGasCost(); blockBlockGasCost == nil || !blockBlockGasCost.IsUint64() || blockBlockGasCost.Cmp(blockGasCost) != 0 { 355 return fmt.Errorf("invalid blockGasCost: have %d, want %d", blockBlockGasCost, blockGasCost) 356 } 357 // Verify the block fee was paid. 358 if err := self.verifyBlockFee( 359 block.BaseFee(), 360 block.BlockGasCost(), 361 block.Transactions(), 362 receipts, 363 ); err != nil { 364 return err 365 } 366 } 367 368 return nil 369 } 370 371 func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, state *state.StateDB, txs []*types.Transaction, 372 uncles []*types.Header, receipts []*types.Receipt, 373 ) (*types.Block, error) { 374 if chain.Config().IsSubnetEVM(new(big.Int).SetUint64(header.Time)) { 375 // we use the parent to determine the fee config 376 // since the current block has not been finalized yet. 377 feeConfig, _, err := chain.GetFeeConfigAt(parent) 378 if err != nil { 379 return nil, err 380 } 381 // Calculate the required block gas cost for this block. 382 header.BlockGasCost = calcBlockGasCost( 383 feeConfig.TargetBlockRate, 384 feeConfig.MinBlockGasCost, 385 feeConfig.MaxBlockGasCost, 386 feeConfig.BlockGasCostStep, 387 parent.BlockGasCost, 388 parent.Time, header.Time, 389 ) 390 // Verify that this block covers the block fee. 391 if err := self.verifyBlockFee( 392 header.BaseFee, 393 header.BlockGasCost, 394 txs, 395 receipts, 396 ); err != nil { 397 return nil, err 398 } 399 } 400 // commit the final state root 401 header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) 402 403 // Header seems complete, assemble into a block and return 404 return types.NewBlock( 405 header, txs, uncles, receipts, new(trie.Trie), 406 ), nil 407 } 408 409 func (self *DummyEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { 410 return big.NewInt(1) 411 } 412 413 func (self *DummyEngine) Close() error { 414 return nil 415 }