Skip to content

Commit

Permalink
Perform native contract initialization for the set of hardforks (#3202)
Browse files Browse the repository at this point in the history
* Close #3200

* Anna's review

* All Hfs when genesis

* fix

* Update src/Neo/SmartContract/Native/NativeContract.cs

Co-authored-by: Anna Shaleva <[email protected]>

* possible fix

* always Initialize with null

* Revert "always Initialize with null"

This reverts commit 68c285c.

* Call with null during deploy

* Update src/Neo/SmartContract/Native/ContractManagement.cs

Co-authored-by: Anna Shaleva <[email protected]>

* Update src/Neo/SmartContract/Native/ContractManagement.cs

Co-authored-by: Anna Shaleva <[email protected]>

* Update src/Neo/SmartContract/Native/ContractManagement.cs

Co-authored-by: Anna Shaleva <[email protected]>

* 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.
#3202 (comment).

Signed-off-by: Anna Shaleva <[email protected]>

---------

Signed-off-by: Anna Shaleva <[email protected]>
Co-authored-by: Anna Shaleva <[email protected]>
Co-authored-by: Jimmy <[email protected]>
  • Loading branch information
3 people authored May 10, 2024
1 parent ef11769 commit 1e6404b
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 17 deletions.
20 changes: 18 additions & 2 deletions src/Neo/SmartContract/Native/ContractManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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
{
Expand All @@ -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() });
}
Expand Down
34 changes: 21 additions & 13 deletions src/Neo/SmartContract/Native/NativeContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,19 +266,14 @@ protected virtual void OnManifestCompose(ContractManifest manifest) { }
/// </summary>
/// <param name="settings">The <see cref="ProtocolSettings"/> where the HardForks are configured.</param>
/// <param name="index">Block index</param>
/// <param name="hardfork">Active hardfork</param>
/// <param name="hardforks">Active hardforks</param>
/// <returns>True if the native contract must be initialized</returns>
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<Hardfork>();

// 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))
{
Expand All @@ -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;
}

Expand Down
6 changes: 4 additions & 2 deletions tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down

0 comments on commit 1e6404b

Please sign in to comment.