コード例 #1
0
        private TransactionReceipt Execute(params byte[] code)
        {
            _stateProvider.CreateAccount(A, 100.Ether());

            _stateProvider.CreateAccount(B, 100.Ether());
            Keccak codeHash = _stateProvider.UpdateCode(code);

            _stateProvider.UpdateCodeHash(TestObject.AddressB, codeHash, _spec.GenesisSpec);

            _stateProvider.Commit(_spec.GenesisSpec);

            Transaction transaction = Build.A.Transaction
                                      .WithGasLimit(100000)
                                      .WithGasPrice(1)
                                      .WithTo(TestObject.AddressB)
                                      .SignedAndResolved(_ethereumSigner, TestObject.PrivateKeyA, 100000)
                                      .TestObject;

            Assert.AreEqual(A, _ethereumSigner.RecoverAddress(transaction, 100000));

            Block block = Build.A.Block.WithNumber(10000).TestObject;
            TransactionReceipt receipt = _processor.Execute(transaction, block.Header);

            return(receipt);
        }
コード例 #2
0
        protected async Task Prepare()
        {
            _wallet          = new DevWallet(new WalletConfig(), _logManager);
            _feeAccount      = _wallet.GetAccounts()[0];
            _consumerAccount = _wallet.GetAccounts()[1];
            _providerAccount = _wallet.GetAccounts()[2];
            _ndmConfig       = new NdmConfig();

            IReleaseSpec  spec         = _releaseSpec;
            ISpecProvider specProvider = new SingleReleaseSpecProvider(spec, 99);
            StateDb       stateDb      = new StateDb();

            _state = new StateProvider(stateDb, new StateDb(), _logManager);
            StorageProvider storageProvider = new StorageProvider(stateDb, _state, _logManager);

            _state.CreateAccount(_consumerAccount, 1000.Ether());
            _state.CreateAccount(_providerAccount, 1.Ether());
            _state.Commit(spec);
            _state.CommitTree();

            VirtualMachine machine = new VirtualMachine(_state, storageProvider, Substitute.For <IBlockhashProvider>(),
                                                        specProvider, _logManager);
            TransactionProcessor processor = new TransactionProcessor(specProvider, _state, storageProvider, machine, _logManager);

            _bridge = new BlockchainBridge(processor);

            TxReceipt receipt = await DeployContract(Bytes.FromHexString(ContractData.GetInitCode(_feeAccount)));

            ((NdmConfig)_ndmConfig).ContractAddress = receipt.ContractAddress.ToString();
            _contractAddress = receipt.ContractAddress;
            _txPool          = new TxPool.TxPool(new InMemoryTxStorage(), Timestamper.Default,
                                                 new EthereumEcdsa(specProvider.ChainId, _logManager), specProvider, new TxPoolConfig(), _state, _logManager);
            _ndmBridge = new NdmBlockchainBridge(_bridge, _bridge, _bridge, _bridge);
        }
コード例 #3
0
        protected void Prepare()
        {
            _wallet          = new DevWallet(new WalletConfig(), _logManager);
            _feeAccount      = _wallet.GetAccounts()[0];
            _consumerAccount = _wallet.GetAccounts()[1];
            _providerAccount = _wallet.GetAccounts()[2];
            _ndmConfig       = new NdmConfig();

            IReleaseSpec  spec         = _releaseSpec;
            ISpecProvider specProvider = new SingleReleaseSpecProvider(spec, 99);
            StateDb       stateDb      = new StateDb();

            _state = new StateProvider(stateDb, new StateDb(), _logManager);
            StorageProvider storageProvider = new StorageProvider(stateDb, _state, _logManager);

            _state.CreateAccount(_consumerAccount, 1000.Ether());
            _state.CreateAccount(_providerAccount, 1.Ether());
            _state.Commit(spec);
            _state.CommitTree();

            VirtualMachine       machine   = new VirtualMachine(_state, storageProvider, Substitute.For <IBlockhashProvider>(), _logManager);
            TransactionProcessor processor = new TransactionProcessor(specProvider, _state, storageProvider, machine, _logManager);

            _bridge = new BlockchainBridge(processor, _releaseSpec);

            TxReceipt receipt = DeployContract(Bytes.FromHexString(ContractData.GetInitCode(_feeAccount)));

            _ndmConfig.ContractAddress = receipt.ContractAddress.ToString();
        }
コード例 #4
0
 public void Setup()
 {
     _stateProvider = new StateProvider(new StateDb(new MemDb()), Substitute.For <IDb>(), LogManager);
     _stateProvider.CreateAccount(_address1, 0);
     _stateProvider.CreateAccount(_address2, 0);
     _stateProvider.Commit(Frontier.Instance);
 }
コード例 #5
0
 public Context()
 {
     StateProvider = new StateProvider(new TrieStore(new MemDb(), LimboLogs.Instance), Substitute.For <IDb>(), LogManager);
     StateProvider.CreateAccount(Address1, 0);
     StateProvider.CreateAccount(Address2, 0);
     StateProvider.Commit(Frontier.Instance);
 }
コード例 #6
0
        // TODO: block processor pipeline (only where rewards needed)
        private void ApplyMinerReward(Block block, BlockReward reward, IReleaseSpec spec)
        {
            if (_logger.IsTrace)
            {
                _logger.Trace($"  {(BigInteger) reward.Value / (BigInteger) Unit.Ether:N3}{Unit.EthSymbol} for account at {reward.Address}");
            }

            if (!_stateProvider.AccountExists(reward.Address))
            {
                _stateProvider.CreateAccount(reward.Address, reward.Value);
            }
            else
            {
                _stateProvider.AddToBalance(reward.Address, reward.Value, spec);
            }
        }
コード例 #7
0
        private void ApplyMinerRewards(Block block)
        {
            if (_logger.IsDebugEnabled)
            {
                _logger.Debug("Applying miner rewards:");
            }
            BlockReward[] rewards = _rewardCalculator.CalculateRewards(block);
            for (int i = 0; i < rewards.Length; i++)
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"    {((decimal)rewards[i].Value / (decimal)Unit.Ether):N3}{Unit.EthSymbol} for account at {rewards[i].Address}");
                }
                if (!_stateProvider.AccountExists(rewards[i].Address))
                {
                    _stateProvider.CreateAccount(rewards[i].Address, rewards[i].Value);
                }
                else
                {
                    _stateProvider.UpdateBalance(rewards[i].Address, rewards[i].Value, _specProvider.GetSpec(block.Number));
                }
            }

            _stateProvider.Commit(_specProvider.GetSpec(block.Number));
        }
コード例 #8
0
ファイル: BlockProcessor.cs プロジェクト: sounak98/nethermind
        private void ApplyMinerReward(Block block, BlockReward reward, IBlockTracer tracer)
        {
            if (_logger.IsTrace)
            {
                _logger.Trace($"  {(decimal) reward.Value / (decimal) Unit.Ether:N3}{Unit.EthSymbol} for account at {reward.Address}");
            }

            if (!_stateProvider.AccountExists(reward.Address))
            {
                _stateProvider.CreateAccount(reward.Address, (UInt256)reward.Value);
            }
            else
            {
                _stateProvider.AddToBalance(reward.Address, (UInt256)reward.Value, _specProvider.GetSpec(block.Number));
            }
        }
コード例 #9
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();
        }
コード例 #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();
        }
コード例 #11
0
        protected async Task Prepare()
        {
            _wallet          = new DevWallet(new WalletConfig(), _logManager);
            _feeAccount      = _wallet.GetAccounts()[0];
            _consumerAccount = _wallet.GetAccounts()[1];
            _providerAccount = _wallet.GetAccounts()[2];
            _ndmConfig       = new NdmConfig();

            IReleaseSpec  spec         = _releaseSpec;
            ISpecProvider specProvider = new SingleReleaseSpecProvider(spec, 99);
            MemDb         stateDb      = new MemDb();
            TrieStore     trieStore    = new TrieStore(stateDb, _logManager);

            _state = new StateProvider(trieStore, new MemDb(), _logManager);
            StorageProvider storageProvider = new StorageProvider(trieStore, _state, _logManager);

            _state.CreateAccount(_consumerAccount, 1000.Ether());
            _state.CreateAccount(_providerAccount, 1.Ether());
            _state.Commit(spec);
            _state.CommitTree(0);

            VirtualMachine machine =
                new VirtualMachine(Substitute.For <IBlockhashProvider>(), specProvider, _logManager);
            TransactionProcessor processor = new TransactionProcessor(specProvider, _state, storageProvider, machine, _logManager);

            _bridge = new BlockchainBridge(processor);

            TxReceipt receipt = await DeployContract(Bytes.FromHexString(ContractData.GetInitCode(_feeAccount)));

            ((NdmConfig)_ndmConfig).ContractAddress = receipt.ContractAddress.ToString();
            _contractAddress = receipt.ContractAddress;
            IBlockTree blockTree = Substitute.For <IBlockTree>();
            Block      block     = Build.A.Block.WithNumber(0).TestObject;

            blockTree.Head.Returns(block);
            TransactionComparerProvider transactionComparerProvider = new TransactionComparerProvider(specProvider, blockTree);

            _txPool = new TxPool.TxPool(
                new EthereumEcdsa(specProvider.ChainId, _logManager),
                new ChainHeadInfoProvider(specProvider, blockTree, _state),
                new TxPoolConfig(),
                new TxValidator(specProvider.ChainId),
                _logManager,
                transactionComparerProvider.GetDefaultComparer());
            _ndmBridge = new NdmBlockchainBridge(_bridge, _bridge, _bridge, _bridge);
        }
コード例 #12
0
ファイル: SystemContract.cs プロジェクト: tr002196/nethermind
 public void EnsureSystemAccount(IStateProvider stateProvider)
 {
     if (!stateProvider.AccountExists(Address.SystemUser))
     {
         stateProvider.CreateAccount(Address.SystemUser, UInt256.Zero);
         stateProvider.Commit(Homestead.Instance);
     }
 }
コード例 #13
0
ファイル: GenesisLoader.cs プロジェクト: prestwich/nethermind
        public Block Load()
        {
            Block genesis = _chainSpec.Genesis;

            foreach ((Address address, ChainSpecAllocation allocation) in _chainSpec.Allocations.OrderBy(a => a.Key))
            {
                _stateProvider.CreateAccount(address, allocation.Balance);

                if (allocation.Code != null)
                {
                    Keccak codeHash = _stateProvider.UpdateCode(allocation.Code);
                    _stateProvider.UpdateCodeHash(address, codeHash, _specProvider.GenesisSpec);
                }

                if (allocation.Storage != null)
                {
                    foreach (KeyValuePair <UInt256, byte[]> storage in allocation.Storage)
                    {
                        _storageProvider.Set(new StorageCell(address, storage.Key), storage.Value.WithoutLeadingZeros().ToArray());
                    }
                }

                if (allocation.Constructor != null)
                {
                    Transaction constructorTransaction = new SystemTransaction()
                    {
                        SenderAddress = address,
                        Init          = allocation.Constructor,
                        GasLimit      = genesis.GasLimit
                    };

                    CallOutputTracer outputTracer = new CallOutputTracer();
                    _transactionProcessor.Execute(constructorTransaction, genesis.Header, outputTracer);

                    if (outputTracer.StatusCode != StatusCode.Success)
                    {
                        throw new InvalidOperationException($"Failed to initialize constructor for address {address}. Error: {outputTracer.Error}");
                    }
                }
            }

            // we no longer need the allocations - 0.5MB RAM, 9000 objects for mainnet
            _chainSpec.Allocations = null;

            _storageProvider.Commit();
            _stateProvider.Commit(_specProvider.GenesisSpec);

            _storageProvider.CommitTrees();
            _stateProvider.CommitTree();

            _dbProvider.StateDb.Commit();
            _dbProvider.CodeDb.Commit();

            genesis.Header.StateRoot = _stateProvider.StateRoot;
            genesis.Header.Hash      = genesis.Header.CalculateHash();

            return(genesis);
        }
コード例 #14
0
        private static void LoadGenesisBlock(
            ChainSpec chainSpec,
            Keccak expectedGenesisHash,
            IBlockTree blockTree,
            IStateProvider stateProvider,
            ISpecProvider specProvider)
        {
            // if we already have a database with blocks then we do not need to load genesis from spec
            if (blockTree.Genesis != null)
            {
                return;
            }

            foreach ((Address address, (UInt256 balance, byte[] code)) in chainSpec.Allocations)
            {
                stateProvider.CreateAccount(address, balance);
                if (code != null)
                {
                    Keccak codeHash = stateProvider.UpdateCode(code);
                    stateProvider.UpdateCodeHash(address, codeHash, specProvider.GenesisSpec);
                }
            }

            stateProvider.Commit(specProvider.GenesisSpec);

            Block genesis = chainSpec.Genesis;

            genesis.StateRoot = stateProvider.StateRoot;
            genesis.Hash      = BlockHeader.CalculateHash(genesis.Header);

            ManualResetEventSlim genesisProcessedEvent = new ManualResetEventSlim(false);

            bool genesisLoaded = false;

            void GenesisProcessed(object sender, BlockEventArgs args)
            {
                genesisLoaded           = true;
                blockTree.NewHeadBlock -= GenesisProcessed;
                genesisProcessedEvent.Set();
            }

            blockTree.NewHeadBlock += GenesisProcessed;
            blockTree.SuggestBlock(genesis);
            genesisProcessedEvent.Wait(TimeSpan.FromSeconds(5));
            if (!genesisLoaded)
            {
                throw new BlockchainException("Genesis block processing failure");
            }

            // if expectedGenesisHash is null here then it means that we do not care about the exact value in advance (e.g. in test scenarios)
            if (expectedGenesisHash != null && blockTree.Genesis.Hash != expectedGenesisHash)
            {
                throw new Exception($"Unexpected genesis hash, expected {expectedGenesisHash}, but was {blockTree.Genesis.Hash}");
            }
        }
コード例 #15
0
        public void Get_storage()
        {
            /* all testing will be touching just a single storage cell */
            var storageCell = new StorageCell(TestItem.AddressA, 1);

            /* to start with we need to create an account that we will be setting storage at */
            _stateProvider.CreateAccount(storageCell.Address, UInt256.One);
            _stateProvider.Commit(MuirGlacier.Instance);
            _stateProvider.CommitTree();

            /* at this stage we have an account with empty storage at the address that we want to test */

            byte[] initialValue = new byte[] { 1, 2, 3 };
            _storageProvider.Set(storageCell, initialValue);
            _storageProvider.Commit();
            _storageProvider.CommitTrees();
            _stateProvider.Commit(MuirGlacier.Instance);
            _stateProvider.CommitTree();

            var retrieved =
                _blockchainBridge.GetStorage(storageCell.Address, storageCell.Index, _stateProvider.StateRoot);

            retrieved.Should().BeEquivalentTo(initialValue);

            /* at this stage we set the value in storage to 1,2,3 at the tested storage cell */

            /* Now we are testing scenario where the storage is being changed by the block processor.
            *  To do that we create some different storage / state access stack that represents the processor.
            *  It is a different stack of objects than the one that is used by the blockchain bridge. */

            byte[] newValue = new byte[] { 1, 2, 3, 4, 5 };

            StateProvider processorStateProvider =
                new StateProvider(_dbProvider.StateDb, _dbProvider.CodeDb, LimboLogs.Instance);

            processorStateProvider.StateRoot = _stateProvider.StateRoot;

            StorageProvider processorStorageProvider =
                new StorageProvider(_dbProvider.StateDb, processorStateProvider, LimboLogs.Instance);

            processorStorageProvider.Set(storageCell, newValue);
            processorStorageProvider.Commit();
            processorStorageProvider.CommitTrees();
            processorStateProvider.Commit(MuirGlacier.Instance);
            processorStateProvider.CommitTree();

            /* At this stage the DB should have the storage value updated to 5.
             * We will try to retrieve the value by taking the state root from the processor.*/

            retrieved =
                _blockchainBridge.GetStorage(storageCell.Address, storageCell.Index, processorStateProvider.StateRoot);
            retrieved.Should().BeEquivalentTo(newValue);

            /* If it failed then it means that the blockchain bridge cached the previous call value */
        }
コード例 #16
0
 private Keccak InitializeAccounts(IDictionary <string, TestAccount> alloc)
 {
     foreach (var account in alloc)
     {
         _stateProvider.CreateAccount(new Address(new Hex(account.Key)), account.Value.Balance.StartsWith("0x")
             ? new BigInteger(new Hex(account.Value.Balance)) : BigInteger.Parse(account.Value.Balance));
     }
     _stateProvider.Commit(_specProvider.GenesisSpec);
     _dbProvider.Commit(_specProvider.GenesisSpec);
     return(_stateProvider.StateRoot);
 }
コード例 #17
0
        public DeltaCache(IHashProvider hashProvider,
                          IMemoryCache memoryCache,
                          IDeltaDfsReader dfsReader,
                          IDeltaCacheChangeTokenProvider changeTokenProvider,
                          IStorageProvider storageProvider,
                          IStateProvider stateProvider,
                          ISnapshotableDb stateDb,
                          IDeltaIndexService deltaIndexService,
                          ILogger logger)
        {
            _deltaIndexService = deltaIndexService;

            stateProvider.CreateAccount(TruffleTestAccount, 1_000_000_000.Kat());
            stateProvider.CreateAccount(CatalystTruffleTestAccount, 1_000_000_000.Kat());

            storageProvider.Commit();
            stateProvider.Commit(CatalystGenesisSpec.Instance);

            storageProvider.CommitTrees();
            stateProvider.CommitTree();

            stateDb.Commit();

            var genesisDelta = new Delta
            {
                TimeStamp = Timestamp.FromDateTime(DateTime.UnixEpoch),
                StateRoot = ByteString.CopyFrom(stateProvider.StateRoot.Bytes),
            };


            GenesisHash = hashProvider.ComputeMultiHash(genesisDelta).ToCid();

            _dfsReader    = dfsReader;
            _logger       = logger;
            _entryOptions = () => new MemoryCacheEntryOptions()
                            .AddExpirationToken(changeTokenProvider.GetChangeToken())
                            .RegisterPostEvictionCallback(EvictionCallback);

            _memoryCache = memoryCache;
            _memoryCache.Set(GenesisHash, genesisDelta);
        }
コード例 #18
0
 private Keccak InitializeAccounts(IDictionary <string, TestAccount> alloc)
 {
     foreach (var account in alloc)
     {
         UInt256.CreateFromBigEndian(out UInt256 allocation, Bytes.FromHexString(account.Value.Balance));
         _stateProvider.CreateAccount(new Address(account.Key), account.Value.Balance.StartsWith("0x")
             ? allocation : UInt256.Parse(account.Value.Balance));
     }
     _stateProvider.Commit(_specProvider.GenesisSpec);
     _dbProvider.Commit(_specProvider.GenesisSpec);
     return(_stateProvider.StateRoot);
 }
コード例 #19
0
        public void Setup()
        {
            MemDb     stateDb   = new();
            TrieStore trieStore = new(stateDb, LimboLogs.Instance);

            _stateProvider = new StateProvider(trieStore, new MemDb(), LimboLogs.Instance);
            _stateProvider.CreateAccount(TestItem.AddressA, 1.Ether());
            _stateProvider.Commit(_specProvider.GenesisSpec);
            _stateProvider.CommitTree(0);

            StorageProvider storageProvider = new(trieStore, _stateProvider, LimboLogs.Instance);
            VirtualMachine  virtualMachine  = new(TestBlockhashProvider.Instance, _specProvider, LimboLogs.Instance);

            _transactionProcessor = new TransactionProcessor(_specProvider, _stateProvider, storageProvider, virtualMachine, LimboLogs.Instance);
            _ethereumEcdsa        = new EthereumEcdsa(_specProvider.ChainId, LimboLogs.Instance);
        }
コード例 #20
0
        public Block Load()
        {
            Block genesis = _chainSpec.Genesis;

            foreach ((Address address, ChainSpecAllocation allocation) in _chainSpec.Allocations)
            {
                _stateProvider.CreateAccount(address, allocation.Balance);
                if (allocation.Code != null)
                {
                    Keccak codeHash = _stateProvider.UpdateCode(allocation.Code);
                    _stateProvider.UpdateCodeHash(address, codeHash, _specProvider.GenesisSpec);
                }

                if (allocation.Constructor != null)
                {
                    Transaction constructorTransaction = new SystemTransaction()
                    {
                        SenderAddress = address,
                        Init          = allocation.Constructor,
                        GasLimit      = genesis.GasLimit
                    };

                    _transactionProcessor.Execute(constructorTransaction, genesis.Header, NullTxTracer.Instance);
                }
            }

            // we no longer need the allocations - 0.5MB RAM, 9000 objects for mainnet
            _chainSpec.Allocations = null;

            _storageProvider.Commit();
            _stateProvider.Commit(_specProvider.GenesisSpec);

            _storageProvider.CommitTrees();
            _stateProvider.CommitTree();

            _dbProvider.StateDb.Commit();
            _dbProvider.CodeDb.Commit();

            genesis.Header.StateRoot = _stateProvider.StateRoot;
            genesis.Header.Hash      = genesis.Header.CalculateHash();

            return(genesis);
        }
コード例 #21
0
        private void Preallocate(Block genesis)
        {
            foreach ((Address address, ChainSpecAllocation allocation) in _chainSpec.Allocations.OrderBy(a => a.Key))
            {
                _stateProvider.CreateAccount(address, allocation.Balance, allocation.Nonce);

                if (allocation.Code != null)
                {
                    Keccak codeHash = _stateProvider.UpdateCode(allocation.Code);
                    _stateProvider.UpdateCodeHash(address, codeHash, _specProvider.GenesisSpec);
                }

                if (allocation.Storage != null)
                {
                    foreach (KeyValuePair <UInt256, byte[]> storage in allocation.Storage)
                    {
                        _storageProvider.Set(new StorageCell(address, storage.Key),
                                             storage.Value.WithoutLeadingZeros().ToArray());
                    }
                }

                if (allocation.Constructor != null)
                {
                    Transaction constructorTransaction = new SystemTransaction()
                    {
                        SenderAddress = address,
                        Data          = allocation.Constructor,
                        GasLimit      = genesis.GasLimit
                    };

                    CallOutputTracer outputTracer = new();
                    _transactionProcessor.Execute(constructorTransaction, genesis.Header, outputTracer);

                    if (outputTracer.StatusCode != StatusCode.Success)
                    {
                        throw new InvalidOperationException(
                                  $"Failed to initialize constructor for address {address}. Error: {outputTracer.Error}");
                    }
                }
            }
        }
コード例 #22
0
        private void InitializeTestState(BlockchainTest test, IStateProvider stateProvider, IStorageProvider storageProvider, ISpecProvider specProvider)
        {
            foreach (KeyValuePair <Address, AccountState> accountState in test.Pre)
            {
                foreach (KeyValuePair <BigInteger, byte[]> storageItem in accountState.Value.Storage)
                {
                    storageProvider.Set(new StorageAddress(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(specProvider.GenesisSpec);
            stateProvider.Commit(specProvider.GenesisSpec);
        }
コード例 #23
0
        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);
                }
            }
        }
コード例 #24
0
        private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTracer, bool isCall)
        {
            bool notSystemTransaction = !transaction.IsSystem();
            bool wasSenderAccountCreatedInsideACall = false;

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

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

            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);
                }
            }
        }
コード例 #25
0
ファイル: EthereumRunner.cs プロジェクト: vicnaum/nethermind
        private void LoadGenesisBlock(Keccak expectedGenesisHash)
        {
            // if we already have a database with blocks then we do not need to load genesis from spec
            if (_blockTree.Genesis != null)
            {
                ValidateGenesisHash(expectedGenesisHash);
                return;
            }

            Block genesis = _chainSpec.Genesis;

            CreateSystemAccounts();

            foreach ((Address address, ChainSpecAllocation allocation) in _chainSpec.Allocations)
            {
                _stateProvider.CreateAccount(address, allocation.Balance);
                if (allocation.Code != null)
                {
                    Keccak codeHash = _stateProvider.UpdateCode(allocation.Code);
                    _stateProvider.UpdateCodeHash(address, codeHash, _specProvider.GenesisSpec);
                }

                if (allocation.Constructor != null)
                {
                    Transaction constructorTransaction = new Transaction(true)
                    {
                        SenderAddress = address,
                        Init          = allocation.Constructor,
                        GasLimit      = genesis.GasLimit
                    };
                    _transactionProcessor.Execute(constructorTransaction, genesis.Header, NullTxTracer.Instance);
                }
            }

            _storageProvider.Commit();
            _stateProvider.Commit(_specProvider.GenesisSpec);

            _storageProvider.CommitTrees();
            _stateProvider.CommitTree();

            _dbProvider.StateDb.Commit();
            _dbProvider.CodeDb.Commit();

            genesis.StateRoot = _stateProvider.StateRoot;
            genesis.Hash      = BlockHeader.CalculateHash(genesis.Header);

            ManualResetEventSlim genesisProcessedEvent = new ManualResetEventSlim(false);

            bool genesisLoaded = false;

            void GenesisProcessed(object sender, BlockEventArgs args)
            {
                genesisLoaded            = true;
                _blockTree.NewHeadBlock -= GenesisProcessed;
                genesisProcessedEvent.Set();
            }

            _blockTree.NewHeadBlock += GenesisProcessed;
            _blockTree.SuggestBlock(genesis);
            genesisProcessedEvent.Wait(TimeSpan.FromSeconds(5));
            if (!genesisLoaded)
            {
                throw new BlockchainException("Genesis block processing failure");
            }

            ValidateGenesisHash(expectedGenesisHash);
        }
コード例 #26
0
        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);
                }
            }
        }
コード例 #27
0
ファイル: DeltaExecutor.cs プロジェクト: umlspec/Catalyst
        /// <summary>
        ///     Executes the <paramref name="entry" />.
        /// </summary>
        /// <param name="entry">Transaction entry to be executed inside the <see cref="VirtualMachine" />.</param>
        /// <param name="stateUpdate"><see cref="Delta" /> to be used for execution environment construction</param>
        /// <param name="txTracer">Tracer to extract the execution steps for debugging or analytics.</param>
        /// <param name="readOnly">Defines whether the state should be reverted after the execution.</param>
        /// <exception cref="TransactionCollisionException">Thrown when deployment address already has some code.</exception>
        /// <exception cref="OutOfGasException">Thrown when not enough gas is available for deposit.</exception>
        private void Execute(PublicEntry entry, StateUpdate stateUpdate, ITxTracer txTracer, bool readOnly)
        {
            var spec = _specProvider.GetSpec(stateUpdate.Number);

            var(sender, recipient) = ExtractSenderAndRecipient(entry);
            var isPrecompile = recipient.IsPrecompiled(spec);
            var env          = PrepareEnv(entry, sender, recipient, stateUpdate, isPrecompile);

            var gasLimit     = entry.GasLimit;
            var intrinsicGas = CalculateIntrinsicGas(entry, spec);

            if (_logger.IsEnabled(LogEventLevel.Verbose))
            {
                _logger.Verbose("Executing entry {entry}", entry);
            }

            if (!ValidateSender(entry, env, txTracer))
            {
                return;
            }

            if (!ValidateIntrinsicGas(entry, env, intrinsicGas, txTracer))
            {
                return;
            }

            if (!ValidateDeltaGasLimit(entry, env, txTracer))
            {
                return;
            }

            if (!_stateProvider.AccountExists(env.Sender))
            {
                if (env.GasPrice == UInt256.Zero)
                {
                    _stateProvider.CreateAccount(env.Sender, UInt256.Zero);
                }
            }

            if (!ValidateSenderBalance(entry, env, intrinsicGas, txTracer))
            {
                return;
            }

            if (!ValidateNonce(entry, env, txTracer))
            {
                return;
            }

            InitEntryExecution(env, gasLimit, spec, txTracer);

            // we prepare two fields to track the amount of gas spent / left
            var unspentGas = gasLimit - intrinsicGas;
            var spentGas   = gasLimit;

            // the snapshots are needed to revert the subroutine state changes in case of an VM exception
            var stateSnapshot   = _stateProvider.TakeSnapshot();
            var storageSnapshot = _storageProvider.TakeSnapshot();

            // we subtract value from sender
            // it will be added to recipient at the later stage (inside the VM)
            _stateProvider.SubtractFromBalance(sender, env.Value, spec);

            // we fail unless we succeed
            var statusCode = StatusCode.Failure;
            TransactionSubstate substate = null;

            try
            {
                if (entry.IsValidDeploymentEntry)
                {
                    PrepareContractAccount(env.CodeSource);
                }

                var executionType = entry.IsValidDeploymentEntry ? ExecutionType.Create : ExecutionType.Call;
                using (var state = new VmState((long)unspentGas, env, executionType, isPrecompile, true, false))
                {
                    substate   = _virtualMachine.Run(state, txTracer);
                    unspentGas = (ulong)state.GasAvailable;
                }

                if (substate.ShouldRevert || substate.IsError)
                {
                    if (_logger.IsEnabled(LogEventLevel.Verbose))
                    {
                        _logger.Verbose("Restoring state from before transaction");
                    }

                    _stateProvider.Restore(stateSnapshot);
                    _storageProvider.Restore(storageSnapshot);
                }
                else
                {
                    if (entry.IsValidDeploymentEntry)
                    {
                        DeployCode(env, substate, ref unspentGas, spec);
                    }

                    DestroyAccounts(substate);
                    statusCode = StatusCode.Success;
                }

                spentGas = Refund(gasLimit, unspentGas, substate, env, spec);
            }
            catch (Exception ex) when(ex is EvmException || ex is OverflowException)
            {
                if (_logger.IsEnabled(LogEventLevel.Verbose))
                {
                    _logger.Verbose($"EVM EXCEPTION: {ex.GetType().Name}");
                }

                _stateProvider.Restore(stateSnapshot);
                _storageProvider.Restore(storageSnapshot);
            }

            if (_logger.IsEnabled(LogEventLevel.Verbose))
            {
                _logger.Verbose("Gas spent: " + spentGas);
            }

            var gasBeneficiary = stateUpdate.GasBeneficiary;
            var wasBeneficiaryAccountDestroyed = statusCode != StatusCode.Failure &&
                                                 (substate?.DestroyList.Contains(gasBeneficiary) ?? false);

            if (!wasBeneficiaryAccountDestroyed)
            {
                if (!_stateProvider.AccountExists(gasBeneficiary))
                {
                    _stateProvider.CreateAccount(gasBeneficiary, spentGas * env.GasPrice);
                }
                else
                {
                    _stateProvider.AddToBalance(gasBeneficiary, spentGas * env.GasPrice, spec);
                }
            }

            if (!readOnly)
            {
                _storageProvider.Commit(txTracer.IsTracingState ? txTracer : null);
                _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : null);
                stateUpdate.GasUsed += (long)spentGas;
            }
            else
            {
                _storageProvider.Reset();
                _stateProvider.Reset();
            }

            if (txTracer.IsTracingReceipt)
            {
                if (statusCode == StatusCode.Failure)
                {
                    txTracer.MarkAsFailed(env.CodeSource, (long)spentGas,
                                          substate?.ShouldRevert ?? false ? substate.Output : Bytes.Empty, substate?.Error);
                }
                else
                {
                    if (substate == null)
                    {
                        throw new InvalidOperationException("Substate should not be null after a successful VM run.");
                    }

                    txTracer.MarkAsSuccess(env.CodeSource, (long)spentGas, substate.Output,
                                           substate.Logs.Any() ? substate.Logs.ToArray() : LogEntry.EmptyLogs);
                }
            }
        }
コード例 #28
0
        public TransactionReceipt Execute(
            Transaction transaction,
            BlockHeader block)
        {
            TransactionTrace trace = null;

            if (_tracer.IsTracingEnabled)
            {
                trace = new TransactionTrace();
            }

            IReleaseSpec spec      = _specProvider.GetSpec(block.Number);
            Address      recipient = transaction.To;
            BigInteger   value     = transaction.Value;
            BigInteger   gasPrice  = transaction.GasPrice;
            long         gasLimit  = (long)transaction.GasLimit;

            byte[] machineCode = transaction.Init;
            byte[] data        = transaction.Data ?? Bytes.Empty;

            Address sender = transaction.SenderAddress;

            if (_logger.IsDebugEnabled)
            {
                _logger.Debug($"SPEC: {spec.GetType().Name}");
                _logger.Debug("HASH: " + transaction.Hash);
                _logger.Debug("IS_CONTRACT_CREATION: " + transaction.IsContractCreation);
                _logger.Debug("IS_MESSAGE_CALL: " + transaction.IsMessageCall);
                _logger.Debug("IS_TRANSFER: " + transaction.IsTransfer);
                _logger.Debug("SENDER: " + sender);
                _logger.Debug("TO: " + transaction.To);
                _logger.Debug("GAS LIMIT: " + transaction.GasLimit);
                _logger.Debug("GAS PRICE: " + transaction.GasPrice);
                _logger.Debug("VALUE: " + transaction.Value);
                _logger.Debug("DATA_LENGTH: " + (transaction.Data?.Length ?? 0));
                _logger.Debug("NONCE: " + transaction.Nonce);
            }

            if (sender == null)
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"SENDER_NOT_SPECIFIED");
                }

                return(GetNullReceipt(block, 0L));
            }

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

            if (_logger.IsDebugEnabled)
            {
                _logger.Debug("INTRINSIC GAS: " + intrinsicGas);
            }

            if (gasLimit < intrinsicGas)
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"GAS_LIMIT_BELOW_INTRINSIC_GAS {gasLimit} < {intrinsicGas}");
                }

                return(GetNullReceipt(block, 0L));
            }

            if (gasLimit > block.GasLimit - block.GasUsed)
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}");
                }

                return(GetNullReceipt(block, 0L));
            }

            if (!_stateProvider.AccountExists(sender))
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"SENDER_ACCOUNT_DOES_NOT_EXIST {sender}");
                }

                _stateProvider.CreateAccount(sender, 0);
            }

            BigInteger senderBalance = _stateProvider.GetBalance(sender);

            if (intrinsicGas * gasPrice + value > senderBalance)
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"INSUFFICIENT_SENDER_BALANCE: ({sender})b = {senderBalance}");
                }

                return(GetNullReceipt(block, 0L));
            }

            if (transaction.Nonce != _stateProvider.GetNonce(sender))
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(sender)})");
                }

                return(GetNullReceipt(block, 0L));
            }

            _stateProvider.IncrementNonce(sender);
            _stateProvider.UpdateBalance(sender, -new BigInteger(gasLimit) * gasPrice, spec);
            _stateProvider.Commit(spec);

            long            unspentGas = gasLimit - intrinsicGas;
            long            spentGas   = gasLimit;
            List <LogEntry> logEntries = new List <LogEntry>();

            if (transaction.IsContractCreation)
            {
                Rlp addressBaseRlp = Rlp.Encode(
                    Rlp.Encode(sender),
                    Rlp.Encode(_stateProvider.GetNonce(sender) - 1));
                Keccak addressBaseKeccak = Keccak.Compute(addressBaseRlp);
                recipient = new Address(addressBaseKeccak);
            }

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

            _stateProvider.UpdateBalance(sender, -value, spec);
            byte statusCode = StatusCode.Failure;

            HashSet <Address> destroyedAccounts = new HashSet <Address>();

            try
            {
                if (transaction.IsContractCreation)
                {
                    // TODO: review tests around it as it fails on Ropsten 230881 when we throw an exception
                    if (_stateProvider.AccountExists(recipient) && !_stateProvider.IsEmptyAccount(recipient))
                    {
                        // TODO: review
//                        throw new TransactionCollisionException();
                    }
                }

                if (transaction.IsTransfer) // TODO: this is never called and wrong, to be removed
                {
                    _stateProvider.UpdateBalance(sender, -value, spec);
                    _stateProvider.UpdateBalance(recipient, value, spec);
                    statusCode = StatusCode.Success;
                }
                else
                {
                    bool isPrecompile = recipient.IsPrecompiled(spec);

                    ExecutionEnvironment env = new ExecutionEnvironment();
                    env.Value            = value;
                    env.TransferValue    = value;
                    env.Sender           = sender;
                    env.ExecutingAccount = recipient;
                    env.CurrentBlock     = block;
                    env.GasPrice         = gasPrice;
                    env.InputData        = data ?? new byte[0];
                    env.CodeInfo         = isPrecompile ? new CodeInfo(recipient) : machineCode == null?_virtualMachine.GetCachedCodeInfo(recipient) : new CodeInfo(machineCode);

                    env.Originator = sender;

                    ExecutionType executionType = isPrecompile
                        ? ExecutionType.DirectPrecompile
                        : transaction.IsContractCreation
                            ? ExecutionType.DirectCreate
                            : ExecutionType.Transaction;

                    TransactionSubstate substate;
                    byte[] output;
                    using (EvmState state = new EvmState(unspentGas, env, executionType, false))
                    {
                        (output, substate) = _virtualMachine.Run(state, spec, trace);
                        unspentGas         = state.GasAvailable;
                    }

                    if (substate.ShouldRevert)
                    {
                        if (_logger.IsDebugEnabled)
                        {
                            _logger.Debug("REVERTING");
                        }

                        logEntries.Clear();
                        destroyedAccounts.Clear();
                        _stateProvider.Restore(snapshot);
                        _storageProvider.Restore(storageSnapshot);
                    }
                    else
                    {
                        if (transaction.IsContractCreation)
                        {
                            long codeDepositGasCost = output.Length * GasCostOf.CodeDeposit;
                            if (spec.IsEip170Enabled && output.Length > 0x6000)
                            {
                                codeDepositGasCost = long.MaxValue;
                            }

                            if (unspentGas < codeDepositGasCost && spec.IsEip2Enabled)
                            {
                                throw new OutOfGasException();
                            }

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

                        logEntries.AddRange(substate.Logs);
                        foreach (Address toBeDestroyed in substate.DestroyList)
                        {
                            destroyedAccounts.Add(toBeDestroyed);
                        }

                        statusCode = StatusCode.Success;
                    }

                    spentGas = Refund(gasLimit, unspentGas, substate, sender, gasPrice, spec);
                }
            }
            catch (Exception ex) when(ex is EvmException || ex is OverflowException)  // TODO: OverflowException? still needed? hope not
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"EVM EXCEPTION: {ex.GetType().Name}");
                }

                logEntries.Clear();
                destroyedAccounts.Clear();
                _stateProvider.Restore(snapshot);
                _storageProvider.Restore(storageSnapshot);
            }

            foreach (Address toBeDestroyed in destroyedAccounts)
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"DESTROYING: {toBeDestroyed}");
                }
                _stateProvider.DeleteAccount(toBeDestroyed);
            }

            if (_logger.IsDebugEnabled)
            {
                _logger.Debug("GAS SPENT: " + spentGas);
            }

            if (!destroyedAccounts.Contains(block.Beneficiary))
            {
                if (!_stateProvider.AccountExists(block.Beneficiary))
                {
                    _stateProvider.CreateAccount(block.Beneficiary, spentGas * gasPrice);
                }
                else
                {
                    _stateProvider.UpdateBalance(block.Beneficiary, spentGas * gasPrice, spec);
                }
            }

            _storageProvider.Commit(spec);
            _stateProvider.Commit(spec);

            block.GasUsed += spentGas;

            if (_tracer.IsTracingEnabled)
            {
                trace.Gas = spentGas;
                _tracer.SaveTrace(Transaction.CalculateHash(transaction), trace);
            }

            return(BuildTransactionReceipt(statusCode, logEntries.Any() ? logEntries.ToArray() : LogEntry.EmptyLogs, block.GasUsed, recipient));
        }