diff --git a/src/Neo/ProtocolSettings.cs b/src/Neo/ProtocolSettings.cs index 5373db8bf0..f1e4e01b11 100644 --- a/src/Neo/ProtocolSettings.cs +++ b/src/Neo/ProtocolSettings.cs @@ -8,14 +8,13 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Microsoft.Extensions.Configuration; -using Neo.Cryptography.ECC; -using Neo.Network.P2P.Payloads; -using Neo.SmartContract.Native; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using Microsoft.Extensions.Configuration; +using Neo.Cryptography.ECC; +using Neo.Network.P2P.Payloads; namespace Neo { @@ -85,10 +84,8 @@ public record ProtocolSettings public uint MaxTraceableBlocks { get; init; } /// - /// Contains the update history of all native contracts. + /// Sets the block height from which a hardfork is activated. /// - public IReadOnlyDictionary NativeUpdateHistory { get; init; } - public ImmutableDictionary Hardforks { get; init; } /// @@ -149,18 +146,6 @@ public record ProtocolSettings MemoryPoolMaxTransactions = 50_000, MaxTraceableBlocks = 2_102_400, InitialGasDistribution = 52_000_000_00000000, - NativeUpdateHistory = new Dictionary - { - [nameof(ContractManagement)] = new[] { 0u }, - [nameof(StdLib)] = new[] { 0u }, - [nameof(CryptoLib)] = new[] { 0u }, - [nameof(LedgerContract)] = new[] { 0u }, - [nameof(NeoToken)] = new[] { 0u }, - [nameof(GasToken)] = new[] { 0u }, - [nameof(PolicyContract)] = new[] { 0u }, - [nameof(RoleManagement)] = new[] { 0u }, - [nameof(OracleContract)] = new[] { 0u } - }, Hardforks = ImmutableDictionary.Empty }; @@ -202,9 +187,6 @@ public static ProtocolSettings Load(IConfigurationSection section) MemoryPoolMaxTransactions = section.GetValue("MemoryPoolMaxTransactions", Default.MemoryPoolMaxTransactions), MaxTraceableBlocks = section.GetValue("MaxTraceableBlocks", Default.MaxTraceableBlocks), InitialGasDistribution = section.GetValue("InitialGasDistribution", Default.InitialGasDistribution), - NativeUpdateHistory = section.GetSection("NativeUpdateHistory").Exists() - ? section.GetSection("NativeUpdateHistory").GetChildren().ToDictionary(p => p.Key, p => p.GetChildren().Select(q => uint.Parse(q.Value)).ToArray()) - : Default.NativeUpdateHistory, Hardforks = section.GetSection("Hardforks").Exists() ? section.GetSection("Hardforks").GetChildren().ToImmutableDictionary(p => Enum.Parse(p.Key), p => uint.Parse(p.Value)) : Default.Hardforks diff --git a/src/Neo/SmartContract/ApplicationEngine.Contract.cs b/src/Neo/SmartContract/ApplicationEngine.Contract.cs index f3d33e7d77..059c9eef07 100644 --- a/src/Neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Contract.cs @@ -95,10 +95,7 @@ protected internal void CallNativeContract(byte version) NativeContract contract = NativeContract.GetContract(CurrentScriptHash); if (contract is null) throw new InvalidOperationException("It is not allowed to use \"System.Contract.CallNative\" directly."); - uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; - if (updates.Length == 0) - throw new InvalidOperationException($"The native contract {contract.Name} is not active."); - if (updates[0] > NativeContract.Ledger.CurrentIndex(Snapshot)) + if (!contract.IsActive(ProtocolSettings, NativeContract.Ledger.CurrentIndex(Snapshot))) throw new InvalidOperationException($"The native contract {contract.Name} is not active."); contract.Invoke(this, version); } @@ -157,9 +154,7 @@ protected internal async void NativeOnPersist() throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) { - uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; - if (updates.Length == 0) continue; - if (updates[0] <= PersistingBlock.Index) + if (contract.IsActive(ProtocolSettings, PersistingBlock.Index)) await contract.OnPersist(this); } } @@ -181,9 +176,7 @@ protected internal async void NativePostPersist() throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) { - uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; - if (updates.Length == 0) continue; - if (updates[0] <= PersistingBlock.Index) + if (contract.IsActive(ProtocolSettings, PersistingBlock.Index)) await contract.PostPersist(this); } } diff --git a/src/Neo/SmartContract/Native/ContractManagement.cs b/src/Neo/SmartContract/Native/ContractManagement.cs index f7ed44542d..62e9bb366d 100644 --- a/src/Neo/SmartContract/Native/ContractManagement.cs +++ b/src/Neo/SmartContract/Native/ContractManagement.cs @@ -106,18 +106,18 @@ internal override async ContractTask OnPersist(ApplicationEngine engine) { foreach (NativeContract contract in Contracts) { - uint[] updates = engine.ProtocolSettings.NativeUpdateHistory[contract.Name]; - if (updates.Length == 0 || updates[0] != engine.PersistingBlock.Index) - continue; - engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState + if (contract.IsInitializeBlock(engine.ProtocolSettings, engine.PersistingBlock.Index)) { - Id = contract.Id, - Nef = contract.Nef, - Hash = contract.Hash, - Manifest = contract.Manifest - })); - engine.Snapshot.Add(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id), new StorageItem(contract.Hash.ToArray())); - await contract.Initialize(engine); + engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState + { + Id = contract.Id, + Nef = contract.Nef, + Hash = contract.Hash, + Manifest = contract.Manifest + })); + engine.Snapshot.Add(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id), new StorageItem(contract.Hash.ToArray())); + await contract.Initialize(engine); + } } } diff --git a/src/Neo/SmartContract/Native/NativeContract.cs b/src/Neo/SmartContract/Native/NativeContract.cs index 63c15466fb..8c8ffe986f 100644 --- a/src/Neo/SmartContract/Native/NativeContract.cs +++ b/src/Neo/SmartContract/Native/NativeContract.cs @@ -87,6 +87,11 @@ public abstract class NativeContract /// public string Name => GetType().Name; + /// + /// Since Hardfork has to start having access to the native contract. + /// + public virtual Hardfork? ActiveIn { get; } = null; + /// /// The nef of the native contract. /// @@ -160,6 +165,42 @@ protected NativeContract() contractsDictionary.Add(Hash, this); } + /// + /// It is the initialize block + /// + /// The where the HardForks are configured. + /// Block index + /// True if the native contract must be initialized + internal bool IsInitializeBlock(ProtocolSettings settings, uint index) + { + if (ActiveIn is null) return index == 0; + + if (!settings.Hardforks.TryGetValue(ActiveIn.Value, out var activeIn)) + { + return false; + } + + return activeIn == index; + } + + /// + /// Is the native contract active + /// + /// The where the HardForks are configured. + /// Block index + /// True if the native contract is active + internal bool IsActive(ProtocolSettings settings, uint index) + { + if (ActiveIn is null) return true; + + if (!settings.Hardforks.TryGetValue(ActiveIn.Value, out var activeIn)) + { + return false; + } + + return activeIn <= index; + } + /// /// Checks whether the committee has witnessed the current transaction. /// diff --git a/tests/Neo.UnitTests/UT_ProtocolSettings.cs b/tests/Neo.UnitTests/UT_ProtocolSettings.cs index 0af209e10f..42be0e69cc 100644 --- a/tests/Neo.UnitTests/UT_ProtocolSettings.cs +++ b/tests/Neo.UnitTests/UT_ProtocolSettings.cs @@ -1,6 +1,5 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract.Native; using Neo.Wallets; namespace Neo.UnitTests @@ -41,11 +40,5 @@ public void TestGetSeedList() { ProtocolSettings.Default.SeedList.Should().BeEquivalentTo(new string[] { "seed1.neo.org:10333", "seed2.neo.org:10333", "seed3.neo.org:10333", "seed4.neo.org:10333", "seed5.neo.org:10333", }); } - - [TestMethod] - public void TestNativeUpdateHistory() - { - ProtocolSettings.Default.NativeUpdateHistory.Count.Should().Be(NativeContract.Contracts.Count); - } } }