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();
            }
        }
Beispiel #2
0
        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);
        }
Beispiel #5
0
 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;
     }
 }
Beispiel #6
0
 public void Dispose()
 {
     _stateProvider.StateRoot = _stateBefore;
     _stateProvider.Reset();
     _storageProvider.Reset();
     _codeDb.ClearTempChanges();
 }
Beispiel #7
0
        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);
        }
Beispiel #10
0
        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();
        }
Beispiel #11
0
        /// <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);
        }
Beispiel #12
0
        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);
        }
Beispiel #13
0
        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();
            }
        }
Beispiel #14
0
        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();
        }
Beispiel #16
0
        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;
            }
        }
Beispiel #18
0
        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);
        }
Beispiel #19
0
        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();
            }
        }
Beispiel #20
0
        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;
            }
        }
Beispiel #21
0
 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);
                }
            }
        }
Beispiel #24
0
        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);
                }
            }
        }
Beispiel #26
0
        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;
            }
        }
Beispiel #27
0
 public void Dispose()
 {
     _stateProvider.Reset();
 }
Beispiel #28
0
 public void Reset()
 {
     StateProvider.Reset();
     StorageProvider.Reset();
 }