Skip to content

Commit

Permalink
Nep17 UnitTests (neo-project#893)
Browse files Browse the repository at this point in the history
* Draft Nep17 UT

* LF

* Fix TotalSupply

* Some UT still failing, checking bugs

* Ensure OnSetOwner is raised during deploy

* Fix string

* Fix mint test

* Test burn

* check burn

* Test transfer

* Remove token reception in Nep17, not required for a template

* Fix csproj

* Try to execute template compilation

* Update src/Neo.SmartContract.Template/templates/neocontractnep17/ProjectName.csproj

* Rename project

* Use same path as tested project

* Fix ut contract=filename

* fix comment

* Update src/Neo.SmartContract.Template/templates/neocontractnep17/Nep17Contract.cs

* Need help

* Create project with template, and compile, new errors

* Generate nef and manifest before compile

* Compile works

* Only left neo-project#895

* forgot files

* LF and remove artifact library

* format

* Update tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/Nep17ContractTests.cs

* Remove artifacts

* Remove artifacts compilation

* Revert "Remove artifacts compilation"

This reverts commit 62466f1.

* Remove artifacts and use the compiled library

* Remove duplicate neo reference in Compiler

* Update Neo.Compiler.CSharp.csproj

* With source works fine

* Reduce changes

* Ut pass in local

* dotnet format

* Prepare for coverage check

* Update test with coverage

* Fix git notifications

* sort coverage dump

* remove using

* fix bug

* Increase transfer coverage using invalid types

* Add to workflow

* Fix test order

* Remove some artifacts

* fix comment

* ir require artifacts

* Remove artifacts and generate them by workflow

* Split build

* fix path

* fix path

* fix nccs dll

* fix relative

* Fix artifacts path

* clean project

* Update README.md

* Sandobx UT

* Clean

* remove nullable

* cleaner

* Update console log

* Clean table

* Update tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/Nep17ContractTests.cs

Co-authored-by: Jimmy <[email protected]>

* Update src/Neo.SmartContract.Testing/Coverage/CoveredContract.cs

Co-authored-by: Christopher Schuchardt <[email protected]>

* cschuchardt88's and Jimmy's feedback & Ownable & Nep17UT

* some fixes

* Rename test

* TestBase

* Add Assert

* Use same format for auth check

* Fix typo

* Update src/Neo.SmartContract.Testing/Coverage/CoveredContract.cs

Co-authored-by: Christopher Schuchardt <[email protected]>

* cschuchardt88's feedback

* Wait for neo-project/neo#3143

---------

Co-authored-by: Jimmy <[email protected]>
Co-authored-by: Christopher Schuchardt <[email protected]>
Co-authored-by: Vitor Nazário Coelho <[email protected]>
  • Loading branch information
4 people committed Mar 21, 2024
1 parent 0ba0906 commit 9ff2042
Show file tree
Hide file tree
Showing 46 changed files with 1,600 additions and 372 deletions.
24 changes: 23 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,18 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Build
- name: Build Neo.Compiler.CSharp
run: dotnet build ./src/Neo.Compiler.CSharp/Neo.Compiler.CSharp.csproj
- name: Build Neo.SmartContract.Template and generate artifacts
run: |
dotnet pack ./src/Neo.SmartContract.Template/Neo.SmartContract.Template.csproj
dotnet new install ./src/Neo.SmartContract.Template/bin/Debug/Neo.SmartContract.Template.*.nupkg
dotnet new neocontractnep17 -n Nep17Contract -o ./src/Neo.SmartContract.Template/bin/Debug/ --force
dotnet new uninstall Neo.SmartContract.Template
dotnet remove ./src/Neo.SmartContract.Template/bin/Debug/Nep17Contract.csproj package Neo.SmartContract.Framework
dotnet add ./src/Neo.SmartContract.Template/bin/Debug/Nep17Contract.csproj reference ./src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll -d ./src/Neo.SmartContract.Template/bin/Debug/Nep17Contract.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/Artifacts/ --generate-artifacts source
- name: Build Solution
run: dotnet build ./neo-devpack-dotnet.sln
- name: Check format
run: |
Expand All @@ -39,6 +50,17 @@ jobs:
run: |
dotnet test ./tests/Neo.SmartContract.Framework.UnitTests \
--no-build \
-l "console;verbosity=normal" \
-p:CollectCoverage=true \
-p:CoverletOutput=${GITHUB_WORKSPACE}/coverage/lcov \
-p:MergeWith=${GITHUB_WORKSPACE}/coverage/coverage.json \
-p:Exclude=\"[Neo.Compiler.CSharp.UnitTests]*\" \
-p:CoverletOutputFormat=lcov
- name: Test Neo.SmartContract.Template.UnitTests
run: |
dotnet test ./tests/Neo.SmartContract.Template.UnitTests \
--no-build \
-l "console;verbosity=detailed" \
-p:CollectCoverage=true \
-p:CoverletOutput=${GITHUB_WORKSPACE}/coverage/lcov \
-p:MergeWith=${GITHUB_WORKSPACE}/coverage/coverage.json \
Expand Down
6 changes: 6 additions & 0 deletions neo-devpack-dotnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.SmartContract.Testing",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.SmartContract.Testing.UnitTests", "tests\Neo.SmartContract.Testing.UnitTests\Neo.SmartContract.Testing.UnitTests.csproj", "{B772B8A9-9362-4C6F-A6D3-2A4138439B2C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.SmartContract.Template.UnitTests", "tests\Neo.SmartContract.Template.UnitTests\Neo.SmartContract.Template.UnitTests.csproj", "{17F45E0B-AB1C-4796-8C99-E5212A5592F8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.Extensions", "neo\src\Neo.Extensions\Neo.Extensions.csproj", "{E5EFB018-810D-4297-8921-940FA0B1ED97}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.IO", "neo\src\Neo.IO\Neo.IO.csproj", "{C2B7927F-AAA5-432A-8E76-B5080BD7EFB9}"
Expand Down Expand Up @@ -102,6 +103,10 @@ Global
{B772B8A9-9362-4C6F-A6D3-2A4138439B2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B772B8A9-9362-4C6F-A6D3-2A4138439B2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B772B8A9-9362-4C6F-A6D3-2A4138439B2C}.Release|Any CPU.Build.0 = Release|Any CPU
{17F45E0B-AB1C-4796-8C99-E5212A5592F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17F45E0B-AB1C-4796-8C99-E5212A5592F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17F45E0B-AB1C-4796-8C99-E5212A5592F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17F45E0B-AB1C-4796-8C99-E5212A5592F8}.Release|Any CPU.Build.0 = Release|Any CPU
{E5EFB018-810D-4297-8921-940FA0B1ED97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E5EFB018-810D-4297-8921-940FA0B1ED97}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5EFB018-810D-4297-8921-940FA0B1ED97}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -129,6 +134,7 @@ Global
{D6D53889-5A10-46A4-BA66-E78B56EC1881} = {49D5873D-7B38-48A5-B853-85146F032091}
{648DCE6F-A0BA-4032-951B-20CF5BBFD998} = {79389FC0-C621-4CEA-AD2B-6074C32E7BCA}
{B772B8A9-9362-4C6F-A6D3-2A4138439B2C} = {D5266066-0AFD-44D5-A83E-2F73668A63C8}
{17F45E0B-AB1C-4796-8C99-E5212A5592F8} = {D5266066-0AFD-44D5-A83E-2F73668A63C8}
{E5EFB018-810D-4297-8921-940FA0B1ED97} = {49D5873D-7B38-48A5-B853-85146F032091}
{C2B7927F-AAA5-432A-8E76-B5080BD7EFB9} = {49D5873D-7B38-48A5-B853-85146F032091}
EndGlobalSection
Expand Down
1 change: 1 addition & 0 deletions src/Neo.Compiler.CSharp/Neo.Compiler.CSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<AssemblyTitle>Neo.Compiler.CSharp</AssemblyTitle>
<TargetFramework>net7.0</TargetFramework>
<AssemblyName>nccs</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Neo.Compiler.CSharp</PackageId>
Expand Down
10 changes: 9 additions & 1 deletion src/Neo.Compiler.CSharp/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,21 @@ namespace Neo.Compiler
{
public class Options
{
public enum GenerateArtifactsKind
{
None,
Source,
Library,
SourceAndLibrary
}

public string? Output { get; set; }
public string? BaseName { get; set; }
public NullableContextOptions Nullable { get; set; }
public bool Checked { get; set; }
public bool Debug { get; set; }
public bool Assembly { get; set; }
public bool NoArtifacts { get; set; }
public GenerateArtifactsKind GenerateArtifacts { get; set; } = GenerateArtifactsKind.Source;
public bool NoOptimize { get; set; }
public bool NoInline { get; set; }
public byte AddressVersion { get; set; }
Expand Down
98 changes: 55 additions & 43 deletions src/Neo.Compiler.CSharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ static int Main(string[] args)
new Option<bool>("--checked", "Indicates whether to check for overflow and underflow."),
new Option<bool>(new[] { "-d", "--debug" }, "Indicates whether to generate debugging information."),
new Option<bool>("--assembly", "Indicates whether to generate assembly."),
new Option<bool>("--no-artifacts", "Instruct the compiler not to generate artifacts."),
new Option<Options.GenerateArtifactsKind>("--generate-artifacts", "Instruct the compiler how to generate artifacts."),
new Option<bool>("--no-optimize", "Instruct the compiler not to optimize the code."),
new Option<bool>("--no-inline", "Instruct the compiler not to insert inline code."),
new Option<byte>("--address-version", () => ProtocolSettings.Default.AddressVersion, "Indicates the address version used by the compiler.")
Expand Down Expand Up @@ -188,62 +188,74 @@ private static int ProcessOutputs(Options options, string folder, CompilationCon
return 1;
}
Console.WriteLine($"Created {path}");
if (!options.NoArtifacts)

if (options.GenerateArtifacts != Options.GenerateArtifactsKind.None)
{
var artifact = manifest.Abi.GetArtifactsSource(baseName);
path = Path.Combine(outputFolder, $"{baseName}.artifacts.cs");
File.WriteAllText(path, artifact);
Console.WriteLine($"Created {path}");
var artifact = manifest.GetArtifactsSource(baseName);

try
if (options.GenerateArtifacts == Options.GenerateArtifactsKind.SourceAndLibrary || options.GenerateArtifacts == Options.GenerateArtifactsKind.Source)
{
// Try to compile the artifacts into a dll

string coreDir = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
path = Path.Combine(outputFolder, $"{baseName}.artifacts.cs");
File.WriteAllText(path, artifact);
Console.WriteLine($"Created {path}");
}

var syntaxTree = CSharpSyntaxTree.ParseText(artifact);
var references = new MetadataReference[]
if (options.GenerateArtifacts == Options.GenerateArtifactsKind.SourceAndLibrary || options.GenerateArtifacts == Options.GenerateArtifactsKind.Library)
{
try
{
MetadataReference.CreateFromFile(Path.Combine(coreDir, "System.Runtime.dll")),
MetadataReference.CreateFromFile(Path.Combine(coreDir, "System.Runtime.InteropServices.dll")),
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(DisplayNameAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Numerics.BigInteger).Assembly.Location),
MetadataReference.CreateFromFile(typeof(UInt160).Assembly.Location),
MetadataReference.CreateFromFile(typeof(SmartContract.Testing.SmartContract).Assembly.Location)
};
// Try to compile the artifacts into a dll

var coreDir = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
var references = new MetadataReference[]
{
MetadataReference.CreateFromFile(Path.Combine(coreDir, "System.Runtime.dll")),
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(DisplayNameAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Numerics.BigInteger).Assembly.Location),
MetadataReference.CreateFromFile(typeof(NeoSystem).Assembly.Location),
MetadataReference.CreateFromFile(typeof(SmartContract.Testing.TestEngine).Assembly.Location)
};

var compilation = CSharpCompilation.Create(baseName, new[] { syntaxTree }, references,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
CSharpCompilationOptions csOptions = new(
OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Debug,
platform: Platform.AnyCpu,
nullableContextOptions: NullableContextOptions.Enable,
deterministic: true);

using var ms = new MemoryStream();
EmitResult result = compilation.Emit(ms);
var syntaxTree = CSharpSyntaxTree.ParseText(artifact, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest));
var compilation = CSharpCompilation.Create(baseName, new[] { syntaxTree }, references, csOptions);

if (!result.Success)
{
var failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
using var ms = new MemoryStream();
EmitResult result = compilation.Emit(ms);

foreach (var diagnostic in failures)
if (!result.Success)
{
Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
var failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);

foreach (var diagnostic in failures)
{
Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
}
}
else
{
ms.Seek(0, SeekOrigin.Begin);
else
{
ms.Seek(0, SeekOrigin.Begin);

// Write dll
// Write dll

path = Path.Combine(outputFolder, $"{baseName}.artifacts.dll");
File.WriteAllBytes(path, ms.ToArray());
Console.WriteLine($"Created {path}");
path = Path.Combine(outputFolder, $"{baseName}.artifacts.dll");
File.WriteAllBytes(path, ms.ToArray());
Console.WriteLine($"Created {path}");
}
}
catch
{
Console.Error.WriteLine("Artifacts compilation error.");
}
}
catch
{
Console.Error.WriteLine("Artifacts compilation error.");
}
}
if (options.Debug)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"language": "C#",
"type": "project"
},
"sourceName": "ProjectName",
"sourceName": "Nep17Contract",
"symbols": {
"NeoVersion": {
"type": "parameter",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@

namespace ProjectName
{
[DisplayName(nameof(Contract1))]
[DisplayName(nameof(Nep17Contract))]
[ManifestExtra("Author", "<Your Name Or Company Here>")]
[ManifestExtra("Description", "<Description Here>")]
[ManifestExtra("Email", "<Your Public Email Here>")]
[ManifestExtra("Version", "<Version String Here>")]
[ContractSourceCode("https://github.com/neo-project/neo-devpack-dotnet/tree/master/src/Neo.SmartContract.Template")]
[ContractSourceCode("https://github.com/neo-project/neo-devpack-dotnet/tree/master/src/Neo.SmartContract.Template/templates/neocontractnep17/Nep17Contract.cs")]
[ContractPermission("*", "*")]
[SupportedStandards("NEP-17")]
public class Contract1 : Nep17Token
public class Nep17Contract : Nep17Token
{
#region Owner

Expand All @@ -33,7 +33,7 @@ public static UInt160 GetOwner()
private static bool IsOwner() =>
Runtime.CheckWitness(GetOwner());

public delegate void OnSetOwnerDelegate(UInt160 newOwner);
public delegate void OnSetOwnerDelegate(UInt160 previousOwner, UInt160 newOwner);

[DisplayName("SetOwner")]
public static event OnSetOwnerDelegate OnSetOwner;
Expand All @@ -45,8 +45,9 @@ public static void SetOwner(UInt160 newOwner)

ExecutionEngine.Assert(newOwner.IsValid && !newOwner.IsZero, "owner must be valid");

UInt160 previous = GetOwner();
Storage.Put(new[] { Prefix_Owner }, newOwner);
OnSetOwner(newOwner);
OnSetOwner(previous, newOwner);
}

#endregion
Expand Down Expand Up @@ -75,40 +76,6 @@ public static void SetOwner(UInt160 newOwner)

#endregion

#region Payment

public static bool Withdraw(UInt160 token, UInt160 to, BigInteger amount)
{
if (IsOwner() == false)
throw new InvalidOperationException("No Authorization!");
if (amount <= 0)
throw new ArgumentOutOfRangeException(nameof(amount));
if (to == null || to.IsValid == false)
throw new ArgumentException("Invalid Address!");
if (token == null || token.IsValid == false)
throw new ArgumentException("Invalid Token Address!");
if (ContractManagement.GetContract(token) == null)
throw new ArgumentException("Token Not A Contract!");
// TODO: Add logic
return true;
}

// NOTE: Allows ALL NEP-17 tokens to be received for this contract
public static void OnNEP17Payment(UInt160 from, BigInteger amount, object data)
{
// TODO: Add logic for specific NEP-17 contract tokens
if (Runtime.CallingScriptHash == NEO.Hash)
{
// TODO: Add logic (Burn, Mint, Transfer, Etc)
}
if (Runtime.CallingScriptHash == GAS.Hash)
{
// TODO: Add logic (Burn, Mint, Transfer, Etc)
}
}

#endregion

// When this contract address is included in the transaction signature,
// this method will be triggered as a VerificationTrigger to verify that the signature is correct.
// For example, this method needs to be called when withdrawing token from the contract.
Expand All @@ -121,6 +88,7 @@ public static string MyMethod()
return Storage.Get(Storage.CurrentContext, "Hello");
}

// This will be executed during deploy
public static void _deploy(object data, bool update)
{
if (update)
Expand All @@ -137,16 +105,15 @@ public static void _deploy(object data, bool update)
ExecutionEngine.Assert(initialOwner.IsValid && !initialOwner.IsZero, "owner must exists");

Storage.Put(new[] { Prefix_Owner }, initialOwner);
OnSetOwner(initialOwner);

// This will be executed during deploy
OnSetOwner(null, initialOwner);
Storage.Put(Storage.CurrentContext, "Hello", "World");
}

public static void Update(ByteString nefFile, string manifest)
public static void Update(ByteString nefFile, string manifest, object data)
{
if (!IsOwner()) throw new Exception("No authorization.");
ContractManagement.Update(nefFile, manifest, null);
if (IsOwner() == false)
throw new InvalidOperationException("No authorization.");
ContractManagement.Update(nefFile, manifest, data);
}

// NOTE: NEP-17 contracts "SHOULD NOT" have "Destroy" method
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Coverage/CoverageBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public float CoveredPercentage
var total = TotalInstructions;
if (total == 0) return 0F;

return (float)CoveredInstructions / total * 100F;
return (float)CoveredInstructions / total;
}
}

Expand Down
Loading

0 comments on commit 9ff2042

Please sign in to comment.