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)); } }
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); }
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); }
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); }
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); }
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); }
#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; }