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); }
/// <summary> /// https://github.com/ethereum/EIPs/pull/2892 /// ADJUSTED_EXPONENT_LENGTH is defined as follows. /// If length_of_EXPONENT <= 32, and all bits in EXPONENT are 0, return 0 /// If length_of_EXPONENT <= 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); } }
/// <summary> /// https://github.com/ethereum/EIPs/pull/2892 /// ADJUSTED_EXPONENT_LENGTH is defined as follows. /// If length_of_EXPONENT <= 32, and all bits in EXPONENT are 0, return 0 /// If length_of_EXPONENT <= 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))); }
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); }
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); } }
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); } }
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; } }
public UInt256 CalculateEffectiveGasPrice(bool eip1559Enabled, UInt256 baseFee) { return(eip1559Enabled ? UInt256.Min(IsEip1559 ? FeeCap : GasPrice, GasPremium + baseFee) : GasPrice); }
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)); }