From 1e6404b2a300f28597bee3326d74485f367fd400 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 9 May 2024 19:41:47 -0700 Subject: [PATCH] Perform native contract initialization for the set of hardforks (#3202) * Close https://github.com/neo-project/neo/issues/3200 * Anna's review * All Hfs when genesis * fix * Update src/Neo/SmartContract/Native/NativeContract.cs Co-authored-by: Anna Shaleva * possible fix * always Initialize with null * Revert "always Initialize with null" This reverts commit 68c285cd97395ca73c4e41ee75998014df103fbd. * Call with null during deploy * Update src/Neo/SmartContract/Native/ContractManagement.cs Co-authored-by: Anna Shaleva * Update src/Neo/SmartContract/Native/ContractManagement.cs Co-authored-by: Anna Shaleva * Update src/Neo/SmartContract/Native/ContractManagement.cs Co-authored-by: Anna Shaleva * Native: adjust failing TestIsInitializeBlock According to new logic, an empty set of hard-forks will be returned in case of genesis initialization with all hardforks disabled. Ref. https://github.com/neo-project/neo/pull/3202#discussion_r1590270244. Signed-off-by: Anna Shaleva --------- Signed-off-by: Anna Shaleva Co-authored-by: Anna Shaleva Co-authored-by: Jimmy --- .../Native/ContractManagement.cs | 20 +++++++++-- .../SmartContract/Native/NativeContract.cs | 34 ++++++++++++------- .../SmartContract/Native/UT_NativeContract.cs | 6 ++-- 3 files changed, 43 insertions(+), 17 deletions(-) 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]