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]