diff --git a/src/Neo/SmartContract/Native/ContractManagement.cs b/src/Neo/SmartContract/Native/ContractManagement.cs index e326421f91..e7a7e122f2 100644 --- a/src/Neo/SmartContract/Native/ContractManagement.cs +++ b/src/Neo/SmartContract/Native/ContractManagement.cs @@ -70,7 +70,7 @@ internal override async ContractTask OnPersistAsync(ApplicationEngine engine) { foreach (NativeContract contract in Contracts) { - if (contract.IsInitializeBlock(engine.ProtocolSettings, engine.PersistingBlock.Index, out Hardfork? hf)) + if (contract.IsInitializeBlock(engine.ProtocolSettings, engine.PersistingBlock.Index, out var hfs)) { ContractState contractState = contract.GetContractState(engine.ProtocolSettings, engine.PersistingBlock.Index); StorageItem state = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Contract).Add(contract.Hash)); @@ -80,6 +80,13 @@ internal override async ContractTask OnPersistAsync(ApplicationEngine engine) // Create the contract state engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(contractState)); engine.Snapshot.Add(CreateStorageKey(Prefix_ContractHash).AddBigEndian(contract.Id), new StorageItem(contract.Hash.ToArray())); + + // Initialize the native smart contract if it's active starting from the genesis. + // If it's not the case, then hardfork-based initialization will be performed down below. + if (contract.ActiveIn is null) + { + await contract.InitializeAsync(engine, null); + } } else { @@ -92,7 +99,16 @@ internal override async ContractTask OnPersistAsync(ApplicationEngine engine) oldContract.Manifest = contractState.Manifest; } - await contract.InitializeAsync(engine, hf); + // Initialize native contract for all hardforks that are active starting from the persisting block. + // If the contract is active starting from some non-nil hardfork, then this hardfork is also included into hfs. + if (hfs?.Length > 0) + { + foreach (var hf in hfs) + { + await contract.InitializeAsync(engine, hf); + } + } + // Emit native contract notification engine.SendNotification(Hash, state is null ? "Deploy" : "Update", new VM.Types.Array(engine.ReferenceCounter) { contract.Hash.ToArray() }); } diff --git a/src/Neo/SmartContract/Native/NativeContract.cs b/src/Neo/SmartContract/Native/NativeContract.cs index bde1f8ac00..70f67de538 100644 --- a/src/Neo/SmartContract/Native/NativeContract.cs +++ b/src/Neo/SmartContract/Native/NativeContract.cs @@ -266,19 +266,14 @@ protected virtual void OnManifestCompose(ContractManifest manifest) { } /// /// The where the HardForks are configured. /// Block index - /// Active hardfork + /// Active hardforks /// True if the native contract must be initialized - internal bool IsInitializeBlock(ProtocolSettings settings, uint index, out Hardfork? hardfork) + internal bool IsInitializeBlock(ProtocolSettings settings, uint index, out Hardfork[] hardforks) { - // If is not configured, the Genesis is the a initialized block - if (index == 0 && ActiveIn is null) - { - hardfork = null; - return true; - } + var hfs = new List(); - // If is in the hardfork height, return true - foreach (Hardfork hf in usedHardforks) + // If is in the hardfork height, add them to return array + foreach (var hf in usedHardforks) { if (!settings.Hardforks.TryGetValue(hf, out var activeIn)) { @@ -288,13 +283,26 @@ internal bool IsInitializeBlock(ProtocolSettings settings, uint index, out Hardf if (activeIn == index) { - hardfork = hf; - return true; + hfs.Add(hf); } } + // Return all initialize hardforks + if (hfs.Count > 0) + { + hardforks = hfs.ToArray(); + return true; + } + + // If is not configured, the Genesis is an initialization block. + if (index == 0 && ActiveIn is null) + { + hardforks = hfs.ToArray(); + return true; + } + // Initialized not required - hardfork = null; + hardforks = null; return false; } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index d7be3b4dcd..1989b4323b 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -75,13 +75,15 @@ public void TestIsInitializeBlock() File.Delete(file); Assert.IsTrue(NativeContract.CryptoLib.IsInitializeBlock(settings, 0, out var hf)); - Assert.IsNull(hf); + Assert.IsNotNull(hf); + Assert.AreEqual(0, hf.Length); Assert.IsFalse(NativeContract.CryptoLib.IsInitializeBlock(settings, 1, out hf)); Assert.IsNull(hf); Assert.IsTrue(NativeContract.CryptoLib.IsInitializeBlock(settings, 20, out hf)); - Assert.AreEqual(Hardfork.HF_Cockatrice, hf); + Assert.AreEqual(1, hf.Length); + Assert.AreEqual(Hardfork.HF_Cockatrice, hf[0]); } [TestMethod]