public Transaction BuildTransaction(long gaslimit, byte[] callData, Address sender, BlockHeader parent, IReleaseSpec spec, UInt256 nonce, bool systemTransaction) { Transaction transaction = systemTransaction ? new SystemTransaction() : new Transaction(); UInt256 fee = BaseFeeCalculator.Calculate(parent, spec); transaction.GasPrice = fee; transaction.GasLimit = gaslimit; transaction.To = _entryPointContractAddress; transaction.ChainId = _specProvider.ChainId; transaction.Nonce = nonce; transaction.Value = 0; transaction.Data = callData; transaction.Type = TxType.EIP1559; transaction.DecodedMaxFeePerGas = fee; transaction.SenderAddress = sender; if (!systemTransaction) { _signer.Sign(transaction); } transaction.Hash = transaction.CalculateHash(); return(transaction); }
public void When_gaslimit_is_on_london_fork(long parentGasLimit, long blockNumber, long gasLimit, bool expectedResult) { OverridableReleaseSpec spec = new(London.Instance) { Eip1559TransitionBlock = 5 }; TestSpecProvider specProvider = new(spec); _validator = new HeaderValidator(_blockTree, _ethash, specProvider, new OneLoggerLogManager(_testLogger)); _parentBlock = Build.A.Block.WithDifficulty(1) .WithGasLimit(parentGasLimit) .WithNumber(blockNumber) .TestObject; _block = Build.A.Block.WithParent(_parentBlock) .WithDifficulty(131072) .WithMixHash(new Keccak("0xd7db5fdd332d3a65d6ac9c4c530929369905734d3ef7a91e373e81d0f010b8e8")) .WithGasLimit(gasLimit) .WithNumber(_parentBlock.Number + 1) .WithBaseFeePerGas(BaseFeeCalculator.Calculate(_parentBlock.Header, specProvider.GetSpec(_parentBlock.Number + 1))) .WithNonce(0).TestObject; _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); Assert.AreEqual(expectedResult, result); }
public IEnumerable <Transaction> GetTransactions(BlockHeader parent, long gasLimit) { long blockNumber = parent.Number + 1; IReleaseSpec releaseSpec = _specProvider.GetSpec(blockNumber); UInt256 baseFee = BaseFeeCalculator.Calculate(parent, releaseSpec); IDictionary <Address, Transaction[]> pendingTransactions = _transactionPool.GetPendingTransactionsBySender(); IComparer <Transaction> comparer = GetComparer(parent, new BlockPreparationContext(baseFee, blockNumber)) .ThenBy(ByHashTxComparer.Instance); // in order to sort properly and not loose transactions we need to differentiate on their identity which provided comparer might not be doing IEnumerable <Transaction> transactions = GetOrderedTransactions(pendingTransactions, comparer); if (_logger.IsDebug) { _logger.Debug($"Collecting pending transactions at block gas limit {gasLimit}."); } int selectedTransactions = 0; int i = 0; // TODO: removing transactions from TX pool here seems to be a bad practice since they will // not come back if the block is ignored? foreach (Transaction tx in transactions) { i++; if (tx.SenderAddress is null) { _transactionPool.RemoveTransaction(tx.Hash !); if (_logger.IsDebug) { _logger.Debug($"Rejecting (null sender) {tx.ToShortString()}"); } continue; } bool success = _txFilterPipeline.Execute(tx, parent); if (success) { if (_logger.IsTrace) { _logger.Trace($"Selected {tx.ToShortString()} to be potentially included in block."); } selectedTransactions++; yield return(tx); } } if (_logger.IsDebug) { _logger.Debug($"Potentially selected {selectedTransactions} out of {i} pending transactions checked."); } }
public void Eip_1559_CalculateBaseFee_should_returns_zero_when_eip1559_not_enabled() { IReleaseSpec releaseSpec = Substitute.For <IReleaseSpec>(); releaseSpec.IsEip1559Enabled.Returns(false); BlockHeader blockHeader = Build.A.BlockHeader.TestObject; blockHeader.Number = 2001; blockHeader.GasLimit = 100; UInt256 baseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); Assert.AreEqual(UInt256.Zero, baseFee); }
public AcceptTxResult IsAllowed(Transaction tx, BlockHeader parentHeader) { long blockNumber = parentHeader.Number + 1; IReleaseSpec releaseSpec = _specProvider.GetSpec(blockNumber); UInt256 baseFee = BaseFeeCalculator.Calculate(parentHeader, releaseSpec); bool isEip1559Enabled = releaseSpec.IsEip1559Enabled; bool skipCheck = tx.IsServiceTransaction || !isEip1559Enabled; bool allowed = skipCheck || tx.MaxFeePerGas >= baseFee; return(allowed ? AcceptTxResult.Accepted : AcceptTxResult.FeeTooLow.WithMessage( $"MaxFeePerGas too low. MaxFeePerGas: {tx.MaxFeePerGas}, BaseFee: {baseFee}, MaxPriorityFeePerGas:{tx.MaxPriorityFeePerGas}, Block number: {blockNumber}")); }
public void Eip_1559_CalculateBaseFee(long gasTarget, long baseFee, long expectedBaseFee, long gasUsed, long?minimalBaseFee = null) { IReleaseSpec releaseSpec = Substitute.For <IReleaseSpec>(); releaseSpec.IsEip1559Enabled.Returns(true); releaseSpec.Eip1559BaseFeeMinValue.Returns((UInt256?)minimalBaseFee); BlockHeader blockHeader = Build.A.BlockHeader.TestObject; blockHeader.Number = 2001; blockHeader.GasLimit = gasTarget * Eip1559Constants.ElasticityMultiplier; blockHeader.BaseFeePerGas = (UInt256)baseFee; blockHeader.GasUsed = gasUsed; UInt256 actualBaseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); Assert.AreEqual((UInt256)expectedBaseFee, actualBaseFee); }
private Block BuildBlock(MevBundle bundle, BlockHeader parent, UInt256?timestamp) { BlockHeader header = new( parent.Hash ?? Keccak.OfAnEmptySequenceRlp, Keccak.OfAnEmptySequenceRlp, Beneficiary, parent.Difficulty, bundle.BlockNumber, GetGasLimit(parent), timestamp ?? parent.Timestamp, Bytes.Empty) { TotalDifficulty = parent.TotalDifficulty + parent.Difficulty }; header.BaseFeePerGas = BaseFeeCalculator.Calculate(parent, _specProvider.GetSpec(header.Number)); header.Hash = header.CalculateHash(); return(new Block(header, bundle.Transactions, Array.Empty <BlockHeader>())); }
/// <summary> /// Note that this does not validate seal which is the responsibility of <see cref="ISealValidator"/>> /// </summary> /// <param name="header">BlockHeader to validate</param> /// <param name="parent">BlockHeader which is the parent of <paramref name="header"/></param> /// <param name="isUncle"><value>True</value> if uncle block, otherwise <value>False</value></param> /// <returns></returns> public bool Validate(BlockHeader header, BlockHeader?parent, bool isUncle = false) { bool hashAsExpected = ValidateHash(header); IReleaseSpec spec = _specProvider.GetSpec(header.Number); bool extraDataValid = header.ExtraData.Length <= spec.MaximumExtraDataSize && (isUncle || _daoBlockNumber == null || header.Number < _daoBlockNumber || header.Number >= _daoBlockNumber + 10 || Bytes.AreEqual(header.ExtraData, DaoExtraData)); if (!extraDataValid) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.Hash}) - DAO extra data not valid"); } } if (parent == null) { if (header.Number == 0) { bool isGenesisValid = ValidateGenesis(header); if (!isGenesisValid) { if (_logger.IsWarn) { _logger.Warn($"Invalid genesis block header ({header.Hash})"); } } return(isGenesisValid); } if (_logger.IsDebug) { _logger.Debug($"Orphan block, could not find parent ({header.ParentHash}) of ({header.Hash})"); } return(false); } bool totalDifficultyCorrect = true; if (header.TotalDifficulty != null) { if (parent.TotalDifficulty + header.Difficulty != header.TotalDifficulty) { if (_logger.IsDebug) { _logger.Debug($"Invalid total difficulty"); } totalDifficultyCorrect = false; } } // seal is validated when synchronizing so we can remove it from here - review and test bool sealParamsCorrect = _sealValidator.ValidateParams(parent, header); if (!sealParamsCorrect) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.Hash}) - seal parameters incorrect"); } } bool gasUsedBelowLimit = header.GasUsed <= header.GasLimit; if (!gasUsedBelowLimit) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.Hash}) - gas used above gas limit"); } } bool gasLimitInRange = ValidateGasLimitRange(header, parent, spec); // bool gasLimitAboveAbsoluteMinimum = header.GasLimit >= 125000; // described in the YellowPaper but not followed bool timestampMoreThanAtParent = header.Timestamp > parent.Timestamp; if (!timestampMoreThanAtParent) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.Hash}) - timestamp before parent"); } } bool numberIsParentPlusOne = header.Number == parent.Number + 1; if (!numberIsParentPlusOne) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.Hash}) - block number is not parent + 1"); } } if (_logger.IsTrace) { _logger.Trace($"Validating block {header.ToString(BlockHeader.Format.Short)}, extraData {header.ExtraData.ToHexString(true)}"); } bool isEip1559Correct = true; bool isEip1559Enabled = spec.IsEip1559Enabled; if (isEip1559Enabled) { UInt256?expectedBaseFee = BaseFeeCalculator.Calculate(parent, spec); isEip1559Correct = expectedBaseFee == header.BaseFeePerGas; if (expectedBaseFee != header.BaseFeePerGas) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.ToString(BlockHeader.Format.Short)}) incorrect base fee. Expected base fee: {expectedBaseFee}, Current base fee: {header.BaseFeePerGas} "); } isEip1559Correct = false; } } return (totalDifficultyCorrect && gasUsedBelowLimit && gasLimitInRange && sealParamsCorrect && // gasLimitAboveAbsoluteMinimum && // described in the YellowPaper but not followed timestampMoreThanAtParent && numberIsParentPlusOne && hashAsExpected && extraDataValid && isEip1559Correct); }
/// <summary> /// Note that this does not validate seal which is the responsibility of <see cref="ISealValidator"/>> /// </summary> /// <param name="header">BlockHeader to validate</param> /// <param name="parent">BlockHeader which is the parent of <paramref name="header"/></param> /// <param name="isUncle"><value>True</value> if uncle block, otherwise <value>False</value></param> /// <returns></returns> public virtual bool Validate(BlockHeader header, BlockHeader?parent, bool isUncle = false) { if (!ValidateFieldLimit(header)) { return(false); } bool hashAsExpected = ValidateHash(header); if (!hashAsExpected) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.Hash}) - invalid block hash"); } } IReleaseSpec spec = _specProvider.GetSpec(header.Number); bool extraDataValid = ValidateExtraData(header, parent, spec, isUncle); if (parent == null) { if (header.Number == 0) { bool isGenesisValid = ValidateGenesis(header); if (!isGenesisValid) { if (_logger.IsWarn) { _logger.Warn($"Invalid genesis block header ({header.Hash})"); } } return(isGenesisValid); } if (_logger.IsDebug) { _logger.Debug($"Orphan block, could not find parent ({header.ParentHash}) of ({header.Hash})"); } return(false); } bool totalDifficultyCorrect = ValidateTotalDifficulty(parent, header); bool sealParamsCorrect = _sealValidator.ValidateParams(parent, header); if (!sealParamsCorrect) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.Hash}) - seal parameters incorrect"); } } bool gasUsedBelowLimit = header.GasUsed <= header.GasLimit; if (!gasUsedBelowLimit) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.Hash}) - gas used above gas limit"); } } bool gasLimitInRange = ValidateGasLimitRange(header, parent, spec); // bool gasLimitAboveAbsoluteMinimum = header.GasLimit >= 125000; // described in the YellowPaper but not followed bool timestampValid = ValidateTimestamp(parent, header); bool numberIsParentPlusOne = header.Number == parent.Number + 1; if (!numberIsParentPlusOne) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.Hash}) - block number is not parent + 1"); } } if (_logger.IsTrace) { _logger.Trace($"Validating block {header.ToString(BlockHeader.Format.Short)}, extraData {header.ExtraData.ToHexString(true)}"); } bool eip1559Valid = true; bool isEip1559Enabled = spec.IsEip1559Enabled; if (isEip1559Enabled) { UInt256?expectedBaseFee = BaseFeeCalculator.Calculate(parent, spec); eip1559Valid = expectedBaseFee == header.BaseFeePerGas; if (expectedBaseFee != header.BaseFeePerGas) { if (_logger.IsWarn) { _logger.Warn($"Invalid block header ({header.ToString(BlockHeader.Format.Short)}) incorrect base fee. Expected base fee: {expectedBaseFee}, Current base fee: {header.BaseFeePerGas} "); } eip1559Valid = false; } } return (totalDifficultyCorrect && gasUsedBelowLimit && gasLimitInRange && sealParamsCorrect && // gasLimitAboveAbsoluteMinimum && // described in the YellowPaper but not followed timestampValid && numberIsParentPlusOne && hashAsExpected && extraDataValid && eip1559Valid); }