diff --git a/src/Neo/Network/P2P/Payloads/NotValidBefore.cs b/src/Neo/Network/P2P/Payloads/NotValidBefore.cs
new file mode 100644
index 0000000000..e77693813b
--- /dev/null
+++ b/src/Neo/Network/P2P/Payloads/NotValidBefore.cs
@@ -0,0 +1,46 @@
+using Neo.IO;
+using Neo.Json;
+using Neo.Persistence;
+using Neo.SmartContract.Native;
+using System.IO;
+
+namespace Neo.Network.P2P.Payloads
+{
+ public class NotValidBefore : TransactionAttribute
+ {
+ ///
+ /// Indicates that the transaction is not valid before this height.
+ ///
+ public uint Height;
+
+ public override TransactionAttributeType Type => TransactionAttributeType.NotValidBefore;
+
+ public override bool AllowMultiple => false;
+
+ public override int Size => base.Size +
+ sizeof(uint); // Height.
+
+ protected override void DeserializeWithoutType(ref MemoryReader reader)
+ {
+ Height = reader.ReadUInt32();
+ }
+
+ protected override void SerializeWithoutType(BinaryWriter writer)
+ {
+ writer.Write(Height);
+ }
+
+ public override JObject ToJson()
+ {
+ JObject json = base.ToJson();
+ json["height"] = Height;
+ return json;
+ }
+
+ public override bool Verify(DataCache snapshot, Transaction tx)
+ {
+ var block_height = NativeContract.Ledger.CurrentIndex(snapshot);
+ return block_height >= Height;
+ }
+ }
+}
diff --git a/src/Neo/Network/P2P/Payloads/TransactionAttributeType.cs b/src/Neo/Network/P2P/Payloads/TransactionAttributeType.cs
index bfda3f34ba..41c69b507d 100644
--- a/src/Neo/Network/P2P/Payloads/TransactionAttributeType.cs
+++ b/src/Neo/Network/P2P/Payloads/TransactionAttributeType.cs
@@ -27,6 +27,12 @@ public enum TransactionAttributeType : byte
/// Indicates that the transaction is an oracle response.
///
[ReflectionCache(typeof(OracleResponse))]
- OracleResponse = 0x11
+ OracleResponse = 0x11,
+
+ ///
+ /// Indicates that the transaction is not valid before .
+ ///
+ [ReflectionCache(typeof(NotValidBefore))]
+ NotValidBefore = 0x20
}
}
diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NotValidBefore.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NotValidBefore.cs
new file mode 100644
index 0000000000..e47159d9ff
--- /dev/null
+++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NotValidBefore.cs
@@ -0,0 +1,75 @@
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.IO;
+using Neo.Network.P2P.Payloads;
+using Neo.SmartContract.Native;
+using System;
+
+namespace Neo.UnitTests.Network.P2P.Payloads
+{
+ [TestClass]
+ public class UT_NotValidBefore
+ {
+ [TestMethod]
+ public void Size_Get()
+ {
+ var test = new NotValidBefore();
+ test.Size.Should().Be(5);
+ }
+
+ [TestMethod]
+ public void ToJson()
+ {
+ var test = new NotValidBefore();
+ test.Height = 42;
+ var json = test.ToJson().ToString();
+ Assert.AreEqual(@"{""type"":""NotValidBefore"",""height"":42}", json);
+ }
+
+ [TestMethod]
+ public void DeserializeAndSerialize()
+ {
+ var test = new NotValidBefore();
+
+ var clone = test.ToArray().AsSerializable();
+ Assert.AreEqual(clone.Type, test.Type);
+
+ // As transactionAttribute
+
+ byte[] buffer = test.ToArray();
+ var reader = new MemoryReader(buffer);
+ clone = TransactionAttribute.DeserializeFrom(ref reader) as NotValidBefore;
+ Assert.AreEqual(clone.Type, test.Type);
+
+ // Wrong type
+
+ buffer[0] = 0xff;
+ reader = new MemoryReader(buffer);
+ try
+ {
+ TransactionAttribute.DeserializeFrom(ref reader);
+ Assert.Fail();
+ }
+ catch (FormatException) { }
+ reader = new MemoryReader(buffer);
+ try
+ {
+ new NotValidBefore().Deserialize(ref reader);
+ Assert.Fail();
+ }
+ catch (FormatException) { }
+ }
+
+ [TestMethod]
+ public void Verify()
+ {
+ var test = new NotValidBefore();
+ var snapshot = TestBlockchain.GetTestSnapshot();
+ test.Height = NativeContract.Ledger.CurrentIndex(snapshot) + 1;
+
+ Assert.IsFalse(test.Verify(snapshot, new Transaction()));
+ test.Height--;
+ Assert.IsTrue(test.Verify(snapshot, new Transaction()));
+ }
+ }
+}