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); }
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 */ }
protected virtual TxReceipt[] ProcessBlock(Block block, IBlockTracer blockTracer, ProcessingOptions options) { TxReceipt[] receipts = ProcessTransactions(block, options, blockTracer); SetReceiptsRoot(block, receipts); ApplyMinerRewards(block, blockTracer); _stateProvider.Commit(_specProvider.GetSpec(block.Number)); _stateProvider.RecalculateStateRoot(); block.Header.StateRoot = _stateProvider.StateRoot; block.Header.Hash = block.Header.CalculateHash(); return(receipts); }
private Block ProcessOne(Block suggestedBlock, ProcessingOptions options, IBlockTracer blockTracer) { if (suggestedBlock.IsGenesis) { return(suggestedBlock); } if (_specProvider.DaoBlockNumber.HasValue && _specProvider.DaoBlockNumber.Value == suggestedBlock.Header.Number) { if (_logger.IsInfo) { _logger.Info("Applying DAO transition"); } ApplyDaoTransition(); } Block block = PrepareBlockForProcessing(suggestedBlock); var receipts = ProcessTransactions(block, options, blockTracer); SetReceiptsRootAndBloom(block, receipts); ApplyMinerRewards(block, blockTracer); _stateProvider.Commit(_specProvider.GetSpec(block.Number)); block.Header.StateRoot = _stateProvider.StateRoot; block.Header.Hash = BlockHeader.CalculateHash(block.Header); if ((options & ProcessingOptions.NoValidation) == 0 && !_blockValidator.ValidateProcessedBlock(block, receipts, suggestedBlock)) { if (_logger.IsError) { _logger.Error($"Processed block is not valid {suggestedBlock.ToString(Block.Format.FullHashAndNumber)}"); } throw new InvalidBlockException(suggestedBlock.Hash); } if ((options & ProcessingOptions.StoreReceipts) != 0) { StoreTxReceipts(block, receipts); } if ((options & ProcessingOptions.StoreTraces) != 0) { StoreTraces(blockTracer as ParityLikeBlockTracer); } BlockProcessed?.Invoke(this, new BlockProcessedEventArgs(block)); return(block); }
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); }
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); }
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(); }
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); }
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); }
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(); }
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 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)); }
public void EnsureSystemAccount(IStateProvider stateProvider) { if (!stateProvider.AccountExists(Address.SystemUser)) { stateProvider.CreateAccount(Address.SystemUser, UInt256.Zero); stateProvider.Commit(Homestead.Instance); } }
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); }
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}"); } }
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); }
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(); } }
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); }
protected virtual TxReceipt[] ProcessBlock(Block block, IBlockTracer blockTracer, ProcessingOptions options) { if (!block.IsGenesis) { var receipts = ProcessTransactions(block, options, blockTracer); SetReceiptsRoot(block, receipts); ApplyMinerRewards(block, blockTracer); _stateProvider.Commit(_specProvider.GetSpec(block.Number)); _stateProvider.RecalculateStateRoot(); block.Header.StateRoot = _stateProvider.StateRoot; block.Header.Hash = block.Header.CalculateHash(); return(receipts); } if (_logger.IsTrace) { _logger.Trace($"Processed block {block.ToString(Block.Format.Short)}"); } return(Array.Empty <TxReceipt>()); }
// TODO: block processor pipeline protected virtual TxReceipt[] ProcessBlock( Block block, IBlockTracer blockTracer, ProcessingOptions options) { IReleaseSpec spec = _specProvider.GetSpec(block.Number); _receiptsTracer.SetOtherTracer(blockTracer); _receiptsTracer.StartNewBlockTrace(block); TxReceipt[] receipts = _blockTransactionsExecutor.ProcessTransactions(block, options, _receiptsTracer, spec); _receiptsTracer.EndBlockTrace(); block.Header.ReceiptsRoot = receipts.GetReceiptsRoot(spec, block.ReceiptsRoot); ApplyMinerRewards(block, blockTracer, spec); _stateProvider.Commit(spec); _stateProvider.RecalculateStateRoot(); block.Header.StateRoot = _stateProvider.StateRoot; block.Header.Hash = block.Header.CalculateHash(); return(receipts); }
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); }
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); }
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); }
public Block Load() { Block genesis = _chainSpec.Genesis; Preallocate(genesis); // we no longer need the allocations - 0.5MB RAM, 9000 objects for mainnet _chainSpec.Allocations = null; _storageProvider.Commit(); _stateProvider.Commit(_specProvider.GenesisSpec); _storageProvider.CommitTrees(0); _stateProvider.CommitTree(0); genesis.Header.StateRoot = _stateProvider.StateRoot; genesis.Header.Hash = genesis.Header.CalculateHash(); return(genesis); }
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); }
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); }
public override TxReceipt[] ProcessTransactions( Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, IReleaseSpec spec) { IEnumerable <Transaction> transactions = GetTransactions(block); int i = 0; LinkedHashSet <Transaction> transactionsInBlock = new(ByHashTxComparer.Instance); foreach (Transaction transaction in transactions) { if (IsAccountAbstractionTransaction(transaction)) { BlockProcessor.TxAction action = ProcessAccountAbstractionTransaction(block, transaction, i++, receiptsTracer, processingOptions, transactionsInBlock); if (action == BlockProcessor.TxAction.Stop) { break; } } else { BlockProcessor.TxAction action = ProcessTransaction(block, transaction, i++, receiptsTracer, processingOptions, transactionsInBlock); if (action == BlockProcessor.TxAction.Stop) { break; } } } _stateProvider.Commit(spec, receiptsTracer); _storageProvider.Commit(receiptsTracer); SetTransactions(block, transactionsInBlock); return(receiptsTracer.TxReceipts.ToArray()); }
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); } } }
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); } } }
/// <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); } } }