예제 #1
0
        private void TimerOnElapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                if (_blockTree.Head == null)
                {
                    _timer.Enabled = true;
                    return;
                }

                if (_scheduledBlock == null)
                {
                    if (_blockTree.Head.Timestamp + _config.BlockPeriod < _timestamp.EpochSeconds)
                    {
                        _signalsQueue.Add(_blockTree.FindBlock(_blockTree.Head.Hash, false));
                    }

                    _timer.Enabled = true;
                    return;
                }

                ulong extraDelayMilliseconds = 0;
                if (_scheduledBlock.Difficulty == Clique.DifficultyNoTurn)
                {
                    int wiggle = _snapshotManager.GetOrCreateSnapshot(_scheduledBlock.Header.Number - 1, _scheduledBlock.Header.ParentHash).Signers.Count / 2 + 1 * Clique.WiggleTime;
                    extraDelayMilliseconds += (ulong)_cryptoRandom.NextInt(wiggle);
                }

                if (_scheduledBlock.Timestamp * 1000 + extraDelayMilliseconds < _timestamp.EpochMilliseconds)
                {
                    if (_scheduledBlock.Number > _blockTree.Head.Number)
                    {
                        if (_logger.IsInfo)
                        {
                            _logger.Info($"Suggesting own block {_scheduledBlock.ToString(Block.Format.HashNumberDiffAndTx)}");
                        }
                        _blockTree.SuggestBlock(_scheduledBlock);
                    }
                    else
                    {
                        if (_logger.IsInfo)
                        {
                            _logger.Info($"Dropping a losing block {_scheduledBlock.ToString(Block.Format.HashNumberDiffAndTx)}");
                        }
                    }

                    _scheduledBlock = null;
                }

                _timer.Enabled = true;
            }
            catch (Exception exception)
            {
                if (_logger.IsError)
                {
                    _logger.Error("Clique block producer failure", exception);
                }
            }
        }
        public void Returns_zero_for_in_turn_blocks()
        {
            Queue <int> randoms        = new(new List <int> {
                Consensus.Clique.Clique.WiggleTime / 2, Consensus.Clique.Clique.WiggleTime, Consensus.Clique.Clique.WiggleTime * 2, Consensus.Clique.Clique.WiggleTime * 3
            });
            ICryptoRandom cryptoRandom = Substitute.For <ICryptoRandom>();

            cryptoRandom.NextInt(Arg.Any <int>()).Returns(ci => randoms.Dequeue());

            Snapshot snapshot = new(1, Keccak.Zero, new SortedList <Address, long>(AddressComparer.Instance)
            {
                { TestItem.AddressA, 1 },
                { TestItem.AddressB, 2 },
                { TestItem.AddressC, 3 },
                { TestItem.AddressD, 4 }
            });

            ISnapshotManager snapshotManager = Substitute.For <ISnapshotManager>();

            snapshotManager.GetOrCreateSnapshot(Arg.Any <long>(), Arg.Any <Keccak>()).Returns(snapshot);
            WiggleRandomizer randomizer = new(cryptoRandom, snapshotManager);

            BlockHeader header1 = Build.A.BlockHeader.WithNumber(1).WithDifficulty(Consensus.Clique.Clique.DifficultyInTurn).TestObject;
            int         wiggle  = randomizer.WiggleFor(header1);

            Assert.AreEqual(0, wiggle);
        }
        public void Wiggle_is_fine()
        {
            Queue <int> randoms        = new(new List <int> {
                100, 600, 1000, 2000, 50
            });
            ICryptoRandom cryptoRandom = Substitute.For <ICryptoRandom>();

            cryptoRandom.NextInt(Arg.Any <int>()).Returns(ci => randoms.Dequeue());

            Snapshot snapshot = new(1, Keccak.Zero, new SortedList <Address, long>(AddressComparer.Instance)
            {
                { TestItem.AddressA, 1 },
                { TestItem.AddressB, 2 },
                { TestItem.AddressC, 3 },
                { TestItem.AddressD, 4 }
            });
            ISnapshotManager snapshotManager = Substitute.For <ISnapshotManager>();

            snapshotManager.GetOrCreateSnapshot(Arg.Any <long>(), Arg.Any <Keccak>()).Returns(snapshot);
            WiggleRandomizer randomizer = new(cryptoRandom, snapshotManager);

            BlockHeader header1 = Build.A.BlockHeader.WithNumber(1).TestObject;

            for (int i = 0; i < 5; i++)
            {
                Assert.AreEqual(100, randomizer.WiggleFor(header1));
            }
        }
예제 #4
0
        public void Wiggle_has_no_min_value()
        {
            Queue <int> randoms = new Queue <int>(new List <int> {
                Clique.WiggleTime / 2, Clique.WiggleTime, Clique.WiggleTime * 2, Clique.WiggleTime * 3
            });
            ICryptoRandom cryptoRandom = Substitute.For <ICryptoRandom>();

            cryptoRandom.NextInt(Arg.Any <int>()).Returns(ci => randoms.Dequeue());

            Snapshot snapshot = new Snapshot(1, Keccak.Zero, new SortedList <Address, long>(AddressComparer.Instance)
            {
                { TestItem.AddressA, 1 },
                { TestItem.AddressB, 2 },
                { TestItem.AddressC, 3 },
                { TestItem.AddressD, 4 }
            });

            ISnapshotManager snapshotManager = Substitute.For <ISnapshotManager>();

            snapshotManager.GetOrCreateSnapshot(Arg.Any <long>(), Arg.Any <Keccak>()).Returns(snapshot);
            WiggleRandomizer randomizer = new WiggleRandomizer(cryptoRandom, snapshotManager);

            BlockHeader header1 = Build.A.BlockHeader.WithNumber(1).TestObject;
            BlockHeader header2 = Build.A.BlockHeader.WithNumber(2).TestObject;
            BlockHeader header3 = Build.A.BlockHeader.WithNumber(3).TestObject;
            int         wiggle  = randomizer.WiggleFor(header1);

            Assert.AreEqual(Clique.WiggleTime / 2, wiggle);

            wiggle = randomizer.WiggleFor(header2);
            Assert.AreEqual(Clique.WiggleTime, wiggle);

            wiggle = randomizer.WiggleFor(header3);
            Assert.AreEqual(Clique.WiggleTime * 2, wiggle);
        }
예제 #5
0
        public int WiggleFor(BlockHeader header)
        {
            if (header.Difficulty == Clique.DifficultyInTurn)
            {
                return(0);
            }

            if (header.Number != _lastWiggleAtNumber)
            {
                int multiplier = _snapshotManager.GetOrCreateSnapshot(header.Number - 1, header.ParentHash).Signers.Count / 2 + 1;
                int randomPart = _cryptoRandom.NextInt(multiplier * Clique.WiggleTime);
                _lastWiggle         = randomPart;
                _lastWiggleAtNumber = header.Number;
            }

            return(_lastWiggle);
        }
예제 #6
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.IsTrace)
                {
                    _logger.Trace($"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 = _timestamper.UnixTime.Seconds;

            BlockHeader header = new BlockHeader(
                parentBlock.Hash,
                Keccak.OfAnEmptySequenceRlp,
                Address.Zero,
                1,
                parentBlock.Number + 1,
                _gasLimitCalculator.GetGasLimit(parentBlock.Header),
                timestamp > parentBlock.Timestamp ? timestamp : parentBlock.Timestamp + 1,
                Array.Empty <byte>());

            // If the block isn't a checkpoint, cast a random vote (good enough for now)
            long 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 && _proposals.Any())
            {
                // 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)];
                    if (_proposals.TryGetValue(header.Beneficiary !, out bool proposal))
                    {
                        header.Nonce = proposal ? Clique.NonceAuthVote : Clique.NonceDropVote;
                    }
                }
            }

            // Set the correct difficulty
            header.BaseFee         = BlockHeader.CalculateBaseFee(parentHeader, _specProvider.GetSpec(header.Number));
            header.Difficulty      = CalculateDifficulty(snapshot, _sealer.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 " + ClientVersion.Version);
            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 < _timestamper.UnixTime.Seconds)
            {
                header.Timestamp = new UInt256(_timestamper.UnixTime.Seconds);
            }

            _stateProvider.StateRoot = parentHeader.StateRoot;

            var   selectedTxs = _txSource.GetTransactions(parentBlock.Header, header.GasLimit);
            Block block       = new Block(header, selectedTxs, Array.Empty <BlockHeader>());

            header.TxRoot       = new TxTrie(block.Transactions).RootHash;
            block.Header.Author = _sealer.Address;
            return(block);
        }
예제 #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.IsTrace)
                {
                    _logger.Trace($"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)
            long 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 = _txPool.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;
                }

                BigInteger requiredBalance = transaction.GasLimit * (BigInteger)transaction.GasPrice + transaction.Value;
                BigInteger balance         = _stateProvider.GetBalance(transaction.SenderAddress);
                if (requiredBalance > 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.TxRoot = block.CalculateTxRoot();
            block.Author  = _address;
            return(block);
        }
예제 #8
0
 public byte[] Pad(byte[] message)
 {
     byte[] padding = _cryptoRandom.GenerateRandomBytes(100 + _cryptoRandom.NextInt(201));
     return(Bytes.Concat(message, padding));
 }
 private void ResetValidationInterval()
 {
     // more or less at the constant component
     // prevents attack on all Nethermind nodes at once
     _sealValidationInterval = SealValidationIntervalConstantComponent - 8 + _cryptoRandom.NextInt(16);
 }
예제 #10
0
#pragma warning disable CA1416
        private void Protect(byte[] data)
        {
            _entropy       = _random.GenerateRandomBytes(_random.NextInt(EntropyMaxLength - EntropyMinLength) + EntropyMinLength);
            _encryptedData = Protect(data, _entropy, DataProtectionScope.CurrentUser);
            _timestamp     = _timestamper.UtcNow;
        }