Skip to content

Commit 8bf2977

Browse files
authored
Merge pull request #241 from Destiner/master
Fix block producing for Clique
2 parents 3bc4235 + 4dd31ad commit 8bf2977

File tree

1 file changed

+116
-42
lines changed

1 file changed

+116
-42
lines changed

src/Nethermind/Nethermind.Clique/CliqueBlockProducer.cs

Lines changed: 116 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,46 +20,90 @@
2020
using System;
2121
using System.Collections.Generic;
2222
using System.Linq;
23+
using System.Numerics;
24+
using System.Text;
2325
using System.Threading.Tasks;
2426
using Nethermind.Blockchain;
27+
using Nethermind.Blockchain.TransactionPools;
2528
using Nethermind.Core;
29+
using Nethermind.Core.Crypto;
30+
using Nethermind.Core.Logging;
2631
using Nethermind.Dirichlet.Numerics;
32+
using Nethermind.Evm;
2733
using Nethermind.Store;
2834

2935
namespace Nethermind.Clique
3036
{
3137
public class CliqueBlockProducer : IBlockProducer
3238
{
39+
private static readonly BigInteger MinGasPriceForMining = 1;
40+
private readonly IBlockTree _blockTree;
41+
private readonly ITimestamp _timestamp;
42+
private readonly ILogger _logger;
43+
44+
private readonly IBlockchainProcessor _processor;
45+
private readonly ITransactionPool _transactionPool;
3346
private CliqueSealEngine _sealEngine;
3447
private CliqueConfig _config;
35-
private BlockTree _blockTree;
3648
private Address _address;
3749
private Dictionary<Address, bool> _proposals = new Dictionary<Address, bool>();
3850

39-
public CliqueBlockProducer(CliqueSealEngine cliqueSealEngine, CliqueConfig config, BlockTree blockTree, Address address)
51+
public CliqueBlockProducer(
52+
ITransactionPool transactionPool,
53+
IBlockchainProcessor devProcessor,
54+
IBlockTree blockTree,
55+
ITimestamp timestamp,
56+
CliqueSealEngine cliqueSealEngine,
57+
CliqueConfig config,
58+
Address address,
59+
ILogManager logManager)
4060
{
61+
_transactionPool = transactionPool ?? throw new ArgumentNullException(nameof(transactionPool));
62+
_processor = devProcessor ?? throw new ArgumentNullException(nameof(devProcessor));
63+
_blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree));
64+
_timestamp = timestamp;
4165
_sealEngine = cliqueSealEngine;
4266
_config = config;
4367
_blockTree = blockTree;
4468
_address = address;
69+
_logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager));
4570
}
4671

4772
public void Start()
4873
{
74+
_transactionPool.NewPending += OnNewPendingTx;
4975
}
5076

5177
public async Task StopAsync()
5278
{
79+
_transactionPool.NewPending -= OnNewPendingTx;
5380
await Task.CompletedTask;
5481
}
5582

56-
private void Prepare(BlockHeader header)
83+
private Block PrepareBlock()
5784
{
85+
BlockHeader parentHeader = _blockTree.Head;
86+
if (parentHeader == null) return null;
87+
88+
Block parent = _blockTree.FindBlock(parentHeader.Hash, false);
89+
UInt256 timestamp = _timestamp.EpochSeconds;
90+
91+
BlockHeader header = new BlockHeader(
92+
parent.Hash,
93+
Keccak.OfAnEmptySequenceRlp,
94+
Address.Zero,
95+
1,
96+
parent.Number + 1,
97+
parent.GasLimit,
98+
timestamp > parent.Timestamp ? timestamp : parent.Timestamp + 1,
99+
new byte[0]);
100+
58101
// If the block isn't a checkpoint, cast a random vote (good enough for now)
59102
UInt256 number = header.Number;
60103
// Assemble the voting snapshot to check which votes make sense
61104
Snapshot snapshot = _sealEngine.GetOrCreateSnapshot(number - 1, header.ParentHash);
62-
if ((ulong)number % _config.Epoch != 0)
105+
bool isEpochBlock = (ulong)number % 30000 == 0;
106+
if (!isEpochBlock)
63107
{
64108
// Gather all the proposals that make sense voting on
65109
List<Address> addresses = new List<Address>();
@@ -91,63 +135,93 @@ private void Prepare(BlockHeader header)
91135

92136
// Set the correct difficulty
93137
header.Difficulty = CalculateDifficulty(snapshot, _address);
94-
// Ensure the extra data has all it's components
95-
if (header.ExtraData.Length < CliqueSealEngine.ExtraVanityLength)
96-
{
97-
for (int i = 0; i < CliqueSealEngine.ExtraVanityLength - header.ExtraData.Length; i++)
98-
{
99-
header.ExtraData.Append((byte)0);
100-
}
101-
}
138+
header.TotalDifficulty = parent.TotalDifficulty + header.Difficulty;
139+
if (_logger.IsDebug) _logger.Debug($"Setting total difficulty to {parent.TotalDifficulty} + {header.Difficulty}.");
140+
141+
// Set extra data
142+
int mainBytesLength = CliqueSealEngine.ExtraVanityLength + CliqueSealEngine.ExtraSealLength;
143+
int signerBytesLength = isEpochBlock ? 20 * snapshot.Signers.Count : 0;
144+
int extraDataLength = mainBytesLength + signerBytesLength;
145+
header.ExtraData = new byte[extraDataLength];
102146

103-
header.ExtraData = header.ExtraData.Take(CliqueSealEngine.ExtraVanityLength).ToArray();
147+
byte[] clientName = Encoding.UTF8.GetBytes("Nethermind");
148+
Array.Copy(clientName, header.ExtraData, clientName.Length);
104149

105-
if ((ulong)number % _config.Epoch == 0)
150+
if (isEpochBlock)
106151
{
107-
foreach (Address signer in snapshot.Signers.Keys)
152+
for (int i = 0; i < snapshot.Signers.Keys.Count; i++)
108153
{
109-
foreach (byte addressByte in signer.Bytes)
110-
{
111-
header.ExtraData.Append(addressByte);
112-
}
154+
Address signer = snapshot.Signers.Keys[i];
155+
int index = CliqueSealEngine.ExtraVanityLength + 20 * i;
156+
Array.Copy(signer.Bytes, 0, header.ExtraData, index, signer.Bytes.Length);
113157
}
114158
}
115159

116-
byte[] extraSeal = new byte[CliqueSealEngine.ExtraSealLength];
117-
for (int i = 0; i < CliqueSealEngine.ExtraSealLength; i++)
118-
{
119-
header.ExtraData.Append((byte)0);
120-
}
121-
122160
// Mix digest is reserved for now, set to empty
161+
header.MixHash = Keccak.Zero;
123162
// Ensure the timestamp has the correct delay
124-
BlockHeader parent = _blockTree.FindHeader(header.ParentHash);
125-
if (parent == null)
126-
{
127-
throw new InvalidOperationException("Unknown ancestor");
128-
}
129-
130163
header.Timestamp = parent.Timestamp + _config.BlockPeriod;
131164
long currentTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
132165
if (header.Timestamp < currentTimestamp)
133166
{
134167
header.Timestamp = new UInt256(currentTimestamp);
135168
}
136-
}
137169

138-
private Block Finalize(StateProvider state, BlockHeader header, Transaction[] txs, BlockHeader[] uncles, TransactionReceipt[] receipts)
139-
{
140-
// No block rewards in PoA, so the state remains as is and uncles are dropped
141-
header.StateRoot = state.StateRoot;
142-
header.OmmersHash = BlockHeader.CalculateHash((BlockHeader)null);
143-
// Assemble and return the final block for sealing
144-
return new Block(header, txs, null);
170+
var transactions = _transactionPool.GetPendingTransactions().OrderBy(t => t?.Nonce); // by nonce in case there are two transactions for the same account
171+
172+
var selectedTxs = new List<Transaction>();
173+
BigInteger gasRemaining = header.GasLimit;
174+
175+
if (_logger.IsDebug) _logger.Debug($"Collecting pending transactions at min gas price {MinGasPriceForMining} and block gas limit {gasRemaining}.");
176+
177+
int total = 0;
178+
foreach (Transaction transaction in transactions)
179+
{
180+
total++;
181+
if (transaction == null) throw new InvalidOperationException("Block transaction is null");
182+
183+
if (transaction.GasPrice < MinGasPriceForMining)
184+
{
185+
if (_logger.IsTrace) _logger.Trace($"Rejecting transaction - gas price ({transaction.GasPrice}) too low (min gas price: {MinGasPriceForMining}.");
186+
continue;
187+
}
188+
189+
if (transaction.GasLimit > gasRemaining)
190+
{
191+
if (_logger.IsTrace) _logger.Trace($"Rejecting transaction - gas limit ({transaction.GasPrice}) more than remaining gas ({gasRemaining}).");
192+
break;
193+
}
194+
195+
selectedTxs.Add(transaction);
196+
gasRemaining -= transaction.GasLimit;
197+
}
198+
199+
if (_logger.IsDebug) _logger.Debug($"Collected {selectedTxs.Count} out of {total} pending transactions.");
200+
201+
202+
Block block = new Block(header, selectedTxs, new BlockHeader[0]);
203+
header.TransactionsRoot = block.CalculateTransactionsRoot();
204+
return block;
145205
}
146206

147-
private UInt256 CalculateDifficulty(ulong time, BlockHeader parent)
207+
private void OnNewPendingTx(object sender, TransactionEventArgs e)
148208
{
149-
Snapshot snapshot = _sealEngine.GetOrCreateSnapshot(parent.Number, parent.Hash);
150-
return CalculateDifficulty(snapshot, _address);
209+
Block block = PrepareBlock();
210+
if (block == null)
211+
{
212+
if (_logger.IsError) _logger.Error("Failed to prepare block for mining.");
213+
return;
214+
}
215+
216+
Block processedBlock = _processor.Process(block, ProcessingOptions.NoValidation | ProcessingOptions.ReadOnlyChain | ProcessingOptions.WithRollback, NullTraceListener.Instance);
217+
if (processedBlock == null)
218+
{
219+
if (_logger.IsError) _logger.Error("Block prepared by block producer was rejected by processor");
220+
return;
221+
}
222+
223+
if (_logger.IsInfo) _logger.Info($"Suggesting newly mined block {processedBlock.ToString(Block.Format.HashAndNumber)}");
224+
_blockTree.SuggestBlock(processedBlock);
151225
}
152226

153227
private UInt256 CalculateDifficulty(Snapshot snapshot, Address signer)

0 commit comments

Comments
 (0)