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);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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);
        }
Beispiel #5
0
        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>()));
        }
Beispiel #8
0
        /// <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);
        }