Skip to content

Commit

Permalink
Allow plugins to send custom messages (neo-project#2101)
Browse files Browse the repository at this point in the history
  • Loading branch information
shargon authored and Shawn committed Jan 8, 2021
1 parent 86e5277 commit 4645de8
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 7 deletions.
34 changes: 32 additions & 2 deletions src/neo/Ledger/Blockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
using Neo.Persistence;
using Neo.Plugins;
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.VM;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;

Expand Down Expand Up @@ -66,6 +68,7 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu
private readonly Dictionary<uint, UnverifiedBlocksList> block_cache_unverified = new Dictionary<uint, UnverifiedBlocksList>();
internal readonly RelayCache RelayCache = new RelayCache(100);
private SnapshotView currentSnapshot;
private ImmutableHashSet<UInt160> extensibleWitnessWhiteList;

public IStore Store { get; }
public ReadOnlyView View { get; }
Expand Down Expand Up @@ -288,8 +291,12 @@ private void OnInventory(IInventory inventory, bool relay = true)
Transaction transaction => OnNewTransaction(transaction),
_ => OnNewInventory(inventory)
};
if (relay && result == VerifyResult.Succeed)
system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = inventory });
if (result == VerifyResult.Succeed)
{
if (relay) system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = inventory });
foreach (IP2PPlugin plugin in Plugin.P2PPlugins)
plugin.OnVerifiedInventory(inventory);
}
SendRelayResult(inventory, result);
}

Expand Down Expand Up @@ -517,6 +524,29 @@ private void SendRelayResult(IInventory inventory, VerifyResult result)
private void UpdateCurrentSnapshot()
{
Interlocked.Exchange(ref currentSnapshot, GetSnapshot())?.Dispose();
var builder = ImmutableHashSet.CreateBuilder<UInt160>();
builder.Add(NativeContract.NEO.GetCommitteeAddress(currentSnapshot));
var validators = NativeContract.NEO.GetNextBlockValidators(currentSnapshot);
builder.Add(GetConsensusAddress(validators));
builder.UnionWith(validators.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash()));
var oracles = NativeContract.RoleManagement.GetDesignatedByRole(currentSnapshot, Role.Oracle, currentSnapshot.Height);
if (oracles.Length > 0)
{
builder.Add(GetConsensusAddress(oracles));
builder.UnionWith(oracles.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash()));
}
var stateValidators = NativeContract.RoleManagement.GetDesignatedByRole(currentSnapshot, Role.StateValidator, currentSnapshot.Height);
if (stateValidators.Length > 0)
{
builder.Add(GetConsensusAddress(stateValidators));
builder.UnionWith(stateValidators.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash()));
}
extensibleWitnessWhiteList = builder.ToImmutable();
}

internal bool IsExtensibleWitnessWhiteListed(UInt160 address)
{
return extensibleWitnessWhiteList.Contains(address);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/neo/Network/P2P/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public static Message Create(MessageCommand command, ISerializable payload = nul
bool tryCompression =
command == MessageCommand.Block ||
command == MessageCommand.Consensus ||
command == MessageCommand.Extensible ||
command == MessageCommand.Transaction ||
command == MessageCommand.Headers ||
command == MessageCommand.Addr ||
Expand Down
2 changes: 2 additions & 0 deletions src/neo/Network/P2P/MessageCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public enum MessageCommand : byte
Block = 0x2c,
[ReflectionCache(typeof(ConsensusPayload))]
Consensus = 0x2d,
[ReflectionCache(typeof(ExtensiblePayload))]
Extensible = 0x2e,
Reject = 0x2f,

//SPV protocol
Expand Down
99 changes: 99 additions & 0 deletions src/neo/Network/P2P/Payloads/ExtensiblePayload.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using Neo.IO;
using Neo.Ledger;
using Neo.Persistence;
using Neo.SmartContract;
using System;
using System.IO;

namespace Neo.Network.P2P.Payloads
{
public class ExtensiblePayload : IInventory
{
public string Category;
public uint ValidBlockStart;
public uint ValidBlockEnd;
public UInt160 Sender;
public byte[] Data;
public Witness Witness;

private UInt256 _hash = null;
public UInt256 Hash
{
get
{
if (_hash == null)
{
_hash = this.CalculateHash();
}
return _hash;
}
}

InventoryType IInventory.InventoryType => InventoryType.Extensible;

public int Size =>
Category.GetVarSize() + //Category
sizeof(uint) + //ValidBlockStart
sizeof(uint) + //ValidBlockEnd
UInt160.Length + //Sender
Data.GetVarSize() + //Data
1 + Witness.Size; //Witness

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

void ISerializable.Deserialize(BinaryReader reader)
{
((IVerifiable)this).DeserializeUnsigned(reader);
if (reader.ReadByte() != 1) throw new FormatException();
Witness = reader.ReadSerializable<Witness>();
}

void IVerifiable.DeserializeUnsigned(BinaryReader reader)
{
Category = reader.ReadVarString(32);
ValidBlockStart = reader.ReadUInt32();
ValidBlockEnd = reader.ReadUInt32();
if (ValidBlockStart > ValidBlockEnd) throw new FormatException();
Sender = reader.ReadSerializable<UInt160>();
Data = reader.ReadVarBytes(ushort.MaxValue);
}

UInt160[] IVerifiable.GetScriptHashesForVerifying(StoreView snapshot)
{
return new[] { Sender }; // This address should be checked by consumer
}

void ISerializable.Serialize(BinaryWriter writer)
{
((IVerifiable)this).SerializeUnsigned(writer);
writer.Write((byte)1); writer.Write(Witness);
}

void IVerifiable.SerializeUnsigned(BinaryWriter writer)
{
writer.WriteVarString(Category);
writer.Write(ValidBlockStart);
writer.Write(ValidBlockEnd);
writer.Write(Sender);
writer.WriteVarBytes(Data);
}

public bool Verify(StoreView snapshot)
{
if (snapshot.PersistingBlock.Index < ValidBlockStart || snapshot.PersistingBlock.Index > ValidBlockEnd) return false;
if (!Blockchain.Singleton.IsExtensibleWitnessWhiteListed(Sender)) return false;
return this.VerifyWitnesses(snapshot, 0_02000000);
}
}
}
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,
Extensible = MessageCommand.Extensible,
Consensus = MessageCommand.Consensus
}
}
5 changes: 2 additions & 3 deletions src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,9 @@ private void OnMessage(Message msg)
OnAddrMessageReceived((AddrPayload)msg.Payload);
break;
case MessageCommand.Block:
OnInventoryReceived((Block)msg.Payload);
break;
case MessageCommand.Consensus:
OnInventoryReceived((ConsensusPayload)msg.Payload);
case MessageCommand.Extensible:
OnInventoryReceived((IInventory)msg.Payload);
break;
case MessageCommand.FilterAdd:
OnFilterAddMessageReceived((FilterAddPayload)msg.Payload);
Expand Down
2 changes: 2 additions & 0 deletions src/neo/Network/P2P/RemoteNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ private void EnqueueMessage(Message message)
{
case MessageCommand.Alert:
case MessageCommand.Consensus:
case MessageCommand.Extensible:
case MessageCommand.FilterAdd:
case MessageCommand.FilterClear:
case MessageCommand.FilterLoad:
Expand Down Expand Up @@ -223,6 +224,7 @@ internal protected override bool IsHighPriority(object message)
switch (msg.Command)
{
case MessageCommand.Consensus:
case MessageCommand.Extensible:
case MessageCommand.FilterAdd:
case MessageCommand.FilterClear:
case MessageCommand.FilterLoad:
Expand Down
2 changes: 1 addition & 1 deletion src/neo/Network/P2P/TaskManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ internal protected override bool IsHighPriority(object message)
case TaskManager.RestartTasks _:
return true;
case TaskManager.NewTasks tasks:
if (tasks.Payload.Type == InventoryType.Block || tasks.Payload.Type == InventoryType.Consensus)
if (tasks.Payload.Type == InventoryType.Block || tasks.Payload.Type == InventoryType.Consensus || tasks.Payload.Type == InventoryType.Extensible)
return true;
return false;
default:
Expand Down
1 change: 1 addition & 0 deletions src/neo/Plugins/IP2PPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public interface IP2PPlugin
{
bool OnP2PMessage(Message message) => true;
bool OnConsensusMessage(ConsensusPayload payload) => true;
void OnVerifiedInventory(IInventory inventory);
}
}
47 changes: 47 additions & 0 deletions tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.IO;
using Neo.Network.P2P.Payloads;
using Neo.SmartContract;
using System;

namespace Neo.UnitTests.Network.P2P.Payloads
{
[TestClass]
public class UT_ExtensiblePayload
{
[TestMethod]
public void Size_Get()
{
var test = new ExtensiblePayload()
{
Sender = Array.Empty<byte>().ToScriptHash(),
Category = "123",
Data = new byte[] { 1, 2, 3 },
Witness = new Witness() { InvocationScript = new byte[] { 3, 5, 6 }, VerificationScript = Array.Empty<byte>() }
};
test.Size.Should().Be(42);
}

[TestMethod]
public void DeserializeAndSerialize()
{
var test = new ExtensiblePayload()
{
Category = "123",
ValidBlockStart = 456,
ValidBlockEnd = 789,
Sender = Array.Empty<byte>().ToScriptHash(),
Data = new byte[] { 1, 2, 3 },
Witness = new Witness() { InvocationScript = new byte[] { 3, 5, 6 }, VerificationScript = Array.Empty<byte>() }
};
var clone = test.ToArray().AsSerializable<ExtensiblePayload>();

Assert.AreEqual(test.Sender, clone.Witness.ScriptHash);
Assert.AreEqual(test.Hash, clone.Hash);
Assert.AreEqual(test.ValidBlockStart, clone.ValidBlockStart);
Assert.AreEqual(test.ValidBlockEnd, clone.ValidBlockEnd);
Assert.AreEqual(test.Category, clone.Category);
}
}
}
6 changes: 5 additions & 1 deletion tests/neo.UnitTests/Plugins/UT_Plugin.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Network.P2P.Payloads;
using Neo.Plugins;
using System;

Expand All @@ -10,7 +11,10 @@ public class UT_Plugin
{
private static readonly object locker = new object();

private class DummyP2PPlugin : IP2PPlugin { }
private class DummyP2PPlugin : IP2PPlugin
{
public void OnVerifiedInventory(IInventory inventory) { }
}
private class dummyPersistencePlugin : IPersistencePlugin { }

[TestMethod]
Expand Down

0 comments on commit 4645de8

Please sign in to comment.