Beispiel #1
0
        public Keccak GetBlockhash(BlockHeader currentBlock, UInt256 number)
        {
            UInt256 current = currentBlock.Number;

            if (number >= current || number < current - UInt256.Min(current, (UInt256)_maxDepth))
            {
                return(null);
            }

            BlockHeader header = _blockTree.FindHeader(currentBlock.ParentHash);

            for (var i = 0; i < _maxDepth; i++)
            {
                if (number == header.Number)
                {
                    return(header.Hash);
                }

                header = _blockTree.FindHeader(header.ParentHash);
                if (_blockTree.IsMainChain(header.Hash))
                {
                    header = _blockTree.FindHeader(number);
                }
            }

            return(null);
        }
Beispiel #2
0
        /// <summary>
        /// https://github.com/ethereum/EIPs/pull/2892
        /// ADJUSTED_EXPONENT_LENGTH is defined as follows.
        /// If length_of_EXPONENT &lt;= 32, and all bits in EXPONENT are 0, return 0
        /// If length_of_EXPONENT &lt;= 32, then return the index of the highest bit in EXPONENT (eg. 1 -> 0, 2 -> 1, 3 -> 1, 255 -> 7, 256 -> 8).
        /// If length_of_EXPONENT > 32, then return 8 * (length_of_EXPONENT - 32) plus the index of the highest bit in the first 32 bytes of EXPONENT (eg. if EXPONENT = \x00\x00\x01\x00.....\x00, with one hundred bytes, then the result is 8 * (100 - 32) + 253 = 797). If all of the first 32 bytes of EXPONENT are zero, return exactly 8 * (length_of_EXPONENT - 32).
        /// </summary>
        /// <param name="inputData"></param>
        /// <param name="releaseSpec"></param>
        /// <returns>Gas cost of the MODEXP operation in the context of EIP2565</returns>
        public long DataGasCost(byte[] inputData, IReleaseSpec releaseSpec)
        {
            try
            {
                byte[] extendedInput = new byte[96];
                inputData.Slice(0, Math.Min(96, inputData.Length))
                .CopyTo(extendedInput.AsSpan().Slice(0, Math.Min(96, inputData.Length)));

                UInt256 baseLength    = new(extendedInput.AsSpan().Slice(0, 32), true);
                UInt256 expLength     = new(extendedInput.AsSpan().Slice(32, 32), true);
                UInt256 modulusLength = new(extendedInput.AsSpan().Slice(64, 32), true);

                UInt256 complexity = MultComplexity(baseLength, modulusLength);

                UInt256 expLengthUpTo32 = UInt256.Min(32, expLength);
                UInt256 startIndex      = 96 + baseLength; //+ expLength - expLengthUpTo32; // Geth takes head here, why?
                UInt256 exp             = new(
                    inputData.SliceWithZeroPaddingEmptyOnError((BigInteger)startIndex, (int)expLengthUpTo32), true);
                UInt256 iterationCount = CalculateIterationCount(expLength, exp);

                return(Math.Max(200L, (long)(complexity * iterationCount / 3)));
            }
            catch (OverflowException)
            {
                return(long.MaxValue);
            }
        }
Beispiel #3
0
        /// <summary>
        /// https://github.com/ethereum/EIPs/pull/2892
        /// ADJUSTED_EXPONENT_LENGTH is defined as follows.
        /// If length_of_EXPONENT &lt;= 32, and all bits in EXPONENT are 0, return 0
        /// If length_of_EXPONENT &lt;= 32, then return the index of the highest bit in EXPONENT (eg. 1 -> 0, 2 -> 1, 3 -> 1, 255 -> 7, 256 -> 8).
        /// If length_of_EXPONENT > 32, then return 8 * (length_of_EXPONENT - 32) plus the index of the highest bit in the first 32 bytes of EXPONENT (eg. if EXPONENT = \x00\x00\x01\x00.....\x00, with one hundred bytes, then the result is 8 * (100 - 32) + 253 = 797). If all of the first 32 bytes of EXPONENT are zero, return exactly 8 * (length_of_EXPONENT - 32).
        /// </summary>
        /// <param name="inputData"></param>
        /// <param name="releaseSpec"></param>
        /// <returns>Gas cost of the MODEXP operation in the context of EIP2565</returns>
        public long DataGasCost(ReadOnlyMemory <byte> inputData, IReleaseSpec releaseSpec)
        {
            if (!releaseSpec.IsEip2565Enabled)
            {
#pragma warning disable 618
                return(ModExpPrecompilePreEip2565.Instance.DataGasCost(inputData, releaseSpec));

#pragma warning restore 618
            }

            try
            {
                Span <byte> extendedInput = stackalloc byte[96];
                inputData.Slice(0, Math.Min(96, inputData.Length)).Span
                .CopyTo(extendedInput.Slice(0, Math.Min(96, inputData.Length)));

                UInt256 baseLength    = new(extendedInput.Slice(0, 32), true);
                UInt256 expLength     = new(extendedInput.Slice(32, 32), true);
                UInt256 modulusLength = new(extendedInput.Slice(64, 32), true);

                UInt256 complexity = MultComplexity(baseLength, modulusLength);

                UInt256 expLengthUpTo32 = UInt256.Min(32, expLength);
                UInt256 startIndex      = 96 + baseLength; //+ expLength - expLengthUpTo32; // Geth takes head here, why?
                UInt256 exp             = new(
                    inputData.Span.SliceWithZeroPaddingEmptyOnError((int)startIndex, (int)expLengthUpTo32), true);
                UInt256 iterationCount = CalculateIterationCount(expLength, exp);

                return(Math.Max(200L, (long)(complexity * iterationCount / 3)));
            }
            catch (OverflowException)
            {
                return(long.MaxValue);
            }
        }
        private BigInteger TimeBomb(IReleaseSpec spec, UInt256 blockNumber)
        {
            if (spec.IsEip1234Enabled)
            {
                blockNumber = blockNumber - UInt256.Min(blockNumber, 5000000);
            }
            else if (spec.IsEip649Enabled)
            {
                blockNumber = blockNumber - UInt256.Min(blockNumber, 3000000);
            }

            return(blockNumber < 200000 ? UInt256.Zero : BigInteger.Pow(2, (int)(BigInteger.Divide(blockNumber, 100000) - 2)));
        }
Beispiel #5
0
        public UInt256 CalculateTransactionPotentialCost(bool eip1559Enabled, UInt256 baseFee)
        {
            if (eip1559Enabled)
            {
                UInt256 gasPrice = baseFee + GasPremium;
                gasPrice = UInt256.Min(gasPrice, FeeCap);
                if (IsServiceTransaction)
                {
                    gasPrice = UInt256.Zero;
                }
                ;

                return(gasPrice * (ulong)GasLimit + Value);
            }

            return(GasPrice * (ulong)GasLimit + Value);
        }
Beispiel #6
0
        public static int Compare(Transaction?x, Transaction?y, UInt256 baseFee, bool isEip1559Enabled)
        {
            if (ReferenceEquals(x, y))
            {
                return(0);
            }
            if (ReferenceEquals(null, y))
            {
                return(1);
            }
            if (ReferenceEquals(null, x))
            {
                return(-1);
            }

            // then by gas price descending

            // EIP1559 changed the way we're sorting transactions. The transaction with a higher miner tip should go first
            if (isEip1559Enabled)
            {
                UInt256 xGasPrice = UInt256.Min(x.FeeCap, x.GasPremium + baseFee);
                UInt256 yGasPrice = UInt256.Min(y.FeeCap, y.GasPremium + baseFee);
                if (xGasPrice < yGasPrice)
                {
                    return(1);
                }
                if (xGasPrice > yGasPrice)
                {
                    return(-1);
                }

                return(y.FeeCap.CompareTo(x.FeeCap));
            }

            // the old way of sorting transactions
            return(y.GasPrice.CompareTo(x.GasPrice));
        }
//        [DataRow(uint.MaxValue, uint.MaxValue)]
//        [DataRow(ulong.MaxValue, ulong.MaxValue)]
        public void UInt256Test(ulong factorMax, ulong modulusMax)
        {
            var random = new MersenneTwister(0).Create <ulong>();

            for (int i = 0; i < 10000; i++)
            {
                var n     = random.Next(modulusMax - 1) + 1;
                var a     = random.Next(factorMax) % n;
                var b     = random.Next(factorMax) % n;
                var c     = random.Next(factorMax) % n;
                var d     = random.Next(factorMax) % n;
                var s     = (int)(b % 32);
                var value = (UInt256)0;
                Assert.AreEqual((BigInteger)a << s, (UInt256)a << s);
                Assert.AreEqual((BigInteger)a >> s, (UInt256)a >> s);
                Assert.AreEqual((BigInteger)a & b, (UInt256)a & b);
                Assert.AreEqual((BigInteger)a & b, a & (UInt256)b);
                Assert.AreEqual((BigInteger)a & b, (UInt256)a & (UInt256)b);
                Assert.AreEqual((BigInteger)a | b, (UInt256)a | b);
                Assert.AreEqual((BigInteger)a | b, a | (UInt256)b);
                Assert.AreEqual((BigInteger)a | b, (UInt256)a | (UInt256)b);
                Assert.AreEqual((BigInteger)a ^ b, (UInt256)a ^ b);
                Assert.AreEqual((BigInteger)a ^ b, a ^ (UInt256)b);
                Assert.AreEqual((BigInteger)a ^ b, (UInt256)a ^ (UInt256)b);
                if (a <= long.MaxValue)
                {
                    Assert.AreEqual(~(BigInteger)a, (long)~(UInt256)a);
                }
                Assert.AreEqual((BigInteger)a + b, (UInt256)a + b);
                Assert.AreEqual((BigInteger)a + b, a + (UInt256)b);
                Assert.AreEqual((BigInteger)a + b, (UInt256)a + (UInt256)b);
                Assert.AreEqual(((BigInteger)a * n + (BigInteger)b * n) % ((BigInteger)1 << 128), (UInt256)a * n + (UInt256)b * n);
                if (a >= b)
                {
                    Assert.AreEqual((BigInteger)a - b, (UInt256)a - b);
                    Assert.AreEqual((BigInteger)a - b, a - (UInt256)b);
                    Assert.AreEqual((BigInteger)a - b, (UInt256)a - (UInt256)b);
                    Assert.AreEqual((BigInteger)a * n - (BigInteger)b * n, (UInt256)a * n - (UInt256)b * n);
                }
                Assert.AreEqual(+(BigInteger)a, +(Int128)a);
                value = a; Assert.AreEqual((BigInteger)a + 1, ++value);
                value = a; Assert.AreEqual((BigInteger)a, value++);
                value = (UInt256)a * b; Assert.AreEqual((BigInteger)a * b + 1, ++value);
                value = (UInt256)a * b; Assert.AreEqual((BigInteger)a * b, value++);
                if (a > 0)
                {
                    value = a; Assert.AreEqual((BigInteger)a - 1, --value);
                    value = a; Assert.AreEqual((BigInteger)a, value--);
                }
                if (a > 0 && b > 0)
                {
                    value = (UInt256)a * b; Assert.AreEqual((BigInteger)a * b - 1, --value);
                    value = (UInt256)a * b; Assert.AreEqual((BigInteger)a * b, value--);
                }
                if (n <= uint.MaxValue)
                {
                    Assert.AreEqual((BigInteger)a * n, (UInt256)a * (uint)n);
                    Assert.AreEqual((BigInteger)b * n, (UInt256)b * (uint)n);
                    Assert.AreEqual((BigInteger)a * b * n % ((BigInteger)1 << 128), (UInt256)a * b * (uint)n);
                    Assert.AreEqual((BigInteger)n * a, (uint)n * (UInt256)a);
                    Assert.AreEqual((BigInteger)n * b, (uint)n * (UInt256)b);
                    Assert.AreEqual((BigInteger)n * a * b % ((BigInteger)1 << 128), (uint)n * ((UInt256)a * (UInt256)b));
                }
                Assert.AreEqual((BigInteger)a * b, a * (UInt256)b);
                Assert.AreEqual((BigInteger)a * b, (UInt256)a * b);
                Assert.AreEqual((BigInteger)a * b, a * (UInt256)b);
                Assert.AreEqual((BigInteger)a * b, (UInt256)a * (UInt256)b);
                if (b > 0)
                {
                    Assert.AreEqual((BigInteger)a % b, (UInt256)a % b);
                    Assert.AreEqual((BigInteger)a % b, a % (UInt256)b);
                    Assert.AreEqual((BigInteger)a % b, (UInt256)a % (UInt256)b);
                }
                Assert.AreEqual((BigInteger)a * b % n, (UInt256)a * b % n);
                Assert.AreEqual((BigInteger)a * b % n, a * (UInt256)b % n);
                Assert.AreEqual((BigInteger)a * b % n, (UInt256)a * (UInt256)b % (UInt256)n);
                if (c > 0 && d > 0)
                {
                    Assert.AreEqual((BigInteger)a * b / ((BigInteger)c * d), (UInt256)a * (UInt256)b / ((UInt256)c * (UInt256)d));
                    Assert.AreEqual((BigInteger)a * b % ((BigInteger)c * d), (UInt256)a * (UInt256)b % ((UInt256)c * (UInt256)d));
                }
                if (b > 0)
                {
                    Assert.AreEqual((BigInteger)a / b, (UInt256)a / b);
                    Assert.AreEqual((BigInteger)a / b, a / (UInt256)b);
                    Assert.AreEqual((BigInteger)a / b, (UInt256)a / (UInt256)b);
                }
                Assert.AreEqual((BigInteger)a * b / n, (UInt256)a * b / n);
                Assert.AreEqual((BigInteger)a * b / n, a * (UInt256)b / n);
                Assert.AreEqual((BigInteger)a * b / n, (UInt256)a * (UInt256)b / (UInt256)n);
                Assert.AreEqual((BigInteger)a < b, (UInt256)a < b);
                Assert.AreEqual((BigInteger)a < b, a < (UInt256)b);
                Assert.AreEqual((BigInteger)a < b, (UInt256)a < (UInt256)b);
                Assert.AreEqual((BigInteger)a <= b, (UInt256)a <= b);
                Assert.AreEqual((BigInteger)a <= b, a <= (UInt256)b);
                Assert.AreEqual((BigInteger)a <= b, (UInt256)a <= (UInt256)b);
                Assert.AreEqual((BigInteger)a > b, (UInt256)a > b);
                Assert.AreEqual((BigInteger)a > b, a > (UInt256)b);
                Assert.AreEqual((BigInteger)a > b, (UInt256)a > (UInt256)b);
                Assert.AreEqual((BigInteger)a >= b, (UInt256)a >= b);
                Assert.AreEqual((BigInteger)a >= b, a >= (UInt256)b);
                Assert.AreEqual((BigInteger)a >= b, (UInt256)a >= (UInt256)b);
                Assert.AreEqual((BigInteger)a == b, (UInt256)a == b);
                Assert.AreEqual((BigInteger)a == b, a == (UInt256)b);
                Assert.AreEqual((BigInteger)a == b, (UInt256)a == (UInt256)b);
                Assert.AreEqual((BigInteger)a != b, (UInt256)a != b);
                Assert.AreEqual((BigInteger)a != b, a != (UInt256)b);
                Assert.AreEqual((BigInteger)a != b, (UInt256)a != (UInt256)b);
                Assert.AreEqual((BigInteger)a * a, UInt256.Square(a));
                Assert.AreEqual(BigInteger.Abs(a), UInt256.Abs(a));
                Assert.AreEqual(BigInteger.Abs((BigInteger)a * b), UInt256.Abs((UInt256)a * b));
                Assert.AreEqual(BigInteger.Min(a, b), UInt256.Min(a, b));
                Assert.AreEqual(BigInteger.Min((BigInteger)a * n, (BigInteger)b * n), UInt256.Min((UInt256)a * n, (UInt256)b * n));
                Assert.AreEqual(BigInteger.Max(a, b), UInt256.Max(a, b));
                Assert.AreEqual(BigInteger.Max((BigInteger)a * n, (BigInteger)b * n), UInt256.Max((UInt256)a * n, (UInt256)b * n));
                for (var j = 0; j < 2; j++)
                {
                    var m         = UInt256.Abs(j == 0 ? (UInt256)a * (UInt256)b : (UInt256)n * (UInt256)n);
                    var floorsqrt = UInt256.FloorSqrt(m);
                    Assert.IsTrue((BigInteger)floorsqrt * floorsqrt <= m && (BigInteger)(floorsqrt + 1) * (floorsqrt + 1) > m);
                    var ceilingsqrt = UInt256.CeilingSqrt(m);
                    Assert.IsTrue((BigInteger)(ceilingsqrt - 1) * (ceilingsqrt - 1) < m && (BigInteger)ceilingsqrt * ceilingsqrt >= m);
                }
                for (var j = 0; j < 2; j++)
                {
                    var m         = j == 0 ? (UInt256)a * (UInt256)b : (UInt256)BigInteger.Pow((BigInteger)Math.Floor(Math.Pow((double)((BigInteger)a * b), (double)1 / 3)), 3);
                    var floorcbrt = UInt256.FloorCbrt(m);
                    Assert.IsTrue((BigInteger)floorcbrt * floorcbrt * floorcbrt <= m && (BigInteger)(floorcbrt + 1) * (floorcbrt + 1) * (floorcbrt + 1) > m);
                    var ceilingcbrt = UInt256.CeilingCbrt(m);
                    Assert.IsTrue((BigInteger)(ceilingcbrt - 1) * (ceilingcbrt - 1) * (ceilingcbrt - 1) < m && (BigInteger)ceilingcbrt * ceilingcbrt * ceilingcbrt >= m);
                }
                Assert.AreEqual(BigInteger.GreatestCommonDivisor((BigInteger)a, (BigInteger)b), UInt256.GreatestCommonDivisor((UInt256)a, (UInt256)b));
                Assert.AreEqual(BigInteger.GreatestCommonDivisor((BigInteger)a * b, (BigInteger)c * d), UInt256.GreatestCommonDivisor((UInt256)a * b, (UInt256)c * d));
                Assert.AreEqual(0, 0);
            }
        }
Beispiel #8
0
        public long DataGasCost(ReadOnlyMemory <byte> inputData, IReleaseSpec releaseSpec)
        {
            try
            {
                Span <byte> extendedInput = stackalloc byte[96];
                inputData.Slice(0, Math.Min(96, inputData.Length)).Span
                .CopyTo(extendedInput.Slice(0, Math.Min(96, inputData.Length)));

                UInt256 baseLength    = new(extendedInput.Slice(0, 32), true);
                UInt256 expLength     = new(extendedInput.Slice(32, 32), true);
                UInt256 modulusLength = new(extendedInput.Slice(64, 32), true);

                UInt256 complexity = MultComplexity(UInt256.Max(baseLength, modulusLength));

                byte[] expSignificantBytes = inputData.Span.SliceWithZeroPaddingEmptyOnError(96 + (int)baseLength, (int)UInt256.Min(expLength, 32));

                UInt256 lengthOver32 = expLength <= 32 ? 0 : expLength - 32;
                UInt256 adjusted     = AdjustedExponentLength(lengthOver32, expSignificantBytes);
                UInt256 gas          = complexity * UInt256.Max(adjusted, UInt256.One) / 20;
                return(gas > long.MaxValue ? long.MaxValue : (long)gas);
            }
            catch (OverflowException)
            {
                return(long.MaxValue);
            }
        }
Beispiel #9
0
        private void CleanInvalidBlocks(Keccak deletePointer)
        {
            BlockHeader    deleteHeader  = FindHeader(deletePointer);
            UInt256        currentNumber = deleteHeader.Number;
            Keccak         currentHash   = deleteHeader.Hash;
            Keccak         nextHash      = null;
            ChainLevelInfo nextLevel     = null;

            while (true)
            {
                ChainLevelInfo currentLevel = nextLevel ?? LoadLevel(currentNumber);
                nextLevel = LoadLevel(currentNumber + 1);

                bool shouldRemoveLevel = false;

                if (currentLevel != null) // preparing update of the level (removal of the invalid branch block)
                {
                    if (currentLevel.BlockInfos.Length == 1)
                    {
                        shouldRemoveLevel = true;
                    }
                    else
                    {
                        for (int i = 0; i < currentLevel.BlockInfos.Length; i++)
                        {
                            if (currentLevel.BlockInfos[0].BlockHash == currentHash)
                            {
                                currentLevel.BlockInfos = currentLevel.BlockInfos.Where(bi => bi.BlockHash != currentHash).ToArray();
                                break;
                            }
                        }
                    }
                }

                if (nextLevel != null) // just finding what the next descendant will be
                {
                    if (nextLevel.BlockInfos.Length == 1)
                    {
                        nextHash = nextLevel.BlockInfos[0].BlockHash;
                    }
                    else
                    {
                        for (int i = 0; i < nextLevel.BlockInfos.Length; i++)
                        {
                            BlockHeader potentialDescendant = FindHeader(nextLevel.BlockInfos[i].BlockHash);
                            if (potentialDescendant.ParentHash == currentHash)
                            {
                                nextHash = potentialDescendant.Hash;
                                break;
                            }
                        }
                    }

                    UpdateDeletePointer(nextHash);
                }
                else
                {
                    UpdateDeletePointer(null);
                }

                try
                {
                    _blockInfoLock.EnterWriteLock();
                    if (shouldRemoveLevel)
                    {
                        BestKnownNumber = UInt256.Min(BestKnownNumber, currentNumber - 1);
                        _blockInfoCache.Delete(currentNumber);
                        _blockInfoDb.Delete(currentNumber);
                    }
                    else
                    {
                        PersistLevel(currentNumber, currentLevel);
                    }
                }
                finally
                {
                    _blockInfoLock.ExitWriteLock();
                }

                if (_logger.IsInfo)
                {
                    _logger.Info($"Deleting invalid block {currentHash} at level {currentNumber}");
                }
                _blockCache.Delete(currentHash);
                _blockDb.Delete(currentHash);

                if (nextHash == null)
                {
                    break;
                }

                currentNumber++;
                currentHash = nextHash;
                nextHash    = null;
            }
        }
Beispiel #10
0
 public UInt256 CalculateEffectiveGasPrice(bool eip1559Enabled, UInt256 baseFee)
 {
     return(eip1559Enabled ? UInt256.Min(IsEip1559 ? FeeCap : GasPrice, GasPremium + baseFee) : GasPrice);
 }
Beispiel #11
0
        private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTracer, bool isCall)
        {
            bool notSystemTransaction = !transaction.IsSystem();
            bool wasSenderAccountCreatedInsideACall = false;

            IReleaseSpec spec = _specProvider.GetSpec(block.Number);

            if (!notSystemTransaction)
            {
                spec = new SystemTransactionReleaseSpec(spec);
            }

            UInt256 value = transaction.Value;

            UInt256 feeCap  = transaction.IsEip1559 ? transaction.FeeCap : transaction.GasPrice;
            UInt256 baseFee = block.BaseFee;

            if (baseFee > feeCap)
            {
                TraceLogInvalidTx(transaction, "MINER_PREMIUM_IS_NEGATIVE");
                QuickFail(transaction, block, txTracer, "miner premium is negative");
                return;
            }

            UInt256 premiumPerGas = UInt256.Min(transaction.GasPremium, feeCap - baseFee);
            UInt256 gasPrice      = premiumPerGas + baseFee;

            long gasLimit = transaction.GasLimit;

            byte[] machineCode = transaction.IsContractCreation ? transaction.Data : null;
            byte[] data        = transaction.IsMessageCall ? transaction.Data : Array.Empty <byte>();

            Address?caller = transaction.SenderAddress;

            if (_logger.IsTrace)
            {
                _logger.Trace($"Executing tx {transaction.Hash}");
            }

            if (caller is null)
            {
                TraceLogInvalidTx(transaction, "SENDER_NOT_SPECIFIED");
                QuickFail(transaction, block, txTracer, "sender not specified");
                return;
            }

            long intrinsicGas = IntrinsicGasCalculator.Calculate(transaction, spec);

            if (_logger.IsTrace)
            {
                _logger.Trace($"Intrinsic gas calculated for {transaction.Hash}: " + intrinsicGas);
            }

            if (notSystemTransaction)
            {
                if (gasLimit < intrinsicGas)
                {
                    TraceLogInvalidTx(transaction, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {gasLimit} < {intrinsicGas}");
                    QuickFail(transaction, block, txTracer, "gas limit below intrinsic gas");
                    return;
                }

                if (!isCall && gasLimit > block.GetActualGasLimit(spec) - block.GasUsed)
                {
                    TraceLogInvalidTx(transaction,
                                      $"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GetActualGasLimit(spec)} - {block.GasUsed}");
                    QuickFail(transaction, block, txTracer, "block gas limit exceeded");
                    return;
                }
            }

            if (!_stateProvider.AccountExists(caller))
            {
                // hacky fix for the potential recovery issue
                if (transaction.Signature != null)
                {
                    transaction.SenderAddress = _ecdsa.RecoverAddress(transaction, !spec.ValidateChainId);
                }

                if (caller != transaction.SenderAddress)
                {
                    if (_logger.IsWarn)
                    {
                        _logger.Warn($"TX recovery issue fixed - tx was coming with sender {caller} and the now it recovers to {transaction.SenderAddress}");
                    }
                    caller = transaction.SenderAddress;
                }
                else
                {
                    TraceLogInvalidTx(transaction, $"SENDER_ACCOUNT_DOES_NOT_EXIST {caller}");
                    if (isCall || gasPrice == UInt256.Zero)
                    {
                        wasSenderAccountCreatedInsideACall = isCall;
                        _stateProvider.CreateAccount(caller, UInt256.Zero);
                    }
                }

                if (caller is null)
                {
                    throw new InvalidDataException(
                              $"Failed to recover sender address on tx {transaction.Hash} when previously recovered sender account did not exist.");
                }
            }

            if (notSystemTransaction)
            {
                UInt256 senderBalance = _stateProvider.GetBalance(caller);
                if (!isCall && (ulong)intrinsicGas * gasPrice + value > senderBalance)
                {
                    TraceLogInvalidTx(transaction, $"INSUFFICIENT_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}");
                    QuickFail(transaction, block, txTracer, "insufficient sender balance");
                    return;
                }

                if (transaction.Nonce != _stateProvider.GetNonce(caller))
                {
                    TraceLogInvalidTx(transaction, $"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(caller)})");
                    QuickFail(transaction, block, txTracer, "wrong transaction nonce");
                    return;
                }

                _stateProvider.IncrementNonce(caller);
            }

            UInt256 senderReservedGasPayment = isCall ? UInt256.Zero : (ulong)gasLimit * gasPrice;

            _stateProvider.SubtractFromBalance(caller, senderReservedGasPayment, spec);
            _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : NullTxTracer.Instance);

            long unspentGas = gasLimit - intrinsicGas;
            long spentGas   = gasLimit;

            int stateSnapshot   = _stateProvider.TakeSnapshot();
            int storageSnapshot = _storageProvider.TakeSnapshot();

            _stateProvider.SubtractFromBalance(caller, value, spec);
            byte statusCode = StatusCode.Failure;
            TransactionSubstate substate = null;

            Address?recipientOrNull = null;

            try
            {
                Address recipient;
                if (transaction.IsContractCreation)
                {
                    recipient = transaction.IsSystem()
                        ? caller
                        : ContractAddress.From(caller, _stateProvider.GetNonce(caller) - 1);

                    if (_stateProvider.AccountExists(recipient))
                    {
                        if (_virtualMachine.GetCachedCodeInfo(recipient, spec).MachineCode.Length != 0 || _stateProvider.GetNonce(recipient) != 0)
                        {
                            if (_logger.IsTrace)
                            {
                                _logger.Trace($"Contract collision at {recipient}"); // the account already owns the contract with the code
                            }

                            throw new TransactionCollisionException();
                        }

                        _stateProvider.UpdateStorageRoot(recipient, Keccak.EmptyTreeHash);
                    }
                }
                else
                {
                    recipient = transaction.To !;
                }

                if (recipient == null)
                {
                    throw new InvalidDataException("Recipient has not been resolved properly before tx execution");
                }

                recipientOrNull = recipient;

                ExecutionEnvironment env = new();
                env.TxExecutionContext = new TxExecutionContext(block, caller, transaction.GasPrice);
                env.Value            = value;
                env.TransferValue    = value;
                env.Caller           = caller;
                env.CodeSource       = recipient;
                env.ExecutingAccount = recipient;
                env.InputData        = data ?? Array.Empty <byte>();
                env.CodeInfo         = machineCode == null?_virtualMachine.GetCachedCodeInfo(recipient, spec) : new CodeInfo(machineCode);

                ExecutionType executionType = transaction.IsContractCreation ? ExecutionType.Create : ExecutionType.Call;
                using (EvmState state = new(unspentGas, env, executionType, true, false))
                {
                    if (spec.UseTxAccessLists)
                    {
                        state.WarmUp(transaction.AccessList); // eip-2930
                    }

                    if (spec.UseHotAndColdStorage)
                    {
                        state.WarmUp(caller);    // eip-2929
                        state.WarmUp(recipient); // eip-2929
                    }

                    substate   = _virtualMachine.Run(state, txTracer);
                    unspentGas = state.GasAvailable;
                }

                if (substate.ShouldRevert || substate.IsError)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace("Restoring state from before transaction");
                    }
                    _stateProvider.Restore(stateSnapshot);
                    _storageProvider.Restore(storageSnapshot);
                }
                else
                {
                    // tks: there is similar code fo contract creation from init and from CREATE
                    // this may lead to inconsistencies (however it is tested extensively in blockchain tests)
                    if (transaction.IsContractCreation)
                    {
                        long codeDepositGasCost = CodeDepositHandler.CalculateCost(substate.Output.Length, spec);
                        if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate)
                        {
                            throw new OutOfGasException();
                        }

                        if (unspentGas >= codeDepositGasCost)
                        {
                            Keccak codeHash = _stateProvider.UpdateCode(substate.Output);
                            _stateProvider.UpdateCodeHash(recipient, codeHash, spec);
                            unspentGas -= codeDepositGasCost;
                        }
                    }

                    foreach (Address toBeDestroyed in substate.DestroyList)
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Trace($"Destroying account {toBeDestroyed}");
                        }
                        _storageProvider.ClearStorage(toBeDestroyed);
                        _stateProvider.DeleteAccount(toBeDestroyed);
                        if (txTracer.IsTracingRefunds)
                        {
                            txTracer.ReportRefund(RefundOf.Destroy);
                        }
                    }

                    statusCode = StatusCode.Success;
                }

                spentGas = Refund(gasLimit, unspentGas, substate, caller, gasPrice, spec);
            }
            catch (Exception ex) when(ex is EvmException || ex is OverflowException)  // TODO: OverflowException? still needed? hope not
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"EVM EXCEPTION: {ex.GetType().Name}");
                }
                _stateProvider.Restore(stateSnapshot);
                _storageProvider.Restore(storageSnapshot);
            }

            if (_logger.IsTrace)
            {
                _logger.Trace("Gas spent: " + spentGas);
            }

            Address gasBeneficiary = block.GasBeneficiary;

            if (statusCode == StatusCode.Failure || !(substate?.DestroyList.Contains(gasBeneficiary) ?? false))
            {
                if (notSystemTransaction)
                {
                    if (!_stateProvider.AccountExists(gasBeneficiary))
                    {
                        _stateProvider.CreateAccount(gasBeneficiary, (ulong)spentGas * premiumPerGas);
                    }
                    else
                    {
                        _stateProvider.AddToBalance(gasBeneficiary, (ulong)spentGas * premiumPerGas, spec);
                    }
                }
            }

            if (!isCall)
            {
                _storageProvider.Commit(txTracer.IsTracingState ? txTracer : NullStorageTracer.Instance);
                _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : NullStateTracer.Instance);
            }
            else
            {
                _storageProvider.Reset();
                _stateProvider.Reset();

                if (wasSenderAccountCreatedInsideACall)
                {
                    _stateProvider.DeleteAccount(caller);
                }
                else
                {
                    _stateProvider.AddToBalance(caller, senderReservedGasPayment, spec);
                    if (notSystemTransaction)
                    {
                        _stateProvider.DecrementNonce(caller);
                    }
                }

                _stateProvider.Commit(spec);
            }

            if (!isCall && notSystemTransaction)
            {
                block.GasUsed += spentGas;
            }

            if (txTracer.IsTracingReceipt)
            {
                Keccak stateRoot     = null;
                bool   eip658Enabled = _specProvider.GetSpec(block.Number).IsEip658Enabled;
                if (!eip658Enabled)
                {
                    _stateProvider.RecalculateStateRoot();
                    stateRoot = _stateProvider.StateRoot;
                }

                if (statusCode == StatusCode.Failure)
                {
                    txTracer.MarkAsFailed(recipientOrNull, spentGas, (substate?.ShouldRevert ?? false) ? substate.Output : Array.Empty <byte>(), substate?.Error, stateRoot);
                }
                else
                {
                    txTracer.MarkAsSuccess(recipientOrNull, spentGas, substate.Output, substate.Logs.Any() ? substate.Logs.ToArray() : Array.Empty <LogEntry>(), stateRoot);
                }
            }
        }
 private UInt256 CalculateUserOperationPremiumGasPrice(UserOperation op, UInt256 baseFeePerGas)
 {
     return(UInt256.Min(op.MaxPriorityFeePerGas, op.MaxFeePerGas - baseFeePerGas));
 }