Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[neox] Merge neo-project/neox to neo-project/master #1793

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/neo/Cryptography/MPT/MPTTrie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public partial class MPTTrie<TKey, TValue>
public MPTTrie(ISnapshot store, UInt256 root)
{
this.store = store ?? throw new ArgumentNullException();
this.root = root is null ? HashNode.EmptyNode : new HashNode(root);
this.root = root is null || root == UInt256.Zero ? HashNode.EmptyNode : new HashNode(root);
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
}

private MPTNode Resolve(HashNode n)
Expand Down
77 changes: 77 additions & 0 deletions src/neo/Ledger/Blockchain.State.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.Collections.Generic;
using Akka.Actor;
using Neo.Cryptography.MPT;
using Neo.Network.P2P;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;

namespace Neo.Ledger
{
public sealed partial class Blockchain : UntypedActor
{
public StateRoot LatestValidatorsStateRoot
{
get
{
var state_root = currentSnapshot.ValidatorsStateRoot.Get();
return state_root.RootHash is null ? null : state_root;
}
}
public long StateHeight => LatestValidatorsStateRoot is null ? -1 : (long)LatestValidatorsStateRoot.Index;

private readonly int MaxStateRootCacheCount = 100;
private Dictionary<uint, StateRoot> state_root_cache = new Dictionary<uint, StateRoot>();

public UInt256 GetLocalStateRoot(uint index)
{
return currentSnapshot.LocalStateRoot.TryGet(index)?.Hash;
}

private VerifyResult OnNewStateRoot(StateRoot root)
{
if (LatestValidatorsStateRoot != null && root.Index <= LatestValidatorsStateRoot.Index) return VerifyResult.AlreadyExists;
if (Height + MaxStateRootCacheCount < root.Index) return VerifyResult.Invalid;
if (Height < root.Index && root.Index <= Height + MaxStateRootCacheCount)
{
if (!state_root_cache.TryAdd(root.Index, root)) return VerifyResult.AlreadyExists;
return VerifyResult.Succeed;
}
var result = PersistCnStateRoot(root);
if (result == VerifyResult.Succeed && Height < root.Index + 2)
system.LocalNode.Tell(new LocalNode.SendDirectly { Inventory = root });
return result;
}

private VerifyResult PersistCnStateRoot(StateRoot root)
{
if (!root.Verify(currentSnapshot)) return VerifyResult.Invalid;
if (currentSnapshot.LocalStateRoot.TryGet(root.Index)?.Hash == root.RootHash)
{
using (SnapshotView snapshot = GetSnapshot())
{
var confirmedRoot = snapshot.ValidatorsStateRoot.GetAndChange();
confirmedRoot.Version = root.Version;
confirmedRoot.Index = root.Index;
confirmedRoot.RootHash = root.RootHash;
confirmedRoot.Witness = root.Witness;
snapshot.Commit();
}
UpdateCurrentSnapshot();
return VerifyResult.Succeed;
}
if (StateRoot.CurrentVersion < root.Version && Height < root.Index + 2)
system.LocalNode.Tell(new LocalNode.SendDirectly { Inventory = root });
return VerifyResult.Invalid;
}

private void CheckStateRootCache()
{
var index = (long)Height - 1;
if (0 <= index && state_root_cache.TryGetValue((uint)index, out StateRoot root))
{
state_root_cache.Remove((uint)index);
OnNewStateRoot(root);
}
}
}
}
2 changes: 2 additions & 0 deletions src/neo/Ledger/Blockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ private void Persist(Block block)
}
foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins)
plugin.OnPersist(snapshot, all_application_executed);
snapshot.UpdateLocalStateRoot();
snapshot.Commit();
List<Exception> commitExceptions = null;
foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins)
Expand All @@ -509,6 +510,7 @@ private void Persist(Block block)
if (commitExceptions != null) throw new AggregateException(commitExceptions);
}
UpdateCurrentSnapshot();
CheckStateRootCache();
block_cache.Remove(block.PrevHash);
MemPool.UpdatePoolForBlockPersisted(block, currentSnapshot);
Context.System.EventStream.Publish(new PersistCompleted { Block = block });
Expand Down
6 changes: 5 additions & 1 deletion src/neo/Network/P2P/MessageCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ public enum MessageCommand : byte
Block = 0x2c,
[ReflectionCache(typeof(ConsensusPayload))]
Consensus = 0x2d,
Reject = 0x2f,
[ReflectionCache(typeof(StateRoot))]
StateRoot = 0x2e,
[ReflectionCache(typeof(GetStateRootPayload))]
GetStateRoot = 0x2f,

//SPV protocol
[ReflectionCache(typeof(FilterLoadPayload))]
Expand All @@ -53,5 +56,6 @@ public enum MessageCommand : byte

//others
Alert = 0x40,
Reject = 0x41,
}
}
30 changes: 30 additions & 0 deletions src/neo/Network/P2P/Payloads/GetStateRootPayload.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Neo.IO;
using System;
using System.IO;

namespace Neo.Network.P2P.Payloads
{
public class GetStateRootPayload : ISerializable
{
public uint Index;
public int Size => sizeof(uint);

public static GetStateRootPayload Create(uint index)
{
return new GetStateRootPayload
{
Index = index
};
}

void ISerializable.Deserialize(BinaryReader reader)
{
Index = reader.ReadUInt32();
}

void ISerializable.Serialize(BinaryWriter writer)
{
writer.Write(Index);
}
}
}
1 change: 1 addition & 0 deletions src/neo/Network/P2P/Payloads/InventoryType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public enum InventoryType : byte
{
TX = MessageCommand.Transaction,
Block = MessageCommand.Block,
StateRoot = MessageCommand.StateRoot,
Consensus = MessageCommand.Consensus
}
}
123 changes: 123 additions & 0 deletions src/neo/Network/P2P/Payloads/StateRoot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using Neo.Cryptography;
using Neo.IO;
using Neo.IO.Json;
using Neo.Ledger;
using Neo.Persistence;
using Neo.SmartContract;
using Neo.SmartContract.Native;
using System;
using System.IO;

namespace Neo.Network.P2P.Payloads
{
public class StateRoot : ICloneable<StateRoot>, IInventory
{
public static readonly byte CurrentVersion = 0;
public byte Version;
public uint Index;
public UInt256 RootHash;
public Witness Witness;

InventoryType IInventory.InventoryType => InventoryType.StateRoot;

private UInt256 _hash = null;

public UInt256 Hash
{
get
{
if (_hash == null)
{
_hash = new UInt256(Crypto.Hash256(this.GetHashData()));
}
return _hash;
}
}

Witness[] IVerifiable.Witnesses
{
get
{
return new[] { Witness };
}
set
{
if (value.Length != 1) throw new ArgumentException();
Witness = value[0];
}
}

public int Size =>
sizeof(byte) + //Version
sizeof(uint) + //Index
UInt256.Length + //RootHash
Witness.Size; //Witness

StateRoot ICloneable<StateRoot>.Clone()
{
return new StateRoot
{
Version = Version,
Index = Index,
RootHash = RootHash,
Witness = Witness,
};
}

void ICloneable<StateRoot>.FromReplica(StateRoot replica)
{
Version = replica.Version;
Index = replica.Index;
RootHash = replica.RootHash;
Witness = replica.Witness;
}

public void Deserialize(BinaryReader reader)
{
this.DeserializeUnsigned(reader);
Witness = reader.ReadSerializable<Witness>();
}

public void DeserializeUnsigned(BinaryReader reader)
{
Version = reader.ReadByte();
Index = reader.ReadUInt32();
RootHash = reader.ReadSerializable<UInt256>();
}

public void Serialize(BinaryWriter writer)
{
this.SerializeUnsigned(writer);
writer.Write(Witness);
}

public void SerializeUnsigned(BinaryWriter writer)
{
writer.Write(Version);
writer.Write(Index);
writer.Write(RootHash);
}

public bool Verify(StoreView snapshot)
{
return this.VerifyWitnesses(snapshot, 1_00000000);
}

public virtual UInt160[] GetScriptHashesForVerifying(StoreView snapshot)
{
var script_hash = Blockchain.Singleton.GetBlock(Index)?.NextConsensus;
if (script_hash is null) throw new System.InvalidOperationException("No script hash for state root verifying");
return new UInt160[] { script_hash };
}

public JObject ToJson()
{
var json = new JObject();
json["version"] = Version;
json["index"] = Index;
json["stateroot"] = RootHash.ToString();
json["witness"] = Witness.ToJson();
return json;
}
}
}
21 changes: 21 additions & 0 deletions src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ private void OnMessage(Message msg)
if (msg.Payload.Size <= Transaction.MaxTransactionSize)
OnInventoryReceived((Transaction)msg.Payload);
break;
case MessageCommand.StateRoot:
OnRootReceived((StateRoot)msg.Payload);
break;
case MessageCommand.GetStateRoot:
OnGetRootReceived((GetStateRootPayload)msg.Payload);
break;
case MessageCommand.Verack:
case MessageCommand.Version:
throw new ProtocolViolationException();
Expand All @@ -119,6 +125,21 @@ private void OnMessage(Message msg)
}
}

private void OnGetRootReceived(GetStateRootPayload payload)
{
var state_root = Blockchain.Singleton.LatestValidatorsStateRoot;
if (state_root is null) return;
if (state_root.Index < payload.Index) return;
EnqueueMessage(Message.Create(MessageCommand.StateRoot, state_root));
}

private void OnRootReceived(StateRoot payload)
{
if (knownHashes.Contains(payload.Hash)) return;
system.Blockchain.Tell(payload);
knownHashes.Add(payload.Hash);
}

private void OnAddrMessageReceived(AddrPayload payload)
{
system.LocalNode.Tell(new Peer.Peers
Expand Down
15 changes: 15 additions & 0 deletions src/neo/Network/P2P/TaskManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,21 @@ private void RequestTasks()
if (lastTaskIndex >= highestBlockIndex) break;
if (!AssignSyncTask(++lastTaskIndex)) break;
}

SyncStateRoot();
}

private void SyncStateRoot()
{
if (Blockchain.Singleton.StateHeight + 1 < Blockchain.Singleton.Height)
{
var session = sessions.Where(p => Blockchain.Singleton.StateHeight <= p.Value.LastBlockIndex)
.OrderByDescending(p => p.Value.LastBlockIndex)
.ThenBy(p => p.Value.IndexTasks.Count)
.FirstOrDefault();
if (session.Value != null)
session.Key.Tell(Message.Create(MessageCommand.GetStateRoot, GetStateRootPayload.Create(Blockchain.Singleton.Height - 1)));
}
}

private void SendPingMessage()
Expand Down
6 changes: 6 additions & 0 deletions src/neo/Persistence/ClonedView.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Neo.Cryptography.MPT;
using Neo.IO;
using Neo.IO.Caching;
using Neo.Ledger;
using Neo.Network.P2P.Payloads;

namespace Neo.Persistence
{
Expand All @@ -11,8 +13,10 @@ internal class ClonedView : StoreView
public override DataCache<UInt160, ContractState> Contracts { get; }
public override DataCache<StorageKey, StorageItem> Storages { get; }
public override DataCache<SerializableWrapper<uint>, HeaderHashList> HeaderHashList { get; }
public override DataCache<SerializableWrapper<uint>, HashIndexState> LocalStateRoot { get; }
public override MetaDataCache<HashIndexState> BlockHashIndex { get; }
public override MetaDataCache<HashIndexState> HeaderHashIndex { get; }
public override MetaDataCache<StateRoot> ValidatorsStateRoot { get; }
public override MetaDataCache<ContractIdState> ContractId { get; }

public ClonedView(StoreView view)
Expand All @@ -23,8 +27,10 @@ public ClonedView(StoreView view)
this.Contracts = view.Contracts.CreateSnapshot();
this.Storages = view.Storages.CreateSnapshot();
this.HeaderHashList = view.HeaderHashList.CreateSnapshot();
this.LocalStateRoot = view.LocalStateRoot.CreateSnapshot();
this.BlockHashIndex = view.BlockHashIndex.CreateSnapshot();
this.HeaderHashIndex = view.HeaderHashIndex.CreateSnapshot();
this.ValidatorsStateRoot = view.ValidatorsStateRoot.CreateSnapshot();
this.ContractId = view.ContractId.CreateSnapshot();
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/neo/Persistence/Helper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Neo.Ledger;

namespace Neo.Persistence
{
Expand All @@ -8,5 +9,13 @@ public static byte[] EnsureNotNull(this byte[] source)
{
return source ?? Array.Empty<byte>();
}

public static void UpdateLocalStateRoot(this SnapshotView snapshot)
{
snapshot.Storages.Commit();
var root = snapshot.LocalStateRoot.GetAndChange(snapshot.Height, () => new HashIndexState());
shargon marked this conversation as resolved.
Show resolved Hide resolved
root.Index = snapshot.Height;
root.Hash = ((MPTDataCache<StorageKey, StorageItem>)snapshot.Storages).Root.Hash;
}
}
}
Loading