Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TestEngine: Migrate current Framework's ut #968

Merged
merged 51 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
068fc2c
Migrate current ut's
shargon Feb 28, 2024
8fe74ba
Restore tests not related to a contract
shargon Feb 28, 2024
2bc0065
revert namespace change
shargon Feb 28, 2024
4e8b1c9
All artifacts
shargon Feb 28, 2024
08fb01b
revert address
shargon Feb 28, 2024
f1a7215
Migrate some UT
shargon Feb 28, 2024
b333f5f
format
shargon Feb 28, 2024
9c7032b
Migrate 3 ut
shargon Feb 28, 2024
8dccd37
migrate 2 ut
shargon Feb 28, 2024
256fab3
fix namespace
shargon Feb 28, 2024
e117866
Remove SyscallTest
shargon Feb 28, 2024
85685e9
Migrate 2 ut more
shargon Feb 28, 2024
4ad16cd
Helper ut
shargon Feb 28, 2024
af725e8
Two uts more
shargon Feb 28, 2024
5ce9050
Workflow
shargon Feb 28, 2024
c4e622a
Two ut more
shargon Feb 28, 2024
1c4fcea
Oracle
shargon Feb 28, 2024
7db1d1e
clean
shargon Feb 28, 2024
d593620
Rename contract and StdLib
shargon Feb 28, 2024
bb831bc
Runtime
shargon Feb 29, 2024
06b573f
One ut more
shargon Feb 29, 2024
9878eea
Native ut
shargon Feb 29, 2024
19416d2
UT fail, how to send an interopInterface? :S
shargon Feb 29, 2024
6e68a3d
Ut pointers
shargon Feb 29, 2024
c80cfce
Fix ut
shargon Feb 29, 2024
f037896
Clean and extend
shargon Feb 29, 2024
e7c9fb6
Contract UT
shargon Feb 29, 2024
576b5a3
TODO BlockchainTest
shargon Feb 29, 2024
ac67c51
Update csproj
shargon Feb 29, 2024
d0f385f
Fix conflicts
shargon Feb 29, 2024
0d7a177
Merge branch 'master' into core-migrate-ut
shargon Feb 29, 2024
4a11571
clean
shargon Feb 29, 2024
413cb35
Merge branch 'core-migrate-ut' of https://github.com/neo-project/neo-…
shargon Feb 29, 2024
fe9c051
Start with blockchain its
shargon Feb 29, 2024
55afbf8
Revert using short
shargon Feb 29, 2024
9daf1ee
TODO Crypto
shargon Feb 29, 2024
dce9e79
Blockchain UT
shargon Feb 29, 2024
d660d69
secp256k1
shargon Feb 29, 2024
a8a320e
Increase coverage
shargon Feb 29, 2024
4fc6761
Merge branch 'master' into core-migrate-ut
shargon Mar 1, 2024
7baff5c
Fix merge
shargon Mar 1, 2024
932db50
fix using sort
shargon Mar 1, 2024
7a8e8a6
Add Uint coverage
shargon Mar 1, 2024
0565bf1
Fix CurrentBlock
shargon Mar 1, 2024
c72c078
previous from storage
shargon Mar 1, 2024
2f93937
Merge branch 'master' into core-migrate-ut
shargon Mar 1, 2024
e12b742
Update src/Neo.SmartContract.Testing/Native/LedgerContract.cs
shargon Mar 1, 2024
f63c9d8
Persisting block
shargon Mar 2, 2024
c4b06e2
clean
shargon Mar 2, 2024
bf70344
Clean
shargon Mar 2, 2024
b870e62
Clean
shargon Mar 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ jobs:
run: |
dotnet test ./tests/Neo.SmartContract.Framework.UnitTests \
--no-build \
-l "console;verbosity=normal" \
-e "COVERAGE_MERGE_JOIN=${GITHUB_WORKSPACE}/coverage-join/coverage.json" \
-l "console;verbosity=detailed" \
-p:CollectCoverage=true \
-p:CoverletOutput=${GITHUB_WORKSPACE}/coverage-join/ \
-p:MergeWith=${GITHUB_WORKSPACE}/coverage-join/coverage.json \
Expand Down
36 changes: 32 additions & 4 deletions src/Neo.SmartContract.Testing/Native/LedgerContract.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Neo.IO;
using Neo.Network.P2P.Payloads;
using Neo.SmartContract.Native;
using Neo.VM;
using System.ComponentModel;
using System.Numerics;
Expand Down Expand Up @@ -30,23 +30,51 @@ public abstract class LedgerContract : SmartContract

#region Safe methods

#region Helpers

/// <summary>
/// Safe helper method
/// </summary>
public Models.Block? GetBlock(UInt256 hash)
=> GetBlock(hash.ToArray());

/// <summary>
/// Safe helper method
/// </summary>
public Models.Block? GetBlock(uint index)
=> GetBlock(new BigInteger(index).ToByteArray());

/// <summary>
/// Safe helper method
/// </summary>
public Models.Transaction? GetTransactionFromBlock(uint blockIndex, uint txIndex)
=> GetTransactionFromBlock(new BigInteger(blockIndex).ToByteArray(), txIndex);

/// <summary>
/// Safe helper method
/// </summary>
public Models.Transaction? GetTransactionFromBlock(UInt256 blockHash, uint txIndex)
=> GetTransactionFromBlock(blockHash.ToArray(), txIndex);

#endregion

/// <summary>
/// Safe method
/// </summary>
[DisplayName("getBlock")]
public abstract TrimmedBlock? GetBlock(byte[]? indexOrHash);
public abstract Models.Block? GetBlock(byte[]? indexOrHash);

/// <summary>
/// Safe method
/// </summary>
[DisplayName("getTransaction")]
public abstract Transaction? GetTransaction(UInt256? hash);
public abstract Models.Transaction? GetTransaction(UInt256? hash);

/// <summary>
/// Safe method
/// </summary>
[DisplayName("getTransactionFromBlock")]
public abstract Transaction? GetTransactionFromBlock(byte[]? blockIndexOrHash, BigInteger? txIndex);
public abstract Models.Transaction? GetTransactionFromBlock(byte[]? blockIndexOrHash, BigInteger? txIndex);

/// <summary>
/// Safe method
Expand Down
37 changes: 37 additions & 0 deletions src/Neo.SmartContract.Testing/Native/Models/Block.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Neo.SmartContract.Testing.Attributes;

namespace Neo.SmartContract.Testing.Native.Models
{
public class Block
{
[FieldOrder(0)]
public UInt256 Hash { get; set; }

Check warning on line 8 in src/Neo.SmartContract.Testing/Native/Models/Block.cs

View workflow job for this annotation

GitHub Actions / Test

Non-nullable property 'Hash' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

[FieldOrder(1)]
public uint Version { get; set; }

[FieldOrder(2)]
public UInt256 PrevHash { get; set; }

[FieldOrder(3)]
public UInt256 MerkleRoot { get; set; }

[FieldOrder(4)]
public ulong Timestamp { get; set; }

[FieldOrder(5)]
public ulong Nonce { get; set; }

[FieldOrder(6)]
public uint Index { get; set; }

[FieldOrder(7)]
public byte PrimaryIndex { get; set; }

[FieldOrder(8)]
public UInt160 NextConsensus { get; set; }

[FieldOrder(9)]
public int TransactionsCount { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
using Neo.SmartContract.Testing.Attributes;
using System.Numerics;

namespace Neo.SmartContract.Testing.Native;

public abstract partial class NeoToken : SmartContract
namespace Neo.SmartContract.Testing.Native.Models
{
public class Candidate
{
Expand Down
31 changes: 31 additions & 0 deletions src/Neo.SmartContract.Testing/Native/Models/Transaction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Neo.SmartContract.Testing.Attributes;

namespace Neo.SmartContract.Testing.Native.Models
{
public class Transaction
{
[FieldOrder(0)]
public UInt256? Hash { get; set; }

[FieldOrder(1)]
public byte Version { get; set; }

[FieldOrder(2)]
public uint Nonce { get; set; }

[FieldOrder(3)]
public UInt160? Sender { get; set; }

[FieldOrder(4)]
public long SystemFee { get; set; }

[FieldOrder(5)]
public long NetworkFee { get; set; }

[FieldOrder(6)]
public uint ValidUntilBlock { get; set; }

[FieldOrder(7)]
public byte[]? Script { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Native/NeoToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public abstract partial class NeoToken : SmartContract, TestingStandards.INep17S
/// <summary>
/// Safe property
/// </summary>
public abstract Candidate[]? Candidates { [DisplayName("getCandidates")] get; }
public abstract Models.Candidate[] Candidates { [DisplayName("getCandidates")] get; }

/// <summary>
/// Safe property
Expand Down
8 changes: 6 additions & 2 deletions src/Neo.SmartContract.Testing/NativeArtifacts.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using Neo.SmartContract.Testing.Native;
using System;
Expand Down Expand Up @@ -80,7 +81,8 @@ public NativeArtifacts(TestEngine engine)
/// Initialize native contracts
/// </summary>
/// <param name="commit">Initialize native contracts</param>
public void Initialize(bool commit = false)
/// <returns>Genesis block</returns>
public Block Initialize(bool commit = false)
{
_engine.Transaction.Script = Array.Empty<byte>(); // Store the script in the current transaction

Expand Down Expand Up @@ -122,7 +124,7 @@ public void Initialize(bool commit = false)

method = native.GetType().GetMethod("PostPersist", BindingFlags.NonPublic | BindingFlags.Instance);

using (var engine = new TestingApplicationEngine(_engine, TriggerType.OnPersist, genesis, clonedSnapshot, genesis))
using (var engine = new TestingApplicationEngine(_engine, TriggerType.PostPersist, genesis, clonedSnapshot, genesis))
{
engine.LoadScript(Array.Empty<byte>());
if (method!.Invoke(native, new object[] { engine }) is not ContractTask task)
Expand All @@ -145,6 +147,8 @@ public void Initialize(bool commit = false)

ApplicationEngine.Log -= _engine.ApplicationEngineLog;
ApplicationEngine.Notify -= _engine.ApplicationEngineNotify;

return genesis;
}
}
}
187 changes: 187 additions & 0 deletions src/Neo.SmartContract.Testing/PersistingBlock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
using Neo.Cryptography;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using Neo.SmartContract.Native;
using Neo.VM;
using System;
using System.Linq;
using System.Reflection;

namespace Neo.SmartContract.Testing
{
public class PersistingBlock
{
private readonly TestEngine _engine;

/// <summary>
/// Underlying block
/// </summary>
internal readonly Block UnderlyingBlock;

/// <summary>
/// Index
/// </summary>
public uint Index => UnderlyingBlock.Header.Index;

/// <summary>
/// Nonce
/// </summary>
public ulong Nonce
{
get => UnderlyingBlock.Header.Nonce;
set => UnderlyingBlock.Header.Nonce = value;
}

/// <summary>
/// Time
/// </summary>
public TimeSpan Timestamp => TimeSpan.FromMilliseconds(UnderlyingBlock.Header.Timestamp);

/// <summary>
/// Constructor
/// </summary>
/// <param name="engine">Engine</param>
/// <param name="currentBlock">Current block</param>
public PersistingBlock(TestEngine engine, Block currentBlock)
{
_engine = engine;
UnderlyingBlock = new Block()
{
Header = CreateNextHeader(currentBlock.Header, TimeSpan.FromSeconds(15), currentBlock.Nonce),
Transactions = Array.Empty<Transaction>(),
};
}

/// <summary>
/// Advance
/// </summary>
/// <param name="elapsed">Elapsed time</param>
public void Advance(TimeSpan elapsed)
{
UnderlyingBlock.Header.Timestamp += (ulong)elapsed.TotalMilliseconds;
}

/// <summary>
/// Skip blocks
/// </summary>
/// <param name="count">Count</param>
/// <param name="elapsed">Elapsed</param>
public void Skip(uint count, TimeSpan elapsed)
{
UnderlyingBlock.Header.Index += count;
UnderlyingBlock.Header.Timestamp += (ulong)elapsed.TotalMilliseconds;
}

/// <summary>
/// Persist block
/// </summary>
/// <param name="tx">Transaction</param>
/// <param name="state">State</param>
/// <returns>Persisted block</returns>
public Block Persist(Transaction tx, VMState state = VMState.HALT)
{
return Persist(new[] { tx }, new[] { state });
}

/// <summary>
/// Persist block
/// </summary>
/// <param name="txs">Transactions</param>
/// <param name="states">States</param>
/// <returns>Persisted block</returns>
public Block Persist(Transaction[] txs, VMState[] states)
{
if (txs.Length != states.Length)
{
throw new ArgumentException("Transactions count and states count are different");
}

// Build block

Block persist = new()
{
Header = UnderlyingBlock.Header,
Transactions = txs,
};
persist.Header.MerkleRoot = MerkleTree.ComputeRoot(txs.Select(p => p.Hash).ToArray());

// Invoke Ledger.OnPersist

var native = NativeContract.Ledger;
var method = native.GetType().GetMethod("OnPersist", BindingFlags.NonPublic | BindingFlags.Instance);

DataCache clonedSnapshot = _engine.Storage.Snapshot.CreateSnapshot();

using (var engine = new TestingApplicationEngine(_engine, TriggerType.OnPersist, persist, clonedSnapshot, persist))
{
engine.LoadScript(Array.Empty<byte>());
if (method!.Invoke(native, new object[] { engine }) is not ContractTask task)
throw new Exception($"Error casting {native.Name}.OnPersist to ContractTask");

task.GetAwaiter().GetResult();

if (engine.Execute() != VMState.HALT)
throw new Exception($"Error executing {native.Name}.OnPersist");
}

// Invoke Ledger.PostPersist

method = native.GetType().GetMethod("PostPersist", BindingFlags.NonPublic | BindingFlags.Instance);

using (var engine = new TestingApplicationEngine(_engine, TriggerType.PostPersist, persist, clonedSnapshot, persist))
{
engine.LoadScript(Array.Empty<byte>());
if (method!.Invoke(native, new object[] { engine }) is not ContractTask task)
throw new Exception($"Error casting {native.Name}.PostPersist to ContractTask");

task.GetAwaiter().GetResult();
if (engine.Execute() != VMState.HALT)
throw new Exception($"Error executing {native.Name}.PostPersist");
}

// Update states

const byte prefix_Transaction = 11;

for (int x = 0; x < txs.Length; x++)
{
var transactionState = clonedSnapshot.TryGet(new KeyBuilder(_engine.Native.Ledger.Storage.Id, prefix_Transaction).Add(txs[x].Hash));
transactionState.GetInteroperable<TransactionState>().State = states[x];
}

// Commit changes and return block

UnderlyingBlock.Header = CreateNextHeader(persist.Header, TimeSpan.FromSeconds(15), persist.Nonce);
clonedSnapshot.Commit();

return persist;
}

/// <summary>
/// Create next header
/// </summary>
/// <param name="previous">Previous</param>
/// <param name="elapsed">Elapsed</param>
/// <param name="nonce">Nonce</param>
/// <returns>Header</returns>
private static Header CreateNextHeader(Header previous, TimeSpan elapsed, ulong nonce = 0)
{
return new Header()
{
Version = previous.Version,
Index = previous.Index + 1,
MerkleRoot = UInt256.Zero,
NextConsensus = previous.NextConsensus,
Nonce = nonce,
PrevHash = previous.Hash,
PrimaryIndex = previous.PrimaryIndex,
Timestamp = previous.Timestamp + (ulong)elapsed.TotalMilliseconds,
Witness = new Witness()
{
InvocationScript = Array.Empty<byte>(),
VerificationScript = Array.Empty<byte>(),
}
};
}
}
}
5 changes: 2 additions & 3 deletions src/Neo.SmartContract.Testing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,11 @@ The publicly exposed read-only properties are as follows:
- **ValidatorsAddress**: Defines the address for the validators of the defined *ProtocolSettings*.
- **CommitteeAddress**: Returns the address of the current chain's committee.
- **Transaction**: Defines the transaction that will be used as `ScriptContainer` for the neo virtual machine, by default it updates the script of the same as calls are composed and executed, and the `Signers` will be used as validators for the `CheckWitness`, regardless of whether the signature is correct or not, so if you want to test with different wallets or scopes, you do not need to sign the transaction correctly, just set the desired signers.
- **CurrentBlock**: Defaults to `Genesis` for the defined `ProtocolSettings`, but the height has been incremented by 1 to avoid issues related to the generation of gas from native contracts.
- **PersistingBlock**: The block that will be persisted.
- **Storage**: Abstracts access to storage, allowing for easy `Snapshots` as well as reverting them. Allows access to the storage of contracts, as well as manually altering their state. It's worth noting that a storage class is provided, which allows for reading the storage from an RPC endpoint. The class in question is named `RpcStore` and is available in the namespace `Neo.SmartContract.Testing.Storage.Rpc`.

And for read and write, we have:

- **Storage**: Abstracts access to storage, allowing for easy `Snapshots` as well as reverting them. Allows access to the storage of contracts, as well as manually altering their state. It's worth noting that a storage class is provided, which allows for reading the storage from an RPC endpoint. The class in question is named `RpcStore` and is available in the namespace `Neo.SmartContract.Testing.Storage.Rpc`.

- **Gas**: Sets the gas execution limit for contract calls. Sets the `NetworkFee` of the `Transaction` object.
- **EnableCoverageCapture**: Enables or disables the coverage capture.
- **Trigger**: The trigger of the execution.
Expand Down
Loading
Loading