diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index 695b92a683..f7f500f3dc 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -109,10 +109,10 @@ public void Deserialize(BinaryReader reader) ViewNumber = reader.ReadByte(); TransactionHashes = reader.ReadSerializableArray(); Transaction[] transactions = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); - PreparationPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); - CommitPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); - ChangeViewPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); - LastChangeViewPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); + PreparationPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); + CommitPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); + ChangeViewPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); + LastChangeViewPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); if (TransactionHashes.Length == 0 && !RequestSentOrReceived) TransactionHashes = null; Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash); diff --git a/src/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs index c25816ee7f..cde0f7aecc 100644 --- a/src/neo/Consensus/RecoveryMessage.cs +++ b/src/neo/Consensus/RecoveryMessage.cs @@ -32,7 +32,7 @@ public RecoveryMessage() : base(ConsensusMessageType.RecoveryMessage) public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); - ChangeViewMessages = reader.ReadSerializableArray(Blockchain.MaxValidators).ToDictionary(p => (int)p.ValidatorIndex); + ChangeViewMessages = reader.ReadSerializableArray(Blockchain.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); if (reader.ReadBoolean()) PrepareRequestMessage = reader.ReadSerializable(); else @@ -42,8 +42,8 @@ public override void Deserialize(BinaryReader reader) PreparationHash = new UInt256(reader.ReadFixedBytes(preparationHashSize)); } - PreparationMessages = reader.ReadSerializableArray(Blockchain.MaxValidators).ToDictionary(p => (int)p.ValidatorIndex); - CommitMessages = reader.ReadSerializableArray(Blockchain.MaxValidators).ToDictionary(p => (int)p.ValidatorIndex); + PreparationMessages = reader.ReadSerializableArray(Blockchain.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); + CommitMessages = reader.ReadSerializableArray(Blockchain.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); } internal ConsensusPayload[] GetChangeViewPayloads(ConsensusContext context, ConsensusPayload payload) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index be66ce2013..b8f185e2c9 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -258,57 +258,5 @@ internal static IPEndPoint Unmap(this IPEndPoint endPoint) return endPoint; return new IPEndPoint(endPoint.Address.Unmap(), endPoint.Port); } - - internal static BigInteger WeightedAverage(this IEnumerable source, Func valueSelector, Func weightSelector) - { - BigInteger sum_weight = BigInteger.Zero; - BigInteger sum_value = BigInteger.Zero; - foreach (T item in source) - { - BigInteger weight = weightSelector(item); - sum_weight += weight; - sum_value += valueSelector(item) * weight; - } - if (sum_value == BigInteger.Zero) return BigInteger.Zero; - return sum_value / sum_weight; - } - - internal static IEnumerable WeightedFilter(this IList source, double start, double end, Func weightSelector, Func resultSelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (start < 0 || start > 1) throw new ArgumentOutOfRangeException(nameof(start)); - if (end < start || start + end > 1) throw new ArgumentOutOfRangeException(nameof(end)); - if (weightSelector == null) throw new ArgumentNullException(nameof(weightSelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - if (source.Count == 0 || start == end) yield break; - double amount = (double)source.Select(weightSelector).Sum(); - BigInteger sum = 0; - double current = 0; - foreach (T item in source) - { - if (current >= end) break; - BigInteger weight = weightSelector(item); - sum += weight; - double old = current; - current = (double)sum / amount; - if (current <= start) continue; - if (old < start) - { - if (current > end) - { - weight = (long)((end - start) * amount); - } - else - { - weight = (long)((current - start) * amount); - } - } - else if (current > end) - { - weight = (long)((end - old) * amount); - } - yield return resultSelector(item, weight); - } - } } } diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 54a4f26327..fd76d1ebca 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -30,10 +30,12 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu public static readonly uint MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock; public const uint DecrementInterval = 2000000; - public const int MaxValidators = 1024; public static readonly uint[] GenerationAmount = { 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; public static readonly TimeSpan TimePerBlock = TimeSpan.FromMilliseconds(MillisecondsPerBlock); - public static readonly ECPoint[] StandbyValidators = ProtocolSettings.Default.StandbyValidators.OfType().Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); + public static readonly byte CommitteeMembersCount = (byte)ProtocolSettings.Default.StandbyCommittee.Length; + public static readonly byte ValidatorsCount = ProtocolSettings.Default.ValidatorsCount; + public static readonly ECPoint[] StandbyCommittee = ProtocolSettings.Default.StandbyCommittee.Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); + public static readonly ECPoint[] StandbyValidators = StandbyCommittee[..ValidatorsCount]; public static readonly Block GenesisBlock = new Block { diff --git a/src/neo/Network/P2P/Payloads/ConsensusData.cs b/src/neo/Network/P2P/Payloads/ConsensusData.cs index 1a8019633d..460f6a8098 100644 --- a/src/neo/Network/P2P/Payloads/ConsensusData.cs +++ b/src/neo/Network/P2P/Payloads/ConsensusData.cs @@ -29,7 +29,7 @@ public UInt256 Hash void ISerializable.Deserialize(BinaryReader reader) { - PrimaryIndex = (uint)reader.ReadVarInt(Blockchain.MaxValidators - 1); + PrimaryIndex = (uint)reader.ReadVarInt((ulong)Blockchain.ValidatorsCount - 1); Nonce = reader.ReadUInt64(); } diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index 8f0a48101e..ca6deaf96e 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -9,7 +9,8 @@ public class ProtocolSettings { public uint Magic { get; } public byte AddressVersion { get; } - public string[] StandbyValidators { get; } + public string[] StandbyCommittee { get; } + public byte ValidatorsCount { get; } public string[] SeedList { get; } public uint MillisecondsPerBlock { get; } public int MemoryPoolMaxTransactions { get; } @@ -47,18 +48,36 @@ private ProtocolSettings(IConfigurationSection section) this.AddressVersion = section.GetValue("AddressVersion", (byte)0x35); IConfigurationSection section_sv = section.GetSection("StandbyValidators"); if (section_sv.Exists()) - this.StandbyValidators = section_sv.GetChildren().Select(p => p.Get()).ToArray(); + this.StandbyCommittee = section_sv.GetChildren().Select(p => p.Get()).ToArray(); else - this.StandbyValidators = new[] + this.StandbyCommittee = new[] { + //Validators "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", "02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", "03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", "02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", "024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", "02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", - "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70" + "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", + + //Other Members + "023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", + "03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", + "03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", + "03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", + "02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", + "03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", + "0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", + "020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", + "0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", + "03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", + "02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", + "0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", + "03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", + "02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a" }; + this.ValidatorsCount = section.GetValue("ValidatorsCount", (byte)7); IConfigurationSection section_sl = section.GetSection("SeedList"); if (section_sl.Exists()) this.SeedList = section_sl.GetChildren().Select(p => p.Get()).ToArray(); diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index 1f05114062..459d65950a 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -24,7 +24,7 @@ internal override bool Initialize(ApplicationEngine engine) { if (!base.Initialize(engine)) return false; if (TotalSupply(engine.Snapshot) != BigInteger.Zero) return false; - UInt160 account = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, Blockchain.StandbyValidators).ToScriptHash(); + UInt160 account = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators); Mint(engine, account, 30_000_000 * Factor); return true; } diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index ec19edb85f..ef633ad597 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -9,7 +9,6 @@ using Neo.VM.Types; using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Numerics; using Array = Neo.VM.Types.Array; @@ -25,8 +24,7 @@ public sealed class NeoToken : Nep5Token public override byte Decimals => 0; public BigInteger TotalAmount { get; } - private const byte Prefix_Validator = 33; - private const byte Prefix_ValidatorsCount = 15; + private const byte Prefix_Candidate = 33; private const byte Prefix_NextValidators = 14; internal NeoToken() @@ -43,18 +41,13 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco { DistributeGas(engine, account, state); if (amount.IsZero) return; - if (state.Votes.Length == 0) return; - foreach (ECPoint pubkey in state.Votes) + if (state.VoteTo != null) { - StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Validator, pubkey.ToArray())); - ValidatorState state_validator = ValidatorState.FromByteArray(storage_validator.Value); + StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Candidate, state.VoteTo.ToArray())); + CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value); state_validator.Votes += amount; storage_validator.Value = state_validator.ToByteArray(); } - StorageItem storage_count = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_ValidatorsCount)); - ValidatorsCountState state_count = ValidatorsCountState.FromByteArray(storage_count.Value); - state_count.Votes[state.Votes.Length - 1] += amount; - storage_count.Value = state_count.ToByteArray(); } private void DistributeGas(ApplicationEngine engine, UInt160 account, AccountState state) @@ -101,10 +94,19 @@ internal override bool Initialize(ApplicationEngine engine) { if (!base.Initialize(engine)) return false; if (base.TotalSupply(engine.Snapshot) != BigInteger.Zero) return false; - UInt160 account = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, Blockchain.StandbyValidators).ToScriptHash(); - Mint(engine, account, TotalAmount); - foreach (ECPoint pubkey in Blockchain.StandbyValidators) - RegisterValidator(engine.Snapshot, pubkey); + BigInteger amount = TotalAmount; + for (int i = 0; i < Blockchain.CommitteeMembersCount; i++) + { + ECPoint pubkey = Blockchain.StandbyCommittee[i]; + RegisterCandidate(engine.Snapshot, pubkey); + BigInteger balance = TotalAmount / 2 / (Blockchain.ValidatorsCount * 2 + (Blockchain.CommitteeMembersCount - Blockchain.ValidatorsCount)); + if (i < Blockchain.ValidatorsCount) balance *= 2; + UInt160 account = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); + Mint(engine, account, balance); + Vote(engine.Snapshot, account, pubkey); + amount -= balance; + } + Mint(engine, Blockchain.GetConsensusAddress(Blockchain.StandbyValidators), amount); return true; } @@ -133,20 +135,51 @@ public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) } [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] - private StackItem RegisterValidator(ApplicationEngine engine, Array args) + private StackItem RegisterCandidate(ApplicationEngine engine, Array args) { ECPoint pubkey = args[0].GetSpan().AsSerializable(); - return RegisterValidator(engine.Snapshot, pubkey); + if (!InteropService.Runtime.CheckWitnessInternal(engine, Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) + return false; + return RegisterCandidate(engine.Snapshot, pubkey); } - private bool RegisterValidator(StoreView snapshot, ECPoint pubkey) + private bool RegisterCandidate(StoreView snapshot, ECPoint pubkey) { - StorageKey key = CreateStorageKey(Prefix_Validator, pubkey); - if (snapshot.Storages.TryGet(key) != null) return false; - snapshot.Storages.Add(key, new StorageItem + StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey); + StorageItem item = snapshot.Storages.GetAndChange(key, () => new StorageItem { - Value = new ValidatorState().ToByteArray() + Value = new CandidateState().ToByteArray() }); + CandidateState state = CandidateState.FromByteArray(item.Value); + state.Registered = true; + item.Value = state.ToByteArray(); + return true; + } + + [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] + private StackItem UnregisterCandidate(ApplicationEngine engine, Array args) + { + ECPoint pubkey = args[0].GetSpan().AsSerializable(); + if (!InteropService.Runtime.CheckWitnessInternal(engine, Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) + return false; + return UnregisterCandidate(engine.Snapshot, pubkey); + } + + private bool UnregisterCandidate(StoreView snapshot, ECPoint pubkey) + { + StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey); + if (snapshot.Storages.TryGet(key) is null) return true; + StorageItem item = snapshot.Storages.GetAndChange(key); + CandidateState state = CandidateState.FromByteArray(item.Value); + if (state.Votes.IsZero) + { + snapshot.Storages.Delete(key); + } + else + { + state.Registered = false; + item.Value = state.ToByteArray(); + } return true; } @@ -154,39 +187,37 @@ private bool RegisterValidator(StoreView snapshot, ECPoint pubkey) private StackItem Vote(ApplicationEngine engine, Array args) { UInt160 account = new UInt160(args[0].GetSpan()); - ECPoint[] pubkeys = ((Array)args[1]).Select(p => p.GetSpan().AsSerializable()).ToArray(); + ECPoint voteTo = args[1].IsNull ? null : args[1].GetSpan().AsSerializable(); if (!InteropService.Runtime.CheckWitnessInternal(engine, account)) return false; + return Vote(engine.Snapshot, account, voteTo); + } + + private bool Vote(StoreView snapshot, UInt160 account, ECPoint voteTo) + { StorageKey key_account = CreateAccountKey(account); - if (engine.Snapshot.Storages.TryGet(key_account) is null) return false; - StorageItem storage_account = engine.Snapshot.Storages.GetAndChange(key_account); + if (snapshot.Storages.TryGet(key_account) is null) return false; + StorageItem storage_account = snapshot.Storages.GetAndChange(key_account); AccountState state_account = new AccountState(storage_account.Value); - foreach (ECPoint pubkey in state_account.Votes) + if (state_account.VoteTo != null) { - StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Validator, pubkey.ToArray())); - ValidatorState state_validator = ValidatorState.FromByteArray(storage_validator.Value); + StorageKey key = CreateStorageKey(Prefix_Candidate, state_account.VoteTo.ToArray()); + StorageItem storage_validator = snapshot.Storages.GetAndChange(key); + CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value); state_validator.Votes -= state_account.Balance; - storage_validator.Value = state_validator.ToByteArray(); - } - pubkeys = pubkeys.Distinct().Where(p => engine.Snapshot.Storages.TryGet(CreateStorageKey(Prefix_Validator, p.ToArray())) != null).ToArray(); - if (pubkeys.Length != state_account.Votes.Length) - { - StorageItem storage_count = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_ValidatorsCount), () => new StorageItem - { - Value = new ValidatorsCountState().ToByteArray() - }); - ValidatorsCountState state_count = ValidatorsCountState.FromByteArray(storage_count.Value); - if (state_account.Votes.Length > 0) - state_count.Votes[state_account.Votes.Length - 1] -= state_account.Balance; - if (pubkeys.Length > 0) - state_count.Votes[pubkeys.Length - 1] += state_account.Balance; - storage_count.Value = state_count.ToByteArray(); + if (!state_validator.Registered && state_validator.Votes.IsZero) + snapshot.Storages.Delete(key); + else + storage_validator.Value = state_validator.ToByteArray(); } - state_account.Votes = pubkeys; + state_account.VoteTo = voteTo; storage_account.Value = state_account.ToByteArray(); - foreach (ECPoint pubkey in state_account.Votes) + if (voteTo != null) { - StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Validator, pubkey.ToArray())); - ValidatorState state_validator = ValidatorState.FromByteArray(storage_validator.Value); + StorageKey key = CreateStorageKey(Prefix_Candidate, voteTo.ToArray()); + if (snapshot.Storages.TryGet(key) is null) return false; + StorageItem storage_validator = snapshot.Storages.GetAndChange(key); + CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value); + if (!state_validator.Registered) return false; state_validator.Votes += state_account.Balance; storage_validator.Value = state_validator.ToByteArray(); } @@ -194,19 +225,19 @@ private StackItem Vote(ApplicationEngine engine, Array args) } [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] - private StackItem GetRegisteredValidators(ApplicationEngine engine, Array args) + private StackItem GetCandidates(ApplicationEngine engine, Array args) { - return new Array(engine.ReferenceCounter, GetRegisteredValidators(engine.Snapshot).Select(p => new Struct(engine.ReferenceCounter, new StackItem[] { p.PublicKey.ToArray(), p.Votes }))); + return new Array(engine.ReferenceCounter, GetCandidates(engine.Snapshot).Select(p => new Struct(engine.ReferenceCounter, new StackItem[] { p.PublicKey.ToArray(), p.Votes }))); } - public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetRegisteredValidators(StoreView snapshot) + public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetCandidates(StoreView snapshot) { - byte[] prefix_key = StorageKey.CreateSearchPrefix(Id, new[] { Prefix_Validator }); + byte[] prefix_key = StorageKey.CreateSearchPrefix(Id, new[] { Prefix_Candidate }); return snapshot.Storages.Find(prefix_key).Select(p => ( p.Key.Key.AsSerializable(1), - ValidatorState.FromByteArray(p.Value.Value).Votes - )); + CandidateState.FromByteArray(p.Value.Value) + )).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes)); } [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] @@ -217,21 +248,23 @@ private StackItem GetValidators(ApplicationEngine engine, Array args) public ECPoint[] GetValidators(StoreView snapshot) { - StorageItem storage_count = snapshot.Storages.TryGet(CreateStorageKey(Prefix_ValidatorsCount)); - if (storage_count is null) return Blockchain.StandbyValidators; - ValidatorsCountState state_count = ValidatorsCountState.FromByteArray(storage_count.Value); - int count = (int)state_count.Votes.Select((p, i) => new - { - Count = i, - Votes = p - }).Where(p => p.Votes.Sign > 0).ToArray().WeightedFilter(0.25, 0.75, p => p.Votes, (p, w) => new - { - p.Count, - Weight = w - }).WeightedAverage(p => p.Count, p => p.Weight); - count = Math.Max(count, Blockchain.StandbyValidators.Length); - HashSet sv = new HashSet(Blockchain.StandbyValidators); - return GetRegisteredValidators(snapshot).Where(p => (p.Votes.Sign > 0) || sv.Contains(p.PublicKey)).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count).OrderBy(p => p).ToArray(); + return GetCommitteeMembers(snapshot, Blockchain.ValidatorsCount).OrderBy(p => p).ToArray(); + } + + [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] + private StackItem GetCommittee(ApplicationEngine engine, Array args) + { + return new Array(engine.ReferenceCounter, GetCommittee(engine.Snapshot).Select(p => (StackItem)p.ToArray())); + } + + public ECPoint[] GetCommittee(StoreView snapshot) + { + return GetCommitteeMembers(snapshot, Blockchain.CommitteeMembersCount).OrderBy(p => p).ToArray(); + } + + private IEnumerable GetCommitteeMembers(StoreView snapshot, int count) + { + return GetCandidates(snapshot).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count); } [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] @@ -250,11 +283,10 @@ public ECPoint[] GetNextBlockValidators(StoreView snapshot) public class AccountState : Nep5AccountState { public uint BalanceHeight; - public ECPoint[] Votes; + public ECPoint VoteTo; public AccountState() { - this.Votes = new ECPoint[0]; } public AccountState(byte[] data) @@ -266,66 +298,36 @@ protected override void FromStruct(Struct @struct) { base.FromStruct(@struct); BalanceHeight = (uint)@struct[1].GetBigInteger(); - Votes = @struct[2].GetSpan().AsSerializableArray(Blockchain.MaxValidators); + VoteTo = @struct[2].IsNull ? null : @struct[2].GetSpan().AsSerializable(); } protected override Struct ToStruct() { Struct @struct = base.ToStruct(); @struct.Add(BalanceHeight); - @struct.Add(Votes.ToByteArray()); + @struct.Add(VoteTo?.ToArray() ?? StackItem.Null); return @struct; } } - internal class ValidatorState + internal class CandidateState { + public bool Registered = true; public BigInteger Votes; - public static ValidatorState FromByteArray(byte[] data) + public static CandidateState FromByteArray(byte[] data) { - return new ValidatorState + Struct @struct = (Struct)BinarySerializer.Deserialize(data, 16, 32); + return new CandidateState { - Votes = new BigInteger(data) + Registered = @struct[0].ToBoolean(), + Votes = @struct[1].GetBigInteger() }; } public byte[] ToByteArray() { - return Votes.ToByteArrayStandard(); - } - } - - internal class ValidatorsCountState - { - public BigInteger[] Votes = new BigInteger[Blockchain.MaxValidators]; - - public static ValidatorsCountState FromByteArray(byte[] data) - { - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader r = new BinaryReader(ms)) - { - BigInteger[] votes = new BigInteger[(int)r.ReadVarInt(Blockchain.MaxValidators)]; - for (int i = 0; i < votes.Length; i++) - votes[i] = new BigInteger(r.ReadVarBytes()); - return new ValidatorsCountState - { - Votes = votes - }; - } - } - - public byte[] ToByteArray() - { - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter w = new BinaryWriter(ms)) - { - w.WriteVarInt(Votes.Length); - foreach (BigInteger vote in Votes) - w.WriteVarBytes(vote.ToByteArrayStandard()); - w.Flush(); - return ms.ToArray(); - } + return BinarySerializer.Serialize(new Struct { Registered, Votes }, 32); } } } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index be6e6a1d77..aa478633c1 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -40,8 +40,7 @@ public void Check_BalanceOfTransferAndBurn() var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; - byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, - Blockchain.StandbyValidators).ToScriptHash().ToArray(); + byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); byte[] to = new byte[20]; @@ -54,16 +53,16 @@ public void Check_BalanceOfTransferAndBurn() // Check unclaim var unclaim = UT_NeoToken.Check_UnclaimedGas(snapshot, from); - unclaim.Value.Should().Be(new BigInteger(600000000000)); + unclaim.Value.Should().Be(new BigInteger(300000048000)); unclaim.State.Should().BeTrue(); // Transfer NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.Zero, true).Should().BeTrue(); - NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(100_000_000); + NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(50000008); NativeContract.NEO.BalanceOf(snapshot, to).Should().Be(0); - NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(3000600000000000); + NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(3000300000048000); NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(0); // Check unclaim @@ -73,7 +72,7 @@ public void Check_BalanceOfTransferAndBurn() unclaim.State.Should().BeTrue(); supply = NativeContract.GAS.TotalSupply(snapshot); - supply.Should().Be(3000600000000000); + supply.Should().Be(3000300000048000); snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 3); // Gas @@ -81,13 +80,13 @@ public void Check_BalanceOfTransferAndBurn() keyCount = snapshot.Storages.GetChangeSet().Count(); - NativeContract.GAS.Transfer(snapshot, from, to, 3000600000000000, false).Should().BeFalse(); // Not signed - NativeContract.GAS.Transfer(snapshot, from, to, 3000600000000001, true).Should().BeFalse(); // More than balance - NativeContract.GAS.Transfer(snapshot, from, to, 3000600000000000, true).Should().BeTrue(); // All balance + NativeContract.GAS.Transfer(snapshot, from, to, 3000300000048000, false).Should().BeFalse(); // Not signed + NativeContract.GAS.Transfer(snapshot, from, to, 3000300000048001, true).Should().BeFalse(); // More than balance + NativeContract.GAS.Transfer(snapshot, from, to, 3000300000048000, true).Should().BeTrue(); // All balance // Balance of - NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000600000000000); + NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000300000048000); NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(0); snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 1); // All @@ -103,19 +102,19 @@ public void Check_BalanceOfTransferAndBurn() // Burn more than expected Assert.ThrowsException(() => - NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000600000000001))); + NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000300000048001))); // Real burn NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(1)); - NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000599999999999); + NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000300000047999); keyCount.Should().Be(snapshot.Storages.GetChangeSet().Count()); // Burn all - NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000599999999999)); + NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000300000047999)); (keyCount - 1).Should().Be(snapshot.Storages.GetChangeSet().Count()); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index ad17dffb28..b2696b4888 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -45,24 +45,23 @@ public void Check_Vote() var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; - byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, - Blockchain.StandbyValidators).ToScriptHash().ToArray(); + byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); // No signature - var ret = Check_Vote(snapshot, from, new byte[][] { }, false); + var ret = Check_Vote(snapshot, from, null, false); ret.Result.Should().BeFalse(); ret.State.Should().BeTrue(); // Wrong address - ret = Check_Vote(snapshot, new byte[19], new byte[][] { }, false); + ret = Check_Vote(snapshot, new byte[19], null, false); ret.Result.Should().BeFalse(); ret.State.Should().BeFalse(); // Wrong ec - ret = Check_Vote(snapshot, from, new byte[][] { new byte[19] }, true); + ret = Check_Vote(snapshot, from, new byte[19], true); ret.Result.Should().BeFalse(); ret.State.Should().BeFalse(); @@ -72,7 +71,7 @@ public void Check_Vote() fakeAddr[0] = 0x5F; fakeAddr[5] = 0xFF; - ret = Check_Vote(snapshot, fakeAddr, new byte[][] { }, true); + ret = Check_Vote(snapshot, fakeAddr, null, true); ret.Result.Should().BeFalse(); ret.State.Should().BeTrue(); @@ -85,11 +84,10 @@ public void Check_UnclaimedGas() var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; - byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, - Blockchain.StandbyValidators).ToScriptHash().ToArray(); + byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); var unclaim = Check_UnclaimedGas(snapshot, from); - unclaim.Value.Should().Be(new BigInteger(600000000000)); + unclaim.Value.Should().Be(new BigInteger(300000048000)); unclaim.State.Should().BeTrue(); unclaim = Check_UnclaimedGas(snapshot, new byte[19]); @@ -102,22 +100,14 @@ public void Check_RegisterValidator() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var ret = Check_RegisterValidator(snapshot, new byte[0]); - ret.State.Should().BeFalse(); - ret.Result.Should().BeFalse(); - - ret = Check_RegisterValidator(snapshot, new byte[33]); - ret.State.Should().BeFalse(); - ret.Result.Should().BeFalse(); - var keyCount = snapshot.Storages.GetChangeSet().Count(); var point = Blockchain.StandbyValidators[0].EncodePoint(true); - ret = Check_RegisterValidator(snapshot, point); // Exists + var ret = Check_RegisterValidator(snapshot, point); // Exists ret.State.Should().BeTrue(); - ret.Result.Should().BeFalse(); + ret.Result.Should().BeTrue(); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount); // No changes + snapshot.Storages.GetChangeSet().Count().Should().Be(++keyCount); // No changes point[20]++; // fake point ret = Check_RegisterValidator(snapshot, point); // New @@ -129,14 +119,13 @@ public void Check_RegisterValidator() // Check GetRegisteredValidators - var validators = NativeContract.NEO.GetRegisteredValidators(snapshot).OrderBy(u => u.PublicKey).ToArray(); - var check = Blockchain.StandbyValidators.Select(u => u.EncodePoint(true)).ToList(); + var members = NativeContract.NEO.GetCandidates(snapshot).OrderBy(u => u.PublicKey).ToArray(); + var check = Blockchain.StandbyCommittee.Select(u => u.EncodePoint(true)).ToList(); check.Add(point); // Add the new member - for (int x = 0; x < validators.Length; x++) + for (int x = 0; x < members.Length; x++) { - Assert.AreEqual(1, check.RemoveAll(u => u.SequenceEqual(validators[x].PublicKey.EncodePoint(true)))); - Assert.AreEqual(0, validators[x].Votes); + Assert.AreEqual(1, check.RemoveAll(u => u.SequenceEqual(members[x].PublicKey.EncodePoint(true)))); } Assert.AreEqual(0, check.Count); @@ -148,8 +137,7 @@ public void Check_Transfer() var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; - byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, - Blockchain.StandbyValidators).ToScriptHash().ToArray(); + byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); byte[] to = new byte[20]; @@ -158,14 +146,14 @@ public void Check_Transfer() // Check unclaim var unclaim = Check_UnclaimedGas(snapshot, from); - unclaim.Value.Should().Be(new BigInteger(600000000000)); + unclaim.Value.Should().Be(new BigInteger(300000048000)); unclaim.State.Should().BeTrue(); // Transfer NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.One, false).Should().BeFalse(); // Not signed NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.One, true).Should().BeTrue(); - NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(99_999_999); + NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(50000007); NativeContract.NEO.BalanceOf(snapshot, to).Should().Be(1); // Check unclaim @@ -199,10 +187,9 @@ public void Check_Transfer() public void Check_BalanceOf() { var snapshot = Blockchain.Singleton.GetSnapshot(); - byte[] account = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, - Blockchain.StandbyValidators).ToScriptHash().ToArray(); + byte[] account = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); - NativeContract.NEO.BalanceOf(snapshot, account).Should().Be(100_000_000); + NativeContract.NEO.BalanceOf(snapshot, account).Should().Be(50_000_008); account[5]++; // Without existing balance @@ -315,25 +302,24 @@ public void TestGetNextBlockValidators2() [TestMethod] public void TestGetRegisteredValidators1() { - using (ApplicationEngine engine = NativeContract.NEO.TestCall("getRegisteredValidators")) + using (ApplicationEngine engine = NativeContract.NEO.TestCall("getCandidates")) { - var result = engine.ResultStack.Peek(); - result.GetType().Should().Be(typeof(VM.Types.Array)); - ((VM.Types.Array)result).Count.Should().Be(7); - ((VM.Types.Struct)((VM.Types.Array)result)[0])[0].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); - ((VM.Types.Struct)((VM.Types.Array)result)[0])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[1])[0].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - ((VM.Types.Struct)((VM.Types.Array)result)[1])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[2])[0].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - ((VM.Types.Struct)((VM.Types.Array)result)[2])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[3])[0].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - ((VM.Types.Struct)((VM.Types.Array)result)[3])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[4])[0].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - ((VM.Types.Struct)((VM.Types.Array)result)[4])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[5])[0].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - ((VM.Types.Struct)((VM.Types.Array)result)[5])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[6])[0].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - ((VM.Types.Struct)((VM.Types.Array)result)[6])[1].GetBigInteger().Should().Be(new BigInteger(0)); + engine.ResultStack.TryPop(out VM.Types.Array array).Should().BeTrue(); + array.Count.Should().Be(21); + ((VM.Types.Struct)array[0])[0].GetSpan().ToHexString().Should().Be("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639"); + ((VM.Types.Struct)array[0])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); + ((VM.Types.Struct)array[1])[0].GetSpan().ToHexString().Should().Be("0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30"); + ((VM.Types.Struct)array[1])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); + ((VM.Types.Struct)array[2])[0].GetSpan().ToHexString().Should().Be("0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d"); + ((VM.Types.Struct)array[2])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); + ((VM.Types.Struct)array[3])[0].GetSpan().ToHexString().Should().Be("023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe"); + ((VM.Types.Struct)array[3])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); + ((VM.Types.Struct)array[4])[0].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + ((VM.Types.Struct)array[4])[1].GetBigInteger().Should().Be(new BigInteger(3571428)); + ((VM.Types.Struct)array[5])[0].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + ((VM.Types.Struct)array[5])[1].GetBigInteger().Should().Be(new BigInteger(3571428)); + ((VM.Types.Struct)array[6])[0].GetSpan().ToHexString().Should().Be("02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad"); + ((VM.Types.Struct)array[6])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); } } @@ -341,29 +327,29 @@ public void TestGetRegisteredValidators1() public void TestGetRegisteredValidators2() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var result = NativeContract.NEO.GetRegisteredValidators(snapshot).ToArray(); - result.Length.Should().Be(7); - result[0].PublicKey.ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); - result[0].Votes.Should().Be(new BigInteger(0)); - result[1].PublicKey.ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - result[1].Votes.Should().Be(new BigInteger(0)); - result[2].PublicKey.ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - result[2].Votes.Should().Be(new BigInteger(0)); - result[3].PublicKey.ToArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - result[3].Votes.Should().Be(new BigInteger(0)); - result[4].PublicKey.ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - result[4].Votes.Should().Be(new BigInteger(0)); - result[5].PublicKey.ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - result[5].Votes.Should().Be(new BigInteger(0)); - result[6].PublicKey.ToArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - result[6].Votes.Should().Be(new BigInteger(0)); + var result = NativeContract.NEO.GetCandidates(snapshot).ToArray(); + result.Length.Should().Be(21); + result[0].PublicKey.ToArray().ToHexString().Should().Be("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639"); + result[0].Votes.Should().Be(new BigInteger(1785714)); + result[1].PublicKey.ToArray().ToHexString().Should().Be("0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30"); + result[1].Votes.Should().Be(new BigInteger(1785714)); + result[2].PublicKey.ToArray().ToHexString().Should().Be("0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d"); + result[2].Votes.Should().Be(new BigInteger(1785714)); + result[3].PublicKey.ToArray().ToHexString().Should().Be("023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe"); + result[3].Votes.Should().Be(new BigInteger(1785714)); + result[4].PublicKey.ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + result[4].Votes.Should().Be(new BigInteger(3571428)); + result[5].PublicKey.ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + result[5].Votes.Should().Be(new BigInteger(3571428)); + result[6].PublicKey.ToArray().ToHexString().Should().Be("02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad"); + result[6].Votes.Should().Be(new BigInteger(1785714)); StorageKey key = NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G); snapshot.Storages.Add(key, new StorageItem { - Value = new ValidatorState().ToByteArray() + Value = new CandidateState().ToByteArray() }); - NativeContract.NEO.GetRegisteredValidators(snapshot).ToArray().Length.Should().Be(8); + NativeContract.NEO.GetCandidates(snapshot).ToArray().Length.Should().Be(22); } [TestMethod] @@ -374,13 +360,13 @@ public void TestGetValidators1() var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); ((VM.Types.Array)result).Count.Should().Be(7); - ((VM.Types.Array)result)[0].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - ((VM.Types.Array)result)[1].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - ((VM.Types.Array)result)[2].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - ((VM.Types.Array)result)[3].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - ((VM.Types.Array)result)[4].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - ((VM.Types.Array)result)[5].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - ((VM.Types.Array)result)[6].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + ((VM.Types.Array)result)[0].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + ((VM.Types.Array)result)[1].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + ((VM.Types.Array)result)[2].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + ((VM.Types.Array)result)[3].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + ((VM.Types.Array)result)[4].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + ((VM.Types.Array)result)[5].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + ((VM.Types.Array)result)[6].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); } } @@ -389,25 +375,13 @@ public void TestGetValidators2() { var snapshot = Blockchain.Singleton.GetSnapshot(); var result = NativeContract.NEO.GetValidators(snapshot); - result[0].ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - result[1].ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - result[2].ToArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - result[3].ToArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - result[4].ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - result[5].ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - result[6].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); - - StorageKey key = CreateStorageKey(15); - ValidatorsCountState state = new ValidatorsCountState(); - for (int i = 0; i < 100; i++) - { - state.Votes[i] = new BigInteger(i + 1); - } - snapshot.Storages.Add(key, new StorageItem() - { - Value = state.ToByteArray() - }); - NativeContract.NEO.GetValidators(snapshot).ToArray().Length.Should().Be(7); + result[0].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + result[1].ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + result[2].ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + result[3].ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + result[4].ToArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + result[5].ToArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + result[6].ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); } [TestMethod] @@ -469,11 +443,11 @@ public void TestVote() UInt160 account = UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4"); StorageKey keyAccount = CreateStorageKey(20, account.ToArray()); StorageKey keyValidator = CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()); - var ret = Check_Vote(snapshot, account.ToArray(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, false); + var ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), false); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); - ret = Check_Vote(snapshot, account.ToArray(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, true); + ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); @@ -481,48 +455,40 @@ public void TestVote() { Value = new AccountState().ToByteArray() }); - ret = Check_Vote(snapshot, account.ToArray(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, true); + ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); ret.State.Should().BeTrue(); - ret.Result.Should().BeTrue(); + ret.Result.Should().BeFalse(); snapshot.Storages.Delete(keyAccount); snapshot.Storages.GetAndChange(keyAccount, () => new StorageItem { Value = new AccountState() { - Votes = new ECPoint[] { ECCurve.Secp256r1.G } + VoteTo = ECCurve.Secp256r1.G }.ToByteArray() }); snapshot.Storages.Add(keyValidator, new StorageItem { - Value = new ValidatorState().ToByteArray() + Value = new CandidateState().ToByteArray() }); - ret = Check_Vote(snapshot, account.ToArray(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, true); + ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); } - [TestMethod] - public void TestValidatorsCountState_FromByteArray() - { - ValidatorsCountState input = new ValidatorsCountState { Votes = new BigInteger[] { new BigInteger(1000) } }; - ValidatorsCountState output = ValidatorsCountState.FromByteArray(input.ToByteArray()); - output.Should().BeEquivalentTo(input); - } - [TestMethod] public void TestValidatorState_FromByteArray() { - ValidatorState input = new ValidatorState { Votes = new BigInteger(1000) }; - ValidatorState output = ValidatorState.FromByteArray(input.ToByteArray()); + CandidateState input = new CandidateState { Votes = new BigInteger(1000) }; + CandidateState output = CandidateState.FromByteArray(input.ToByteArray()); output.Should().BeEquivalentTo(input); } [TestMethod] public void TestValidatorState_ToByteArray() { - ValidatorState input = new ValidatorState { Votes = new BigInteger(1000) }; - input.ToByteArray().ToHexString().Should().Be("e803"); + CandidateState input = new CandidateState { Votes = new BigInteger(1000) }; + input.ToByteArray().ToHexString().Should().Be("410220012102e803"); } internal (bool State, bool Result) Transfer4TesingOnBalanceChanging(BigInteger amount, bool addVotes) @@ -539,23 +505,13 @@ public void TestValidatorState_ToByteArray() { Value = new AccountState() { - Votes = new ECPoint[] { ECCurve.Secp256r1.G }, + VoteTo = ECCurve.Secp256r1.G, Balance = new BigInteger(1000) }.ToByteArray() }); snapshot.Storages.Add(NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G), new StorageItem { - Value = new ValidatorState().ToByteArray() - }); - - ValidatorsCountState state = new ValidatorsCountState(); - for (int i = 0; i < 100; i++) - { - state.Votes[i] = new BigInteger(i + 1); - } - snapshot.Storages.Add(CreateStorageKey(15), new StorageItem() - { - Value = state.ToByteArray() + Value = new CandidateState().ToByteArray() }); } else @@ -577,7 +533,7 @@ public void TestValidatorState_ToByteArray() return (true, result.ToBoolean()); } - internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] account, byte[][] pubkeys, bool signAccount) + internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] account, byte[] pubkey, bool signAccount) { var engine = new ApplicationEngine(TriggerType.Application, new Nep5NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot, 0, true); @@ -586,11 +542,11 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] var script = new ScriptBuilder(); - foreach (var ec in pubkeys) script.EmitPush(ec); - script.EmitPush(pubkeys.Length); - script.Emit(OpCode.PACK); - - script.EmitPush(account.ToArray()); + if (pubkey is null) + script.Emit(OpCode.PUSHNULL); + else + script.EmitPush(pubkey); + script.EmitPush(account); script.EmitPush(2); script.Emit(OpCode.PACK); script.EmitPush("vote"); @@ -609,7 +565,8 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] internal static (bool State, bool Result) Check_RegisterValidator(StoreView snapshot, byte[] pubkey) { - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + var engine = new ApplicationEngine(TriggerType.Application, + new Nep5NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, 0, true); engine.LoadScript(NativeContract.NEO.Script); @@ -617,7 +574,7 @@ internal static (bool State, bool Result) Check_RegisterValidator(StoreView snap script.EmitPush(pubkey); script.EmitPush(1); script.Emit(OpCode.PACK); - script.EmitPush("registerValidator"); + script.EmitPush("registerCandidate"); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -685,7 +642,7 @@ internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint[] votes) + internal static void CheckBalance(byte[] account, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint voteTo) { var st = (VM.Types.Struct)BinarySerializer.Deserialize(trackable.Item.Value, 16, 32); @@ -694,7 +651,7 @@ internal static void CheckBalance(byte[] account, DataCache(Blockchain.MaxValidators)).Should().BeEquivalentTo(votes); // Votes + st[2].GetSpan().AsSerializable().Should().BeEquivalentTo(voteTo); // Votes trackable.Key.Key.Should().BeEquivalentTo(new byte[] { 20 }.Concat(account)); trackable.Item.IsConstant.Should().Be(false); diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index d9223afcd9..51e74d3750 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -4,7 +4,6 @@ using Neo.SmartContract; using Neo.Wallets; using System; -using System.Collections.Generic; using System.Net; using System.Numerics; using System.Security.Cryptography; @@ -118,154 +117,5 @@ public void TestUnmapForIPEndPoin() var endPoint2 = new IPEndPoint(addr2, 8888); endPoint2.Unmap().Should().Be(endPoint); } - - [TestMethod] - public void TestWeightedAverage() - { - var foo1 = new Foo - { - Value = 1, - Weight = 2 - }; - var foo2 = new Foo - { - Value = 2, - Weight = 3 - }; - var list = new List - { - foo1,foo2 - }; - list.WeightedAverage(p => p.Value, p => p.Weight).Should().Be(new BigInteger(1)); - - var foo3 = new Foo - { - Value = 1, - Weight = 0 - }; - var foo4 = new Foo - { - Value = 2, - Weight = 0 - }; - var list2 = new List - { - foo3, foo4 - }; - list2.WeightedAverage(p => p.Value, p => p.Weight).Should().Be(BigInteger.Zero); - } - - [TestMethod] - public void WeightFilter() - { - var w1 = new Woo - { - Value = 1 - }; - var w2 = new Woo - { - Value = 2 - }; - var list = new List - { - w1, w2 - }; - var ret = list.WeightedFilter(0.3, 0.6, p => p.Value, (p, w) => new Result - { - Info = p, - Weight = w - }); - var sum = BigInteger.Zero; - foreach (Result res in ret) - { - sum = BigInteger.Add(res.Weight, sum); - } - sum.Should().Be(BigInteger.Zero); - - var w3 = new Woo - { - Value = 3 - }; - - var list2 = new List - { - w1, w2, w3 - }; - var ret2 = list2.WeightedFilter(0.3, 0.4, p => p.Value, (p, w) => new Result - { - Info = p, - Weight = w - }); - sum = BigInteger.Zero; - foreach (Result res in ret2) - { - sum = BigInteger.Add(res.Weight, sum); - } - sum.Should().Be(BigInteger.Zero); - - CheckArgumentOutOfRangeException(-1, 0.4, p => p.Value, list2); - - CheckArgumentOutOfRangeException(0.2, 1.4, p => p.Value, list2); - - CheckArgumentOutOfRangeException(0.8, 0.3, p => p.Value, list2); - - CheckArgumentOutOfRangeException(0.3, 0.8, p => p.Value, list2); - - CheckArgumentNullException(0.3, 0.6, null, list2); - - CheckArgumentNullException(0.3, 0.4, p => p.Value, null); - - list2.WeightedFilter(0.3, 0.3, p => p.Value, (p, w) => new Result - { - Info = p, - Weight = w - }).WeightedAverage(p => p.Weight, p => p.Weight).Should().Be(0); - - - var list3 = new List(); - list3.WeightedFilter(0.3, 0.6, p => p.Value, (p, w) => new Result - { - Info = p, - Weight = w - }).WeightedAverage(p => p.Weight, p => p.Weight).Should().Be(0); - - } - - private static void CheckArgumentOutOfRangeException(double start, double end, Func func, List list) - { - Action action = () => list.WeightedFilter(start, end, func, (p, w) => new Result - { - Info = p, - Weight = w - }).WeightedAverage(p => p.Weight, p => p.Weight); - action.Should().Throw(); - } - - private static void CheckArgumentNullException(double start, double end, Func func, List list) - { - Action action = () => list.WeightedFilter(start, end, func, (p, w) => new Result - { - Info = p, - Weight = w - }).WeightedAverage(p => p.Weight, p => p.Weight); - action.Should().Throw(); - } - } - - class Foo - { - public int Weight { set; get; } - public int Value { set; get; } - } - - class Woo - { - public int Value { set; get; } - } - - class Result - { - public Woo Info { set; get; } - public BigInteger Weight { set; get; } } }