public Keccak SendTransaction(Transaction transaction) { try { _readerWriterLockSlim.EnterWriteLock(); _stateProvider.StateRoot = _blockTree.Head.StateRoot; if (transaction.SenderAddress == null) { transaction.SenderAddress = _wallet.GetAccounts()[0]; } transaction.Nonce = _stateProvider.GetNonce(transaction.SenderAddress); _wallet.Sign(transaction, _blockTree.ChainId); transaction.Hash = Transaction.CalculateHash(transaction); if (_stateProvider.GetNonce(transaction.SenderAddress) != transaction.Nonce) { throw new InvalidOperationException("Invalid nonce"); } _transactionPool.AddTransaction(transaction, _blockTree.Head.Number); _stateProvider.Reset(); return(transaction.Hash); } finally { _readerWriterLockSlim.ExitWriteLock(); } }
private void QuickFail(Transaction tx, StateUpdate block, ITxTracer txTracer, bool readOnly) { block.GasUsed += (long)tx.GasLimit; Address recipient = tx.To ?? Address.OfContract(tx.SenderAddress, _stateProvider.GetNonce(tx.SenderAddress)); if (txTracer.IsTracingReceipt) { txTracer.MarkAsFailed(recipient, (long)tx.GasLimit, Bytes.Empty, "invalid"); } }
private (Address sender, Address recipient) ExtractSenderAndRecipient(PublicEntry entry) { var sender = entry.SenderAddress.ToAddress(); var recipient = entry.ReceiverAddress.ToAddress(); if (entry.IsValidDeploymentEntry) { recipient = ContractAddress.From(sender, _stateProvider.GetNonce(sender)); } return(sender, recipient); }
private void QuickFail(Transaction tx, BlockHeader block, ITxTracer txTracer, string reason) { block.GasUsed += tx.GasLimit; Address recipient = tx.To ?? ContractAddress.From(tx.SenderAddress, _stateProvider.GetNonce(tx.SenderAddress)); _stateProvider.RecalculateStateRoot(); Keccak stateRoot = _specProvider.GetSpec(block.Number).IsEip658Enabled ? null : _stateProvider.StateRoot; if (txTracer.IsTracingReceipt) { txTracer.MarkAsFailed(recipient, tx.GasLimit, Array.Empty <byte>(), reason ?? "invalid", stateRoot); } }
private (Address sender, Address recipient) ExtractSenderAndRecipient(PublicEntry entry) { var sender = GetAccountAddress(entry.SenderAddress); var recipient = entry.TargetContract == null ? GetAccountAddress(entry.ReceiverAddress) : new Address(entry.TargetContract); if (entry.IsValidDeploymentEntry) { recipient = Address.OfContract(sender, _stateProvider.GetNonce(sender)); } return(sender, recipient); }
public Transaction BuildTransaction(long gaslimit, byte[] callData, Address sender, BlockHeader parent, IReleaseSpec spec, 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 = _stateProvider.GetNonce(_signer.Address); 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 should_return_valid_pending_and_queued_transactions() { var nonce = 3; _stateProvider.GetNonce(_address).Returns(new UInt256(nonce)); var transactions = GetTransactions(); var info = _infoProvider.GetInfo(transactions); info.Pending.Count.Should().Be(1); info.Queued.Count.Should().Be(1); var pending = info.Pending.First(); pending.Key.Should().Be(_address); pending.Value.Count.Should().Be(3); pending.Value.Sum(v => v.Value.Length).Should().Be(4); VerifyNonceAndTransactions(pending.Value.ElementAt(0), 3, 2); VerifyNonceAndTransactions(pending.Value.ElementAt(1), 4, 1); VerifyNonceAndTransactions(pending.Value.ElementAt(2), 5, 1); var queued = info.Queued.First(); queued.Key.Should().Be(_address); queued.Value.Count.Should().Be(4); queued.Value.Sum(v => v.Value.Length).Should().Be(4); VerifyNonceAndTransactions(queued.Value.ElementAt(0), 1, 1); VerifyNonceAndTransactions(queued.Value.ElementAt(1), 2, 1); VerifyNonceAndTransactions(queued.Value.ElementAt(2), 8, 1); VerifyNonceAndTransactions(queued.Value.ElementAt(3), 9, 1); }
public void should_return_valid_pending_and_queued_transactions() { var nonce = 3; _stateProvider.GetNonce(_address).Returns(new UInt256(nonce)); var transactions = GetTransactions(); _txPool.GetPendingTransactions().Returns(transactions); var info = _infoProvider.GetInfo(); info.Pending.Count.Should().Be(1); info.Queued.Count.Should().Be(1); var pending = info.Pending.First(); pending.Key.Should().Be(_address); pending.Value.Count.Should().Be(3); VerifyNonceAndTransactions(pending.Value, 3); VerifyNonceAndTransactions(pending.Value, 4); VerifyNonceAndTransactions(pending.Value, 5); var queued = info.Queued.First(); queued.Key.Should().Be(_address); queued.Value.Count.Should().Be(4); VerifyNonceAndTransactions(queued.Value, 1); VerifyNonceAndTransactions(queued.Value, 2); VerifyNonceAndTransactions(queued.Value, 8); VerifyNonceAndTransactions(queued.Value, 9); }
private Transaction CreateReportMaliciousTransactionCore(PersistentReport persistentReport) { var transaction = ValidatorContract.ReportMalicious(persistentReport.ValidatorAddress, persistentReport.BlockNumber, persistentReport.Proof); transaction.Nonce = _stateProvider.GetNonce(ValidatorContract.NodeAddress); return(transaction); }
public CallOutput Call(BlockHeader blockHeader, Transaction transaction) { _stateProvider.StateRoot = _blockTree.Head.StateRoot; BlockHeader header = new BlockHeader(blockHeader.Hash, Keccak.OfAnEmptySequenceRlp, blockHeader.Beneficiary, blockHeader.Difficulty, blockHeader.Number + 1, (long)transaction.GasLimit, blockHeader.Timestamp + 1, Bytes.Empty); transaction.Nonce = _stateProvider.GetNonce(transaction.SenderAddress); transaction.Hash = Transaction.CalculateHash(transaction); CallOutputTracer callOutputTracer = new CallOutputTracer(); _transactionProcessor.CallAndRestore(transaction, header, callOutputTracer); _stateProvider.Reset(); _storageProvider.Reset(); return(new CallOutput { Error = callOutputTracer.Error, GasSpent = callOutputTracer.GasSpent, OutputData = callOutputTracer.ReturnValue }); }
public TxPoolInfo GetInfo() { var transactions = _txPool.GetPendingTransactions(); var groupedTransactions = transactions.GroupBy(t => t.SenderAddress); var pendingTransactions = new Dictionary <Address, IDictionary <ulong, Transaction> >(); var queuedTransactions = new Dictionary <Address, IDictionary <ulong, Transaction> >(); foreach (var group in groupedTransactions) { var address = group.Key; if (address == null) { continue; } var accountNonce = _stateProvider.GetNonce(address); var expectedNonce = accountNonce; var pending = new Dictionary <ulong, Transaction>(); var queued = new Dictionary <ulong, Transaction>(); var transactionsGroupedByNonce = group.OrderBy(t => t.Nonce); foreach (var transaction in transactionsGroupedByNonce) { var transactionNonce = (ulong)transaction.Nonce; if (transaction.Nonce < accountNonce) { queued.Add(transactionNonce, transaction); continue; } if (transaction.Nonce == accountNonce || accountNonce != expectedNonce && transaction.Nonce == expectedNonce) { pending.Add(transactionNonce, transaction); expectedNonce = transaction.Nonce + 1; continue; } queued.Add(transactionNonce, transaction); } if (pending.Any()) { pendingTransactions[address] = pending; } if (queued.Any()) { queuedTransactions[address] = queued; } } return(new TxPoolInfo(pendingTransactions, queuedTransactions)); }
private void QuickFail(Transaction tx, BlockHeader block, ITxTracer txTracer, bool eip658NotEnabled, string?reason) { block.GasUsed += tx.GasLimit; Address recipient = tx.To ?? ContractAddress.From( tx.SenderAddress ?? Address.Zero, _stateProvider.GetNonce(tx.SenderAddress ?? Address.Zero)); if (txTracer.IsTracingReceipt) { Keccak?stateRoot = null; if (eip658NotEnabled) { _stateProvider.RecalculateStateRoot(); stateRoot = _stateProvider.StateRoot; } txTracer.MarkAsFailed(recipient, tx.GasLimit, Array.Empty <byte>(), reason ?? "invalid", stateRoot); } }
public TransactionPoolInfo GetInfo(Transaction[] transactions) { var groupedTransactions = transactions.GroupBy(t => t.SenderAddress); var pendingTransactions = new Dictionary <Address, IDictionary <UInt256, Transaction[]> >(); var queuedTransactions = new Dictionary <Address, IDictionary <UInt256, Transaction[]> >(); foreach (var group in groupedTransactions) { var address = group.Key; if (address == null) { continue; } var accountNonce = _stateProvider.GetNonce(address); var expectedNonce = accountNonce; var pending = new Dictionary <UInt256, Transaction[]>(); var queued = new Dictionary <UInt256, Transaction[]>(); var transactionsGroupedByNonce = group.OrderBy(t => t.Nonce).GroupBy(t => t.Nonce); foreach (var nonceGroup in transactionsGroupedByNonce) { if (nonceGroup.Key < accountNonce) { queued.Add(nonceGroup.Key, nonceGroup.ToArray()); continue; } if (nonceGroup.Key == accountNonce || accountNonce != expectedNonce && nonceGroup.Key == expectedNonce) { pending.Add(nonceGroup.Key, nonceGroup.ToArray()); expectedNonce = nonceGroup.Key + 1; continue; } queued.Add(nonceGroup.Key, nonceGroup.ToArray()); } if (pending.Any()) { pendingTransactions[address] = pending; } if (queued.Any()) { queuedTransactions[address] = queued; } } return(new TransactionPoolInfo(pendingTransactions, queuedTransactions)); }
public AddingTxEventArgs CanAddTransaction(Block block, Transaction currentTx, IReadOnlySet <Transaction> transactionsInBlock, IStateProvider stateProvider) { AddingTxEventArgs args = new(transactionsInBlock.Count, currentTx, block, transactionsInBlock); long gasRemaining = block.Header.GasLimit - block.GasUsed; // No more gas available in block for any transactions, // the only case we have to really stop if (GasCostOf.Transaction > gasRemaining) { return(args.Set(TxAction.Stop, "Block full")); } if (currentTx.SenderAddress is null) { return(args.Set(TxAction.Skip, "Null sender")); } if (currentTx.GasLimit > gasRemaining) { return(args.Set(TxAction.Skip, $"Not enough gas in block, gas limit {currentTx.GasLimit} > {gasRemaining}")); } if (transactionsInBlock.Contains(currentTx)) { return(args.Set(TxAction.Skip, "Transaction already in block")); } IReleaseSpec spec = _specProvider.GetSpec(block.Number); if (stateProvider.IsInvalidContractSender(spec, currentTx.SenderAddress)) { return(args.Set(TxAction.Skip, $"Sender is contract")); } UInt256 expectedNonce = stateProvider.GetNonce(currentTx.SenderAddress); if (expectedNonce != currentTx.Nonce) { return(args.Set(TxAction.Skip, $"Invalid nonce - expected {expectedNonce}")); } UInt256 balance = stateProvider.GetBalance(currentTx.SenderAddress); if (!HasEnoughFounds(currentTx, balance, args, block, spec)) { return(args); } AddingTransaction?.Invoke(this, args); return(args); }
private void QuickFail(Transaction tx, BlockHeader block, ITxTracer txTracer, string reason) { if (tx.IsEip1559) { block.GasUsedEip1559 += tx.GasLimit; } else { block.GasUsedLegacy += tx.GasLimit; } Address recipient = tx.To ?? ContractAddress.From(tx.SenderAddress, _stateProvider.GetNonce(tx.SenderAddress)); // TODO: this is possibly an unnecessary calculation inside EIP-658 _stateProvider.RecalculateStateRoot(); Keccak stateRoot = _specProvider.GetSpec(block.Number).IsEip658Enabled ? null : _stateProvider.StateRoot; if (txTracer.IsTracingReceipt) { txTracer.MarkAsFailed(recipient, tx.GasLimit, Array.Empty <byte>(), reason ?? "invalid", stateRoot); } }
public long EstimateGas(Block block, Transaction transaction) { _stateProvider.StateRoot = _blockTree.Head.StateRoot; BlockHeader header = new BlockHeader(block.Hash, Keccak.OfAnEmptySequenceRlp, block.Beneficiary, block.Difficulty, block.Number + 1, block.GasLimit, block.Timestamp + 1, Bytes.Empty); transaction.Nonce = _stateProvider.GetNonce(transaction.SenderAddress); transaction.Hash = Nethermind.Core.Transaction.CalculateHash(transaction); CallOutputTracer callOutputTracer = new CallOutputTracer(); _transactionProcessor.CallAndRestore(transaction, header, callOutputTracer); _stateProvider.Reset(); _storageProvider.Reset(); return(callOutputTracer.GasSpent); }
public static void ProcessTransaction(this ITransactionProcessorAdapter transactionProcessor, Block block, Transaction currentTx, BlockReceiptsTracer receiptsTracer, ProcessingOptions processingOptions, IStateProvider stateProvider) { if (processingOptions.ContainsFlag(ProcessingOptions.DoNotVerifyNonce)) { currentTx.Nonce = stateProvider.GetNonce(currentTx.SenderAddress); } receiptsTracer.StartNewTxTrace(currentTx); transactionProcessor.Execute(currentTx, block.Header, receiptsTracer); receiptsTracer.EndTxTrace(); }
public TestContext(bool forSealing, ReportingContractBasedValidator.Cache cache = null, Address[] initialValidators = null) { BlockHeader parentHeader = Build.A.BlockHeader.TestObject; IValidatorContract validatorContract = Substitute.For <IValidatorContract>(); Address[] validators = initialValidators ?? new[] { MaliciousMinerAddress, NodeAddress }; validatorContract.GetValidators(parentHeader).Returns(validators); ContractBasedValidator = new ContractBasedValidator( validatorContract, Substitute.For <IBlockTree>(), Substitute.For <IReceiptFinder>(), Substitute.For <IValidatorStore>(), Substitute.For <IValidSealerStrategy>(), Substitute.For <IAuRaBlockFinalizationManager>(), parentHeader, LimboLogs.Instance, 0, PosdaoTransition, forSealing); ContractBasedValidator.Validators ??= validators; ReportingValidatorContract = Substitute.For <IReportingValidatorContract>(); ReportingValidatorContract.NodeAddress.Returns(NodeAddress); TxSender = Substitute.For <ITxSender>(); ITxPool txPool = Substitute.For <ITxPool>(); IStateProvider stateProvider = Substitute.For <IStateProvider>(); ISpecProvider specProvider = Substitute.For <ISpecProvider>(); stateProvider.GetNonce(ReportingValidatorContract.NodeAddress).Returns(UInt256.One); Validator = new ReportingContractBasedValidator( ContractBasedValidator, ReportingValidatorContract, PosdaoTransition, TxSender, txPool, new MiningConfig(), stateProvider, cache ?? new ReportingContractBasedValidator.Cache(), specProvider, Substitute.For <IGasPriceOracle>(), LimboLogs.Instance); }
private TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, IBlockTracer blockTracer) { _receiptsTracer.SetOtherTracer(blockTracer); _receiptsTracer.StartNewBlockTrace(block); for (int i = 0; i < block.Transactions.Length; i++) { Transaction currentTx = block.Transactions[i]; if ((processingOptions & ProcessingOptions.DoNotVerifyNonce) != 0) { currentTx.Nonce = _stateProvider.GetNonce(currentTx.SenderAddress); } _receiptsTracer.StartNewTxTrace(currentTx.Hash); _transactionProcessor.Execute(currentTx, block.Header, _receiptsTracer); _receiptsTracer.EndTxTrace(); TransactionProcessed?.Invoke(this, new TxProcessedEventArgs(i, currentTx, _receiptsTracer.TxReceipts[i])); } return(_receiptsTracer.TxReceipts); }
private Transaction BuildSimulateValidationTransaction( UserOperation userOperation, BlockHeader parent, IReleaseSpec spec) { AbiSignature abiSignature = _entryPointContractAbi.Functions["simulateValidation"].GetCallInfo().Signature; UserOperationAbi userOperationAbi = userOperation.Abi; byte[] computedCallData = _abiEncoder.Encode( AbiEncodingStyle.IncludeSignature, abiSignature, userOperationAbi); Transaction transaction = _userOperationTxBuilder.BuildTransaction((long)userOperation.PreVerificationGas + (long)userOperation.VerificationGas, computedCallData, Address.Zero, parent, spec, _stateProvider.GetNonce(Address.Zero), true); return(transaction); }
public TransactionReceipt Execute( Transaction transaction, BlockHeader block) { TransactionTrace trace = null; if (_tracer.IsTracingEnabled) { trace = new TransactionTrace(); } IReleaseSpec spec = _specProvider.GetSpec(block.Number); Address recipient = transaction.To; BigInteger value = transaction.Value; BigInteger gasPrice = transaction.GasPrice; long gasLimit = (long)transaction.GasLimit; byte[] machineCode = transaction.Init; byte[] data = transaction.Data ?? Bytes.Empty; Address sender = transaction.SenderAddress; if (_logger.IsDebugEnabled) { _logger.Debug($"SPEC: {spec.GetType().Name}"); _logger.Debug("HASH: " + transaction.Hash); _logger.Debug("IS_CONTRACT_CREATION: " + transaction.IsContractCreation); _logger.Debug("IS_MESSAGE_CALL: " + transaction.IsMessageCall); _logger.Debug("IS_TRANSFER: " + transaction.IsTransfer); _logger.Debug("SENDER: " + sender); _logger.Debug("TO: " + transaction.To); _logger.Debug("GAS LIMIT: " + transaction.GasLimit); _logger.Debug("GAS PRICE: " + transaction.GasPrice); _logger.Debug("VALUE: " + transaction.Value); _logger.Debug("DATA_LENGTH: " + (transaction.Data?.Length ?? 0)); _logger.Debug("NONCE: " + transaction.Nonce); } if (sender == null) { if (_logger.IsDebugEnabled) { _logger.Debug($"SENDER_NOT_SPECIFIED"); } return(GetNullReceipt(block, 0L)); } long intrinsicGas = _intrinsicGasCalculator.Calculate(transaction, spec); if (_logger.IsDebugEnabled) { _logger.Debug("INTRINSIC GAS: " + intrinsicGas); } if (gasLimit < intrinsicGas) { if (_logger.IsDebugEnabled) { _logger.Debug($"GAS_LIMIT_BELOW_INTRINSIC_GAS {gasLimit} < {intrinsicGas}"); } return(GetNullReceipt(block, 0L)); } if (gasLimit > block.GasLimit - block.GasUsed) { if (_logger.IsDebugEnabled) { _logger.Debug($"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}"); } return(GetNullReceipt(block, 0L)); } if (!_stateProvider.AccountExists(sender)) { if (_logger.IsDebugEnabled) { _logger.Debug($"SENDER_ACCOUNT_DOES_NOT_EXIST {sender}"); } _stateProvider.CreateAccount(sender, 0); } BigInteger senderBalance = _stateProvider.GetBalance(sender); if (intrinsicGas * gasPrice + value > senderBalance) { if (_logger.IsDebugEnabled) { _logger.Debug($"INSUFFICIENT_SENDER_BALANCE: ({sender})b = {senderBalance}"); } return(GetNullReceipt(block, 0L)); } if (transaction.Nonce != _stateProvider.GetNonce(sender)) { if (_logger.IsDebugEnabled) { _logger.Debug($"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(sender)})"); } return(GetNullReceipt(block, 0L)); } _stateProvider.IncrementNonce(sender); _stateProvider.UpdateBalance(sender, -new BigInteger(gasLimit) * gasPrice, spec); _stateProvider.Commit(spec); long unspentGas = gasLimit - intrinsicGas; long spentGas = gasLimit; List <LogEntry> logEntries = new List <LogEntry>(); if (transaction.IsContractCreation) { Rlp addressBaseRlp = Rlp.Encode( Rlp.Encode(sender), Rlp.Encode(_stateProvider.GetNonce(sender) - 1)); Keccak addressBaseKeccak = Keccak.Compute(addressBaseRlp); recipient = new Address(addressBaseKeccak); } int snapshot = _stateProvider.TakeSnapshot(); int storageSnapshot = _storageProvider.TakeSnapshot(); _stateProvider.UpdateBalance(sender, -value, spec); byte statusCode = StatusCode.Failure; HashSet <Address> destroyedAccounts = new HashSet <Address>(); try { if (transaction.IsContractCreation) { // TODO: review tests around it as it fails on Ropsten 230881 when we throw an exception if (_stateProvider.AccountExists(recipient) && !_stateProvider.IsEmptyAccount(recipient)) { // TODO: review // throw new TransactionCollisionException(); } } if (transaction.IsTransfer) // TODO: this is never called and wrong, to be removed { _stateProvider.UpdateBalance(sender, -value, spec); _stateProvider.UpdateBalance(recipient, value, spec); statusCode = StatusCode.Success; } else { bool isPrecompile = recipient.IsPrecompiled(spec); ExecutionEnvironment env = new ExecutionEnvironment(); env.Value = value; env.TransferValue = value; env.Sender = sender; env.ExecutingAccount = recipient; env.CurrentBlock = block; env.GasPrice = gasPrice; env.InputData = data ?? new byte[0]; env.CodeInfo = isPrecompile ? new CodeInfo(recipient) : machineCode == null?_virtualMachine.GetCachedCodeInfo(recipient) : new CodeInfo(machineCode); env.Originator = sender; ExecutionType executionType = isPrecompile ? ExecutionType.DirectPrecompile : transaction.IsContractCreation ? ExecutionType.DirectCreate : ExecutionType.Transaction; TransactionSubstate substate; byte[] output; using (EvmState state = new EvmState(unspentGas, env, executionType, false)) { (output, substate) = _virtualMachine.Run(state, spec, trace); unspentGas = state.GasAvailable; } if (substate.ShouldRevert) { if (_logger.IsDebugEnabled) { _logger.Debug("REVERTING"); } logEntries.Clear(); destroyedAccounts.Clear(); _stateProvider.Restore(snapshot); _storageProvider.Restore(storageSnapshot); } else { if (transaction.IsContractCreation) { long codeDepositGasCost = output.Length * GasCostOf.CodeDeposit; if (spec.IsEip170Enabled && output.Length > 0x6000) { codeDepositGasCost = long.MaxValue; } if (unspentGas < codeDepositGasCost && spec.IsEip2Enabled) { throw new OutOfGasException(); } if (unspentGas >= codeDepositGasCost) { Keccak codeHash = _stateProvider.UpdateCode(output); _stateProvider.UpdateCodeHash(recipient, codeHash, spec); unspentGas -= codeDepositGasCost; } } logEntries.AddRange(substate.Logs); foreach (Address toBeDestroyed in substate.DestroyList) { destroyedAccounts.Add(toBeDestroyed); } statusCode = StatusCode.Success; } spentGas = Refund(gasLimit, unspentGas, substate, sender, gasPrice, spec); } } catch (Exception ex) when(ex is EvmException || ex is OverflowException) // TODO: OverflowException? still needed? hope not { if (_logger.IsDebugEnabled) { _logger.Debug($"EVM EXCEPTION: {ex.GetType().Name}"); } logEntries.Clear(); destroyedAccounts.Clear(); _stateProvider.Restore(snapshot); _storageProvider.Restore(storageSnapshot); } foreach (Address toBeDestroyed in destroyedAccounts) { if (_logger.IsDebugEnabled) { _logger.Debug($"DESTROYING: {toBeDestroyed}"); } _stateProvider.DeleteAccount(toBeDestroyed); } if (_logger.IsDebugEnabled) { _logger.Debug("GAS SPENT: " + spentGas); } if (!destroyedAccounts.Contains(block.Beneficiary)) { if (!_stateProvider.AccountExists(block.Beneficiary)) { _stateProvider.CreateAccount(block.Beneficiary, spentGas * gasPrice); } else { _stateProvider.UpdateBalance(block.Beneficiary, spentGas * gasPrice, spec); } } _storageProvider.Commit(spec); _stateProvider.Commit(spec); block.GasUsed += spentGas; if (_tracer.IsTracingEnabled) { trace.Gas = spentGas; _tracer.SaveTrace(Transaction.CalculateHash(transaction), trace); } return(BuildTransactionReceipt(statusCode, logEntries.Any() ? logEntries.ToArray() : LogEntry.EmptyLogs, block.GasUsed, recipient)); }
private Block PrepareBlock(Block parentBlock) { BlockHeader parentHeader = parentBlock.Header; if (parentHeader == null) { if (_logger.IsError) { _logger.Error($"Preparing new block on top of {parentBlock.ToString(Block.Format.Short)} - parent header is null"); } return(null); } if (_recentNotAllowedParent == parentBlock.Hash) { return(null); } if (!_sealer.CanSeal(parentHeader.Number + 1, parentHeader.Hash)) { if (_logger.IsInfo) { _logger.Info($"Not allowed to sign block ({parentBlock.Number + 1})"); } _recentNotAllowedParent = parentHeader.Hash; return(null); } if (_logger.IsInfo) { _logger.Info($"Preparing new block on top of {parentBlock.ToString(Block.Format.Short)}"); } UInt256 timestamp = _timestamp.EpochSeconds; BlockHeader header = new BlockHeader( parentBlock.Hash, Keccak.OfAnEmptySequenceRlp, Address.Zero, 1, parentBlock.Number + 1, parentBlock.GasLimit, timestamp > parentBlock.Timestamp ? timestamp : parentBlock.Timestamp + 1, new byte[0]); // If the block isn't a checkpoint, cast a random vote (good enough for now) UInt256 number = header.Number; // Assemble the voting snapshot to check which votes make sense Snapshot snapshot = _snapshotManager.GetOrCreateSnapshot(number - 1, header.ParentHash); bool isEpochBlock = (ulong)number % 30000 == 0; if (!isEpochBlock) { // Gather all the proposals that make sense voting on List <Address> addresses = new List <Address>(); foreach (var proposal in _proposals) { Address address = proposal.Key; bool authorize = proposal.Value; if (_snapshotManager.IsValidVote(snapshot, address, authorize)) { addresses.Add(address); } } // If there's pending proposals, cast a vote on them if (addresses.Count > 0) { header.Beneficiary = addresses[_cryptoRandom.NextInt(addresses.Count)]; header.Nonce = _proposals[header.Beneficiary] ? Clique.NonceAuthVote : Clique.NonceDropVote; } } // Set the correct difficulty header.Difficulty = CalculateDifficulty(snapshot, _address); header.TotalDifficulty = parentBlock.TotalDifficulty + header.Difficulty; if (_logger.IsDebug) { _logger.Debug($"Setting total difficulty to {parentBlock.TotalDifficulty} + {header.Difficulty}."); } // Set extra data int mainBytesLength = Clique.ExtraVanityLength + Clique.ExtraSealLength; int signerBytesLength = isEpochBlock ? 20 * snapshot.Signers.Count : 0; int extraDataLength = mainBytesLength + signerBytesLength; header.ExtraData = new byte[extraDataLength]; byte[] clientName = Encoding.UTF8.GetBytes("Nethermind"); Array.Copy(clientName, header.ExtraData, clientName.Length); if (isEpochBlock) { for (int i = 0; i < snapshot.Signers.Keys.Count; i++) { Address signer = snapshot.Signers.Keys[i]; int index = Clique.ExtraVanityLength + 20 * i; Array.Copy(signer.Bytes, 0, header.ExtraData, index, signer.Bytes.Length); } } // Mix digest is reserved for now, set to empty header.MixHash = Keccak.Zero; // Ensure the timestamp has the correct delay header.Timestamp = parentBlock.Timestamp + _config.BlockPeriod; if (header.Timestamp < _timestamp.EpochSeconds) { header.Timestamp = new UInt256(_timestamp.EpochSeconds); } var transactions = _transactionPool.GetPendingTransactions().OrderBy(t => t.Nonce).ThenByDescending(t => t.GasPrice).ThenBy(t => t.GasLimit); // by nonce in case there are two transactions for the same account var selectedTxs = new List <Transaction>(); BigInteger gasRemaining = header.GasLimit; if (_logger.IsDebug) { _logger.Debug($"Collecting pending transactions at min gas price {MinGasPriceForMining} and block gas limit {gasRemaining}."); } int total = 0; _stateProvider.StateRoot = parentHeader.StateRoot; Dictionary <Address, UInt256> nonces = new Dictionary <Address, UInt256>(); foreach (Transaction transaction in transactions) { total++; if (transaction.SenderAddress == null) { if (_logger.IsError) { _logger.Error("Rejecting null sender pending transaction."); } continue; } if (transaction.GasPrice < MinGasPriceForMining) { if (_logger.IsTrace) { _logger.Trace($"Rejecting transaction - gas price ({transaction.GasPrice}) too low (min gas price: {MinGasPriceForMining}."); } continue; } if (transaction.Nonce != _stateProvider.GetNonce(transaction.SenderAddress) && (!nonces.ContainsKey(transaction.SenderAddress) || nonces[transaction.SenderAddress] + 1 != transaction.Nonce)) { if (_logger.IsTrace) { _logger.Trace($"Rejecting transaction based on nonce."); } continue; } if (transaction.GasLimit > gasRemaining) { if (_logger.IsTrace) { _logger.Trace($"Rejecting transaction - gas limit ({transaction.GasPrice}) more than remaining gas ({gasRemaining})."); } continue; } UInt256 requiredBalance = transaction.GasLimit + transaction.Value; UInt256 balance = _stateProvider.GetBalance(transaction.SenderAddress); if (transaction.GasLimit + transaction.Value > balance) { if (_logger.IsTrace) { _logger.Trace($"Rejecting transaction - insufficient balance - required {requiredBalance}, actual {balance}."); } continue; } selectedTxs.Add(transaction); nonces[transaction.SenderAddress] = transaction.Nonce; gasRemaining -= transaction.GasLimit; } if (_logger.IsDebug) { _logger.Debug($"Collected {selectedTxs.Count} out of {total} pending transactions."); } Block block = new Block(header, selectedTxs, new BlockHeader[0]); header.TransactionsRoot = block.CalculateTransactionsRoot(); block.Author = _address; return(block); }
public IEnumerable <Transaction> SelectTransactions(long gasLimit) { UInt256 GetCurrentNonce(IDictionary <Address, UInt256> noncesDictionary, Address address) { if (!noncesDictionary.TryGetValue(address, out var nonce)) { noncesDictionary[address] = nonce = _stateProvider.GetNonce(address); } return(nonce); } UInt256 GetRemainingBalance(IDictionary <Address, UInt256> balances, Address address) { if (!balances.TryGetValue(address, out var balance)) { balances[address] = balance = _stateProvider.GetBalance(address); } return(balance); } bool HasEnoughFounds(IDictionary <Address, UInt256> balances, Transaction transaction) { var balance = GetRemainingBalance(balances, transaction.SenderAddress); var transactionPotentialCost = transaction.GasPrice * (ulong)transaction.GasLimit + transaction.Value; if (balance < transactionPotentialCost) { if (_logger.IsTrace) { _logger.Trace($"Rejecting transaction - transaction cost ({transactionPotentialCost}) is higher than sender balance ({balance})."); } return(false); } else { balances[transaction.SenderAddress] = balance - transactionPotentialCost; return(true); } } var pendingTransactions = _transactionPool.GetPendingTransactions(); var transactions = pendingTransactions.OrderBy(t => t.Nonce).ThenByDescending(t => t.GasPrice).ThenBy(t => t.GasLimit); IDictionary <Address, UInt256> remainingBalance = new Dictionary <Address, UInt256>(); Dictionary <Address, UInt256> nonces = new Dictionary <Address, UInt256>(); List <Transaction> selected = new List <Transaction>(); long gasRemaining = gasLimit; if (_logger.IsDebug) { _logger.Debug($"Collecting pending transactions at min gas price {_minGasPriceForMining} and block gas limit {gasRemaining}."); } foreach (Transaction transaction in transactions) { if (transaction.SenderAddress == null) { if (_logger.IsTrace) { _logger.Trace("Rejecting null sender pending transaction."); } continue; } if (transaction.GasPrice < _minGasPriceForMining) { if (_logger.IsTrace) { _logger.Trace($"Rejecting transaction - gas price ({transaction.GasPrice}) too low (min gas price: {_minGasPriceForMining}."); } continue; } if (GetCurrentNonce(nonces, transaction.SenderAddress) != transaction.Nonce) { if (_logger.IsTrace) { _logger.Trace($"Rejecting transaction based on nonce."); } continue; } if (transaction.GasLimit > gasRemaining) { if (_logger.IsTrace) { _logger.Trace($"Rejecting transaction - gas limit ({transaction.GasLimit}) more than remaining gas ({gasRemaining})."); } break; } if (!HasEnoughFounds(remainingBalance, transaction)) { continue; } selected.Add(transaction); nonces[transaction.SenderAddress] = transaction.Nonce + 1; gasRemaining -= transaction.GasLimit; } if (_logger.IsDebug) { _logger.Debug($"Collected {selected.Count} out of {pendingTransactions.Length} pending transactions."); } return(selected); }
public IEnumerable <Transaction> GetTransactions(BlockHeader parent, long gasLimit) { IDictionary <Address, HashSet <UInt256> > usedAccessList = new Dictionary <Address, HashSet <UInt256> >(); // IList<UserOperation> userOperationsToInclude = new List<UserOperation>(); IDictionary <Address, IList <UserOperation> > userOperationsToIncludeByEntryPoint = new Dictionary <Address, IList <UserOperation> >(); ulong gasUsed = 0; IList <Tuple <Address, UserOperation> > combinedUserOperations = new List <Tuple <Address, UserOperation> >(); foreach (Address entryPoint in _userOperationPools.Keys) { IEnumerable <UserOperation> entryPointUserOperations = _userOperationPools[entryPoint] .GetUserOperations() .Where(op => op.MaxFeePerGas >= parent.BaseFeePerGas); foreach (UserOperation userOperation in entryPointUserOperations) { combinedUserOperations.Add(Tuple.Create(entryPoint, userOperation)); } } IList <Tuple <Address, UserOperation> > sortedUserOperations = combinedUserOperations.OrderByDescending( op => CalculateUserOperationPremiumGasPrice(op.Item2, parent.BaseFeePerGas)) .ToList(); foreach (Tuple <Address, UserOperation> addressedUserOperation in sortedUserOperations) { (Address entryPoint, UserOperation userOperation) = addressedUserOperation; ulong userOperationTotalGasLimit = (ulong)userOperation.CallGas + (ulong)userOperation.PreVerificationGas + (ulong)userOperation.VerificationGas; if (gasUsed + userOperationTotalGasLimit > (ulong)gasLimit) { continue; } // no intersect of accessed addresses between ops if (userOperation.AccessList.AccessListOverlaps(usedAccessList)) { continue; } // simulate again to make sure the op is still valid ResultWrapper <Keccak> result = _userOperationSimulators[entryPoint].Simulate(userOperation, parent); if (result.Result != Result.Success) { //if (_logger.IsDebug) commented out for testing { _logger.Debug($"UserOperation {userOperation.Hash} resimulation unsuccessful: {result.Result.Error}"); // TODO: Remove logging, just for testing _logger.Info($"UserOperation {userOperation.Hash} resimulation unsuccessful: {result.Result.Error}"); bool removeResult = _userOperationPools[entryPoint].RemoveUserOperation(userOperation.Hash); if (_logger.IsDebug) { _logger.Debug( removeResult ? "Removed UserOperation {userOperation.Hash} from Pool" : "Failed to remove UserOperation {userOperation} from Pool"); } } continue; } gasUsed += userOperationTotalGasLimit; // add user operation with correct entryPoint if (userOperationsToIncludeByEntryPoint.TryGetValue(entryPoint, out IList <UserOperation>?userOperations)) { userOperations.Add(userOperation); } else { userOperationsToIncludeByEntryPoint[entryPoint] = new List <UserOperation> { userOperation }; } // add userOp accessList to combined list foreach (KeyValuePair <Address, HashSet <UInt256> > kv in userOperation.AccessList.Data) { if (usedAccessList.ContainsKey(kv.Key)) { usedAccessList[kv.Key].UnionWith(kv.Value); } else { usedAccessList[kv.Key] = kv.Value; } } } if (userOperationsToIncludeByEntryPoint.Count == 0) { yield break; } UInt256 initialNonce = _stateProvider.GetNonce(_signer.Address); UInt256 txsBuilt = 0; // build transaction for each entryPoint with ops to be included foreach (KeyValuePair <Address, UserOperationTxBuilder> kv in _userOperationTxBuilders) { Address entryPoint = kv.Key; IUserOperationTxBuilder txBuilder = kv.Value; bool foundUserOperations = userOperationsToIncludeByEntryPoint.TryGetValue(entryPoint, out IList <UserOperation>?userOperationsToInclude); if (!foundUserOperations) { continue; } long totalGasUsed = userOperationsToInclude !.Aggregate((long)0, (sum, op) => sum + (long)op.CallGas + (long)op.PreVerificationGas + (long)op.VerificationGas); // build test transaction to make sure it succeeds as a batch of ops Transaction userOperationTransaction = txBuilder.BuildTransactionFromUserOperations( userOperationsToInclude !, parent, totalGasUsed, initialNonce, _specProvider.GetSpec(parent.Number + 1)); if (_logger.IsDebug) { _logger.Debug($"Constructed tx from {userOperationsToInclude!.Count} userOperations: {userOperationTransaction.Hash}"); } // TODO: Remove logging, just for testing _logger.Info($"Constructed tx from {userOperationsToInclude!.Count} userOperations: {userOperationTransaction.Hash}"); BlockchainBridge.CallOutput callOutput = _userOperationSimulators[entryPoint].EstimateGas(parent, userOperationTransaction, CancellationToken.None); FailedOp?failedOp = txBuilder.DecodeEntryPointOutputError(callOutput.OutputData); if (failedOp is not null || callOutput.Error != null) { // TODO punish paymaster continue; } // construct tx with previously estimated gas limit Transaction updatedUserOperationTransaction = _userOperationTxBuilders[entryPoint].BuildTransactionFromUserOperations( userOperationsToInclude, parent, callOutput.GasSpent + 200000, initialNonce + txsBuilt, _specProvider.GetSpec(parent.Number + 1)); txsBuilt++; yield return(updatedUserOperationTransaction); } }
private List <string> RunAssertions(LegacyBlockchainTest test, Block headBlock, IStorageProvider storageProvider, IStateProvider stateProvider) { if (test.PostStateRoot != null) { return(test.PostStateRoot != stateProvider.StateRoot ? new List <string> { "state root mismatch" } : Enumerable.Empty <string>().ToList()); } TestBlockHeaderJson testHeaderJson = test.Blocks .Where(b => b.BlockHeader != null) .SingleOrDefault(b => new Keccak(b.BlockHeader.Hash) == headBlock.Hash)?.BlockHeader ?? test.GenesisBlockHeader; BlockHeader testHeader = JsonToBlockchainTest.Convert(testHeaderJson); List <string> differences = new List <string>(); var deletedAccounts = test.Pre.Where(pre => !test.PostState.ContainsKey(pre.Key)); foreach (KeyValuePair <Address, AccountState> deletedAccount in deletedAccounts) { if (stateProvider.AccountExists(deletedAccount.Key)) { differences.Add($"Pre state account {deletedAccount.Key} was not deleted as expected."); } } foreach (KeyValuePair <Address, AccountState> accountState in test.PostState) { int differencesBefore = differences.Count; if (differences.Count > 8) { Console.WriteLine("More than 8 differences..."); break; } bool accountExists = stateProvider.AccountExists(accountState.Key); BigInteger?balance = accountExists ? stateProvider.GetBalance(accountState.Key) : (BigInteger?)null; BigInteger?nonce = accountExists ? stateProvider.GetNonce(accountState.Key) : (BigInteger?)null; if (accountState.Value.Balance != balance) { differences.Add($"{accountState.Key} balance exp: {accountState.Value.Balance}, actual: {balance}, diff: {balance - accountState.Value.Balance}"); } if (accountState.Value.Nonce != nonce) { differences.Add($"{accountState.Key} nonce exp: {accountState.Value.Nonce}, actual: {nonce}"); } byte[] code = accountExists ? stateProvider.GetCode(accountState.Key) : new byte[0]; if (!Bytes.AreEqual(accountState.Value.Code, code)) { differences.Add($"{accountState.Key} code exp: {accountState.Value.Code?.Length}, actual: {code?.Length}"); } if (differences.Count != differencesBefore) { _logger.Info($"ACCOUNT STATE ({accountState.Key}) HAS DIFFERENCES"); } differencesBefore = differences.Count; KeyValuePair <UInt256, byte[]>[] clearedStorages = new KeyValuePair <UInt256, byte[]> [0]; if (test.Pre.ContainsKey(accountState.Key)) { clearedStorages = test.Pre[accountState.Key].Storage.Where(s => !accountState.Value.Storage.ContainsKey(s.Key)).ToArray(); } foreach (KeyValuePair <UInt256, byte[]> clearedStorage in clearedStorages) { byte[] value = !stateProvider.AccountExists(accountState.Key) ? Bytes.Empty : storageProvider.Get(new StorageAddress(accountState.Key, clearedStorage.Key)); if (!value.IsZero()) { differences.Add($"{accountState.Key} storage[{clearedStorage.Key}] exp: 0x00, actual: {value.ToHexString(true)}"); } } foreach (KeyValuePair <UInt256, byte[]> storageItem in accountState.Value.Storage) { byte[] value = !stateProvider.AccountExists(accountState.Key) ? Bytes.Empty : storageProvider.Get(new StorageAddress(accountState.Key, storageItem.Key)) ?? new byte[0]; if (!Bytes.AreEqual(storageItem.Value, value)) { differences.Add($"{accountState.Key} storage[{storageItem.Key}] exp: {storageItem.Value.ToHexString(true)}, actual: {value.ToHexString(true)}"); } } if (differences.Count != differencesBefore) { _logger.Info($"ACCOUNT STORAGE ({accountState.Key}) HAS DIFFERENCES"); } } BigInteger gasUsed = headBlock.Header.GasUsed; if ((testHeader?.GasUsed ?? 0) != gasUsed) { differences.Add($"GAS USED exp: {testHeader?.GasUsed ?? 0}, actual: {gasUsed}"); } if (headBlock.Transactions.Any() && testHeader.Bloom.ToString() != headBlock.Header.Bloom.ToString()) { differences.Add($"BLOOM exp: {testHeader.Bloom}, actual: {headBlock.Header.Bloom}"); } if (testHeader.StateRoot != stateProvider.StateRoot) { differences.Add($"STATE ROOT exp: {testHeader.StateRoot}, actual: {stateProvider.StateRoot}"); } if (testHeader.TxRoot != headBlock.Header.TxRoot) { differences.Add($"TRANSACTIONS ROOT exp: {testHeader.TxRoot}, actual: {headBlock.Header.TxRoot}"); } if (testHeader.ReceiptsRoot != headBlock.Header.ReceiptsRoot) { differences.Add($"RECEIPT ROOT exp: {testHeader.ReceiptsRoot}, actual: {headBlock.Header.ReceiptsRoot}"); } if (test.LastBlockHash != headBlock.Hash) { differences.Add($"LAST BLOCK HASH exp: {test.LastBlockHash}, actual: {headBlock.Hash}"); } foreach (string difference in differences) { _logger.Info(difference); } return(differences); }
public BigInteger GetNonce(Address address) { return(_stateProvider.GetNonce(address)); }
public IEnumerable <Transaction> SelectTransactions(long gasLimit) { UInt256 GetCurrentNonce(IDictionary <Address, UInt256> noncesDictionary, Address address) { if (!noncesDictionary.TryGetValue(address, out var nonce)) { noncesDictionary[address] = nonce = _stateProvider.GetNonce(address); } return(nonce); } UInt256 GetRemainingBalance(IDictionary <Address, UInt256> balances, Address address) { if (!balances.TryGetValue(address, out var balance)) { balances[address] = balance = _stateProvider.GetBalance(address); } return(balance); } bool HasEnoughFounds(IDictionary <Address, UInt256> balances, Transaction transaction) { var balance = GetRemainingBalance(balances, transaction.SenderAddress); var transactionPotentialCost = transaction.GasPrice * (ulong)transaction.GasLimit + transaction.Value; if (balance < transactionPotentialCost) { if (_logger.IsTrace) { _logger.Trace($"Rejecting transaction - transaction cost ({transactionPotentialCost}) is higher than sender balance ({balance})."); } return(false); } balances[transaction.SenderAddress] = balance - transactionPotentialCost; return(true); } var pendingTransactions = _transactionPool.GetPendingTransactions(); var transactions = pendingTransactions.OrderBy(t => t.Nonce).ThenByDescending(t => t.GasPrice).ThenBy(t => t.GasLimit); IDictionary <Address, UInt256> remainingBalance = new Dictionary <Address, UInt256>(); Dictionary <Address, UInt256> nonces = new Dictionary <Address, UInt256>(); List <Transaction> selected = new List <Transaction>(); long gasRemaining = gasLimit; if (_logger.IsDebug) { _logger.Debug($"Collecting pending transactions at min gas price {_minGasPriceForMining} and block gas limit {gasRemaining}."); } foreach (Transaction tx in transactions) { if (gasRemaining < Transaction.BaseTxGasCost) { continue; } if (tx.GasLimit > gasRemaining) { if (_logger.IsInfo) { _logger.Info($"Rejecting (tx gas limit {tx.GasLimit} above remaining block gas {gasRemaining}) {tx.ToShortString()}"); } continue; } if (tx.SenderAddress == null) { _transactionPool.RemoveTransaction(tx.Hash, 0); if (_logger.IsInfo) { _logger.Info($"Rejecting (null sender) {tx.ToShortString()}"); } continue; } if (tx.GasPrice < _minGasPriceForMining) { if (_logger.IsInfo) { _logger.Info($"Rejecting (gas price too low - min gas price: {_minGasPriceForMining}) {tx.ToShortString()}"); } continue; } UInt256 expectedNonce = GetCurrentNonce(nonces, tx.SenderAddress); if (expectedNonce != tx.Nonce) { if (tx.Nonce < expectedNonce) { _transactionPool.RemoveTransaction(tx.Hash, 0); } if (tx.Nonce > expectedNonce + 16) { _transactionPool.RemoveTransaction(tx.Hash, 0); } if (_logger.IsInfo) { _logger.Info($"Rejecting (invalid nonce - expected {expectedNonce}) {tx.ToShortString()}"); } continue; } if (!HasEnoughFounds(remainingBalance, tx)) { if (_logger.IsInfo) { _logger.Info($"Rejecting (sender balance too low) {tx.ToShortString()}"); } continue; } selected.Add(tx); nonces[tx.SenderAddress] = tx.Nonce + 1; gasRemaining -= tx.GasLimit; } if (_logger.IsDebug) { _logger.Debug($"Collected {selected.Count} out of {pendingTransactions.Length} pending transactions."); } return(selected); }
public void Execute(Transaction transaction, BlockHeader block, ITxTracer txTracer, bool readOnly) { IReleaseSpec spec = _specProvider.GetSpec(block.Number); Address recipient = transaction.To; UInt256 value = transaction.Value; UInt256 gasPrice = transaction.GasPrice; long gasLimit = (long)transaction.GasLimit; byte[] machineCode = transaction.Init; byte[] data = transaction.Data ?? Bytes.Empty; Address sender = transaction.SenderAddress; if (_logger.IsTrace) { _logger.Trace($"Executing tx {transaction.Hash}"); } if (sender == null) { TraceLogInvalidTx(transaction, "SENDER_NOT_SPECIFIED"); if (txTracer.IsTracingReceipt) { txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit); } return; } long intrinsicGas = _intrinsicGasCalculator.Calculate(transaction, spec); if (_logger.IsTrace) { _logger.Trace($"Intrinsic gas calculated for {transaction.Hash}: " + intrinsicGas); } if (gasLimit < intrinsicGas) { TraceLogInvalidTx(transaction, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {gasLimit} < {intrinsicGas}"); if (txTracer.IsTracingReceipt) { txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit); } return; } if (gasLimit > block.GasLimit - block.GasUsed) { TraceLogInvalidTx(transaction, $"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}"); if (txTracer.IsTracingReceipt) { txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit); } return; } if (!_stateProvider.AccountExists(sender)) { TraceLogInvalidTx(transaction, $"SENDER_ACCOUNT_DOES_NOT_EXIST {sender}"); if (gasPrice == UInt256.Zero) { _stateProvider.CreateAccount(sender, UInt256.Zero); } } UInt256 senderBalance = _stateProvider.GetBalance(sender); if ((ulong)intrinsicGas * gasPrice + value > senderBalance) { TraceLogInvalidTx(transaction, $"INSUFFICIENT_SENDER_BALANCE: ({sender})_BALANCE = {senderBalance}"); if (txTracer.IsTracingReceipt) { txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit); } return; } if (transaction.Nonce != _stateProvider.GetNonce(sender)) { TraceLogInvalidTx(transaction, $"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(sender)})"); if (txTracer.IsTracingReceipt) { txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit); } return; } _stateProvider.IncrementNonce(sender); _stateProvider.SubtractFromBalance(sender, (ulong)gasLimit * gasPrice, spec); // TODO: I think we can skip this commit and decrease the tree operations this way _stateProvider.Commit(_specProvider.GetSpec(block.Number), txTracer.IsTracingState ? txTracer : null); long unspentGas = gasLimit - intrinsicGas; long spentGas = gasLimit; int stateSnapshot = _stateProvider.TakeSnapshot(); int storageSnapshot = _storageProvider.TakeSnapshot(); _stateProvider.SubtractFromBalance(sender, value, spec); byte statusCode = StatusCode.Failure; TransactionSubstate substate = null; try { if (transaction.IsContractCreation) { recipient = Address.OfContract(sender, _stateProvider.GetNonce(sender) - 1); if (_stateProvider.AccountExists(recipient)) { if ((_virtualMachine.GetCachedCodeInfo(recipient)?.MachineCode?.Length ?? 0) != 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); } } bool isPrecompile = recipient.IsPrecompiled(spec); ExecutionEnvironment env = new ExecutionEnvironment(); env.Value = value; env.TransferValue = value; env.Sender = sender; env.ExecutingAccount = recipient; env.CurrentBlock = block; env.GasPrice = gasPrice; env.InputData = data ?? new byte[0]; env.CodeInfo = isPrecompile ? new CodeInfo(recipient) : machineCode == null?_virtualMachine.GetCachedCodeInfo(recipient) : new CodeInfo(machineCode); env.Originator = sender; ExecutionType executionType = transaction.IsContractCreation ? ExecutionType.Create : ExecutionType.Call; using (EvmState state = new EvmState(unspentGas, env, executionType, isPrecompile, true, false)) { substate = _virtualMachine.Run(state, spec, 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 = substate.Output.Length * GasCostOf.CodeDeposit; if (spec.IsEip170Enabled && substate.Output.Length > 0x6000) { codeDepositGasCost = long.MaxValue; } if (unspentGas < codeDepositGasCost && spec.IsEip2Enabled) { 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}"); } _stateProvider.DeleteAccount(toBeDestroyed); } statusCode = StatusCode.Success; } spentGas = Refund(gasLimit, unspentGas, substate, sender, 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 (!_stateProvider.AccountExists(gasBeneficiary)) { _stateProvider.CreateAccount(gasBeneficiary, (ulong)spentGas * gasPrice); } else { _stateProvider.AddToBalance(gasBeneficiary, (ulong)spentGas * gasPrice, spec); } } if (!readOnly) { _storageProvider.Commit(txTracer.IsTracingState ? txTracer : null); _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : null); } else { _storageProvider.Reset(); _stateProvider.Reset(); } if (!readOnly) { block.GasUsed += spentGas; } if (txTracer.IsTracingReceipt) { if (statusCode == StatusCode.Failure) { txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit); } else { txTracer.MarkAsSuccess(recipient, spentGas, substate.Output, substate.Logs.Any() ? substate.Logs.ToArray() : LogEntry.EmptyLogs); } } }
private void RunAssertions(BlockchainTest test, Block headBlock, IStorageProvider storageProvider, IStateProvider stateProvider) { TestBlockHeaderJson testHeaderJson = test.Blocks .Where(b => b.BlockHeader != null) .SingleOrDefault(b => new Keccak(b.BlockHeader.Hash) == headBlock.Hash)?.BlockHeader ?? test.GenesisBlockHeader; BlockHeader testHeader = Convert(testHeaderJson); List <string> differences = new List <string>(); foreach (KeyValuePair <Address, AccountState> accountState in test.PostState) { int differencesBefore = differences.Count; if (differences.Count > 8) { Console.WriteLine("More than 8 differences..."); break; } bool accountExists = stateProvider.AccountExists(accountState.Key); BigInteger?balance = accountExists ? stateProvider.GetBalance(accountState.Key) : (BigInteger?)null; BigInteger?nonce = accountExists ? stateProvider.GetNonce(accountState.Key) : (BigInteger?)null; if (accountState.Value.Balance != balance) { differences.Add($"{accountState.Key} balance exp: {accountState.Value.Balance}, actual: {balance}, diff: {balance - accountState.Value.Balance}"); } if (accountState.Value.Nonce != nonce) { differences.Add($"{accountState.Key} nonce exp: {accountState.Value.Nonce}, actual: {nonce}"); } byte[] code = accountExists ? stateProvider.GetCode(accountState.Key) : new byte[0]; if (!Bytes.UnsafeCompare(accountState.Value.Code, code)) { differences.Add($"{accountState.Key} code exp: {accountState.Value.Code?.Length}, actual: {code?.Length}"); } if (differences.Count != differencesBefore) { _logger.Info($"ACCOUNT STATE ({accountState.Key}) HAS DIFFERENCES"); } differencesBefore = differences.Count; foreach (KeyValuePair <BigInteger, byte[]> storageItem in accountState.Value.Storage) { byte[] value = storageProvider.Get(new StorageAddress(accountState.Key, storageItem.Key)) ?? new byte[0]; if (!Bytes.UnsafeCompare(storageItem.Value, value)) { differences.Add($"{accountState.Key} storage[{storageItem.Key}] exp: {Hex.FromBytes(storageItem.Value, true)}, actual: {Hex.FromBytes(value, true)}"); } } if (differences.Count != differencesBefore) { _logger.Info($"ACCOUNT STORAGE ({accountState.Key}) HAS DIFFERENCES"); } } BigInteger gasUsed = headBlock.Header.GasUsed; if ((testHeader?.GasUsed ?? 0) != gasUsed) { differences.Add($"GAS USED exp: {testHeader?.GasUsed ?? 0}, actual: {gasUsed}"); } if (headBlock.Transactions.Any() && testHeader.Bloom.ToString() != headBlock.Header.Bloom.ToString()) { differences.Add($"BLOOM exp: {testHeader.Bloom}, actual: {headBlock.Header.Bloom}"); } if (testHeader.StateRoot != stateProvider.StateRoot) { differences.Add($"STATE ROOT exp: {testHeader.StateRoot}, actual: {stateProvider.StateRoot}"); } if (testHeader.TransactionsRoot != headBlock.Header.TransactionsRoot) { differences.Add($"TRANSACTIONS ROOT exp: {testHeader.TransactionsRoot}, actual: {headBlock.Header.TransactionsRoot}"); } if (testHeader.ReceiptsRoot != headBlock.Header.ReceiptsRoot) { differences.Add($"RECEIPT ROOT exp: {testHeader.ReceiptsRoot}, actual: {headBlock.Header.ReceiptsRoot}"); } if (test.LastBlockHash != headBlock.Hash) { differences.Add($"LAST BLOCK HASH exp: {test.LastBlockHash}, actual: {headBlock.Hash}"); } foreach (string difference in differences) { _logger.Info(difference); } Assert.Zero(differences.Count, "differences"); }