Skip to content

Commit

Permalink
Allow Native Contracts to be updated (#2942)
Browse files Browse the repository at this point in the history
* Update manifest

* Fix comment

* Format & fix methods

* Fix new

* Initialize storage fixes

* Fix IsInitializeBlock

* Fix update

* Fix GetContractState

* Optimize

* Fix first invoke without sync

* Remove current methods

* Clean usings

* Improve reading Initialize

* Clean OnManifestCompose

* Use cache for all native contracts

* Fix ut

* Move cache to ApplicationEngine

* Clean code

* Allow nullable attribute

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

* Add base call

* Fix one #2941 (comment)

* Fix IsInitializeBlock #2941 (comment)

* Add ContractEventAttribute constructors for ActiveIn

* Ensure ommited hfs

* Rename

* Case insensitive hf config

* Increase coverage

* More uts

* Rename

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

* format code

* Fix ProtocolSettings

* Update src/Neo/SmartContract/ApplicationEngine.cs

* format

* reorder using

* Fix UT

* Adding keccak256 (#2925)

* Create codeql.yml

* Keccak256

* Delete .github/workflows/codeql.yml

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

* add more keccak256 test cases as required

* Create codeql.yml

* Keccak256

* Delete .github/workflows/codeql.yml

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

* add more keccak256 test cases as required

* HF_Manticore

* Fix copyright

* Create codeql.yml

* Keccak256

* Delete .github/workflows/codeql.yml

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

* add more keccak256 test cases as required

* Create codeql.yml

* Keccak256

* Delete .github/workflows/codeql.yml

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

* add more keccak256 test cases as required

* HF_Manticore

* Fix copyright

* HF_Manticore

* Update CryptoLib.cs

* Update CryptoLib.cs

* Update UT_CryptoLib.cs

* Apply suggestions from code review

* clean usings

---------

Co-authored-by: Shargon <shargon@gmail.com>

* Fix net standard

* Add ut

* Fix update

* Fix update

---------

Co-authored-by: Jimmy <jinghui@wayne.edu>
  • Loading branch information
shargon and Jim8y authored Feb 23, 2024
1 parent 1fffb95 commit b467613
Show file tree
Hide file tree
Showing 27 changed files with 949 additions and 317 deletions.
3 changes: 2 additions & 1 deletion src/Neo/Hardfork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Neo
public enum Hardfork : byte
{
HF_Aspidochelone,
HF_Basilisk
HF_Basilisk,
HF_Cockatrice
}
}
3 changes: 2 additions & 1 deletion src/Neo/NeoSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ static NeoSystem()
/// <param name="storageProvider">The storage engine used to create the <see cref="IStoreProvider"/> objects. If this parameter is <see langword="null"/>, a default in-memory storage engine will be used.</param>
/// <param name="storagePath">The path of the storage. If <paramref name="storageProvider"/> is the default in-memory storage engine, this parameter is ignored.</param>
public NeoSystem(ProtocolSettings settings, string? storageProvider = null, string? storagePath = null) :

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / PublishPackage

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / PublishPackage

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / PublishPackage

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 119 in src/Neo/NeoSystem.cs

View workflow job for this annotation

GitHub Actions / PublishPackage

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
this(settings, StoreFactory.GetStoreProvider(storageProvider ?? nameof(MemoryStore)), storagePath)
this(settings, StoreFactory.GetStoreProvider(storageProvider ?? nameof(MemoryStore))
?? throw new ArgumentException($"Can't find the storage provider {storageProvider}", nameof(storageProvider)), storagePath)
{
}

Expand Down
2 changes: 1 addition & 1 deletion src/Neo/Persistence/DataCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public void Add(StorageKey key, StorageItem value)
{
TrackState.Deleted => TrackState.Changed,
TrackState.NotFound => TrackState.Added,
_ => throw new ArgumentException()
_ => throw new ArgumentException($"The element currently has state {trackable.State}")
};
}
else
Expand Down
52 changes: 47 additions & 5 deletions src/Neo/ProtocolSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ namespace Neo
/// </summary>
public record ProtocolSettings
{
private static readonly IList<Hardfork> AllHardforks = Enum.GetValues(typeof(Hardfork)).Cast<Hardfork>().ToArray();

/// <summary>
/// The magic number of the NEO network.
/// </summary>
Expand Down Expand Up @@ -115,7 +117,7 @@ public record ProtocolSettings
MemoryPoolMaxTransactions = 50_000,
MaxTraceableBlocks = 2_102_400,
InitialGasDistribution = 52_000_000_00000000,
Hardforks = ImmutableDictionary<Hardfork, uint>.Empty
Hardforks = EnsureOmmitedHardforks(new Dictionary<Hardfork, uint>()).ToImmutableDictionary()
};

public static ProtocolSettings? Custom { get; set; }

Check warning on line 123 in src/Neo/ProtocolSettings.cs

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 123 in src/Neo/ProtocolSettings.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 123 in src/Neo/ProtocolSettings.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 123 in src/Neo/ProtocolSettings.cs

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 123 in src/Neo/ProtocolSettings.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 123 in src/Neo/ProtocolSettings.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 123 in src/Neo/ProtocolSettings.cs

View workflow job for this annotation

GitHub Actions / PublishPackage

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 123 in src/Neo/ProtocolSettings.cs

View workflow job for this annotation

GitHub Actions / PublishPackage

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
Expand Down Expand Up @@ -159,17 +161,39 @@ public static ProtocolSettings Load(IConfigurationSection section)
MaxTraceableBlocks = section.GetValue("MaxTraceableBlocks", Default.MaxTraceableBlocks),
InitialGasDistribution = section.GetValue("InitialGasDistribution", Default.InitialGasDistribution),
Hardforks = section.GetSection("Hardforks").Exists()
? section.GetSection("Hardforks").GetChildren().ToImmutableDictionary(p => Enum.Parse<Hardfork>(p.Key), p => uint.Parse(p.Value))
? EnsureOmmitedHardforks(section.GetSection("Hardforks").GetChildren().ToDictionary(p => Enum.Parse<Hardfork>(p.Key, true), p => uint.Parse(p.Value))).ToImmutableDictionary()
: Default.Hardforks
};
}

/// <summary>
/// Explicitly set the height of all old omitted hardforks to 0 for proper IsHardforkEnabled behaviour.
/// </summary>
/// <param name="hardForks">HardForks</param>
/// <returns>Processed hardfork configuration</returns>
private static Dictionary<Hardfork, uint> EnsureOmmitedHardforks(Dictionary<Hardfork, uint> hardForks)
{
foreach (Hardfork hf in AllHardforks)
{
if (!hardForks.ContainsKey(hf))
{
hardForks[hf] = 0;
}
else
{
break;
}
}

return hardForks;
}

private static void CheckingHardfork(ProtocolSettings settings)
{
var allHardforks = Enum.GetValues(typeof(Hardfork)).Cast<Hardfork>().ToList();
// Check for continuity in configured hardforks
var sortedHardforks = settings.Hardforks.Keys
.OrderBy(h => allHardforks.IndexOf(h))
.OrderBy(allHardforks.IndexOf)
.ToList();

for (int i = 0; i < sortedHardforks.Count - 1; i++)
Expand All @@ -179,17 +203,35 @@ private static void CheckingHardfork(ProtocolSettings settings)

// If they aren't consecutive, return false.
if (nextIndex - currentIndex > 1)
throw new Exception("Hardfork configuration is not continuous.");
throw new ArgumentException("Hardfork configuration is not continuous.");
}
// Check that block numbers are not higher in earlier hardforks than in later ones
for (int i = 0; i < sortedHardforks.Count - 1; i++)
{
if (settings.Hardforks[sortedHardforks[i]] > settings.Hardforks[sortedHardforks[i + 1]])
{
// This means the block number for the current hardfork is greater than the next one, which should not be allowed.
throw new Exception($"The Hardfork configuration for {sortedHardforks[i]} is greater than for {sortedHardforks[i + 1]}");
throw new ArgumentException($"The Hardfork configuration for {sortedHardforks[i]} is greater than for {sortedHardforks[i + 1]}");
}
}
}

/// <summary>
/// Check if the Hardfork is Enabled
/// </summary>
/// <param name="hardfork">Hardfork</param>
/// <param name="index">Block index</param>
/// <returns>True if enabled</returns>
public bool IsHardforkEnabled(Hardfork hardfork, uint index)
{
if (Hardforks.TryGetValue(hardfork, out uint height))
{
// If the hardfork has a specific height in the configuration, check the block height.
return index >= height;
}

// If the hardfork isn't specified in the configuration, return false.
return false;
}
}
}
45 changes: 23 additions & 22 deletions src/Neo/SmartContract/ApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ public partial class ApplicationEngine : ExecutionEngine
/// </summary>
public static event EventHandler<LogEventArgs> Log;

private static readonly IList<Hardfork> AllHardforks = Enum.GetValues(typeof(Hardfork)).Cast<Hardfork>().ToArray();
private static Dictionary<uint, InteropDescriptor> services;
private readonly long gas_amount;
private Dictionary<Type, object> states;
Expand Down Expand Up @@ -647,6 +646,25 @@ public T GetState<T>()
return (T)state;
}

public T GetState<T>(Func<T> factory)
{
if (states is null)
{
T state = factory();
SetState(state);
return state;
}
else
{
if (!states.TryGetValue(typeof(T), out object state))
{
state = factory();
SetState(state);
}
return (T)state;
}
}

public void SetState<T>(T state)
{
states ??= new Dictionary<Type, object>();
Expand All @@ -655,28 +673,11 @@ public void SetState<T>(T state)

public bool IsHardforkEnabled(Hardfork hardfork)
{
// Return true if there's no specific configuration or PersistingBlock is null
if (PersistingBlock is null || ProtocolSettings.Hardforks.Count == 0)
return true;

// If the hardfork isn't specified in the configuration, check if it's a new one.
if (!ProtocolSettings.Hardforks.ContainsKey(hardfork))
{
int currentHardforkIndex = AllHardforks.IndexOf(hardfork);
int lastConfiguredHardforkIndex = AllHardforks.IndexOf(ProtocolSettings.Hardforks.Keys.Last());
// Return true if PersistingBlock is null and Hardfork is enabled
if (PersistingBlock is null)
return ProtocolSettings.Hardforks.ContainsKey(hardfork);

// If it's a newer hardfork compared to the ones in the configuration, disable it.
if (currentHardforkIndex > lastConfiguredHardforkIndex)
return false;
}

if (ProtocolSettings.Hardforks.TryGetValue(hardfork, out uint height))
{
// If the hardfork has a specific height in the configuration, check the block height.
return PersistingBlock.Index >= height;
}
// If no specific conditions are met, return true.
return true;
return ProtocolSettings.IsHardforkEnabled(hardfork, PersistingBlock.Index);
}
}
}
165 changes: 165 additions & 0 deletions src/Neo/SmartContract/Native/ContractEventAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Copyright (C) 2015-2024 The Neo Project.
//
// ContractEventAttribute.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.SmartContract.Manifest;
using System;
using System.Diagnostics;

namespace Neo.SmartContract.Native
{
[DebuggerDisplay("{Descriptor.Name}")]
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = true)]
internal class ContractEventAttribute : Attribute
{
public int Order { get; init; }
public ContractEventDescriptor Descriptor { get; set; }
public Hardfork? ActiveIn { get; init; } = null;

public ContractEventAttribute(Hardfork activeIn, int order, string name,
string arg1Name, ContractParameterType arg1Value) : this(order, name, arg1Name, arg1Value)
{
ActiveIn = activeIn;
}

public ContractEventAttribute(int order, string name, string arg1Name, ContractParameterType arg1Value)
{
Order = order;
Descriptor = new ContractEventDescriptor()
{
Name = name,
Parameters = new ContractParameterDefinition[]
{
new ContractParameterDefinition()
{
Name = arg1Name,
Type = arg1Value
}
}
};
}

public ContractEventAttribute(Hardfork activeIn, int order, string name,
string arg1Name, ContractParameterType arg1Value,
string arg2Name, ContractParameterType arg2Value) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value)
{
ActiveIn = activeIn;
}

public ContractEventAttribute(int order, string name,
string arg1Name, ContractParameterType arg1Value,
string arg2Name, ContractParameterType arg2Value)
{
Order = order;
Descriptor = new ContractEventDescriptor()
{
Name = name,
Parameters = new ContractParameterDefinition[]
{
new ContractParameterDefinition()
{
Name = arg1Name,
Type = arg1Value
},
new ContractParameterDefinition()
{
Name = arg2Name,
Type = arg2Value
}
}
};
}

public ContractEventAttribute(Hardfork activeIn, int order, string name,
string arg1Name, ContractParameterType arg1Value,
string arg2Name, ContractParameterType arg2Value,
string arg3Name, ContractParameterType arg3Value) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value, arg3Name, arg3Value)
{
ActiveIn = activeIn;
}

public ContractEventAttribute(int order, string name,
string arg1Name, ContractParameterType arg1Value,
string arg2Name, ContractParameterType arg2Value,
string arg3Name, ContractParameterType arg3Value
)
{
Order = order;
Descriptor = new ContractEventDescriptor()
{
Name = name,
Parameters = new ContractParameterDefinition[]
{
new ContractParameterDefinition()
{
Name = arg1Name,
Type = arg1Value
},
new ContractParameterDefinition()
{
Name = arg2Name,
Type = arg2Value
},
new ContractParameterDefinition()
{
Name = arg3Name,
Type = arg3Value
}
}
};
}

public ContractEventAttribute(Hardfork activeIn, int order, string name,
string arg1Name, ContractParameterType arg1Value,
string arg2Name, ContractParameterType arg2Value,
string arg3Name, ContractParameterType arg3Value,
string arg4Name, ContractParameterType arg4Value) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value, arg3Name, arg3Value, arg4Name, arg4Value)
{
ActiveIn = activeIn;
}

public ContractEventAttribute(int order, string name,
string arg1Name, ContractParameterType arg1Value,
string arg2Name, ContractParameterType arg2Value,
string arg3Name, ContractParameterType arg3Value,
string arg4Name, ContractParameterType arg4Value
)
{
Order = order;
Descriptor = new ContractEventDescriptor()
{
Name = name,
Parameters = new ContractParameterDefinition[]
{
new ContractParameterDefinition()
{
Name = arg1Name,
Type = arg1Value
},
new ContractParameterDefinition()
{
Name = arg2Name,
Type = arg2Value
},
new ContractParameterDefinition()
{
Name = arg3Name,
Type = arg3Value
},
new ContractParameterDefinition()
{
Name = arg4Name,
Type = arg4Value
}
}
};
}
}
}
Loading

0 comments on commit b467613

Please sign in to comment.