Skip to content

Commit 7ad79e4

Browse files
committed
*: introduce StateRootInHeader
Close #1526. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
1 parent 5cc2cfd commit 7ad79e4

File tree

14 files changed

+176
-13
lines changed

14 files changed

+176
-13
lines changed

src/Neo/NeoSystem.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ public NeoSystem(ProtocolSettings settings, IStoreProvider storageProvider, stri
164164
{
165165
Header = new Header
166166
{
167+
Version = settings.IsHardforkEnabled(Hardfork.HF_Faun, 0) ? (uint)BlockVersion.V1 : (uint)BlockVersion.V0,
167168
PrevHash = UInt256.Zero,
168169
MerkleRoot = UInt256.Zero,
169170
Timestamp = (new DateTime(2016, 7, 15, 15, 8, 21, DateTimeKind.Utc)).ToTimestampMS(),
@@ -331,5 +332,16 @@ public bool ContainsConflictHash(UInt256 hash, IEnumerable<UInt160> signers)
331332
{
332333
return NativeContract.Ledger.ContainsConflictHash(StoreView, hash, signers, this.GetMaxTraceableBlocks());
333334
}
335+
336+
/// <summary>
337+
/// Returns index of the latest block persisted to native Ledger contract.
338+
/// </summary>
339+
/// <returns>
340+
/// Index of the latest persisted block.
341+
/// </returns>
342+
public uint CurrentIndex()
343+
{
344+
return NativeContract.Ledger.CurrentIndex(StoreView);
345+
}
334346
}
335347
}

src/Neo/Network/P2P/Payloads/Block.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ public sealed class Block : IEquatable<Block>, IInventory
8080
/// </summary>
8181
public UInt160 NextConsensus => Header.NextConsensus;
8282

83+
/// <summary>
84+
/// MPT root hash calculated after the previous block is persisted.
85+
/// </summary>
86+
public UInt256 PrevStateRoot => Header.PrevStateRoot;
87+
8388
/// <summary>
8489
/// The witness of the block.
8590
/// </summary>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (C) 2015-2025 The Neo Project.
2+
//
3+
// BlockVersion.cs file belongs to the neo project and is free
4+
// software distributed under the MIT software license, see the
5+
// accompanying file LICENSE in the main directory of the
6+
// repository or http://www.opensource.org/licenses/mit-license.php
7+
// for more details.
8+
//
9+
// Redistribution and use in source and binary forms with or without
10+
// modifications are permitted.
11+
12+
namespace Neo.Network.P2P.Payloads
13+
{
14+
/// <summary>
15+
/// Represents the block version.
16+
/// </summary>
17+
public enum BlockVersion : byte
18+
{
19+
/// <summary>
20+
/// Initial block version.
21+
/// </summary>
22+
V0,
23+
/// <summary>
24+
/// Block version 1 which adds PrevStateRoot field to the header.
25+
/// </summary>
26+
V1,
27+
}
28+
}

src/Neo/Network/P2P/Payloads/Header.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public sealed class Header : IEquatable<Header>, IVerifiable
3535
private uint index;
3636
private byte primaryIndex;
3737
private UInt160 nextConsensus;
38+
private UInt256 prevStateRoot;
3839

3940
/// <summary>
4041
/// The witness of the block.
@@ -113,6 +114,15 @@ public UInt160 NextConsensus
113114
set { nextConsensus = value; _hash = null; }
114115
}
115116

117+
/// <summary>
118+
/// MPT root hash got after the previous block processing.
119+
/// </summary>
120+
public UInt256 PrevStateRoot
121+
{
122+
get => prevStateRoot;
123+
set { prevStateRoot = value; _hash = null; }
124+
}
125+
116126
private UInt256 _hash = null;
117127

118128
/// <inheritdoc/>
@@ -137,6 +147,7 @@ public UInt256 Hash
137147
sizeof(uint) + // Index
138148
sizeof(byte) + // PrimaryIndex
139149
UInt160.Length + // NextConsensus
150+
(version == (uint)BlockVersion.V0 ? 0 : UInt256.Length) + // PrevStateRoot
140151
(Witness is null ? 1 : 1 + Witness.Size); // Witness, cannot be null for valid header
141152

142153
Witness[] IVerifiable.Witnesses
@@ -166,14 +177,16 @@ void IVerifiable.DeserializeUnsigned(ref MemoryReader reader)
166177
{
167178
_hash = null;
168179
version = reader.ReadUInt32();
169-
if (version > 0) throw new FormatException($"`version`({version}) in Header must be 0");
180+
if (version > (uint)BlockVersion.V1) throw new FormatException($"`version`({version}) in Header must be 0 or 1");
170181
prevHash = reader.ReadSerializable<UInt256>();
171182
merkleRoot = reader.ReadSerializable<UInt256>();
172183
timestamp = reader.ReadUInt64();
173184
nonce = reader.ReadUInt64();
174185
index = reader.ReadUInt32();
175186
primaryIndex = reader.ReadByte();
176187
nextConsensus = reader.ReadSerializable<UInt160>();
188+
if (version == (uint)BlockVersion.V1)
189+
prevStateRoot = reader.ReadSerializable<UInt256>();
177190
}
178191

179192
public bool Equals(Header other)
@@ -217,6 +230,8 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer)
217230
writer.Write(index);
218231
writer.Write(primaryIndex);
219232
writer.Write(nextConsensus);
233+
if (version == (uint)BlockVersion.V1)
234+
writer.Write(prevStateRoot);
220235
}
221236

222237
/// <summary>
@@ -238,13 +253,17 @@ public JObject ToJson(ProtocolSettings settings)
238253
json["primary"] = primaryIndex;
239254
json["nextconsensus"] = nextConsensus.ToAddress(settings.AddressVersion);
240255
json["witnesses"] = new JArray(Witness.ToJson());
256+
if (version == (uint)BlockVersion.V1)
257+
json["previousstateroot"] = prevStateRoot.ToString();
241258
return json;
242259
}
243260

244261
internal bool Verify(ProtocolSettings settings, DataCache snapshot)
245262
{
246263
if (primaryIndex >= settings.ValidatorsCount)
247264
return false;
265+
if (version != (uint)GetExpectedVersion(settings))
266+
return false;
248267
TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, prevHash);
249268
if (prev is null) return false;
250269
if (prev.Index + 1 != index) return false;
@@ -258,6 +277,8 @@ internal bool Verify(ProtocolSettings settings, DataCache snapshot, HeaderCache
258277
{
259278
Header prev = headerCache.Last;
260279
if (prev is null) return Verify(settings, snapshot);
280+
if (version != (uint)GetExpectedVersion(settings))
281+
return false;
261282
if (primaryIndex >= settings.ValidatorsCount)
262283
return false;
263284
if (prev.Hash != prevHash) return false;
@@ -266,6 +287,11 @@ internal bool Verify(ProtocolSettings settings, DataCache snapshot, HeaderCache
266287
return this.VerifyWitness(settings, snapshot, prev.nextConsensus, Witness, 3_00000000L, out _);
267288
}
268289

290+
private BlockVersion GetExpectedVersion(ProtocolSettings settings)
291+
{
292+
return settings.IsHardforkEnabled(Hardfork.HF_Faun, Index) ? BlockVersion.V1 : BlockVersion.V0;
293+
}
294+
269295
public Header Clone()
270296
{
271297
return new Header()
@@ -278,6 +304,7 @@ public Header Clone()
278304
Index = index,
279305
PrimaryIndex = primaryIndex,
280306
NextConsensus = nextConsensus,
307+
PrevStateRoot = prevStateRoot,
281308
Witness = Witness?.Clone(),
282309
_hash = _hash
283310
};

src/Neo/SmartContract/Native/TrimmedBlock.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Neo.VM;
1616
using Neo.VM.Types;
1717
using System;
18+
using System.Collections.Generic;
1819
using System.IO;
1920
using System.Linq;
2021
using Array = Neo.VM.Types.Array;
@@ -110,8 +111,7 @@ void IInteroperable.FromStackItem(StackItem stackItem)
110111

111112
StackItem IInteroperable.ToStackItem(IReferenceCounter referenceCounter)
112113
{
113-
return new Array(referenceCounter,
114-
[
114+
var block = new List<StackItem>() {
115115
// Computed properties
116116
Header.Hash.ToArray(),
117117

@@ -125,9 +125,14 @@ StackItem IInteroperable.ToStackItem(IReferenceCounter referenceCounter)
125125
Header.PrimaryIndex,
126126
Header.NextConsensus.ToArray(),
127127

128-
// Block properties
129-
Hashes.Length
130-
]);
128+
};
129+
if (Header.Version == (uint)BlockVersion.V1)
130+
block.Add(Header.PrevStateRoot.ToArray());
131+
132+
// Block properties
133+
block.Add(Hashes.Length);
134+
135+
return new Array(referenceCounter, block);
131136
}
132137
}
133138
}

src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ public ExtensiblePayload MakePrepareRequest()
115115
PrevHash = Block.PrevHash,
116116
Timestamp = Block.Timestamp,
117117
Nonce = Block.Nonce,
118-
TransactionHashes = TransactionHashes
118+
TransactionHashes = TransactionHashes,
119+
PrevStateRoot = Block.PrevStateRoot,
119120
});
120121
}
121122

@@ -141,6 +142,7 @@ public ExtensiblePayload MakeRecoveryMessage()
141142
Nonce = Block.Nonce,
142143
BlockIndex = Block.Index,
143144
ValidatorIndex = Block.PrimaryIndex,
145+
PrevStateRoot = Block.PrevStateRoot,
144146
TransactionHashes = TransactionHashes
145147
};
146148
}

src/Plugins/DBFTPlugin/Consensus/ConsensusContext.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,16 +199,19 @@ public void Reset(byte viewNumber)
199199
Snapshot?.Dispose();
200200
Snapshot = neoSystem.GetSnapshotCache();
201201
uint height = NativeContract.Ledger.CurrentIndex(Snapshot);
202+
var isFaun = neoSystem.Settings.IsHardforkEnabled(Hardfork.HF_Faun, height + 1);
202203
Block = new Block
203204
{
204205
Header = new Header
205206
{
207+
Version = isFaun ? (uint)BlockVersion.V1 : (uint)BlockVersion.V0,
206208
PrevHash = NativeContract.Ledger.CurrentHash(Snapshot),
207209
Index = height + 1,
208210
NextConsensus = Contract.GetBFTAddress(
209211
NeoToken.ShouldRefreshCommittee(height + 1, neoSystem.Settings.CommitteeMembersCount) ?
210212
NativeContract.NEO.ComputeNextBlockValidators(Snapshot, neoSystem.Settings) :
211-
NativeContract.NEO.GetNextBlockValidators(Snapshot, neoSystem.Settings.ValidatorsCount))
213+
NativeContract.NEO.GetNextBlockValidators(Snapshot, neoSystem.Settings.ValidatorsCount)),
214+
PrevStateRoot = isFaun ? StateService.StatePlugin.GetStateRootHash(height) : null
212215
}
213216
};
214217
TimePerBlock = neoSystem.GetTimePerBlock();
@@ -294,6 +297,8 @@ public void Deserialize(ref MemoryReader reader)
294297
Block.Header.NextConsensus = reader.ReadSerializable<UInt160>();
295298
if (Block.NextConsensus.Equals(UInt160.Zero))
296299
Block.Header.NextConsensus = null;
300+
if (Block.Version == (uint)BlockVersion.V1)
301+
Block.Header.PrevStateRoot = reader.ReadSerializable<UInt256>();
297302
ViewNumber = reader.ReadByte();
298303
TransactionHashes = reader.ReadSerializableArray<UInt256>(ushort.MaxValue);
299304
Transaction[] transactions = reader.ReadSerializableArray<Transaction>(ushort.MaxValue);
@@ -320,6 +325,8 @@ public void Serialize(BinaryWriter writer)
320325
writer.Write(Block.Nonce);
321326
writer.Write(Block.PrimaryIndex);
322327
writer.Write(Block.NextConsensus ?? UInt160.Zero);
328+
if (Block.Version == (uint)BlockVersion.V1)
329+
writer.Write(Block.PrevStateRoot);
323330
writer.Write(ViewNumber);
324331
writer.Write(TransactionHashes ?? Array.Empty<UInt256>());
325332
writer.Write(Transactions?.Values.ToArray() ?? Array.Empty<Transaction>());

src/Plugins/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private void OnPrepareRequestReceived(ExtensiblePayload payload, PrepareRequest
7979
{
8080
if (context.RequestSentOrReceived || context.NotAcceptingPayloadsDueToViewChanging) return;
8181
if (message.ValidatorIndex != context.Block.PrimaryIndex || message.ViewNumber != context.ViewNumber) return;
82-
if (message.Version != context.Block.Version || message.PrevHash != context.Block.PrevHash) return;
82+
if (message.Version != context.Block.Version || message.PrevHash != context.Block.PrevHash || message.PrevStateRoot != context.Block.PrevStateRoot) return;
8383
if (message.TransactionHashes.Length > neoSystem.Settings.MaxTransactionsPerBlock) return;
8484
Log($"{nameof(OnPrepareRequestReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex} tx={message.TransactionHashes.Length}");
8585
if (message.Timestamp <= context.PrevHeader.Timestamp || message.Timestamp > TimeProvider.Current.UtcNow.AddMilliseconds(8 * context.TimePerBlock.TotalMilliseconds).ToTimestampMS())

src/Plugins/DBFTPlugin/DBFTPlugin.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
<ItemGroup>
1414
<ProjectReference Include="..\..\Neo.ConsoleService\Neo.ConsoleService.csproj" />
15+
<ProjectReference Include="..\StateService\StateService.csproj" />
1516
</ItemGroup>
1617

1718
<ItemGroup>

src/Plugins/DBFTPlugin/DBFTPlugin.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@
77
"MaxBlockSize": 2097152,
88
"MaxBlockSystemFee": 150000000000,
99
"UnhandledExceptionPolicy": "StopNode"
10-
}
10+
},
11+
"Dependency": [
12+
"StateService"
13+
]
1114
}

0 commit comments

Comments
 (0)