예제 #1
0
        public void should_add_pending_transactions()
        {
            _transactionPool = CreatePool(_noTransactionStorage);
            var transactions = AddTransactionsToPool();

            _transactionPool.GetPendingTransactions().Length.Should().Be(transactions.Length);
        }
예제 #2
0
        public void should_delete_pending_transactions()
        {
            _transactionPool = CreatePool(_noTransactionStorage);
            var transactions = AddTransactionsToPool();

            DeleteTransactionsFromPool(transactions);
            _transactionPool.GetPendingTransactions().Should().BeEmpty();
        }
예제 #3
0
        public void should_add_valid_transactions()
        {
            _transactionPool = CreatePool(_noTransactionStorage);
            Transaction          tx     = Build.A.Transaction.SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA, RopstenSpecProvider.ByzantiumBlockNumber).TestObject;
            AddTransactionResult result = _transactionPool.AddTransaction(tx, 1);

            _transactionPool.GetPendingTransactions().Length.Should().Be(1);
            result.Should().Be(AddTransactionResult.Added);
        }
예제 #4
0
        public void should_ignore_old_scheme_signatures()
        {
            _transactionPool = CreatePool(_noTransactionStorage);
            Transaction          tx     = Build.A.Transaction.SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA, 1).TestObject;
            AddTransactionResult result = _transactionPool.AddTransaction(tx, 1);

            _transactionPool.GetPendingTransactions().Length.Should().Be(0);
            result.Should().Be(AddTransactionResult.OldScheme);
        }
예제 #5
0
        public void should_ignore_transactions_with_different_chain_id()
        {
            _transactionPool = CreatePool(_noTransactionStorage);
            EthereumEcdsa        ecdsa  = new EthereumEcdsa(MainNetSpecProvider.Instance, _logManager);
            Transaction          tx     = Build.A.Transaction.SignedAndResolved(ecdsa, TestItem.PrivateKeyA, MainNetSpecProvider.ByzantiumBlockNumber).TestObject;
            AddTransactionResult result = _transactionPool.AddTransaction(tx, 1);

            _transactionPool.GetPendingTransactions().Length.Should().Be(0);
            result.Should().Be(AddTransactionResult.InvalidChainId);
        }
예제 #6
0
        public void should_ignore_already_known()
        {
            _transactionPool = CreatePool(_noTransactionStorage);
            Transaction          tx      = Build.A.Transaction.SignedAndResolved(_ethereumSigner, TestItem.PrivateKeyA, RopstenSpecProvider.ByzantiumBlockNumber).TestObject;
            AddTransactionResult result1 = _transactionPool.AddTransaction(tx, 1);
            AddTransactionResult result2 = _transactionPool.AddTransaction(tx, 1);

            _transactionPool.GetPendingTransactions().Length.Should().Be(1);
            result1.Should().Be(AddTransactionResult.Added);
            result2.Should().Be(AddTransactionResult.AlreadyKnown);
        }
예제 #7
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);
        }
예제 #8
0
        private Block PrepareBlock()
        {
            BlockHeader parentHeader = _blockTree.Head;

            if (parentHeader == null)
            {
                return(null);
            }

            Block   parent    = _blockTree.FindBlock(parentHeader.Hash, false);
            UInt256 timestamp = _timestamp.EpochSeconds;

            BlockHeader header = new BlockHeader(
                parent.Hash,
                Keccak.OfAnEmptySequenceRlp,
                Address.Zero,
                1,
                parent.Number + 1,
                parent.GasLimit,
                timestamp > parent.Timestamp ? timestamp : parent.Timestamp + 1,
                Encoding.UTF8.GetBytes("Nethermind"));

            header.TotalDifficulty = parent.TotalDifficulty + header.Difficulty;
            if (_logger.IsDebug)
            {
                _logger.Debug($"Setting total difficulty to {parent.TotalDifficulty} + {header.Difficulty}.");
            }

            var transactions = _transactionPool.GetPendingTransactions().OrderBy(t => t?.Nonce); // 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;

            foreach (Transaction transaction in transactions)
            {
                total++;
                if (transaction == null)
                {
                    throw new InvalidOperationException("Block transaction is null");
                }

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

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

                selectedTxs.Add(transaction);
                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();
            return(block);
        }
예제 #9
0
 public TransactionPoolInfo GetTransactionPoolInfo()
 => _transactionPoolInfoProvider.GetInfo(_transactionPool.GetPendingTransactions());