Skip to content

Commit

Permalink
Add role designation history (neo-project#2007)
Browse files Browse the repository at this point in the history
* record historical designation

* fix ut

* sort

* Remove sort

* use different key

* fix some

* Store it as BiEndian

* avoid in same block

* Optimize

* use findrange

* optimize index get

* Erik's feedback

* Fix UT

* fix empty sequence

* optimize height

* fix ut

* index

* fix ut

Co-authored-by: Luchuan <luchuan@ngd.neo.org>
Co-authored-by: Shargon <shargon@gmail.com>
Co-authored-by: Erik Zhang <erik@neo.org>
  • Loading branch information
4 people authored and Shawn committed Jan 8, 2021
1 parent 1e2f21f commit 8d1c8b5
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/neo/Network/P2P/Payloads/OracleResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public override bool Verify(StoreView snapshot, Transaction tx)
OracleRequest request = NativeContract.Oracle.GetRequest(snapshot, Id);
if (request is null) return false;
if (tx.NetworkFee + tx.SystemFee != request.GasForResponse) return false;
UInt160 oracleAccount = Blockchain.GetConsensusAddress(NativeContract.Designate.GetDesignatedByRole(snapshot, Role.Oracle));
UInt160 oracleAccount = Blockchain.GetConsensusAddress(NativeContract.Designate.GetDesignatedByRole(snapshot, Role.Oracle, snapshot.Height + 1));
return tx.Signers.Any(p => p.Account.Equals(oracleAccount));
}
}
Expand Down
34 changes: 20 additions & 14 deletions src/neo/SmartContract/Native/Designate/DesignateContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Neo.Cryptography;
using Neo.Cryptography.ECC;
using Neo.IO;
using Neo.IO.Caching;
using Neo.Ledger;
using Neo.Persistence;
using Neo.SmartContract.Manifest;
Expand All @@ -24,20 +25,18 @@ internal DesignateContract()
Manifest.Features = ContractFeatures.HasStorage;
}

internal override void Initialize(ApplicationEngine engine)
{
foreach (byte role in Enum.GetValues(typeof(Role)))
{
engine.Snapshot.Storages.Add(CreateStorageKey(role), new StorageItem(new NodeList()));
}
}

[ContractMethod(0_01000000, CallFlags.AllowStates)]
public ECPoint[] GetDesignatedByRole(StoreView snapshot, Role role)
public ECPoint[] GetDesignatedByRole(StoreView snapshot, Role role, uint index)
{
if (!Enum.IsDefined(typeof(Role), role))
throw new ArgumentOutOfRangeException(nameof(role));
return snapshot.Storages[CreateStorageKey((byte)role)].GetInteroperable<NodeList>().ToArray();
if (snapshot.Height + 1 < index)
throw new ArgumentOutOfRangeException(nameof(index));
byte[] key = CreateStorageKey((byte)role).AddBigEndian(index).ToArray();
byte[] boundary = CreateStorageKey((byte)role).ToArray();
return snapshot.Storages.FindRange(key, boundary, SeekDirection.Backward)
.Select(u => u.Value.GetInteroperable<NodeList>().ToArray())
.FirstOrDefault() ?? System.Array.Empty<ECPoint>();
}

[ContractMethod(0, CallFlags.AllowModifyStates)]
Expand All @@ -47,19 +46,26 @@ private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] node
throw new ArgumentException();
if (!Enum.IsDefined(typeof(Role), role))
throw new ArgumentOutOfRangeException(nameof(role));
if (!CheckCommittee(engine)) throw new InvalidOperationException();
NodeList list = engine.Snapshot.Storages.GetAndChange(CreateStorageKey((byte)role)).GetInteroperable<NodeList>();
list.Clear();
if (!CheckCommittee(engine))
throw new InvalidOperationException(nameof(DesignateAsRole));
if (engine.Snapshot.PersistingBlock is null)
throw new InvalidOperationException(nameof(DesignateAsRole));
uint index = engine.Snapshot.PersistingBlock.Index + 1;
var key = CreateStorageKey((byte)role).AddBigEndian(index);
if (engine.Snapshot.Storages.Contains(key))
throw new InvalidOperationException();
NodeList list = new NodeList();
list.AddRange(nodes);
list.Sort();
engine.Snapshot.Storages.Add(key, new StorageItem(list));
}

private class NodeList : List<ECPoint>, IInteroperable
{
public void FromStackItem(StackItem stackItem)
{
foreach (StackItem item in (VM.Types.Array)stackItem)
Add(ECPoint.DecodePoint(item.GetSpan(), ECCurve.Secp256r1));
Add(item.GetSpan().AsSerializable<ECPoint>());
}

public StackItem ToStackItem(ReferenceCounter referenceCounter)
Expand Down
2 changes: 1 addition & 1 deletion src/neo/SmartContract/Native/Oracle/OracleContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ protected override void PostPersist(ApplicationEngine engine)
if (list.Count == 0) engine.Snapshot.Storages.Delete(key);

//Mint GAS for oracle nodes
nodes ??= NativeContract.Designate.GetDesignatedByRole(engine.Snapshot, Role.Oracle).Select(p => (Contract.CreateSignatureRedeemScript(p).ToScriptHash(), BigInteger.Zero)).ToArray();
nodes ??= NativeContract.Designate.GetDesignatedByRole(engine.Snapshot, Role.Oracle, engine.Snapshot.PersistingBlock.Index).Select(p => (Contract.CreateSignatureRedeemScript(p).ToScriptHash(), BigInteger.Zero)).ToArray();
if (nodes.Length > 0)
{
int index = (int)(response.Id % (ulong)nodes.Length);
Expand Down
4 changes: 3 additions & 1 deletion tests/neo.UnitTests/Extensions/NativeContractExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ public static StackItem Call(this NativeContract contract, StoreView snapshot, I
throw exception ?? new InvalidOperationException();
}

return engine.ResultStack.Pop();
if (0 < engine.ResultStack.Count)
return engine.ResultStack.Pop();
return null;
}
}
}
70 changes: 70 additions & 0 deletions tests/neo.UnitTests/SmartContract/Native/UT_DesignateContract.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Cryptography.ECC;
using Neo.IO;
using Neo.Ledger;
using Neo.Network.P2P.Payloads;
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.SmartContract.Native.Designate;
using Neo.UnitTests.Extensions;
using System.Linq;
using System.Numerics;

namespace Neo.UnitTests.SmartContract.Native
{
[TestClass]
public class UT_DesignateContract
{
[TestInitialize]
public void TestSetup()
{
TestBlockchain.InitializeMockNeoSystem();
}

[TestMethod]
public void TestSetAndGet()
{
using var snapshot1 = Blockchain.Singleton.GetSnapshot();
snapshot1.PersistingBlock = new Block
{
Index = 0,
};
UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot1);
ECPoint[] validators = NativeContract.NEO.ComputeNextBlockValidators(snapshot1);
var ret = NativeContract.Designate.Call(
snapshot1,
new Nep5NativeContractExtensions.ManualWitness(committeeMultiSigAddr),
"designateAsRole",
new ContractParameter(ContractParameterType.Integer) { Value = new BigInteger((int)Role.StateValidator) },
new ContractParameter(ContractParameterType.Array) { Value = validators.Select(p => new ContractParameter(ContractParameterType.ByteArray) { Value = p.ToArray() }).ToList() }
);
snapshot1.Commit();
using var snapshot2 = Blockchain.Singleton.GetSnapshot();
ret = NativeContract.Designate.Call(
snapshot2,
"getDesignatedByRole",
new ContractParameter(ContractParameterType.Integer) { Value = new BigInteger((int)Role.StateValidator) },
new ContractParameter(ContractParameterType.Integer) { Value = new BigInteger(1u) }
);
ret.Should().BeOfType<VM.Types.Array>();
(ret as VM.Types.Array).Count.Should().Be(7);
(ret as VM.Types.Array)[0].GetSpan().ToHexString().Should().Be(validators[0].ToArray().ToHexString());
(ret as VM.Types.Array)[1].GetSpan().ToHexString().Should().Be(validators[1].ToArray().ToHexString());
(ret as VM.Types.Array)[2].GetSpan().ToHexString().Should().Be(validators[2].ToArray().ToHexString());
(ret as VM.Types.Array)[3].GetSpan().ToHexString().Should().Be(validators[3].ToArray().ToHexString());
(ret as VM.Types.Array)[4].GetSpan().ToHexString().Should().Be(validators[4].ToArray().ToHexString());
(ret as VM.Types.Array)[5].GetSpan().ToHexString().Should().Be(validators[5].ToArray().ToHexString());
(ret as VM.Types.Array)[6].GetSpan().ToHexString().Should().Be(validators[6].ToArray().ToHexString());

ret = NativeContract.Designate.Call(
snapshot2,
"getDesignatedByRole",
new ContractParameter(ContractParameterType.Integer) { Value = new BigInteger((int)Role.StateValidator) },
new ContractParameter(ContractParameterType.Integer) { Value = new BigInteger(0) }
);
ret.Should().BeOfType<VM.Types.Array>();
(ret as VM.Types.Array).Count.Should().Be(0);
}
}
}

0 comments on commit 8d1c8b5

Please sign in to comment.