public Keccak SendTransaction(Transaction transaction)
        {
            try
            {
                _readerWriterLockSlim.EnterWriteLock();
                _stateProvider.StateRoot = _blockTree.Head.StateRoot;

                if (transaction.SenderAddress == null)
                {
                    transaction.SenderAddress = _wallet.GetAccounts()[0];
                }

                transaction.Nonce = _stateProvider.GetNonce(transaction.SenderAddress);
                _wallet.Sign(transaction, _blockTree.ChainId);
                transaction.Hash = Transaction.CalculateHash(transaction);

                if (_stateProvider.GetNonce(transaction.SenderAddress) != transaction.Nonce)
                {
                    throw new InvalidOperationException("Invalid nonce");
                }

                _transactionPool.AddTransaction(transaction, _blockTree.Head.Number);

                _stateProvider.Reset();
                return(transaction.Hash);
            }
            finally
            {
                _readerWriterLockSlim.ExitWriteLock();
            }
        }
Exemple #2
0
        private void QuickFail(Transaction tx, StateUpdate block, ITxTracer txTracer, bool readOnly)
        {
            block.GasUsed += (long)tx.GasLimit;
            Address recipient = tx.To ?? Address.OfContract(tx.SenderAddress, _stateProvider.GetNonce(tx.SenderAddress));

            if (txTracer.IsTracingReceipt)
            {
                txTracer.MarkAsFailed(recipient, (long)tx.GasLimit, Bytes.Empty, "invalid");
            }
        }
Exemple #3
0
        private (Address sender, Address recipient) ExtractSenderAndRecipient(PublicEntry entry)
        {
            var sender    = entry.SenderAddress.ToAddress();
            var recipient = entry.ReceiverAddress.ToAddress();

            if (entry.IsValidDeploymentEntry)
            {
                recipient = ContractAddress.From(sender, _stateProvider.GetNonce(sender));
            }

            return(sender, recipient);
        }
        private void QuickFail(Transaction tx, BlockHeader block, ITxTracer txTracer, string reason)
        {
            block.GasUsed += tx.GasLimit;
            Address recipient = tx.To ?? ContractAddress.From(tx.SenderAddress, _stateProvider.GetNonce(tx.SenderAddress));

            _stateProvider.RecalculateStateRoot();
            Keccak stateRoot = _specProvider.GetSpec(block.Number).IsEip658Enabled ? null : _stateProvider.StateRoot;

            if (txTracer.IsTracingReceipt)
            {
                txTracer.MarkAsFailed(recipient, tx.GasLimit, Array.Empty <byte>(), reason ?? "invalid", stateRoot);
            }
        }
Exemple #5
0
        private (Address sender, Address recipient) ExtractSenderAndRecipient(PublicEntry entry)
        {
            var sender    = GetAccountAddress(entry.SenderAddress);
            var recipient = entry.TargetContract == null
                ? GetAccountAddress(entry.ReceiverAddress)
                : new Address(entry.TargetContract);

            if (entry.IsValidDeploymentEntry)
            {
                recipient = Address.OfContract(sender, _stateProvider.GetNonce(sender));
            }

            return(sender, recipient);
        }
        public Transaction BuildTransaction(long gaslimit, byte[] callData, Address sender, BlockHeader parent, IReleaseSpec spec,
                                            bool systemTransaction)
        {
            Transaction transaction = systemTransaction ? new SystemTransaction() : new Transaction();

            UInt256 fee = BaseFeeCalculator.Calculate(parent, spec);

            transaction.GasPrice            = fee;
            transaction.GasLimit            = gaslimit;
            transaction.To                  = _entryPointContractAddress;
            transaction.ChainId             = _specProvider.ChainId;
            transaction.Nonce               = _stateProvider.GetNonce(_signer.Address);
            transaction.Value               = 0;
            transaction.Data                = callData;
            transaction.Type                = TxType.EIP1559;
            transaction.DecodedMaxFeePerGas = fee;
            transaction.SenderAddress       = sender;

            if (!systemTransaction)
            {
                _signer.Sign(transaction);
            }
            transaction.Hash = transaction.CalculateHash();

            return(transaction);
        }
Exemple #7
0
        public void should_return_valid_pending_and_queued_transactions()
        {
            var nonce = 3;

            _stateProvider.GetNonce(_address).Returns(new UInt256(nonce));
            var transactions = GetTransactions();
            var info         = _infoProvider.GetInfo(transactions);

            info.Pending.Count.Should().Be(1);
            info.Queued.Count.Should().Be(1);

            var pending = info.Pending.First();

            pending.Key.Should().Be(_address);
            pending.Value.Count.Should().Be(3);
            pending.Value.Sum(v => v.Value.Length).Should().Be(4);
            VerifyNonceAndTransactions(pending.Value.ElementAt(0), 3, 2);
            VerifyNonceAndTransactions(pending.Value.ElementAt(1), 4, 1);
            VerifyNonceAndTransactions(pending.Value.ElementAt(2), 5, 1);

            var queued = info.Queued.First();

            queued.Key.Should().Be(_address);
            queued.Value.Count.Should().Be(4);
            queued.Value.Sum(v => v.Value.Length).Should().Be(4);
            VerifyNonceAndTransactions(queued.Value.ElementAt(0), 1, 1);
            VerifyNonceAndTransactions(queued.Value.ElementAt(1), 2, 1);
            VerifyNonceAndTransactions(queued.Value.ElementAt(2), 8, 1);
            VerifyNonceAndTransactions(queued.Value.ElementAt(3), 9, 1);
        }
        public void should_return_valid_pending_and_queued_transactions()
        {
            var nonce = 3;

            _stateProvider.GetNonce(_address).Returns(new UInt256(nonce));
            var transactions = GetTransactions();

            _txPool.GetPendingTransactions().Returns(transactions);
            var info = _infoProvider.GetInfo();

            info.Pending.Count.Should().Be(1);
            info.Queued.Count.Should().Be(1);

            var pending = info.Pending.First();

            pending.Key.Should().Be(_address);
            pending.Value.Count.Should().Be(3);
            VerifyNonceAndTransactions(pending.Value, 3);
            VerifyNonceAndTransactions(pending.Value, 4);
            VerifyNonceAndTransactions(pending.Value, 5);

            var queued = info.Queued.First();

            queued.Key.Should().Be(_address);
            queued.Value.Count.Should().Be(4);
            VerifyNonceAndTransactions(queued.Value, 1);
            VerifyNonceAndTransactions(queued.Value, 2);
            VerifyNonceAndTransactions(queued.Value, 8);
            VerifyNonceAndTransactions(queued.Value, 9);
        }
        private Transaction CreateReportMaliciousTransactionCore(PersistentReport persistentReport)
        {
            var transaction = ValidatorContract.ReportMalicious(persistentReport.ValidatorAddress, persistentReport.BlockNumber, persistentReport.Proof);

            transaction.Nonce = _stateProvider.GetNonce(ValidatorContract.NodeAddress);
            return(transaction);
        }
        public CallOutput Call(BlockHeader blockHeader, Transaction transaction)
        {
            _stateProvider.StateRoot = _blockTree.Head.StateRoot;
            BlockHeader header = new BlockHeader(blockHeader.Hash, Keccak.OfAnEmptySequenceRlp, blockHeader.Beneficiary,
                                                 blockHeader.Difficulty, blockHeader.Number + 1, (long)transaction.GasLimit, blockHeader.Timestamp + 1, Bytes.Empty);

            transaction.Nonce = _stateProvider.GetNonce(transaction.SenderAddress);
            transaction.Hash  = Transaction.CalculateHash(transaction);
            CallOutputTracer callOutputTracer = new CallOutputTracer();

            _transactionProcessor.CallAndRestore(transaction, header, callOutputTracer);
            _stateProvider.Reset();
            _storageProvider.Reset();
            return(new CallOutput {
                Error = callOutputTracer.Error, GasSpent = callOutputTracer.GasSpent, OutputData = callOutputTracer.ReturnValue
            });
        }
        public TxPoolInfo GetInfo()
        {
            var transactions        = _txPool.GetPendingTransactions();
            var groupedTransactions = transactions.GroupBy(t => t.SenderAddress);
            var pendingTransactions = new Dictionary <Address, IDictionary <ulong, Transaction> >();
            var queuedTransactions  = new Dictionary <Address, IDictionary <ulong, Transaction> >();

            foreach (var group in groupedTransactions)
            {
                var address = group.Key;
                if (address == null)
                {
                    continue;
                }

                var accountNonce  = _stateProvider.GetNonce(address);
                var expectedNonce = accountNonce;
                var pending       = new Dictionary <ulong, Transaction>();
                var queued        = new Dictionary <ulong, Transaction>();
                var transactionsGroupedByNonce = group.OrderBy(t => t.Nonce);

                foreach (var transaction in transactionsGroupedByNonce)
                {
                    var transactionNonce = (ulong)transaction.Nonce;

                    if (transaction.Nonce < accountNonce)
                    {
                        queued.Add(transactionNonce, transaction);
                        continue;
                    }

                    if (transaction.Nonce == accountNonce ||
                        accountNonce != expectedNonce && transaction.Nonce == expectedNonce)
                    {
                        pending.Add(transactionNonce, transaction);
                        expectedNonce = transaction.Nonce + 1;
                        continue;
                    }

                    queued.Add(transactionNonce, transaction);
                }

                if (pending.Any())
                {
                    pendingTransactions[address] = pending;
                }

                if (queued.Any())
                {
                    queuedTransactions[address] = queued;
                }
            }

            return(new TxPoolInfo(pendingTransactions, queuedTransactions));
        }
        private void QuickFail(Transaction tx, BlockHeader block, ITxTracer txTracer, bool eip658NotEnabled,
                               string?reason)
        {
            block.GasUsed += tx.GasLimit;

            Address recipient = tx.To ?? ContractAddress.From(
                tx.SenderAddress ?? Address.Zero,
                _stateProvider.GetNonce(tx.SenderAddress ?? Address.Zero));

            if (txTracer.IsTracingReceipt)
            {
                Keccak?stateRoot = null;
                if (eip658NotEnabled)
                {
                    _stateProvider.RecalculateStateRoot();
                    stateRoot = _stateProvider.StateRoot;
                }

                txTracer.MarkAsFailed(recipient, tx.GasLimit, Array.Empty <byte>(), reason ?? "invalid", stateRoot);
            }
        }
        public TransactionPoolInfo GetInfo(Transaction[] transactions)
        {
            var groupedTransactions = transactions.GroupBy(t => t.SenderAddress);
            var pendingTransactions = new Dictionary <Address, IDictionary <UInt256, Transaction[]> >();
            var queuedTransactions  = new Dictionary <Address, IDictionary <UInt256, Transaction[]> >();

            foreach (var group in groupedTransactions)
            {
                var address = group.Key;
                if (address == null)
                {
                    continue;
                }

                var accountNonce  = _stateProvider.GetNonce(address);
                var expectedNonce = accountNonce;
                var pending       = new Dictionary <UInt256, Transaction[]>();
                var queued        = new Dictionary <UInt256, Transaction[]>();
                var transactionsGroupedByNonce = group.OrderBy(t => t.Nonce).GroupBy(t => t.Nonce);

                foreach (var nonceGroup in transactionsGroupedByNonce)
                {
                    if (nonceGroup.Key < accountNonce)
                    {
                        queued.Add(nonceGroup.Key, nonceGroup.ToArray());
                        continue;
                    }

                    if (nonceGroup.Key == accountNonce ||
                        accountNonce != expectedNonce && nonceGroup.Key == expectedNonce)
                    {
                        pending.Add(nonceGroup.Key, nonceGroup.ToArray());
                        expectedNonce = nonceGroup.Key + 1;
                        continue;
                    }

                    queued.Add(nonceGroup.Key, nonceGroup.ToArray());
                }

                if (pending.Any())
                {
                    pendingTransactions[address] = pending;
                }

                if (queued.Any())
                {
                    queuedTransactions[address] = queued;
                }
            }

            return(new TransactionPoolInfo(pendingTransactions, queuedTransactions));
        }
            public AddingTxEventArgs CanAddTransaction(Block block, Transaction currentTx, IReadOnlySet <Transaction> transactionsInBlock, IStateProvider stateProvider)
            {
                AddingTxEventArgs args = new(transactionsInBlock.Count, currentTx, block, transactionsInBlock);

                long gasRemaining = block.Header.GasLimit - block.GasUsed;

                // No more gas available in block for any transactions,
                // the only case we have to really stop
                if (GasCostOf.Transaction > gasRemaining)
                {
                    return(args.Set(TxAction.Stop, "Block full"));
                }

                if (currentTx.SenderAddress is null)
                {
                    return(args.Set(TxAction.Skip, "Null sender"));
                }

                if (currentTx.GasLimit > gasRemaining)
                {
                    return(args.Set(TxAction.Skip, $"Not enough gas in block, gas limit {currentTx.GasLimit} > {gasRemaining}"));
                }

                if (transactionsInBlock.Contains(currentTx))
                {
                    return(args.Set(TxAction.Skip, "Transaction already in block"));
                }

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

                if (stateProvider.IsInvalidContractSender(spec, currentTx.SenderAddress))
                {
                    return(args.Set(TxAction.Skip, $"Sender is contract"));
                }

                UInt256 expectedNonce = stateProvider.GetNonce(currentTx.SenderAddress);

                if (expectedNonce != currentTx.Nonce)
                {
                    return(args.Set(TxAction.Skip, $"Invalid nonce - expected {expectedNonce}"));
                }

                UInt256 balance = stateProvider.GetBalance(currentTx.SenderAddress);

                if (!HasEnoughFounds(currentTx, balance, args, block, spec))
                {
                    return(args);
                }

                AddingTransaction?.Invoke(this, args);
                return(args);
            }
Exemple #15
0
        private void QuickFail(Transaction tx, BlockHeader block, ITxTracer txTracer, string reason)
        {
            if (tx.IsEip1559)
            {
                block.GasUsedEip1559 += tx.GasLimit;
            }
            else
            {
                block.GasUsedLegacy += tx.GasLimit;
            }

            Address recipient = tx.To ?? ContractAddress.From(tx.SenderAddress, _stateProvider.GetNonce(tx.SenderAddress));

            // TODO: this is possibly an unnecessary calculation inside EIP-658
            _stateProvider.RecalculateStateRoot();
            Keccak stateRoot = _specProvider.GetSpec(block.Number).IsEip658Enabled ? null : _stateProvider.StateRoot;

            if (txTracer.IsTracingReceipt)
            {
                txTracer.MarkAsFailed(recipient, tx.GasLimit, Array.Empty <byte>(), reason ?? "invalid", stateRoot);
            }
        }
        public long EstimateGas(Block block, Transaction transaction)
        {
            _stateProvider.StateRoot = _blockTree.Head.StateRoot;
            BlockHeader header = new BlockHeader(block.Hash, Keccak.OfAnEmptySequenceRlp, block.Beneficiary,
                                                 block.Difficulty, block.Number + 1, block.GasLimit, block.Timestamp + 1, Bytes.Empty);

            transaction.Nonce = _stateProvider.GetNonce(transaction.SenderAddress);
            transaction.Hash  = Nethermind.Core.Transaction.CalculateHash(transaction);
            CallOutputTracer callOutputTracer = new CallOutputTracer();

            _transactionProcessor.CallAndRestore(transaction, header, callOutputTracer);
            _stateProvider.Reset();
            _storageProvider.Reset();
            return(callOutputTracer.GasSpent);
        }
Exemple #17
0
        public static void ProcessTransaction(this ITransactionProcessorAdapter transactionProcessor,
                                              Block block,
                                              Transaction currentTx,
                                              BlockReceiptsTracer receiptsTracer,
                                              ProcessingOptions processingOptions,
                                              IStateProvider stateProvider)
        {
            if (processingOptions.ContainsFlag(ProcessingOptions.DoNotVerifyNonce))
            {
                currentTx.Nonce = stateProvider.GetNonce(currentTx.SenderAddress);
            }

            receiptsTracer.StartNewTxTrace(currentTx);
            transactionProcessor.Execute(currentTx, block.Header, receiptsTracer);
            receiptsTracer.EndTxTrace();
        }
Exemple #18
0
            public TestContext(bool forSealing, ReportingContractBasedValidator.Cache cache = null, Address[] initialValidators = null)
            {
                BlockHeader        parentHeader      = Build.A.BlockHeader.TestObject;
                IValidatorContract validatorContract = Substitute.For <IValidatorContract>();

                Address[] validators = initialValidators ?? new[] { MaliciousMinerAddress, NodeAddress };
                validatorContract.GetValidators(parentHeader).Returns(validators);

                ContractBasedValidator = new ContractBasedValidator(
                    validatorContract,
                    Substitute.For <IBlockTree>(),
                    Substitute.For <IReceiptFinder>(),
                    Substitute.For <IValidatorStore>(),
                    Substitute.For <IValidSealerStrategy>(),
                    Substitute.For <IAuRaBlockFinalizationManager>(),
                    parentHeader,
                    LimboLogs.Instance,
                    0,
                    PosdaoTransition,
                    forSealing);

                ContractBasedValidator.Validators ??= validators;

                ReportingValidatorContract = Substitute.For <IReportingValidatorContract>();
                ReportingValidatorContract.NodeAddress.Returns(NodeAddress);

                TxSender = Substitute.For <ITxSender>();
                ITxPool        txPool        = Substitute.For <ITxPool>();
                IStateProvider stateProvider = Substitute.For <IStateProvider>();
                ISpecProvider  specProvider  = Substitute.For <ISpecProvider>();

                stateProvider.GetNonce(ReportingValidatorContract.NodeAddress).Returns(UInt256.One);

                Validator = new ReportingContractBasedValidator(
                    ContractBasedValidator,
                    ReportingValidatorContract,
                    PosdaoTransition,
                    TxSender,
                    txPool,
                    new MiningConfig(),
                    stateProvider,
                    cache ?? new ReportingContractBasedValidator.Cache(),
                    specProvider,
                    Substitute.For <IGasPriceOracle>(),
                    LimboLogs.Instance);
            }
Exemple #19
0
        private TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, IBlockTracer blockTracer)
        {
            _receiptsTracer.SetOtherTracer(blockTracer);
            _receiptsTracer.StartNewBlockTrace(block);

            for (int i = 0; i < block.Transactions.Length; i++)
            {
                Transaction currentTx = block.Transactions[i];
                if ((processingOptions & ProcessingOptions.DoNotVerifyNonce) != 0)
                {
                    currentTx.Nonce = _stateProvider.GetNonce(currentTx.SenderAddress);
                }

                _receiptsTracer.StartNewTxTrace(currentTx.Hash);
                _transactionProcessor.Execute(currentTx, block.Header, _receiptsTracer);
                _receiptsTracer.EndTxTrace();

                TransactionProcessed?.Invoke(this, new TxProcessedEventArgs(i, currentTx, _receiptsTracer.TxReceipts[i]));
            }

            return(_receiptsTracer.TxReceipts);
        }
        private Transaction BuildSimulateValidationTransaction(
            UserOperation userOperation,
            BlockHeader parent,
            IReleaseSpec spec)
        {
            AbiSignature     abiSignature     = _entryPointContractAbi.Functions["simulateValidation"].GetCallInfo().Signature;
            UserOperationAbi userOperationAbi = userOperation.Abi;

            byte[] computedCallData = _abiEncoder.Encode(
                AbiEncodingStyle.IncludeSignature,
                abiSignature,
                userOperationAbi);

            Transaction transaction = _userOperationTxBuilder.BuildTransaction((long)userOperation.PreVerificationGas + (long)userOperation.VerificationGas,
                                                                               computedCallData,
                                                                               Address.Zero,
                                                                               parent,
                                                                               spec,
                                                                               _stateProvider.GetNonce(Address.Zero),
                                                                               true);

            return(transaction);
        }
        public TransactionReceipt Execute(
            Transaction transaction,
            BlockHeader block)
        {
            TransactionTrace trace = null;

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

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

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

            Address sender = transaction.SenderAddress;

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

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

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

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

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

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

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

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

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

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

                _stateProvider.CreateAccount(sender, 0);
            }

            BigInteger senderBalance = _stateProvider.GetBalance(sender);

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

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

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

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

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

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

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

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

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

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

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

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

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

                    env.Originator = sender;

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

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

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

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

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

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

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

                        statusCode = StatusCode.Success;
                    }

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

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

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

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

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

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

            block.GasUsed += spentGas;

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

            return(BuildTransactionReceipt(statusCode, logEntries.Any() ? logEntries.ToArray() : LogEntry.EmptyLogs, block.GasUsed, recipient));
        }
Exemple #22
0
        private Block PrepareBlock(Block parentBlock)
        {
            BlockHeader parentHeader = parentBlock.Header;

            if (parentHeader == null)
            {
                if (_logger.IsError)
                {
                    _logger.Error($"Preparing new block on top of {parentBlock.ToString(Block.Format.Short)} - parent header is null");
                }
                return(null);
            }

            if (_recentNotAllowedParent == parentBlock.Hash)
            {
                return(null);
            }

            if (!_sealer.CanSeal(parentHeader.Number + 1, parentHeader.Hash))
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Not allowed to sign block ({parentBlock.Number + 1})");
                }
                _recentNotAllowedParent = parentHeader.Hash;
                return(null);
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"Preparing new block on top of {parentBlock.ToString(Block.Format.Short)}");
            }

            UInt256 timestamp = _timestamp.EpochSeconds;

            BlockHeader header = new BlockHeader(
                parentBlock.Hash,
                Keccak.OfAnEmptySequenceRlp,
                Address.Zero,
                1,
                parentBlock.Number + 1,
                parentBlock.GasLimit,
                timestamp > parentBlock.Timestamp ? timestamp : parentBlock.Timestamp + 1,
                new byte[0]);

            // If the block isn't a checkpoint, cast a random vote (good enough for now)
            UInt256 number = header.Number;
            // Assemble the voting snapshot to check which votes make sense
            Snapshot snapshot     = _snapshotManager.GetOrCreateSnapshot(number - 1, header.ParentHash);
            bool     isEpochBlock = (ulong)number % 30000 == 0;

            if (!isEpochBlock)
            {
                // Gather all the proposals that make sense voting on
                List <Address> addresses = new List <Address>();
                foreach (var proposal in _proposals)
                {
                    Address address   = proposal.Key;
                    bool    authorize = proposal.Value;
                    if (_snapshotManager.IsValidVote(snapshot, address, authorize))
                    {
                        addresses.Add(address);
                    }
                }

                // If there's pending proposals, cast a vote on them
                if (addresses.Count > 0)
                {
                    header.Beneficiary = addresses[_cryptoRandom.NextInt(addresses.Count)];
                    header.Nonce       = _proposals[header.Beneficiary] ? Clique.NonceAuthVote : Clique.NonceDropVote;
                }
            }

            // Set the correct difficulty
            header.Difficulty      = CalculateDifficulty(snapshot, _address);
            header.TotalDifficulty = parentBlock.TotalDifficulty + header.Difficulty;
            if (_logger.IsDebug)
            {
                _logger.Debug($"Setting total difficulty to {parentBlock.TotalDifficulty} + {header.Difficulty}.");
            }

            // Set extra data
            int mainBytesLength   = Clique.ExtraVanityLength + Clique.ExtraSealLength;
            int signerBytesLength = isEpochBlock ? 20 * snapshot.Signers.Count : 0;
            int extraDataLength   = mainBytesLength + signerBytesLength;

            header.ExtraData = new byte[extraDataLength];

            byte[] clientName = Encoding.UTF8.GetBytes("Nethermind");
            Array.Copy(clientName, header.ExtraData, clientName.Length);

            if (isEpochBlock)
            {
                for (int i = 0; i < snapshot.Signers.Keys.Count; i++)
                {
                    Address signer = snapshot.Signers.Keys[i];
                    int     index  = Clique.ExtraVanityLength + 20 * i;
                    Array.Copy(signer.Bytes, 0, header.ExtraData, index, signer.Bytes.Length);
                }
            }

            // Mix digest is reserved for now, set to empty
            header.MixHash = Keccak.Zero;
            // Ensure the timestamp has the correct delay
            header.Timestamp = parentBlock.Timestamp + _config.BlockPeriod;
            if (header.Timestamp < _timestamp.EpochSeconds)
            {
                header.Timestamp = new UInt256(_timestamp.EpochSeconds);
            }

            var transactions = _transactionPool.GetPendingTransactions().OrderBy(t => t.Nonce).ThenByDescending(t => t.GasPrice).ThenBy(t => t.GasLimit); // by nonce in case there are two transactions for the same account

            var        selectedTxs  = new List <Transaction>();
            BigInteger gasRemaining = header.GasLimit;

            if (_logger.IsDebug)
            {
                _logger.Debug($"Collecting pending transactions at min gas price {MinGasPriceForMining} and block gas limit {gasRemaining}.");
            }

            int total = 0;

            _stateProvider.StateRoot = parentHeader.StateRoot;

            Dictionary <Address, UInt256> nonces = new Dictionary <Address, UInt256>();

            foreach (Transaction transaction in transactions)
            {
                total++;
                if (transaction.SenderAddress == null)
                {
                    if (_logger.IsError)
                    {
                        _logger.Error("Rejecting null sender pending transaction.");
                    }
                    continue;
                }

                if (transaction.GasPrice < MinGasPriceForMining)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Rejecting transaction - gas price ({transaction.GasPrice}) too low (min gas price: {MinGasPriceForMining}.");
                    }
                    continue;
                }

                if (transaction.Nonce != _stateProvider.GetNonce(transaction.SenderAddress) && (!nonces.ContainsKey(transaction.SenderAddress) || nonces[transaction.SenderAddress] + 1 != transaction.Nonce))
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Rejecting transaction based on nonce.");
                    }
                    continue;
                }

                if (transaction.GasLimit > gasRemaining)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Rejecting transaction - gas limit ({transaction.GasPrice}) more than remaining gas ({gasRemaining}).");
                    }
                    continue;
                }

                UInt256 requiredBalance = transaction.GasLimit + transaction.Value;
                UInt256 balance         = _stateProvider.GetBalance(transaction.SenderAddress);
                if (transaction.GasLimit + transaction.Value > balance)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Rejecting transaction - insufficient balance - required {requiredBalance}, actual {balance}.");
                    }
                    continue;
                }

                selectedTxs.Add(transaction);
                nonces[transaction.SenderAddress] = transaction.Nonce;
                gasRemaining -= transaction.GasLimit;
            }

            if (_logger.IsDebug)
            {
                _logger.Debug($"Collected {selectedTxs.Count} out of {total} pending transactions.");
            }

            Block block = new Block(header, selectedTxs, new BlockHeader[0]);

            header.TransactionsRoot = block.CalculateTransactionsRoot();
            block.Author            = _address;
            return(block);
        }
Exemple #23
0
        public IEnumerable <Transaction> SelectTransactions(long gasLimit)
        {
            UInt256 GetCurrentNonce(IDictionary <Address, UInt256> noncesDictionary, Address address)
            {
                if (!noncesDictionary.TryGetValue(address, out var nonce))
                {
                    noncesDictionary[address] = nonce = _stateProvider.GetNonce(address);
                }

                return(nonce);
            }

            UInt256 GetRemainingBalance(IDictionary <Address, UInt256> balances, Address address)
            {
                if (!balances.TryGetValue(address, out var balance))
                {
                    balances[address] = balance = _stateProvider.GetBalance(address);
                }

                return(balance);
            }

            bool HasEnoughFounds(IDictionary <Address, UInt256> balances, Transaction transaction)
            {
                var balance = GetRemainingBalance(balances, transaction.SenderAddress);
                var transactionPotentialCost = transaction.GasPrice * (ulong)transaction.GasLimit + transaction.Value;

                if (balance < transactionPotentialCost)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Rejecting transaction - transaction cost ({transactionPotentialCost}) is higher than sender balance ({balance}).");
                    }
                    return(false);
                }
                else
                {
                    balances[transaction.SenderAddress] = balance - transactionPotentialCost;
                    return(true);
                }
            }

            var pendingTransactions = _transactionPool.GetPendingTransactions();
            var transactions        = pendingTransactions.OrderBy(t => t.Nonce).ThenByDescending(t => t.GasPrice).ThenBy(t => t.GasLimit);
            IDictionary <Address, UInt256> remainingBalance = new Dictionary <Address, UInt256>();
            Dictionary <Address, UInt256>  nonces           = new Dictionary <Address, UInt256>();
            List <Transaction>             selected         = new List <Transaction>();
            long gasRemaining = gasLimit;

            if (_logger.IsDebug)
            {
                _logger.Debug($"Collecting pending transactions at min gas price {_minGasPriceForMining} and block gas limit {gasRemaining}.");
            }

            foreach (Transaction transaction in transactions)
            {
                if (transaction.SenderAddress == null)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace("Rejecting null sender pending transaction.");
                    }
                    continue;
                }

                if (transaction.GasPrice < _minGasPriceForMining)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Rejecting transaction - gas price ({transaction.GasPrice}) too low (min gas price: {_minGasPriceForMining}.");
                    }
                    continue;
                }

                if (GetCurrentNonce(nonces, transaction.SenderAddress) != transaction.Nonce)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Rejecting transaction based on nonce.");
                    }
                    continue;
                }

                if (transaction.GasLimit > gasRemaining)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Rejecting transaction - gas limit ({transaction.GasLimit}) more than remaining gas ({gasRemaining}).");
                    }
                    break;
                }

                if (!HasEnoughFounds(remainingBalance, transaction))
                {
                    continue;
                }

                selected.Add(transaction);
                nonces[transaction.SenderAddress] = transaction.Nonce + 1;
                gasRemaining -= transaction.GasLimit;
            }

            if (_logger.IsDebug)
            {
                _logger.Debug($"Collected {selected.Count} out of {pendingTransactions.Length} pending transactions.");
            }

            return(selected);
        }
        public IEnumerable <Transaction> GetTransactions(BlockHeader parent, long gasLimit)
        {
            IDictionary <Address, HashSet <UInt256> > usedAccessList = new Dictionary <Address, HashSet <UInt256> >();
            // IList<UserOperation> userOperationsToInclude = new List<UserOperation>();
            IDictionary <Address, IList <UserOperation> > userOperationsToIncludeByEntryPoint =
                new Dictionary <Address, IList <UserOperation> >();
            ulong gasUsed = 0;

            IList <Tuple <Address, UserOperation> > combinedUserOperations = new List <Tuple <Address, UserOperation> >();

            foreach (Address entryPoint in _userOperationPools.Keys)
            {
                IEnumerable <UserOperation> entryPointUserOperations =
                    _userOperationPools[entryPoint]
                    .GetUserOperations()
                    .Where(op => op.MaxFeePerGas >= parent.BaseFeePerGas);

                foreach (UserOperation userOperation in entryPointUserOperations)
                {
                    combinedUserOperations.Add(Tuple.Create(entryPoint, userOperation));
                }
            }
            IList <Tuple <Address, UserOperation> > sortedUserOperations =
                combinedUserOperations.OrderByDescending(
                    op =>
                    CalculateUserOperationPremiumGasPrice(op.Item2, parent.BaseFeePerGas))
                .ToList();

            foreach (Tuple <Address, UserOperation> addressedUserOperation in sortedUserOperations)
            {
                (Address entryPoint, UserOperation userOperation) = addressedUserOperation;

                ulong userOperationTotalGasLimit = (ulong)userOperation.CallGas +
                                                   (ulong)userOperation.PreVerificationGas +
                                                   (ulong)userOperation.VerificationGas;

                if (gasUsed + userOperationTotalGasLimit > (ulong)gasLimit)
                {
                    continue;
                }

                // no intersect of accessed addresses between ops
                if (userOperation.AccessList.AccessListOverlaps(usedAccessList))
                {
                    continue;
                }

                // simulate again to make sure the op is still valid
                ResultWrapper <Keccak> result = _userOperationSimulators[entryPoint].Simulate(userOperation, parent);
                if (result.Result != Result.Success)
                {
                    //if (_logger.IsDebug) commented out for testing
                    {
                        _logger.Debug($"UserOperation {userOperation.Hash} resimulation unsuccessful: {result.Result.Error}");
                        // TODO: Remove logging, just for testing
                        _logger.Info($"UserOperation {userOperation.Hash} resimulation unsuccessful: {result.Result.Error}");

                        bool removeResult = _userOperationPools[entryPoint].RemoveUserOperation(userOperation.Hash);
                        if (_logger.IsDebug)
                        {
                            _logger.Debug(
                                removeResult ?
                                "Removed UserOperation {userOperation.Hash} from Pool"
                                : "Failed to remove UserOperation {userOperation} from Pool");
                        }
                    }

                    continue;
                }

                gasUsed += userOperationTotalGasLimit;

                // add user operation with correct entryPoint
                if (userOperationsToIncludeByEntryPoint.TryGetValue(entryPoint, out IList <UserOperation>?userOperations))
                {
                    userOperations.Add(userOperation);
                }
                else
                {
                    userOperationsToIncludeByEntryPoint[entryPoint] = new List <UserOperation> {
                        userOperation
                    };
                }

                // add userOp accessList to combined list
                foreach (KeyValuePair <Address, HashSet <UInt256> > kv in userOperation.AccessList.Data)
                {
                    if (usedAccessList.ContainsKey(kv.Key))
                    {
                        usedAccessList[kv.Key].UnionWith(kv.Value);
                    }
                    else
                    {
                        usedAccessList[kv.Key] = kv.Value;
                    }
                }
            }

            if (userOperationsToIncludeByEntryPoint.Count == 0)
            {
                yield break;
            }

            UInt256 initialNonce = _stateProvider.GetNonce(_signer.Address);
            UInt256 txsBuilt     = 0;

            // build transaction for each entryPoint with ops to be included
            foreach (KeyValuePair <Address, UserOperationTxBuilder> kv in _userOperationTxBuilders)
            {
                Address entryPoint = kv.Key;
                IUserOperationTxBuilder txBuilder = kv.Value;

                bool foundUserOperations =
                    userOperationsToIncludeByEntryPoint.TryGetValue(entryPoint, out IList <UserOperation>?userOperationsToInclude);
                if (!foundUserOperations)
                {
                    continue;
                }

                long totalGasUsed = userOperationsToInclude !.Aggregate((long)0,
                                                                        (sum, op) =>
                                                                        sum +
                                                                        (long)op.CallGas +
                                                                        (long)op.PreVerificationGas +
                                                                        (long)op.VerificationGas);

                // build test transaction to make sure it succeeds as a batch of ops
                Transaction userOperationTransaction =
                    txBuilder.BuildTransactionFromUserOperations(
                        userOperationsToInclude !,
                        parent,
                        totalGasUsed,
                        initialNonce,
                        _specProvider.GetSpec(parent.Number + 1));
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Constructed tx from {userOperationsToInclude!.Count} userOperations: {userOperationTransaction.Hash}");
                }
                // TODO: Remove logging, just for testing
                _logger.Info($"Constructed tx from {userOperationsToInclude!.Count} userOperations: {userOperationTransaction.Hash}");

                BlockchainBridge.CallOutput callOutput = _userOperationSimulators[entryPoint].EstimateGas(parent, userOperationTransaction, CancellationToken.None);
                FailedOp?failedOp = txBuilder.DecodeEntryPointOutputError(callOutput.OutputData);
                if (failedOp is not null || callOutput.Error != null)
                {
                    // TODO punish paymaster
                    continue;
                }

                // construct tx with previously estimated gas limit
                Transaction updatedUserOperationTransaction =
                    _userOperationTxBuilders[entryPoint].BuildTransactionFromUserOperations(
                        userOperationsToInclude,
                        parent,
                        callOutput.GasSpent + 200000,
                        initialNonce + txsBuilt,
                        _specProvider.GetSpec(parent.Number + 1));

                txsBuilt++;
                yield return(updatedUserOperationTransaction);
            }
        }
Exemple #25
0
        private List <string> RunAssertions(LegacyBlockchainTest test, Block headBlock, IStorageProvider storageProvider, IStateProvider stateProvider)
        {
            if (test.PostStateRoot != null)
            {
                return(test.PostStateRoot != stateProvider.StateRoot ? new List <string> {
                    "state root mismatch"
                } : Enumerable.Empty <string>().ToList());
            }

            TestBlockHeaderJson testHeaderJson = test.Blocks
                                                 .Where(b => b.BlockHeader != null)
                                                 .SingleOrDefault(b => new Keccak(b.BlockHeader.Hash) == headBlock.Hash)?.BlockHeader ?? test.GenesisBlockHeader;
            BlockHeader   testHeader  = JsonToBlockchainTest.Convert(testHeaderJson);
            List <string> differences = new List <string>();

            var deletedAccounts = test.Pre.Where(pre => !test.PostState.ContainsKey(pre.Key));

            foreach (KeyValuePair <Address, AccountState> deletedAccount in deletedAccounts)
            {
                if (stateProvider.AccountExists(deletedAccount.Key))
                {
                    differences.Add($"Pre state account {deletedAccount.Key} was not deleted as expected.");
                }
            }

            foreach (KeyValuePair <Address, AccountState> accountState in test.PostState)
            {
                int differencesBefore = differences.Count;

                if (differences.Count > 8)
                {
                    Console.WriteLine("More than 8 differences...");
                    break;
                }

                bool       accountExists = stateProvider.AccountExists(accountState.Key);
                BigInteger?balance       = accountExists ? stateProvider.GetBalance(accountState.Key) : (BigInteger?)null;
                BigInteger?nonce         = accountExists ? stateProvider.GetNonce(accountState.Key) : (BigInteger?)null;

                if (accountState.Value.Balance != balance)
                {
                    differences.Add($"{accountState.Key} balance exp: {accountState.Value.Balance}, actual: {balance}, diff: {balance - accountState.Value.Balance}");
                }

                if (accountState.Value.Nonce != nonce)
                {
                    differences.Add($"{accountState.Key} nonce exp: {accountState.Value.Nonce}, actual: {nonce}");
                }

                byte[] code = accountExists ? stateProvider.GetCode(accountState.Key) : new byte[0];
                if (!Bytes.AreEqual(accountState.Value.Code, code))
                {
                    differences.Add($"{accountState.Key} code exp: {accountState.Value.Code?.Length}, actual: {code?.Length}");
                }

                if (differences.Count != differencesBefore)
                {
                    _logger.Info($"ACCOUNT STATE ({accountState.Key}) HAS DIFFERENCES");
                }

                differencesBefore = differences.Count;

                KeyValuePair <UInt256, byte[]>[] clearedStorages = new KeyValuePair <UInt256, byte[]> [0];
                if (test.Pre.ContainsKey(accountState.Key))
                {
                    clearedStorages = test.Pre[accountState.Key].Storage.Where(s => !accountState.Value.Storage.ContainsKey(s.Key)).ToArray();
                }

                foreach (KeyValuePair <UInt256, byte[]> clearedStorage in clearedStorages)
                {
                    byte[] value = !stateProvider.AccountExists(accountState.Key) ? Bytes.Empty : storageProvider.Get(new StorageAddress(accountState.Key, clearedStorage.Key));
                    if (!value.IsZero())
                    {
                        differences.Add($"{accountState.Key} storage[{clearedStorage.Key}] exp: 0x00, actual: {value.ToHexString(true)}");
                    }
                }

                foreach (KeyValuePair <UInt256, byte[]> storageItem in accountState.Value.Storage)
                {
                    byte[] value = !stateProvider.AccountExists(accountState.Key) ? Bytes.Empty : storageProvider.Get(new StorageAddress(accountState.Key, storageItem.Key)) ?? new byte[0];
                    if (!Bytes.AreEqual(storageItem.Value, value))
                    {
                        differences.Add($"{accountState.Key} storage[{storageItem.Key}] exp: {storageItem.Value.ToHexString(true)}, actual: {value.ToHexString(true)}");
                    }
                }

                if (differences.Count != differencesBefore)
                {
                    _logger.Info($"ACCOUNT STORAGE ({accountState.Key}) HAS DIFFERENCES");
                }
            }

            BigInteger gasUsed = headBlock.Header.GasUsed;

            if ((testHeader?.GasUsed ?? 0) != gasUsed)
            {
                differences.Add($"GAS USED exp: {testHeader?.GasUsed ?? 0}, actual: {gasUsed}");
            }

            if (headBlock.Transactions.Any() && testHeader.Bloom.ToString() != headBlock.Header.Bloom.ToString())
            {
                differences.Add($"BLOOM exp: {testHeader.Bloom}, actual: {headBlock.Header.Bloom}");
            }

            if (testHeader.StateRoot != stateProvider.StateRoot)
            {
                differences.Add($"STATE ROOT exp: {testHeader.StateRoot}, actual: {stateProvider.StateRoot}");
            }

            if (testHeader.TxRoot != headBlock.Header.TxRoot)
            {
                differences.Add($"TRANSACTIONS ROOT exp: {testHeader.TxRoot}, actual: {headBlock.Header.TxRoot}");
            }

            if (testHeader.ReceiptsRoot != headBlock.Header.ReceiptsRoot)
            {
                differences.Add($"RECEIPT ROOT exp: {testHeader.ReceiptsRoot}, actual: {headBlock.Header.ReceiptsRoot}");
            }

            if (test.LastBlockHash != headBlock.Hash)
            {
                differences.Add($"LAST BLOCK HASH exp: {test.LastBlockHash}, actual: {headBlock.Hash}");
            }

            foreach (string difference in differences)
            {
                _logger.Info(difference);
            }

            return(differences);
        }
 public BigInteger GetNonce(Address address)
 {
     return(_stateProvider.GetNonce(address));
 }
        public IEnumerable <Transaction> SelectTransactions(long gasLimit)
        {
            UInt256 GetCurrentNonce(IDictionary <Address, UInt256> noncesDictionary, Address address)
            {
                if (!noncesDictionary.TryGetValue(address, out var nonce))
                {
                    noncesDictionary[address] = nonce = _stateProvider.GetNonce(address);
                }

                return(nonce);
            }

            UInt256 GetRemainingBalance(IDictionary <Address, UInt256> balances, Address address)
            {
                if (!balances.TryGetValue(address, out var balance))
                {
                    balances[address] = balance = _stateProvider.GetBalance(address);
                }

                return(balance);
            }

            bool HasEnoughFounds(IDictionary <Address, UInt256> balances, Transaction transaction)
            {
                var balance = GetRemainingBalance(balances, transaction.SenderAddress);
                var transactionPotentialCost = transaction.GasPrice * (ulong)transaction.GasLimit + transaction.Value;

                if (balance < transactionPotentialCost)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Rejecting transaction - transaction cost ({transactionPotentialCost}) is higher than sender balance ({balance}).");
                    }
                    return(false);
                }

                balances[transaction.SenderAddress] = balance - transactionPotentialCost;
                return(true);
            }

            var pendingTransactions = _transactionPool.GetPendingTransactions();
            var transactions        = pendingTransactions.OrderBy(t => t.Nonce).ThenByDescending(t => t.GasPrice).ThenBy(t => t.GasLimit);
            IDictionary <Address, UInt256> remainingBalance = new Dictionary <Address, UInt256>();
            Dictionary <Address, UInt256>  nonces           = new Dictionary <Address, UInt256>();
            List <Transaction>             selected         = new List <Transaction>();
            long gasRemaining = gasLimit;

            if (_logger.IsDebug)
            {
                _logger.Debug($"Collecting pending transactions at min gas price {_minGasPriceForMining} and block gas limit {gasRemaining}.");
            }

            foreach (Transaction tx in transactions)
            {
                if (gasRemaining < Transaction.BaseTxGasCost)
                {
                    continue;
                }

                if (tx.GasLimit > gasRemaining)
                {
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Rejecting (tx gas limit {tx.GasLimit} above remaining block gas {gasRemaining}) {tx.ToShortString()}");
                    }
                    continue;
                }

                if (tx.SenderAddress == null)
                {
                    _transactionPool.RemoveTransaction(tx.Hash, 0);
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Rejecting (null sender) {tx.ToShortString()}");
                    }
                    continue;
                }

                if (tx.GasPrice < _minGasPriceForMining)
                {
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Rejecting (gas price too low - min gas price: {_minGasPriceForMining}) {tx.ToShortString()}");
                    }
                    continue;
                }

                UInt256 expectedNonce = GetCurrentNonce(nonces, tx.SenderAddress);
                if (expectedNonce != tx.Nonce)
                {
                    if (tx.Nonce < expectedNonce)
                    {
                        _transactionPool.RemoveTransaction(tx.Hash, 0);
                    }

                    if (tx.Nonce > expectedNonce + 16)
                    {
                        _transactionPool.RemoveTransaction(tx.Hash, 0);
                    }

                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Rejecting (invalid nonce - expected {expectedNonce}) {tx.ToShortString()}");
                    }
                    continue;
                }

                if (!HasEnoughFounds(remainingBalance, tx))
                {
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Rejecting (sender balance too low) {tx.ToShortString()}");
                    }
                    continue;
                }

                selected.Add(tx);
                nonces[tx.SenderAddress] = tx.Nonce + 1;
                gasRemaining            -= tx.GasLimit;
            }

            if (_logger.IsDebug)
            {
                _logger.Debug($"Collected {selected.Count} out of {pendingTransactions.Length} pending transactions.");
            }

            return(selected);
        }
        public void Execute(Transaction transaction, BlockHeader block, ITxTracer txTracer, bool readOnly)
        {
            IReleaseSpec spec      = _specProvider.GetSpec(block.Number);
            Address      recipient = transaction.To;
            UInt256      value     = transaction.Value;
            UInt256      gasPrice  = transaction.GasPrice;
            long         gasLimit  = (long)transaction.GasLimit;

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

            Address sender = transaction.SenderAddress;

            if (_logger.IsTrace)
            {
                _logger.Trace($"Executing tx {transaction.Hash}");
            }

            if (sender == null)
            {
                TraceLogInvalidTx(transaction, "SENDER_NOT_SPECIFIED");
                if (txTracer.IsTracingReceipt)
                {
                    txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit);
                }
                return;
            }

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

            if (_logger.IsTrace)
            {
                _logger.Trace($"Intrinsic gas calculated for {transaction.Hash}: " + intrinsicGas);
            }

            if (gasLimit < intrinsicGas)
            {
                TraceLogInvalidTx(transaction, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {gasLimit} < {intrinsicGas}");
                if (txTracer.IsTracingReceipt)
                {
                    txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit);
                }
                return;
            }

            if (gasLimit > block.GasLimit - block.GasUsed)
            {
                TraceLogInvalidTx(transaction, $"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}");
                if (txTracer.IsTracingReceipt)
                {
                    txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit);
                }
                return;
            }

            if (!_stateProvider.AccountExists(sender))
            {
                TraceLogInvalidTx(transaction, $"SENDER_ACCOUNT_DOES_NOT_EXIST {sender}");
                if (gasPrice == UInt256.Zero)
                {
                    _stateProvider.CreateAccount(sender, UInt256.Zero);
                }
            }

            UInt256 senderBalance = _stateProvider.GetBalance(sender);

            if ((ulong)intrinsicGas * gasPrice + value > senderBalance)
            {
                TraceLogInvalidTx(transaction, $"INSUFFICIENT_SENDER_BALANCE: ({sender})_BALANCE = {senderBalance}");
                if (txTracer.IsTracingReceipt)
                {
                    txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit);
                }
                return;
            }

            if (transaction.Nonce != _stateProvider.GetNonce(sender))
            {
                TraceLogInvalidTx(transaction, $"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(sender)})");
                if (txTracer.IsTracingReceipt)
                {
                    txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit);
                }
                return;
            }

            _stateProvider.IncrementNonce(sender);
            _stateProvider.SubtractFromBalance(sender, (ulong)gasLimit * gasPrice, spec);

            // TODO: I think we can skip this commit and decrease the tree operations this way
            _stateProvider.Commit(_specProvider.GetSpec(block.Number), txTracer.IsTracingState ? txTracer : null);

            long unspentGas = gasLimit - intrinsicGas;
            long spentGas   = gasLimit;

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

            _stateProvider.SubtractFromBalance(sender, value, spec);
            byte statusCode = StatusCode.Failure;
            TransactionSubstate substate = null;

            try
            {
                if (transaction.IsContractCreation)
                {
                    recipient = Address.OfContract(sender, _stateProvider.GetNonce(sender) - 1);
                    if (_stateProvider.AccountExists(recipient))
                    {
                        if ((_virtualMachine.GetCachedCodeInfo(recipient)?.MachineCode?.Length ?? 0) != 0 || _stateProvider.GetNonce(recipient) != 0)
                        {
                            if (_logger.IsTrace)
                            {
                                _logger.Trace($"Contract collision at {recipient}"); // the account already owns the contract with the code
                            }

                            throw new TransactionCollisionException();
                        }

                        _stateProvider.UpdateStorageRoot(recipient, Keccak.EmptyTreeHash);
                    }
                }

                bool isPrecompile = recipient.IsPrecompiled(spec);

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

                env.Originator = sender;

                ExecutionType executionType = transaction.IsContractCreation ? ExecutionType.Create : ExecutionType.Call;
                using (EvmState state = new EvmState(unspentGas, env, executionType, isPrecompile, true, false))
                {
                    substate   = _virtualMachine.Run(state, spec, txTracer);
                    unspentGas = state.GasAvailable;
                }

                if (substate.ShouldRevert || substate.IsError)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace("Restoring state from before transaction");
                    }
                    _stateProvider.Restore(stateSnapshot);
                    _storageProvider.Restore(storageSnapshot);
                }
                else
                {
                    // tks: there is similar code fo contract creation from init and from CREATE
                    // this may lead to inconsistencies (however it is tested extensively in blockchain tests)
                    if (transaction.IsContractCreation)
                    {
                        long codeDepositGasCost = substate.Output.Length * GasCostOf.CodeDeposit;
                        if (spec.IsEip170Enabled && substate.Output.Length > 0x6000)
                        {
                            codeDepositGasCost = long.MaxValue;
                        }

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

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

                    foreach (Address toBeDestroyed in substate.DestroyList)
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Trace($"Destroying account {toBeDestroyed}");
                        }
                        _stateProvider.DeleteAccount(toBeDestroyed);
                    }

                    statusCode = StatusCode.Success;
                }

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

            if (_logger.IsTrace)
            {
                _logger.Trace("Gas spent: " + spentGas);
            }

            Address gasBeneficiary = block.GasBeneficiary;

            if (statusCode == StatusCode.Failure || !(substate?.DestroyList.Contains(gasBeneficiary) ?? false))
            {
                if (!_stateProvider.AccountExists(gasBeneficiary))
                {
                    _stateProvider.CreateAccount(gasBeneficiary, (ulong)spentGas * gasPrice);
                }
                else
                {
                    _stateProvider.AddToBalance(gasBeneficiary, (ulong)spentGas * gasPrice, spec);
                }
            }

            if (!readOnly)
            {
                _storageProvider.Commit(txTracer.IsTracingState ? txTracer : null);
                _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : null);
            }
            else
            {
                _storageProvider.Reset();
                _stateProvider.Reset();
            }

            if (!readOnly)
            {
                block.GasUsed += spentGas;
            }

            if (txTracer.IsTracingReceipt)
            {
                if (statusCode == StatusCode.Failure)
                {
                    txTracer.MarkAsFailed(recipient, (long)transaction.GasLimit);
                }
                else
                {
                    txTracer.MarkAsSuccess(recipient, spentGas, substate.Output, substate.Logs.Any() ? substate.Logs.ToArray() : LogEntry.EmptyLogs);
                }
            }
        }
Exemple #29
0
        private void RunAssertions(BlockchainTest test, Block headBlock, IStorageProvider storageProvider, IStateProvider stateProvider)
        {
            TestBlockHeaderJson testHeaderJson = test.Blocks
                                                 .Where(b => b.BlockHeader != null)
                                                 .SingleOrDefault(b => new Keccak(b.BlockHeader.Hash) == headBlock.Hash)?.BlockHeader ?? test.GenesisBlockHeader;
            BlockHeader   testHeader  = Convert(testHeaderJson);
            List <string> differences = new List <string>();

            foreach (KeyValuePair <Address, AccountState> accountState in test.PostState)
            {
                int differencesBefore = differences.Count;

                if (differences.Count > 8)
                {
                    Console.WriteLine("More than 8 differences...");
                    break;
                }

                bool       accountExists = stateProvider.AccountExists(accountState.Key);
                BigInteger?balance       = accountExists ? stateProvider.GetBalance(accountState.Key) : (BigInteger?)null;
                BigInteger?nonce         = accountExists ? stateProvider.GetNonce(accountState.Key) : (BigInteger?)null;

                if (accountState.Value.Balance != balance)
                {
                    differences.Add($"{accountState.Key} balance exp: {accountState.Value.Balance}, actual: {balance}, diff: {balance - accountState.Value.Balance}");
                }

                if (accountState.Value.Nonce != nonce)
                {
                    differences.Add($"{accountState.Key} nonce exp: {accountState.Value.Nonce}, actual: {nonce}");
                }

                byte[] code = accountExists ? stateProvider.GetCode(accountState.Key) : new byte[0];
                if (!Bytes.UnsafeCompare(accountState.Value.Code, code))
                {
                    differences.Add($"{accountState.Key} code exp: {accountState.Value.Code?.Length}, actual: {code?.Length}");
                }

                if (differences.Count != differencesBefore)
                {
                    _logger.Info($"ACCOUNT STATE ({accountState.Key}) HAS DIFFERENCES");
                }

                differencesBefore = differences.Count;

                foreach (KeyValuePair <BigInteger, byte[]> storageItem in accountState.Value.Storage)
                {
                    byte[] value = storageProvider.Get(new StorageAddress(accountState.Key, storageItem.Key)) ?? new byte[0];
                    if (!Bytes.UnsafeCompare(storageItem.Value, value))
                    {
                        differences.Add($"{accountState.Key} storage[{storageItem.Key}] exp: {Hex.FromBytes(storageItem.Value, true)}, actual: {Hex.FromBytes(value, true)}");
                    }
                }

                if (differences.Count != differencesBefore)
                {
                    _logger.Info($"ACCOUNT STORAGE ({accountState.Key}) HAS DIFFERENCES");
                }
            }


            BigInteger gasUsed = headBlock.Header.GasUsed;

            if ((testHeader?.GasUsed ?? 0) != gasUsed)
            {
                differences.Add($"GAS USED exp: {testHeader?.GasUsed ?? 0}, actual: {gasUsed}");
            }

            if (headBlock.Transactions.Any() && testHeader.Bloom.ToString() != headBlock.Header.Bloom.ToString())
            {
                differences.Add($"BLOOM exp: {testHeader.Bloom}, actual: {headBlock.Header.Bloom}");
            }

            if (testHeader.StateRoot != stateProvider.StateRoot)
            {
                differences.Add($"STATE ROOT exp: {testHeader.StateRoot}, actual: {stateProvider.StateRoot}");
            }

            if (testHeader.TransactionsRoot != headBlock.Header.TransactionsRoot)
            {
                differences.Add($"TRANSACTIONS ROOT exp: {testHeader.TransactionsRoot}, actual: {headBlock.Header.TransactionsRoot}");
            }

            if (testHeader.ReceiptsRoot != headBlock.Header.ReceiptsRoot)
            {
                differences.Add($"RECEIPT ROOT exp: {testHeader.ReceiptsRoot}, actual: {headBlock.Header.ReceiptsRoot}");
            }

            if (test.LastBlockHash != headBlock.Hash)
            {
                differences.Add($"LAST BLOCK HASH exp: {test.LastBlockHash}, actual: {headBlock.Hash}");
            }

            foreach (string difference in differences)
            {
                _logger.Info(difference);
            }

            Assert.Zero(differences.Count, "differences");
        }