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 UpdateLedgerFromDelta(Cid deltaHash) { var stateSnapshot = _stateDb.TakeSnapshot(); if (stateSnapshot != -1) { if (_logger.IsEnabled(LogEventLevel.Error)) { _logger.Error("Uncommitted state ({stateSnapshot}) when processing from a branch root {branchStateRoot} starting with delta {deltaHash}", stateSnapshot, null, deltaHash); } } var snapshotStateRoot = _stateProvider.StateRoot; try { if (!_synchroniser.DeltaCache.TryGetOrAddConfirmedDelta(deltaHash, out var nextDeltaInChain)) { _logger.Warning("Failed to retrieve Delta with hash {hash} from the Dfs, ledger has not been updated.", deltaHash); return; } Cid parentCid = Cid.Read(nextDeltaInChain.PreviousDeltaDfsHash.ToByteArray()); if (!_synchroniser.DeltaCache.TryGetOrAddConfirmedDelta(parentCid, out Delta parentDelta)) { _logger.Warning("Failed to retrieve parent Delta with hash {hash} from the Dfs, ledger has not been updated.", deltaHash); return; } ReceiptDeltaTracer tracer = new ReceiptDeltaTracer(nextDeltaInChain, deltaHash); // add here a receipts tracer or similar, depending on what data needs to be stored for each contract _stateProvider.Reset(); _storageProvider.Reset(); _stateProvider.StateRoot = new Keccak(parentDelta.StateRoot?.ToByteArray()); _deltaExecutor.Execute(nextDeltaInChain, tracer); // store receipts if (tracer.Receipts.Any()) { _receipts.Put(deltaHash, tracer.Receipts.ToArray(), nextDeltaInChain.PublicEntries.ToArray()); } _stateDb.Commit(); _latestKnownDelta = deltaHash; WriteLatestKnownDelta(deltaHash); } catch { Restore(stateSnapshot, snapshotStateRoot); } }
public Block[] Process(Keccak branchStateRoot, Block[] suggestedBlocks, ProcessingOptions options, IBlockTracer blockTracer) { if (_logger.IsTrace) { _logger.Trace($"Processing block {suggestedBlocks[0].Number} from state root: {branchStateRoot}"); } if (suggestedBlocks.Length == 0) { return(Array.Empty <Block>()); } int stateSnapshot = _stateDb.TakeSnapshot(); int codeSnapshot = _codeDb.TakeSnapshot(); Keccak snapshotStateRoot = _stateProvider.StateRoot; if (branchStateRoot != null && _stateProvider.StateRoot != branchStateRoot) { /* discarding the other branch data - chain reorganization */ Metrics.Reorganizations++; _storageProvider.Reset(); _stateProvider.Reset(); _stateProvider.StateRoot = branchStateRoot; } var processedBlocks = new Block[suggestedBlocks.Length]; try { for (int i = 0; i < suggestedBlocks.Length; i++) { processedBlocks[i] = ProcessOne(suggestedBlocks[i], options, blockTracer); if (_logger.IsTrace) { _logger.Trace($"Committing trees - state root {_stateProvider.StateRoot}"); } _stateProvider.CommitTree(); _storageProvider.CommitTrees(); } if ((options & ProcessingOptions.ReadOnlyChain) != 0) { Restore(stateSnapshot, codeSnapshot, snapshotStateRoot); } else { _stateDb.Commit(); _codeDb.Commit(); } return(processedBlocks); } catch (InvalidBlockException) { Restore(stateSnapshot, codeSnapshot, snapshotStateRoot); throw; } }
public Keccak SendTransaction(Transaction transaction) { _stateProvider.StateRoot = _blockTree.Head.StateRoot; transaction.Hash = Transaction.CalculateHash(transaction); transaction.Timestamp = _timestamp.EpochSeconds; _transactionPool.AddTransaction(transaction, _blockTree.Head.Number); _stateProvider.Reset(); return(transaction.Hash); }
private void InitBranch(Keccak branchStateRoot) { /* Please note that we do not reset the state if branch state root is null. * That said, I do not remember in what cases we receive null here.*/ if (branchStateRoot != null && _stateProvider.StateRoot != branchStateRoot) { /* Discarding the other branch data - chain reorganization. * We cannot use cached values any more because they may have been written * by blocks that are being reorganized out.*/ Metrics.Reorganizations++; _storageProvider.Reset(); _stateProvider.Reset(); _stateProvider.StateRoot = branchStateRoot; } }
public void Dispose() { _stateProvider.StateRoot = _stateBefore; _stateProvider.Reset(); _storageProvider.Reset(); _codeDb.ClearTempChanges(); }
private void InitializeTestState(BlockchainTest test, IStateProvider stateProvider, IStorageProvider storageProvider, ISpecProvider specProvider) { foreach (KeyValuePair <Address, AccountState> accountState in ((IEnumerable <KeyValuePair <Address, AccountState> >)test.Pre ?? Array.Empty <KeyValuePair <Address, AccountState> >())) { foreach (KeyValuePair <UInt256, byte[]> storageItem in accountState.Value.Storage) { storageProvider.Set(new StorageCell(accountState.Key, storageItem.Key), storageItem.Value); } stateProvider.CreateAccount(accountState.Key, accountState.Value.Balance); Keccak codeHash = stateProvider.UpdateCode(accountState.Value.Code); stateProvider.UpdateCodeHash(accountState.Key, codeHash, specProvider.GenesisSpec); for (int i = 0; i < accountState.Value.Nonce; i++) { stateProvider.IncrementNonce(accountState.Key); } } storageProvider.Commit(); stateProvider.Commit(specProvider.GenesisSpec); storageProvider.CommitTrees(0); stateProvider.CommitTree(0); storageProvider.Reset(); stateProvider.Reset(); }
private void CallAndRestore(BlockHeader blockHeader, Transaction transaction, UInt256 timestamp, ITxTracer tracer) { if (transaction.SenderAddress == null) { transaction.SenderAddress = Address.SystemUser; } _stateProvider.StateRoot = blockHeader.StateRoot; try { if (transaction.Nonce == 0) { transaction.Nonce = GetNonce(_stateProvider.StateRoot, transaction.SenderAddress); } BlockHeader callHeader = new BlockHeader( blockHeader.Hash, Keccak.OfAnEmptySequenceRlp, Address.Zero, 0, blockHeader.Number + 1, blockHeader.GasLimit, timestamp, Bytes.Empty); transaction.Hash = transaction.CalculateHash(); _transactionProcessor.CallAndRestore(transaction, callHeader, tracer); } finally { _stateProvider.Reset(); _storageProvider.Reset(); } }
public void Commit_trees_clear_caches_get_previous_root() { // block 1 StorageProvider storageProvider = BuildStorageProvider(); storageProvider.Set(new StorageCell(_address1, 1), _values[1]); storageProvider.Commit(); _stateProvider.Commit(Frontier.Instance); storageProvider.CommitTrees(); _stateProvider.CommitTree(); // block 2 Keccak stateRoot = _stateProvider.StateRoot; storageProvider.Set(new StorageCell(_address1, 1), _values[2]); storageProvider.Commit(); _stateProvider.Commit(Frontier.Instance); // revert _stateProvider.Reset(); storageProvider.Reset(); _stateProvider.StateRoot = stateRoot; byte[] valueAfter = storageProvider.Get(new StorageCell(_address1, 1)); Assert.AreEqual(_values[1], valueAfter); }
private void InitializeTestState(GeneralStateTest test, IStateProvider stateProvider, IStorageProvider storageProvider, ISpecProvider specProvider) { foreach (KeyValuePair <Address, AccountState> accountState in test.Pre) { foreach (KeyValuePair <UInt256, byte[]> storageItem in accountState.Value.Storage) { storageProvider.Set(new StorageCell(accountState.Key, storageItem.Key), storageItem.Value.WithoutLeadingZeros().ToArray()); } stateProvider.CreateAccount(accountState.Key, accountState.Value.Balance); Keccak codeHash = stateProvider.UpdateCode(accountState.Value.Code); stateProvider.UpdateCodeHash(accountState.Key, codeHash, specProvider.GenesisSpec); for (int i = 0; i < accountState.Value.Nonce; i++) { stateProvider.IncrementNonce(accountState.Key); } } storageProvider.Commit(); stateProvider.Commit(specProvider.GenesisSpec); storageProvider.CommitTrees(); stateProvider.CommitTree(); storageProvider.Reset(); stateProvider.Reset(); }
/// <summary> /// Resets state provider to state. /// </summary> /// <param name="stateProvider"></param> /// <param name="state"></param> /// <returns>Current state provider state before reset.</returns> public static Keccak ResetStateTo(this IStateProvider stateProvider, Keccak state) { Keccak currentState = stateProvider.StateRoot; stateProvider.Reset(); stateProvider.StateRoot = state; stateProvider.RecalculateStateRoot(); return(currentState); }
public Keccak SendTransaction(Transaction transaction, bool isOwn = false) { _stateProvider.StateRoot = _blockTree.Head.StateRoot; transaction.Hash = Transaction.CalculateHash(transaction); transaction.Timestamp = _timestamper.EpochSeconds; var result = _txPool.AddTransaction(transaction, _blockTree.Head.Number, isOwn); if (isOwn && result == AddTxResult.OwnNonceAlreadyUsed) { transaction.Nonce = _txPool.ReserveOwnTransactionNonce(transaction.SenderAddress); Sign(transaction); transaction.Hash = Transaction.CalculateHash(transaction); _txPool.AddTransaction(transaction, _blockTree.Head.Number, true); } _stateProvider.Reset(); return(transaction.Hash); }
private void Execute(Delta delta, ITxTracer txTracer, bool readOnly) { var stateUpdate = ToStateUpdate(delta); // revert state if any fails (take snapshot) foreach (var publicEntry in delta.PublicEntries) { Execute(publicEntry, stateUpdate, txTracer, readOnly); } if (!readOnly) { // we should assign block rewards here (or in Ledger) _stateProvider.CommitTree(); _storageProvider.CommitTrees(); } else { _storageProvider.Reset(); _stateProvider.Reset(); } }
public Keccak SendTransaction(Transaction tx, TxHandlingOptions txHandlingOptions) { _stateProvider.StateRoot = _blockTree.Head.StateRoot; try { if (tx.Signature == null) { if (_wallet.IsUnlocked(tx.SenderAddress)) { Sign(tx); } else { throw new SecurityException("Your account is locked. Unlock the account via CLI, personal_unlockAccount or use Trusted Signer."); } } tx.Hash = tx.CalculateHash(); tx.Timestamp = _timestamper.EpochSeconds; AddTxResult result = _txPool.AddTransaction(tx, _blockTree.Head.Number, txHandlingOptions); if (result == AddTxResult.OwnNonceAlreadyUsed && (txHandlingOptions & TxHandlingOptions.ManagedNonce) == TxHandlingOptions.ManagedNonce) { // below the temporary NDM support - needs some review tx.Nonce = _txPool.ReserveOwnTransactionNonce(tx.SenderAddress); Sign(tx); tx.Hash = tx.CalculateHash(); _txPool.AddTransaction(tx, _blockTree.Head.Number, txHandlingOptions); } return(tx.Hash); } finally { _stateProvider.Reset(); } }
public void Execute(DeltaBuilderContext context) { var previousRoot = context.PreviousDelta.StateRoot; Keccak stateRoot = previousRoot.IsEmpty ? Keccak.EmptyTreeHash : new Keccak(previousRoot.ToByteArray()); if (_logger.IsEnabled(LogEventLevel.Debug)) { _logger.Error($"Running state calculation for delta {context.ProducedDelta.DeltaNumber}"); } // here we need a read only delta executor (like in block builders - everything reverts in the end) _stateProvider.StateRoot = stateRoot; _deltaExecutor.CallAndReset(context.ProducedDelta, NullTxTracer.Instance); _stateProvider.Reset(); }
private void Restore(int stateSnapshot, int codeSnapshot, Keccak snapshotStateRoot) { if (_logger.IsEnabled(LogEventLevel.Verbose)) { _logger.Verbose("Reverting deltas {stateRoot}", _stateProvider.StateRoot); } _stateDb.Restore(stateSnapshot); _codeDb.Restore(codeSnapshot); _storageProvider.Reset(); _stateProvider.Reset(); _stateProvider.StateRoot = snapshotStateRoot; if (_logger.IsEnabled(LogEventLevel.Verbose)) { _logger.Verbose("Reverted deltas {stateRoot}", _stateProvider.StateRoot); } }
public Block?Trace(Block block, IBlockTracer blockTracer) { try { blockTracer.StartNewBlockTrace(block); /* We force process since we want to process a block that has already been processed in the past and normally it would be ignored. * We also want to make it read only so the state is not modified persistently in any way. */ Block?processedBlock = _blockProcessor.Process(block, ProcessingOptions.ForceProcessing | ProcessingOptions.ReadOnlyChain | ProcessingOptions.NoValidation, blockTracer); blockTracer.EndBlockTrace(); return(processedBlock); } catch (Exception) { _stateProvider.Reset(); throw; } }
public Keccak Trace(Block block, IBlockTracer blockTracer) { /* We force process since we want to process a block that has already been processed in the past and normally it would be ignored. * We also want to make it read only so the state is not modified persistently in any way. */ blockTracer.StartNewBlockTrace(block); try { _blockProcessor.Process(block, ProcessingOptions.Trace, blockTracer); } catch (Exception) { _stateProvider.Reset(); throw; } return(_stateProvider.StateRoot); }
private void Execute(Delta delta, ITxTracer txTracer, bool readOnly) { var stateUpdate = ToStateUpdate(delta); // revert state if any fails (take snapshot) foreach (var publicEntry in delta.PublicEntries) { Execute(publicEntry, stateUpdate, txTracer); } var spec = _specProvider.GetSpec(stateUpdate.Number); _storageProvider.Commit(txTracer.IsTracingState ? txTracer : null); _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : null); _stateProvider.RecalculateStateRoot(); if (!readOnly) { if (new Keccak(delta.StateRoot.ToByteArray()) != _stateProvider.StateRoot) { if (_logger.IsEnabled(LogEventLevel.Error)) { _logger.Error("Invalid delta state root - found {found} and should be {shouldBe}", _stateProvider.StateRoot, new Keccak(delta.StateRoot.ToByteArray())); } } // compare state roots _storageProvider.CommitTrees(); _stateProvider.CommitTree(); } else { delta.StateRoot = _stateProvider.StateRoot.ToByteString(); if (_logger.IsEnabled(LogEventLevel.Debug)) { _logger.Debug($"Setting candidate delta {delta.DeltaNumber} root to {delta.StateRoot.ToKeccak()}"); } _stateProvider.Reset(); _storageProvider.Reset(); } }
public Block[] Process(Keccak branchStateRoot, Block[] suggestedBlocks, bool tryOnly) { if (suggestedBlocks.Length == 0) { return(Array.Empty <Block>()); } IDb db = _dbProvider.GetOrCreateStateDb(); int dbSnapshot = _dbProvider.TakeSnapshot(); Keccak snapshotStateRoot = _stateProvider.StateRoot; if (branchStateRoot != null && _stateProvider.StateRoot != branchStateRoot) { // discarding one of the branches _storageProvider.ClearCaches(); _stateProvider.Reset(); _stateProvider.StateRoot = branchStateRoot; } Block[] processedBlocks = new Block[suggestedBlocks.Length]; try { for (int i = 0; i < suggestedBlocks.Length; i++) { processedBlocks[i] = ProcessOne(suggestedBlocks[i], tryOnly); } if (tryOnly) { if (_logger.IsTrace) { _logger.Trace($"REVERTING BLOCKS - STATE ROOT {_stateProvider.StateRoot}"); } _dbProvider.Restore(dbSnapshot); _storageProvider.ClearCaches(); _stateProvider.Reset(); _stateProvider.StateRoot = snapshotStateRoot; if (_logger.IsTrace) { _logger.Trace($"REVERTED BLOCKS (JUST VALIDATED FOR MINING) - STATE ROOT {_stateProvider.StateRoot}"); } } else { db.StartBatch(); _dbProvider.Commit(_specProvider.GetSpec(suggestedBlocks[0].Number)); db.CommitBatch(); } return(processedBlocks); } catch (InvalidBlockException) // TODO: which exception to catch here? { if (_logger.IsTrace) { _logger.Trace($"REVERTING BLOCKS - STATE ROOT {_stateProvider.StateRoot}"); } _dbProvider.Restore(dbSnapshot); _storageProvider.ClearCaches(); _stateProvider.Reset(); _stateProvider.StateRoot = snapshotStateRoot; if (_logger.IsTrace) { _logger.Trace($"REVERTED BLOCKS - STATE ROOT {_stateProvider.StateRoot}"); } if (_logger.IsWarn) { _logger.Warn($"Invalid block"); } throw; } }
public void Dispose() { _stateProvider.StateRoot = _stateBefore; _stateProvider.Reset(); _storageProvider.Reset(); }
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); } Address recipient = transaction.To; UInt256 value = transaction.Value; UInt256 gasPrice = transaction.GasPrice; long gasLimit = transaction.GasLimit; byte[] machineCode = transaction.Init; byte[] data = transaction.Data ?? Array.Empty <byte>(); Address sender = transaction.SenderAddress; if (_logger.IsTrace) { _logger.Trace($"Executing tx {transaction.Hash}"); } if (sender == 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.GasLimit - block.GasUsed) { TraceLogInvalidTx(transaction, $"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}"); QuickFail(transaction, block, txTracer, "block gas limit exceeded"); return; } } if (!_stateProvider.AccountExists(sender)) { // hacky fix for the potential recovery issue if (transaction.Signature != null) { transaction.SenderAddress = _ecdsa.RecoverAddress(transaction, !spec.ValidateChainId); } if (sender != transaction.SenderAddress) { if (_logger.IsWarn) { _logger.Warn($"TX recovery issue fixed - tx was coming with sender {sender} and the now it recovers to {transaction.SenderAddress}"); } sender = transaction.SenderAddress; } else { TraceLogInvalidTx(transaction, $"SENDER_ACCOUNT_DOES_NOT_EXIST {sender}"); if (isCall || gasPrice == UInt256.Zero) { wasSenderAccountCreatedInsideACall = isCall; _stateProvider.CreateAccount(sender, UInt256.Zero); } } } if (notSystemTransaction) { UInt256 senderBalance = _stateProvider.GetBalance(sender); if (!isCall && (ulong)intrinsicGas * gasPrice + value > senderBalance) { TraceLogInvalidTx(transaction, $"INSUFFICIENT_SENDER_BALANCE: ({sender})_BALANCE = {senderBalance}"); QuickFail(transaction, block, txTracer, "insufficient sender balance"); return; } if (transaction.Nonce != _stateProvider.GetNonce(sender)) { TraceLogInvalidTx(transaction, $"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(sender)})"); QuickFail(transaction, block, txTracer, "wrong transaction nonce"); return; } _stateProvider.IncrementNonce(sender); } UInt256 senderReservedGasPayment = isCall ? UInt256.Zero : (ulong)gasLimit * gasPrice; _stateProvider.SubtractFromBalance(sender, senderReservedGasPayment, spec); _stateProvider.Commit(spec, 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 = transaction.IsSystem() ? transaction.SenderAddress : ContractAddress.From(sender, _stateProvider.GetNonce(sender) - 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); } } ExecutionEnvironment env = new ExecutionEnvironment(); env.Value = value; env.TransferValue = value; env.Sender = sender; env.CodeSource = recipient; env.ExecutingAccount = recipient; env.CurrentBlock = block; env.GasPrice = gasPrice; env.InputData = data ?? Array.Empty <byte>(); env.CodeInfo = machineCode == null?_virtualMachine.GetCachedCodeInfo(recipient, spec) : new CodeInfo(machineCode); env.Originator = sender; ExecutionType executionType = transaction.IsContractCreation ? ExecutionType.Create : ExecutionType.Call; using (EvmState state = new EvmState(unspentGas, env, executionType, true, false)) { 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.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}"); } _storageProvider.ClearStorage(toBeDestroyed); _stateProvider.DeleteAccount(toBeDestroyed); if (txTracer.IsTracingRefunds) { txTracer.ReportRefund(RefundOf.Destroy); } } 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 (notSystemTransaction) { if (!_stateProvider.AccountExists(gasBeneficiary)) { _stateProvider.CreateAccount(gasBeneficiary, (ulong)spentGas * gasPrice); } else { _stateProvider.AddToBalance(gasBeneficiary, (ulong)spentGas * gasPrice, spec); } } } if (!isCall) { _storageProvider.Commit(txTracer.IsTracingState ? txTracer : null); _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : null); } else { _storageProvider.Reset(); _stateProvider.Reset(); if (wasSenderAccountCreatedInsideACall) { _stateProvider.DeleteAccount(sender); } else { _stateProvider.AddToBalance(sender, senderReservedGasPayment, spec); if (notSystemTransaction) { _stateProvider.DecrementNonce(sender); } } _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(recipient, spentGas, (substate?.ShouldRevert ?? false) ? substate.Output : Array.Empty <byte>(), substate?.Error, stateRoot); } else { txTracer.MarkAsSuccess(recipient, spentGas, substate.Output, substate.Logs.Any() ? substate.Logs.ToArray() : Array.Empty <LogEntry>(), stateRoot); } } }
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); } } }
public Block[] Process(Keccak branchStateRoot, Block[] suggestedBlocks, ProcessingOptions options, IBlockTracer blockTracer) { if (suggestedBlocks.Length == 0) { return(Array.Empty <Block>()); } int stateSnapshot = _stateDb.TakeSnapshot(); int codeSnapshot = _codeDb.TakeSnapshot(); if (stateSnapshot != -1 || codeSnapshot != -1) { if (_logger.IsError) { _logger.Error($"Uncommitted state ({stateSnapshot}, {codeSnapshot}) when processing from a branch root {branchStateRoot} starting with block {suggestedBlocks[0].ToString(Block.Format.Short)}"); } } Keccak snapshotStateRoot = _stateProvider.StateRoot; if (branchStateRoot != null && _stateProvider.StateRoot != branchStateRoot) { /* discarding the other branch data - chain reorganization */ Metrics.Reorganizations++; _storageProvider.Reset(); _stateProvider.Reset(); _stateProvider.StateRoot = branchStateRoot; } var readOnly = (options & ProcessingOptions.ReadOnlyChain) != 0; var processedBlocks = new Block[suggestedBlocks.Length]; try { for (int i = 0; i < suggestedBlocks.Length; i++) { processedBlocks[i] = ProcessOne(suggestedBlocks[i], options, blockTracer); if (_logger.IsTrace) { _logger.Trace($"Committing trees - state root {_stateProvider.StateRoot}"); } _stateProvider.CommitTree(); _storageProvider.CommitTrees(); if (!readOnly) { BlockProcessed?.Invoke(this, new BlockProcessedEventArgs(processedBlocks[i])); } } if (readOnly) { _receiptsTracer.BeforeRestore(_stateProvider); Restore(stateSnapshot, codeSnapshot, snapshotStateRoot); } else { _stateDb.Commit(); _codeDb.Commit(); } return(processedBlocks); } catch (InvalidBlockException) { Restore(stateSnapshot, codeSnapshot, snapshotStateRoot); throw; } }
private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTracer, ExecutionOptions executionOptions) { bool eip658NotEnabled = !_specProvider.GetSpec(block.Number).IsEip658Enabled; // restore is CallAndRestore - previous call, we will restore state after the execution bool restore = (executionOptions & ExecutionOptions.Restore) == ExecutionOptions.Restore; bool noValidation = (executionOptions & ExecutionOptions.NoValidation) == ExecutionOptions.NoValidation; // commit - is for standard execute, we will commit thee state after execution bool commit = (executionOptions & ExecutionOptions.Commit) == ExecutionOptions.Commit || eip658NotEnabled; //!commit - is for build up during block production, we won't commit state after each transaction to support rollbacks //we commit only after all block is constructed bool notSystemTransaction = !transaction.IsSystem(); bool deleteCallerAccount = false; IReleaseSpec spec = _specProvider.GetSpec(block.Number); if (!notSystemTransaction) { spec = new SystemTransactionReleaseSpec(spec); } UInt256 value = transaction.Value; if (!transaction.TryCalculatePremiumPerGas(block.BaseFeePerGas, out UInt256 premiumPerGas) && !noValidation) { TraceLogInvalidTx(transaction, "MINER_PREMIUM_IS_NEGATIVE"); QuickFail(transaction, block, txTracer, eip658NotEnabled, "miner premium is negative"); return; } UInt256 effectiveGasPrice = transaction.CalculateEffectiveGasPrice(spec.IsEip1559Enabled, block.BaseFeePerGas); 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, eip658NotEnabled, "sender not specified"); return; } if (!noValidation && _stateProvider.IsInvalidContractSender(spec, caller)) { TraceLogInvalidTx(transaction, "SENDER_IS_CONTRACT"); QuickFail(transaction, block, txTracer, eip658NotEnabled, "sender has deployed code"); 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, eip658NotEnabled, "gas limit below intrinsic gas"); return; } if (!noValidation && gasLimit > block.GasLimit - block.GasUsed) { TraceLogInvalidTx(transaction, $"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}"); QuickFail(transaction, block, txTracer, eip658NotEnabled, "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 (!commit || noValidation || effectiveGasPrice == UInt256.Zero) { deleteCallerAccount = !commit || restore; _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."); } } UInt256 senderReservedGasPayment = noValidation ? UInt256.Zero : (ulong)gasLimit * effectiveGasPrice; if (notSystemTransaction) { UInt256 senderBalance = _stateProvider.GetBalance(caller); if (!noValidation && ((ulong)intrinsicGas * effectiveGasPrice + value > senderBalance || senderReservedGasPayment + value > senderBalance)) { TraceLogInvalidTx(transaction, $"INSUFFICIENT_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}"); QuickFail(transaction, block, txTracer, eip658NotEnabled, "insufficient sender balance"); return; } if (!noValidation && spec.IsEip1559Enabled && !transaction.IsFree() && senderBalance < (UInt256)transaction.GasLimit * transaction.MaxFeePerGas + value) { TraceLogInvalidTx(transaction, $"INSUFFICIENT_MAX_FEE_PER_GAS_FOR_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}, MAX_FEE_PER_GAS: {transaction.MaxFeePerGas}"); QuickFail(transaction, block, txTracer, eip658NotEnabled, "insufficient MaxFeePerGas for sender balance"); return; } if (transaction.Nonce != _stateProvider.GetNonce(caller)) { TraceLogInvalidTx(transaction, $"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(caller)})"); QuickFail(transaction, block, txTracer, eip658NotEnabled, "wrong transaction nonce"); return; } _stateProvider.IncrementNonce(caller); } _stateProvider.SubtractFromBalance(caller, senderReservedGasPayment, spec); if (commit) { _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : NullTxTracer.Instance); } long unspentGas = gasLimit - intrinsicGas; long spentGas = gasLimit; Snapshot snapshot = _worldState.TakeSnapshot(); _stateProvider.SubtractFromBalance(caller, value, spec); byte statusCode = StatusCode.Failure; TransactionSubstate substate = null; Address?recipientOrNull = null; try { Address?recipient = transaction.GetRecipient(transaction.IsContractCreation ? _stateProvider.GetNonce(caller) : 0); if (transaction.IsContractCreation) { // if transaction is a contract creation then recipient address is the contract deployment address Address contractAddress = recipient; PrepareAccountForContractDeployment(contractAddress !, spec); } if (recipient == null) { // this transaction is not a contract creation so it should have the recipient known and not null throw new InvalidDataException("Recipient has not been resolved properly before tx execution"); } recipientOrNull = recipient; ExecutionEnvironment env = new(); env.TxExecutionContext = new TxExecutionContext(block, caller, effectiveGasPrice); 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(_worldState, recipient, spec) : new CodeInfo(machineCode); ExecutionType executionType = transaction.IsContractCreation ? ExecutionType.Create : ExecutionType.Call; using (EvmState state = new(unspentGas, env, executionType, true, snapshot, 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, _worldState, txTracer); unspentGas = state.GasAvailable; if (txTracer.IsTracingAccess) { txTracer.ReportAccess(state.AccessedAddresses, state.AccessedStorageCells); } } if (substate.ShouldRevert || substate.IsError) { if (_logger.IsTrace) { _logger.Trace("Restoring state from before transaction"); } _worldState.Restore(snapshot); } 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 (CodeDepositHandler.CodeIsInvalid(spec, substate.Output)) { throw new InvalidCodeException(); } 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(spec.IsEip3529Enabled)); } } statusCode = StatusCode.Success; } spentGas = Refund(gasLimit, unspentGas, substate, caller, effectiveGasPrice, 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}"); } _worldState.Restore(snapshot); } if (_logger.IsTrace) { _logger.Trace("Gas spent: " + spentGas); } Address gasBeneficiary = block.GasBeneficiary; bool gasBeneficiaryNotDestroyed = substate?.DestroyList.Contains(gasBeneficiary) != true; if (statusCode == StatusCode.Failure || gasBeneficiaryNotDestroyed) { if (notSystemTransaction) { UInt256 fees = (ulong)spentGas * premiumPerGas; if (_stateProvider.AccountExists(gasBeneficiary)) { _stateProvider.AddToBalance(gasBeneficiary, fees, spec); } else { _stateProvider.CreateAccount(gasBeneficiary, fees); } if (!transaction.IsFree() && spec.IsEip1559Enabled && spec.Eip1559FeeCollector is not null) { UInt256 burntFees = (ulong)spentGas * block.BaseFeePerGas; if (!burntFees.IsZero) { if (_stateProvider.AccountExists(spec.Eip1559FeeCollector)) { _stateProvider.AddToBalance(spec.Eip1559FeeCollector, burntFees, spec); } else { _stateProvider.CreateAccount(spec.Eip1559FeeCollector, burntFees); } } } } } if (restore) { _storageProvider.Reset(); _stateProvider.Reset(); if (deleteCallerAccount) { _stateProvider.DeleteAccount(caller); } else { _stateProvider.AddToBalance(caller, senderReservedGasPayment, spec); if (notSystemTransaction) { _stateProvider.DecrementNonce(caller); } _stateProvider.Commit(spec); } } else if (commit) { _storageProvider.Commit(txTracer.IsTracingState ? txTracer : NullStorageTracer.Instance); _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : NullStateTracer.Instance); } if (!noValidation && notSystemTransaction) { block.GasUsed += spentGas; } if (txTracer.IsTracingReceipt) { Keccak stateRoot = null; if (eip658NotEnabled) { _stateProvider.RecalculateStateRoot(); stateRoot = _stateProvider.StateRoot; } if (statusCode == StatusCode.Failure) { txTracer.MarkAsFailed(recipientOrNull, spentGas, (substate?.ShouldRevert ?? false) ? substate.Output.ToArray() : Array.Empty <byte>(), substate?.Error, stateRoot); } else { txTracer.MarkAsSuccess(recipientOrNull, spentGas, substate.Output.ToArray(), substate.Logs.Any() ? substate.Logs.ToArray() : Array.Empty <LogEntry>(), stateRoot); } } }
public Block[] Process(Keccak branchStateRoot, Block[] suggestedBlocks, bool tryOnly) { int dbSnapshot = _dbProvider.TakeSnapshot(); Keccak snapshotStateRoot = _stateProvider.StateRoot; if (branchStateRoot != null && _stateProvider.StateRoot != branchStateRoot) { // discarding one of the branches _storageProvider.ClearCaches(); _stateProvider.Reset(); _stateProvider.StateRoot = branchStateRoot; } Block[] processedBlocks = new Block[suggestedBlocks.Length]; try { for (int i = 0; i < suggestedBlocks.Length; i++) { processedBlocks[i] = ProcessOne(suggestedBlocks[i], tryOnly); } if (tryOnly) { if (_logger.IsDebugEnabled) { _logger.Debug($"REVERTING BLOCKS - STATE ROOT {_stateProvider.StateRoot}"); } _dbProvider.Restore(dbSnapshot); _storageProvider.ClearCaches(); _stateProvider.Reset(); _stateProvider.StateRoot = snapshotStateRoot; if (_logger.IsDebugEnabled) { _logger.Debug($"REVERTED BLOCKS (JUST VALIDATED FOR MINING) - STATE ROOT {_stateProvider.StateRoot}"); } } return(processedBlocks); } catch (InvalidBlockException) // TODO: which exception to catch here? { if (_logger.IsDebugEnabled) { _logger.Debug($"REVERTING BLOCKS - STATE ROOT {_stateProvider.StateRoot}"); } _dbProvider.Restore(dbSnapshot); _storageProvider.ClearCaches(); _stateProvider.Reset(); _stateProvider.StateRoot = snapshotStateRoot; if (_logger.IsDebugEnabled) { _logger.Debug($"REVERTED BLOCKS - STATE ROOT {_stateProvider.StateRoot}"); } if (_logger.IsErrorEnabled) { _logger.Error($"THROWING INVALID BLOCK"); } throw; } }
public void Dispose() { _stateProvider.Reset(); }
public void Reset() { StateProvider.Reset(); StorageProvider.Reset(); }