From d4795f7783c1b7417868ff24f9641ab6387a6d27 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Sun, 17 Sep 2023 22:31:30 -0400 Subject: [PATCH] V1.0.0 (#10) * Init unit test Added TryRemove method and ContainsTask to CronScduler Moved LastRunTimestamp to ICrobJob and implemented in run functions * Added Enable and Disable crontab jobs to console commands * Changed console commands output order * Added remove job when file is deleted. * Updated docs * Updated jobs.md and README.md Added config_json.md * updated README.md for logo * Changed schema for signers; moved to wallet section Updated docs * Changed namespaces Added Next Run to console display --- ALL.sln | 12 +- README.md | 59 +++++-- docs/config_json.md | 21 +++ docs/jobs.md | 67 ++++++-- examples/HelloInvokeMethod.job | 3 +- examples/HelloTransfer.job | 12 +- imgs/crontab-color-palette.png | Bin 0 -> 39392 bytes ...n-logo-black-on-transparent-background.png | Bin 0 -> 19209 bytes ...n-logo-color-on-transparent-background.png | Bin 0 -> 20767 bytes ...n-logo-white-on-transparent-background.png | Bin 0 -> 19067 bytes imgs/fonts/Monofett Regular.ttf | Bin 0 -> 52232 bytes imgs/logo-red.png | Bin 0 -> 12379 bytes imgs/logo-transparent.png | Bin 0 -> 10097 bytes src/Crontab/CronPlugin.Console.cs | 89 ++++++---- src/Crontab/CronPlugin.Events.cs | 11 +- src/Crontab/CronPlugin.Methods.cs | 4 +- src/Crontab/CronPlugin.cs | 3 +- src/Crontab/Crontab.csproj | 6 +- src/Crontab/Jobs/CronBasicJob.cs | 18 +- src/Crontab/Jobs/CronContract.cs | 2 + src/Crontab/Jobs/CronEntry.cs | 29 ++++ src/Crontab/Jobs/CronJobType.cs | 2 - src/Crontab/Jobs/CronScheduler.cs | 78 +++++---- src/Crontab/Jobs/CronTransferJob.cs | 13 +- src/Crontab/Jobs/ICronJob.cs | 6 +- .../{ => Settings}/CronJobBasicSettings.cs | 2 +- .../CronJobContractParameterSettings.cs | 6 +- .../{ => Settings}/CronJobContractSettings.cs | 2 +- .../{ => Settings}/CronJobTransferSettings.cs | 2 +- .../{ => Settings}/CronJobWalletSettings.cs | 3 +- .../{ => Settings}/CronPluginJobSettings.cs | 2 +- .../{ => Settings}/CronPluginSettings.cs | 2 +- .../{ => Settings}/CronTransferSettings.cs | 3 +- .../{ => Settings}/ICronJobSettings.cs | 2 +- src/Crontab/Utils/ContractUtils.cs | 95 ++++++++++ src/Crontab/Utils/WalletUtils.cs | 79 +++++++++ src/Crontab/WalletUtils.cs | 162 ------------------ tests/Crontab.Test/Crontab.Test.csproj | 29 ++++ tests/Crontab.Test/GlobalUsings.cs | 7 + tests/Crontab.Test/Helpers/UT_JobHelper.cs | 35 ++++ tests/Crontab.Test/UT_CronScheduler.cs | 64 +++++++ 41 files changed, 642 insertions(+), 288 deletions(-) create mode 100644 docs/config_json.md create mode 100644 imgs/crontab-color-palette.png create mode 100644 imgs/crontab-high-resolution-logo-black-on-transparent-background.png create mode 100644 imgs/crontab-high-resolution-logo-color-on-transparent-background.png create mode 100644 imgs/crontab-high-resolution-logo-white-on-transparent-background.png create mode 100644 imgs/fonts/Monofett Regular.ttf create mode 100644 imgs/logo-red.png create mode 100644 imgs/logo-transparent.png create mode 100644 src/Crontab/Jobs/CronEntry.cs rename src/Crontab/{ => Settings}/CronJobBasicSettings.cs (96%) rename src/Crontab/{ => Settings}/CronJobContractParameterSettings.cs (72%) rename src/Crontab/{ => Settings}/CronJobContractSettings.cs (92%) rename src/Crontab/{ => Settings}/CronJobTransferSettings.cs (96%) rename src/Crontab/{ => Settings}/CronJobWalletSettings.cs (82%) rename src/Crontab/{ => Settings}/CronPluginJobSettings.cs (89%) rename src/Crontab/{ => Settings}/CronPluginSettings.cs (96%) rename src/Crontab/{ => Settings}/CronTransferSettings.cs (85%) rename src/Crontab/{ => Settings}/ICronJobSettings.cs (91%) create mode 100644 src/Crontab/Utils/ContractUtils.cs create mode 100644 src/Crontab/Utils/WalletUtils.cs delete mode 100644 src/Crontab/WalletUtils.cs create mode 100644 tests/Crontab.Test/Crontab.Test.csproj create mode 100644 tests/Crontab.Test/GlobalUsings.cs create mode 100644 tests/Crontab.Test/Helpers/UT_JobHelper.cs create mode 100644 tests/Crontab.Test/UT_CronScheduler.cs diff --git a/ALL.sln b/ALL.sln index 80d6980..17421f2 100644 --- a/ALL.sln +++ b/ALL.sln @@ -7,7 +7,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3002EB89-067 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{E6DF2325-582C-4859-B3ED-8887A3326D3D}" ProjectSection(SolutionItems) = preProject - docs\jobs.md = docs\jobs.md + docs\CONFIG_JSON.md = docs\CONFIG_JSON.md + docs\JOBS.md = docs\JOBS.md README.md = README.md EndProjectSection EndProject @@ -19,6 +20,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{6A EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crontab", "src\Crontab\Crontab.csproj", "{1B500F7A-36A3-42C2-9A77-470EDB25B638}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4DB93B25-158A-40C8-A19D-F2E5E4409B15}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crontab.Test", "tests\Crontab.Test\Crontab.Test.csproj", "{01DAE7D5-28B9-410E-8216-0A5421E924D0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -29,12 +34,17 @@ Global {1B500F7A-36A3-42C2-9A77-470EDB25B638}.Debug|Any CPU.Build.0 = Debug|Any CPU {1B500F7A-36A3-42C2-9A77-470EDB25B638}.Release|Any CPU.ActiveCfg = Release|Any CPU {1B500F7A-36A3-42C2-9A77-470EDB25B638}.Release|Any CPU.Build.0 = Release|Any CPU + {01DAE7D5-28B9-410E-8216-0A5421E924D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01DAE7D5-28B9-410E-8216-0A5421E924D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01DAE7D5-28B9-410E-8216-0A5421E924D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01DAE7D5-28B9-410E-8216-0A5421E924D0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {1B500F7A-36A3-42C2-9A77-470EDB25B638} = {3002EB89-0674-4B08-B155-353140FCCFCD} + {01DAE7D5-28B9-410E-8216-0A5421E924D0} = {4DB93B25-158A-40C8-A19D-F2E5E4409B15} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5ED09B52-78F7-49E0-8A0B-1A196F12118B} diff --git a/README.md b/README.md index 0582d1f..0af2351 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,50 @@ -[![Static Badge](https://img.shields.io/badge/license-MIT-green)](/LICENSE) -![GitHub tag (with filter)](https://img.shields.io/github/v/tag/cschuchardt88/neo-cron-plugin) -![GitHub all releases](https://img.shields.io/github/downloads/cschuchardt88/neo-cron-plugin/total) - +

+ Crontab-logo +

+```bash +* * * * * Crontab Expression Chart +│ │ │ │ │ +│ │ │ │ │ +│ │ │ │ |_________ Day of Week (0 – 6) (0 is Sunday) +│ │ │ |____________ Month (1 – 12), * means every month +│ │ |______________ Day of Month (1 – 31), * means every day +│ |________________ Hour (0 – 23), * means every hour +|___________________ Minute (0 – 59), * means every minute +``` +

+ + license-MIT + + + neo-cron-plugin-tags + + + neo-cron-plugin-releases-downloads + +

# neo-cron-plugin -Crontab task scheduler for executing blockchain tasks. +Task scheduler for sending transactions to the blockchain. Just as the +name implies `Crontab` does just that! Schedule jobs to invoke contracts +or transfer funds at certain times of the day, month, year, hour and +minute. ## Features -- Crontab Scheduler -- Invoke Contracts -- Invoke Transfers (_Nep17_) +- Task Scheduler +- Manage jobs in `cli` console. +- Send transaction types. + - Invoke Contract Methods + - Send Nep-17 Transfers + +## Upcoming Features +- Send `VM` scripts in transactions. +- Detailed error reporting. +- enable/disable jobs in their config file. + +Have a feature you want to recommend for this project. Just create an +[issue](https://github.com/cschuchardt88/neo-cron-plugin/issues). # Install This plugin requires at least `neo-cli` version @@ -19,12 +52,14 @@ This plugin requires at least `neo-cli` version download and extract the `.zip` file. **Next Steps** +1. _Open `neo-cli` directory._ 1. _Create a folder in the `Plugins` directory called `Crontab`._ 1. _Copy & Paste `Crontab.dll`, `config.json` and `NCrontab.dll` into `Plugins\Crontab` directory._ -1. _Edit `config.json` with your configuration. [More datails](/docs)_ +1. _Edit `config.json` with your configuration. [More details](/docs/CONFIG_JSON.md)_ # Example Tasks -You can find more datails on how to configure [jobs here](/docs/jobs.md). +You can find more details on how to create and configure jobs [here](/docs/JOBS.md). -- Invoke a [contract](/examples/HelloInvokeMethod.job) -- Invoke a [transfer](/examples/HelloTransfer.job) +**Schedule Job Examples** +- [contract](/examples/HelloInvokeMethod.job) +- [transfer](/examples/HelloTransfer.job) diff --git a/docs/config_json.md b/docs/config_json.md new file mode 100644 index 0000000..1af2485 --- /dev/null +++ b/docs/config_json.md @@ -0,0 +1,21 @@ +# Config File +Is placed in the same directory as `Crontab.dll`. Located in `Plugins\Crontab` +directory. + +## Root Section +| Property | Type | Description | +| ---: | :---: | :--- | +|PluginConfiguration|object|note: Standard for `NEO` plugins| + +## PluginConfiguration Section +| Property | Type | Description | +| ---: | :---: | :--- | +|Network|uint32|Network you want to execute tasks on.| +|MaxGasInvoke|int64|Max gas allow on the `NEO` `VM`.| +|Job|object|see [job](#job-section) section for more details.| + +## Job Section +| Property | Type | Description | +| ---: | :---: | :--- | +|Path|string|Where your `*.job` files are. Defaults to `jobs` folder in the `neo-cli` root directory. _note: You must create this folder_.| +|Timeout|uint32|Max time in seconds that a job can run for. It's recommended that it should be no more than 30 seconds. diff --git a/docs/jobs.md b/docs/jobs.md index c738221..23b2bb7 100644 --- a/docs/jobs.md +++ b/docs/jobs.md @@ -2,12 +2,10 @@ In this section you will learn how to create and configure job(s). ## Create a Job -Add `*.job` file to the path of `JobsPath` in the _config.json_. In this -location the plugin will pull all files; including ones from other -directories. +Create a create file with any name with `.job` extension. Place file in +`JobPath` location directory from [_config.json_](/docs/config_json.md) file. - -## Template (_*.job_) File +## Basic Template (_*.job_) File ```json { "Type": "", @@ -27,16 +25,46 @@ directories. } ``` -## Job Config +## Transfer Template (_*.job_) File +```json +{ + "Type": "Transfer", + "Name": "", + "Expression": "", + "RunOnce": false, + "Wallet": { + "Path": "", + "Password": "", + "Account": "" + }, + "Transfer": { + "AssetId": "", + "SendTo": "", + "SendAmount": 0.00, + "Comment": null, + "Signers": [] + } +} +``` + +## Root Section | Property | Type | Description | | ---: | :---: | :--- | |Type|enum|`Basic` or `Transfer`| -|Name|string|is the name of the job.| -|Expression|string|is the crontab expression.| -|RunOnce|boolean|Only execute the job once.| +|Name|string|Name of the job.| +|Expression|string|Crontab schedule expression. In `5` or `6` part format.| +|RunOnce|boolean|**Only** execute the job once.| |Contract|object|See [details](#contract-section).| |Wallet|object|See [details](#wallet-section).| +## Transfer Section +| Property | Type | Description | +| ---: | :---: | :--- | +|AssetId|string|Smart contract hash. As `ScrtipHash` format.| +|SendTo|string|**NEO** Wallet address. As `ScriptHash` format| +|SendAmount|decimal|Non-negative number.| +|Comment|string|`data` field in a transfer method. Unsure? Leave blank.| + ## Contract Section | Property | Type | Description | | ---: | :---: | :--- | @@ -47,15 +75,16 @@ directories. ## Wallet Section | Property | Type | Description | | ---: | :---: | :--- | -|Path|string|file path plus filename with extension. _note: path is relative to where file neo-cli.exe_| +|Path|string|Full file name path. Path is relative to where `neo-cli.exe` is.| |Password|string|Password to open wallet file.| -|Account|string| `hash160` of the account in the wallet. _note: Transactions will preformed with this account._| +|Account|string|`hash160` of the account in the wallet. Transactions will be preformed with this account as sender and witness.| +|Signers|Array|An Array of strings as ScriptHash `hash160`. Defaults to wallet account.| ## Contract Parameters | Property | Type | Description | | ---: | :---: | :--- | -|Type|enum| see [details](#contract-parameter-enum).| -|Value|string| string format of the value type.| +|Type|enum|see [details](#contract-parameter-enum).| +|Value|string|string format of the value type.| ## Contract Parameter Enum Arrays and Maps are not currently supported. That kind of data @@ -66,9 +95,9 @@ per `byte` down for transactions. | ---: | :---: | :--- | |ByteString|`Base64`|| |Signature|`Base64`|| -|Boolean|`Bool`|`True` or `False`| -|Integer|`BigInt`|| -|String|`utf-8`|| -|Hash160|`string`|_note: valid? depends if contract checks for validation_.| -|Hash256|`string`|| -|PublicKey|`hexstring`|| +|Boolean|`Boolean`|Value must be `True` or `False` in string form.| +|Integer|`BigInt`|**Only** `numbers` in string form up to 32 characters long.| +|String|`utf-8`|Make sure you use `utf-8` text encoding.| +|Hash160|`hexstring`|`ScriptHash` format with prefix `0x`| +|Hash256|`hexstring`| `SHA-256` hash starting with prefix `0x`.| +|PublicKey|`hexstring`|`ECPoint` `Secp256r1` public key in hex format with no prefix.| diff --git a/examples/HelloInvokeMethod.job b/examples/HelloInvokeMethod.job index 59e4a1e..e91691c 100644 --- a/examples/HelloInvokeMethod.job +++ b/examples/HelloInvokeMethod.job @@ -6,7 +6,8 @@ "Wallet": { "Path": "wallets/main.json", "Password": "main", - "Account": "0x2dfa6249a7b8ee132370f0385165339982e52e53" + "Account": "0x951bf95707bb92a0c89d4308095697af60474472", + "Signers": [] }, "Contract": { "ScriptHash": "0x0313bdad26721ae6867df516139d4e52f749f360", diff --git a/examples/HelloTransfer.job b/examples/HelloTransfer.job index c52fe43..6fb1a31 100644 --- a/examples/HelloTransfer.job +++ b/examples/HelloTransfer.job @@ -6,15 +6,13 @@ "Wallet": { "Path": "wallets/main.json", "Password": "main", - "Account": "0x2dfa6249a7b8ee132370f0385165339982e52e53" + "Account": "0x951bf95707bb92a0c89d4308095697af60474472", + "Signers": [] }, "Transfer": { - "AssetId": "0x0313bdad26721ae6867df516139d4e52f749f360", - "SendTo": "0x2dfa6249a7b8ee132370f0385165339982e52e53", + "AssetId": "0xd2a4cff31913016155e38e474a2c06d08be276cf", + "SendTo": "0x495df01c83bfa34cf6e4cc904391c5d129ccbfe0", "SendAmount": 0.001, - "Comment": null, - "Signers": [ - "0x2dfa6249a7b8ee132370f0385165339982e52e53" - ] + "Comment": null } } diff --git a/imgs/crontab-color-palette.png b/imgs/crontab-color-palette.png new file mode 100644 index 0000000000000000000000000000000000000000..4cc4488a668e9e9a7efb03294023153de28e2132 GIT binary patch literal 39392 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 CYTN<< literal 0 HcmV?d00001 diff --git a/imgs/crontab-high-resolution-logo-black-on-transparent-background.png b/imgs/crontab-high-resolution-logo-black-on-transparent-background.png new file mode 100644 index 0000000000000000000000000000000000000000..329f2d96c9c274a98685220f1aa41581e0d8266b GIT binary patch literal 19209 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 CronScheduler.PrecisionMinute()) - ConsoleHelper.Info(" RunNext: ", $"{job.Value.Schedule.GetNextOccurrence(DateTime.Now):MM/dd/yyyy hh:mm tt}"); + ConsoleHelper.Info(" ID: ", $"{entry.Key:n}"); + ConsoleHelper.Info(" File: ", $"{entry.Value.Settings.Filename}"); + ConsoleHelper.Info(" Name: ", $"{entry.Value.Settings.Name}"); + ConsoleHelper.Info(" Schedule: ", $"{entry.Value.Settings.Expression}"); + ConsoleHelper.Info(" Enabled: ", $"{entry.Value.IsEnabled}"); + ConsoleHelper.Info(" Run Once: ", $"{entry.Value.Settings.RunOnce}"); + if (entry.Value.Job.NextRunTimestamp != default) + ConsoleHelper.Info(" Next Run: ", $"{entry.Value.Job.NextRunTimestamp:MM/dd/yyyy hh:mm tt}"); else - { - if (job.Value.LastRunTime != default) - ConsoleHelper.Info(" RunLast: ", $"{job.Value.LastRunTime.ToLocalTime():MM/dd/yyyy hh:mm tt}"); - else - ConsoleHelper.Info(" RunLast: ", $"Processing..."); - } + ConsoleHelper.Info(" Next Run: ", $"N/A"); + if (entry.Value.Job.LastRunTimestamp != default) + ConsoleHelper.Info(" Last Run: ", $"{entry.Value.Job.LastRunTimestamp.ToLocalTime():MM/dd/yyyy hh:mm tt}"); + else + ConsoleHelper.Info(" Last Run: ", $"N/A"); - ConsoleHelper.Info(" Filename: ", $"\"{job.Value.Settings.Filename}\""); - if (job.Value.Settings.GetType() == typeof(CronJobBasicSettings)) + if (entry.Value.Settings.GetType() == typeof(CronJobBasicSettings)) { - var contractSettings = job.Value.Settings as CronJobBasicSettings; + var contractSettings = entry.Value.Settings as CronJobBasicSettings; ConsoleHelper.Info("", "-------", "Contract", "-------"); ConsoleHelper.Info("ScriptHash: ", $"{contractSettings.Contract.ScriptHash}"); ConsoleHelper.Info(" Method: ", $"{contractSettings.Contract.Method}"); ConsoleHelper.Info("Parameters: ", $"[{string.Join(", ", contractSettings.Contract.Params.Select(s => $"\"{s.Value}\""))}]"); } - else if (job.Value.Settings.GetType() == typeof(CronJobTransferSettings)) + else if (entry.Value.Settings.GetType() == typeof(CronJobTransferSettings)) { - var transferSettings = job.Value.Settings as CronJobTransferSettings; + var transferSettings = entry.Value.Settings as CronJobTransferSettings; ConsoleHelper.Info("", "-------", "Transfer", "-------"); - ConsoleHelper.Info(" AssetId: ", $"{transferSettings.Transfer.AssetId}"); - ConsoleHelper.Info(" To: ", $"{transferSettings.Transfer.SendTo}"); + ConsoleHelper.Info(" Asset Id: ", $"{transferSettings.Transfer.AssetId}"); + ConsoleHelper.Info(" Send To: ", $"{transferSettings.Transfer.SendTo}"); ConsoleHelper.Info(" Amount: ", $"{transferSettings.Transfer.SendAmount}"); - ConsoleHelper.Info(" Signers: ", $"[{string.Join(", ", transferSettings.Transfer.Signers.Select(s => $"\"{s}\""))})]"); - ConsoleHelper.Info(" Data: ", $"\"{transferSettings.Transfer.Comment}\""); + ConsoleHelper.Info(" Comment: ", $"{transferSettings.Transfer.Comment}"); } ConsoleHelper.Info("", "--------", "Wallet", "--------"); - ConsoleHelper.Info(" Path: ", $"\"{job.Value.Settings.Wallet.Path}\""); - ConsoleHelper.Info(" Account: ", $"{job.Value.Settings.Wallet.Account}"); + ConsoleHelper.Info(" Path: ", $"{entry.Value.Settings.Wallet.Path}"); + ConsoleHelper.Info(" Account: ", $"{entry.Value.Settings.Wallet.Account}"); + ConsoleHelper.Info(" Signers: ", $"[{string.Join(", ", entry.Value.Job.Signers.Select(s => $"\"{s.Account}\""))}]"); if (_scheduler.Entries.Count > 1) - ConsoleHelper.Info(); + ConsoleHelper.Info("--------------------------------------------"); } - if (_scheduler.Entries.Any() == true) - ConsoleHelper.Info("----------------------"); + if (_scheduler.Entries.Count == 1) + ConsoleHelper.Info("--------------------------------------------"); - ConsoleHelper.Info(" Total: ", $"{_scheduler.Entries.Count}", " job(s)."); + ConsoleHelper.Info("", "Total: ", $"{_scheduler.Entries.Count}", " job(s)."); } } diff --git a/src/Crontab/CronPlugin.Events.cs b/src/Crontab/CronPlugin.Events.cs index 37e005c..ff37522 100644 --- a/src/Crontab/CronPlugin.Events.cs +++ b/src/Crontab/CronPlugin.Events.cs @@ -8,11 +8,20 @@ namespace Neo.Plugins.Crontab; public partial class CronPlugin { - private void OnCreated(object sender, FileSystemEventArgs e) + private void OnJobFileCreated(object sender, FileSystemEventArgs e) { if (e.ChangeType != WatcherChangeTypes.Created) return; LoadJobs(e.FullPath); } + + private void OnJobFileDeleted(object sender, FileSystemEventArgs e) + { + if (e.ChangeType != WatcherChangeTypes.Deleted) + return; + + if (_scheduler.TryGetKey(e.FullPath, out var jobEntryId)) + _ = _scheduler.TryRemove(jobEntryId, out _); + } } diff --git a/src/Crontab/CronPlugin.Methods.cs b/src/Crontab/CronPlugin.Methods.cs index 59efbcd..fcceec2 100644 --- a/src/Crontab/CronPlugin.Methods.cs +++ b/src/Crontab/CronPlugin.Methods.cs @@ -8,6 +8,7 @@ using NCrontab; using Neo.ConsoleService; using Neo.Plugins.Crontab.Jobs; +using Neo.Plugins.Crontab.Settings; namespace Neo.Plugins.Crontab; @@ -24,7 +25,8 @@ private void StartFileWatcher() EnableRaisingEvents = true, }; - _fileSystemWatcher.Created += OnCreated; + _fileSystemWatcher.Created += OnJobFileCreated; + _fileSystemWatcher.Deleted += OnJobFileDeleted; } private void SearchForJobs() diff --git a/src/Crontab/CronPlugin.cs b/src/Crontab/CronPlugin.cs index 6345a53..d27cedd 100644 --- a/src/Crontab/CronPlugin.cs +++ b/src/Crontab/CronPlugin.cs @@ -5,13 +5,14 @@ // the main directory of the project for more details. using Neo.Plugins.Crontab.Jobs; +using Neo.Plugins.Crontab.Settings; namespace Neo.Plugins.Crontab; public partial class CronPlugin : Plugin { public override string Name => "Crontab"; - public override string Description => "Crontab task scheduler for executing blockchain tasks."; + public override string Description => "Task scheduler for sending transactions to the blockchain."; internal static NeoSystem NeoSystem { get; private set; } diff --git a/src/Crontab/Crontab.csproj b/src/Crontab/Crontab.csproj index d694c07..cb8ee74 100644 --- a/src/Crontab/Crontab.csproj +++ b/src/Crontab/Crontab.csproj @@ -1,7 +1,7 @@ - 1.0.0-beta + 1.0.0 net7.0 Neo.Plugins.Crontab Neo.Plugins.Crontab @@ -14,6 +14,10 @@ disable + + + + PreserveNewest diff --git a/src/Crontab/Jobs/CronBasicJob.cs b/src/Crontab/Jobs/CronBasicJob.cs index 481fd74..0b7f588 100644 --- a/src/Crontab/Jobs/CronBasicJob.cs +++ b/src/Crontab/Jobs/CronBasicJob.cs @@ -4,6 +4,9 @@ // MIT software license, see the accompanying file LICENSE in // the main directory of the project for more details. +using Neo.Network.P2P.Payloads; +using Neo.Plugins.Crontab.Settings; +using Neo.Plugins.Crontab.Utils; using Neo.Wallets; namespace Neo.Plugins.Crontab.Jobs; @@ -16,6 +19,9 @@ internal class CronBasicJob : ICronJob public CronContract Contract { get; private init; } public Wallet Wallet { get; private init; } public UInt160 Sender { get; private init; } + public Signer[] Signers { get; private init; } + public DateTime NextRunTimestamp { get; set; } + public DateTime LastRunTimestamp { get; private set; } public static CronBasicJob Create(CronJobBasicSettings settings) => new() @@ -23,12 +29,18 @@ public static CronBasicJob Create(CronJobBasicSettings settings) => Name = settings.Name, Expression = settings.Expression, Contract = new(UInt160.Parse(settings.Contract.ScriptHash), settings.Contract.Method, settings.Contract.Params), - Wallet = Wallet.Open(settings.Wallet.Path, settings.Wallet.Password, CronPlugin.NeoSystem.Settings), - Sender = UInt160.Parse(settings.Wallet.Account), + Wallet = settings.Wallet != null ? + Wallet.Open(settings.Wallet.Path, settings.Wallet.Password, CronPlugin.NeoSystem.Settings) : + null, + Sender = settings.Wallet != null ? + UInt160.Parse(settings.Wallet.Account) : + null, + Signers = settings.Wallet?.Signers?.Select(s => new Signer() { Account = UInt160.Parse(s), Scopes = WitnessScope.CalledByEntry }).ToArray() ?? Array.Empty(), }; - public void Run() + public void Run(DateTime timerNow) { + LastRunTimestamp = timerNow; WalletUtils.MakeInvokeAndSendTx(this); } } diff --git a/src/Crontab/Jobs/CronContract.cs b/src/Crontab/Jobs/CronContract.cs index fe44454..585dde5 100644 --- a/src/Crontab/Jobs/CronContract.cs +++ b/src/Crontab/Jobs/CronContract.cs @@ -4,6 +4,8 @@ // MIT software license, see the accompanying file LICENSE in // the main directory of the project for more details. +using Neo.Plugins.Crontab.Settings; + namespace Neo.Plugins.Crontab.Jobs; internal class CronContract diff --git a/src/Crontab/Jobs/CronEntry.cs b/src/Crontab/Jobs/CronEntry.cs new file mode 100644 index 0000000..4af6dcd --- /dev/null +++ b/src/Crontab/Jobs/CronEntry.cs @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Christopher R Schuchardt +// +// The neo-cron-plugin is free software distributed under the +// MIT software license, see the accompanying file LICENSE in +// the main directory of the project for more details. + +using NCrontab; +using Neo.Plugins.Crontab.Settings; + +namespace Neo.Plugins.Crontab.Jobs; + +internal class CronEntry +{ + public ICronJob Job { get; } + public ICronJobSettings Settings { get; } + public CrontabSchedule Schedule { get; } + public bool IsEnabled { get; internal set; } + + internal CronEntry( + CrontabSchedule schedule, + ICronJob job, + ICronJobSettings settings) + { + Schedule = schedule; + Job = job; + Settings = settings; + IsEnabled = true; + } +} diff --git a/src/Crontab/Jobs/CronJobType.cs b/src/Crontab/Jobs/CronJobType.cs index 0912f70..2835c56 100644 --- a/src/Crontab/Jobs/CronJobType.cs +++ b/src/Crontab/Jobs/CronJobType.cs @@ -10,6 +10,4 @@ internal enum CronJobType : byte { Basic = 0x00, Transfer = 0x01, - CreateAddress = 0x02, - CreateWallet = 0x03, } diff --git a/src/Crontab/Jobs/CronScheduler.cs b/src/Crontab/Jobs/CronScheduler.cs index 4398260..095e05d 100644 --- a/src/Crontab/Jobs/CronScheduler.cs +++ b/src/Crontab/Jobs/CronScheduler.cs @@ -4,15 +4,16 @@ // MIT software license, see the accompanying file LICENSE in // the main directory of the project for more details. -using NCrontab; +using Neo.Plugins.Crontab.Settings; using System.Collections.Concurrent; namespace Neo.Plugins.Crontab.Jobs; internal class CronScheduler : IDisposable { - public ConcurrentDictionary Entries { get; } + public IReadOnlyDictionary Entries => _entries; + private readonly ConcurrentDictionary _entries; private readonly ConcurrentDictionary> _tasks; private readonly PeriodicTimer _timer; private readonly CancellationTokenSource _cancelWaitTask; @@ -20,7 +21,7 @@ internal class CronScheduler : IDisposable public CronScheduler() { _tasks = new(); - Entries = new(); + _entries = new(); _cancelWaitTask = new(); _timer = new(TimeSpan.FromSeconds(1)); _ = Task.Run(async () => await WaitForTimer(_cancelWaitTask.Token)); @@ -30,17 +31,46 @@ public void Dispose() { _cancelWaitTask.Cancel(); _timer.Dispose(); + _cancelWaitTask.Dispose(); } public bool TryAdd(CronEntry entry, out Guid entryId) { entryId = Guid.NewGuid(); - if (Entries.TryAdd(entryId, entry)) + if (_entries.TryAdd(entryId, entry)) return true; entryId = default; return false; } + public bool TryRemove(Guid entryId, out CronEntry jobEntry) + { + if (_entries.TryRemove(entryId, out jobEntry) == false) + return false; + else + { + var tmpEntry = jobEntry; + foreach (var allJobTaskList in _tasks.Values) + allJobTaskList + .FindAll(w => ReferenceEquals(tmpEntry.Job, w)) + .ForEach(f => allJobTaskList.Remove(f)); + return true; + } + } + + public bool TryGetKey(string filename, out Guid jobEntryId) + { + var jobEntryKvp = _entries.SingleOrDefault(s => s.Value.Settings.Filename.Equals(filename, StringComparison.InvariantCultureIgnoreCase)); + jobEntryId = jobEntryKvp.Key; + if (jobEntryKvp.Key == Guid.Empty && jobEntryKvp.Value == null) + return false; + else + return true; + } + + public bool ContainsTask(ICronJob job) => + _tasks.Values.SelectMany(sm => sm).Contains(job); + internal static DateTime PrecisionMinute() { var now = DateTime.UtcNow; @@ -50,19 +80,20 @@ internal static DateTime PrecisionMinute() private async Task WaitForTimer(CancellationToken token) { DateTime lastRun = default; + while (await _timer.WaitForNextTickAsync(token) && token.IsCancellationRequested == false) { + _entries.Values.ToList().ForEach(LoadDateTimeOccurrences); + var now = PrecisionMinute(); if (lastRun == now) continue; - Entries.Values.ToList().ForEach(LoadDateTimeOccurrences); - if (_tasks.TryGetValue(now, out var jobs) == true) { using var cancelTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(CronPluginSettings.Current.Job.Timeout)); - await Task.WhenAll(jobs.Select(s => Task.Run(s.Run, cancelTokenSource.Token))).ConfigureAwait(false); + await Task.WhenAll(jobs.Select(s => Task.Run(() => s.Run(now), cancelTokenSource.Token))).ConfigureAwait(false); _tasks.TryRemove(now, out _); } lastRun = now; @@ -78,37 +109,26 @@ private void LoadDateTimeOccurrences(CronEntry entry) foreach (var occurrence in entry.Schedule.GetNextOccurrences(now, now.AddMinutes(1))) { if (_tasks.TryGetValue(occurrence, out var jobs) == false) + { _tasks[occurrence] = new() { entry.Job }; + DisableEntryAfterRunOnce(entry); + entry.Job.NextRunTimestamp = occurrence; + } else { if (jobs.SingleOrDefault(s => ReferenceEquals(s, entry.Job)) == null) + { jobs.Add(entry.Job); - } - if (entry.Settings.RunOnce == true) - { - entry.LastRunTime = occurrence; - entry.IsEnabled = false; + DisableEntryAfterRunOnce(entry); + entry.Job.NextRunTimestamp = occurrence; + } } } } -} -internal class CronEntry -{ - public ICronJob Job { get; } - public ICronJobSettings Settings { get; } - public CrontabSchedule Schedule { get; } - public bool IsEnabled { get; internal set; } - public DateTime LastRunTime { get; internal set; } - - internal CronEntry( - CrontabSchedule schedule, - ICronJob job, - ICronJobSettings settings) + private static void DisableEntryAfterRunOnce(CronEntry entry) { - Schedule = schedule; - Job = job; - Settings = settings; - IsEnabled = true; + if (entry.Settings.RunOnce == true) + entry.IsEnabled = false; } } diff --git a/src/Crontab/Jobs/CronTransferJob.cs b/src/Crontab/Jobs/CronTransferJob.cs index 38f0e15..98dca28 100644 --- a/src/Crontab/Jobs/CronTransferJob.cs +++ b/src/Crontab/Jobs/CronTransferJob.cs @@ -5,6 +5,8 @@ // the main directory of the project for more details. using Neo.Network.P2P.Payloads; +using Neo.Plugins.Crontab.Settings; +using Neo.Plugins.Crontab.Utils; using Neo.Wallets; namespace Neo.Plugins.Crontab.Jobs; @@ -17,10 +19,12 @@ internal class CronTransferJob : ICronJob public UInt160 TokenHash { get; init; } public UInt160 SendTo { get; init; } public decimal SendAmount { get; init; } - public Signer[] Signers { get; init; } public string Comment { get; init; } public Wallet Wallet { get; init; } public UInt160 Sender { get; init; } + public Signer[] Signers { get; init; } + public DateTime NextRunTimestamp { get; set; } + public DateTime LastRunTimestamp { get; private set; } public static CronTransferJob Create(CronJobTransferSettings settings) => new() @@ -31,15 +35,14 @@ public static CronTransferJob Create(CronJobTransferSettings settings) => SendTo = UInt160.Parse(settings.Transfer.SendTo), SendAmount = decimal.Parse(settings.Transfer.SendAmount), Sender = UInt160.Parse(settings.Wallet.Account), - Signers = settings.Transfer.Signers == null || settings.Transfer.Signers.Length == 0 ? - new[] { new Signer() { Account = UInt160.Parse(settings.Wallet.Account), Scopes = WitnessScope.CalledByEntry } } : - settings.Transfer.Signers.Select(s => new Signer() { Account = UInt160.Parse(s), Scopes = WitnessScope.CalledByEntry }).ToArray(), Comment = settings.Transfer.Comment, Wallet = Wallet.Open(settings.Wallet.Path, settings.Wallet.Password, CronPlugin.NeoSystem.Settings), + Signers = settings.Wallet?.Signers?.Select(s => new Signer() { Account = UInt160.Parse(s), Scopes = WitnessScope.CalledByEntry }).ToArray() ?? Array.Empty(), }; - public void Run() + public void Run(DateTime timerNow) { + LastRunTimestamp = timerNow; WalletUtils.MakeTransferAndSendTx(this); } } diff --git a/src/Crontab/Jobs/ICronJob.cs b/src/Crontab/Jobs/ICronJob.cs index d5d3cba..9067f2e 100644 --- a/src/Crontab/Jobs/ICronJob.cs +++ b/src/Crontab/Jobs/ICronJob.cs @@ -4,6 +4,7 @@ // MIT software license, see the accompanying file LICENSE in // the main directory of the project for more details. +using Neo.Network.P2P.Payloads; using Neo.Wallets; namespace Neo.Plugins.Crontab.Jobs; @@ -15,5 +16,8 @@ internal interface ICronJob string Expression { get; } Wallet Wallet { get; } UInt160 Sender { get; } - void Run(); + Signer[] Signers { get; } + DateTime LastRunTimestamp { get; } + DateTime NextRunTimestamp { get; set; } + void Run(DateTime timerNow); } diff --git a/src/Crontab/CronJobBasicSettings.cs b/src/Crontab/Settings/CronJobBasicSettings.cs similarity index 96% rename from src/Crontab/CronJobBasicSettings.cs rename to src/Crontab/Settings/CronJobBasicSettings.cs index befd8ff..440fe05 100644 --- a/src/Crontab/CronJobBasicSettings.cs +++ b/src/Crontab/Settings/CronJobBasicSettings.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Configuration; -namespace Neo.Plugins.Crontab; +namespace Neo.Plugins.Crontab.Settings; public class CronJobBasicSettings : ICronJobSettings { diff --git a/src/Crontab/CronJobContractParameterSettings.cs b/src/Crontab/Settings/CronJobContractParameterSettings.cs similarity index 72% rename from src/Crontab/CronJobContractParameterSettings.cs rename to src/Crontab/Settings/CronJobContractParameterSettings.cs index 710cc83..fb3e2ff 100644 --- a/src/Crontab/CronJobContractParameterSettings.cs +++ b/src/Crontab/Settings/CronJobContractParameterSettings.cs @@ -4,10 +4,12 @@ // MIT software license, see the accompanying file LICENSE in // the main directory of the project for more details. -namespace Neo.Plugins.Crontab; +using Neo.SmartContract; + +namespace Neo.Plugins.Crontab.Settings; public class CronJobContractParameterSettings { - public string Type { get; set; } + public ContractParameterType Type { get; set; } public string Value { get; set; } } diff --git a/src/Crontab/CronJobContractSettings.cs b/src/Crontab/Settings/CronJobContractSettings.cs similarity index 92% rename from src/Crontab/CronJobContractSettings.cs rename to src/Crontab/Settings/CronJobContractSettings.cs index f47b0f1..7643010 100644 --- a/src/Crontab/CronJobContractSettings.cs +++ b/src/Crontab/Settings/CronJobContractSettings.cs @@ -4,7 +4,7 @@ // MIT software license, see the accompanying file LICENSE in // the main directory of the project for more details. -namespace Neo.Plugins.Crontab; +namespace Neo.Plugins.Crontab.Settings; public class CronJobContractSettings { diff --git a/src/Crontab/CronJobTransferSettings.cs b/src/Crontab/Settings/CronJobTransferSettings.cs similarity index 96% rename from src/Crontab/CronJobTransferSettings.cs rename to src/Crontab/Settings/CronJobTransferSettings.cs index e613aba..87c9a22 100644 --- a/src/Crontab/CronJobTransferSettings.cs +++ b/src/Crontab/Settings/CronJobTransferSettings.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Configuration; -namespace Neo.Plugins.Crontab; +namespace Neo.Plugins.Crontab.Settings; internal class CronJobTransferSettings : ICronJobSettings { diff --git a/src/Crontab/CronJobWalletSettings.cs b/src/Crontab/Settings/CronJobWalletSettings.cs similarity index 82% rename from src/Crontab/CronJobWalletSettings.cs rename to src/Crontab/Settings/CronJobWalletSettings.cs index 84574ea..7d32c1a 100644 --- a/src/Crontab/CronJobWalletSettings.cs +++ b/src/Crontab/Settings/CronJobWalletSettings.cs @@ -4,11 +4,12 @@ // MIT software license, see the accompanying file LICENSE in // the main directory of the project for more details. -namespace Neo.Plugins.Crontab; +namespace Neo.Plugins.Crontab.Settings; public class CronJobWalletSettings { public string Path { get; set; } public string Password { get; set; } public string Account { get; set; } + public string[] Signers { get; set; } } diff --git a/src/Crontab/CronPluginJobSettings.cs b/src/Crontab/Settings/CronPluginJobSettings.cs similarity index 89% rename from src/Crontab/CronPluginJobSettings.cs rename to src/Crontab/Settings/CronPluginJobSettings.cs index c7070de..beee9a4 100644 --- a/src/Crontab/CronPluginJobSettings.cs +++ b/src/Crontab/Settings/CronPluginJobSettings.cs @@ -4,7 +4,7 @@ // MIT software license, see the accompanying file LICENSE in // the main directory of the project for more details. -namespace Neo.Plugins.Crontab; +namespace Neo.Plugins.Crontab.Settings; public class CronPluginJobSettings { diff --git a/src/Crontab/CronPluginSettings.cs b/src/Crontab/Settings/CronPluginSettings.cs similarity index 96% rename from src/Crontab/CronPluginSettings.cs rename to src/Crontab/Settings/CronPluginSettings.cs index 131db23..9133ca1 100644 --- a/src/Crontab/CronPluginSettings.cs +++ b/src/Crontab/Settings/CronPluginSettings.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Configuration; -namespace Neo.Plugins.Crontab; +namespace Neo.Plugins.Crontab.Settings; public class CronPluginSettings { diff --git a/src/Crontab/CronTransferSettings.cs b/src/Crontab/Settings/CronTransferSettings.cs similarity index 85% rename from src/Crontab/CronTransferSettings.cs rename to src/Crontab/Settings/CronTransferSettings.cs index ece60b7..0214474 100644 --- a/src/Crontab/CronTransferSettings.cs +++ b/src/Crontab/Settings/CronTransferSettings.cs @@ -4,13 +4,12 @@ // MIT software license, see the accompanying file LICENSE in // the main directory of the project for more details. -namespace Neo.Plugins.Crontab; +namespace Neo.Plugins.Crontab.Settings; public class CronTransferSettings { public string AssetId { get; set; } public string SendTo { get; set; } public string SendAmount { get; set; } - public string[] Signers { get; set; } public string Comment { get; set; } } diff --git a/src/Crontab/ICronJobSettings.cs b/src/Crontab/Settings/ICronJobSettings.cs similarity index 91% rename from src/Crontab/ICronJobSettings.cs rename to src/Crontab/Settings/ICronJobSettings.cs index 2c1c1ac..fa4d486 100644 --- a/src/Crontab/ICronJobSettings.cs +++ b/src/Crontab/Settings/ICronJobSettings.cs @@ -4,7 +4,7 @@ // MIT software license, see the accompanying file LICENSE in // the main directory of the project for more details. -namespace Neo.Plugins.Crontab; +namespace Neo.Plugins.Crontab.Settings; internal interface ICronJobSettings { diff --git a/src/Crontab/Utils/ContractUtils.cs b/src/Crontab/Utils/ContractUtils.cs new file mode 100644 index 0000000..0bbc7aa --- /dev/null +++ b/src/Crontab/Utils/ContractUtils.cs @@ -0,0 +1,95 @@ +// Copyright (C) 2023 Christopher R Schuchardt +// +// The neo-cron-plugin is free software distributed under the +// MIT software license, see the accompanying file LICENSE in +// the main directory of the project for more details. + +using Neo.Cryptography.ECC; +using Neo.Plugins.Crontab.Jobs; +using Neo.Plugins.Crontab.Settings; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using System.Numerics; + +namespace Neo.Plugins.Crontab.Utils; + +internal static class ContractUtils +{ + public static bool MethodExists(UInt160 scriptHash, string methodName, int parameterCount = -1) + { + var contract = NativeContract.ContractManagement.GetContract(CronPlugin.NeoSystem.StoreView, scriptHash); + if (contract == null) + return false; + else + return contract.Manifest.Abi.GetMethod(methodName, parameterCount) != null; + } + + public static bool BuildInvokeMethod(CronContract cronContract, out byte[] script) + { + script = null; + if (MethodExists(cronContract.ScriptHash, cronContract.Method, cronContract.Params.Length) == false) + return false; + else + { + var args = cronContract.Params.Select(ConvertParameters).ToArray(); + using var sb = new ScriptBuilder(); + if (args.Length > 0) + sb.EmitDynamicCall(cronContract.ScriptHash, cronContract.Method, args); + else + sb.EmitDynamicCall(cronContract.ScriptHash, cronContract.Method); + + script = sb.ToArray(); + + return true; + } + } + + public static ContractParameter ConvertParameters(CronJobContractParameterSettings parameterSettings) + { + return parameterSettings.Type switch + { + ContractParameterType.ByteArray => new() + { + Type = ContractParameterType.ByteArray, + Value = Convert.FromBase64String(parameterSettings.Value), + }, + ContractParameterType.Signature => new() + { + Type = ContractParameterType.Signature, + Value = Convert.FromBase64String(parameterSettings.Value), + }, + ContractParameterType.Boolean => new() + { + Type = ContractParameterType.Boolean, + Value = bool.Parse(parameterSettings.Value), + }, + ContractParameterType.Integer => new() + { + Type = ContractParameterType.Integer, + Value = BigInteger.Parse(parameterSettings.Value), + }, + ContractParameterType.String => new() + { + Type = ContractParameterType.String, + Value = parameterSettings.Value, + }, + ContractParameterType.Hash160 => new() + { + Type = ContractParameterType.Hash160, + Value = UInt160.Parse(parameterSettings.Value), + }, + ContractParameterType.Hash256 => new() + { + Type = ContractParameterType.Hash256, + Value = UInt256.Parse(parameterSettings.Value), + }, + ContractParameterType.PublicKey => new() + { + Type = ContractParameterType.PublicKey, + Value = ECPoint.Parse(parameterSettings.Value, ECCurve.Secp256r1), + }, + _ => throw new NotSupportedException($"{parameterSettings.Type} is not supported.") + }; + } +} diff --git a/src/Crontab/Utils/WalletUtils.cs b/src/Crontab/Utils/WalletUtils.cs new file mode 100644 index 0000000..a7001d4 --- /dev/null +++ b/src/Crontab/Utils/WalletUtils.cs @@ -0,0 +1,79 @@ +// Copyright (C) 2023 Christopher R Schuchardt +// +// The neo-cron-plugin is free software distributed under the +// MIT software license, see the accompanying file LICENSE in +// the main directory of the project for more details. + +using Akka.Actor; +using Neo.ConsoleService; +using Neo.Network.P2P.Payloads; +using Neo.Plugins.Crontab.Jobs; +using Neo.Plugins.Crontab.Settings; +using Neo.SmartContract; +using Neo.Wallets; + +namespace Neo.Plugins.Crontab.Utils; + +internal static class WalletUtils +{ + public static void MakeTransferAndSendTx(CronTransferJob transferJob) + { + var asset = new AssetDescriptor(CronPlugin.NeoSystem.StoreView, CronPlugin.NeoSystem.Settings, transferJob.TokenHash); + var amount = new BigDecimal(transferJob.SendAmount, asset.Decimals); + + try + { + var tx = transferJob.Wallet.MakeTransaction(CronPlugin.NeoSystem.StoreView, new[] + { + new TransferOutput() + { + AssetId = transferJob.TokenHash, + Value = amount, + ScriptHash = transferJob.SendTo, + Data = transferJob.Comment, + } + }, transferJob.Sender, transferJob.Signers); + SignAndSendTx(transferJob.Wallet, tx); + } + catch (Exception ex) + { + ConsoleHelper.Error($"Cron:Job[\"{transferJob.Name}\"]::\"{ex.Message}\""); + } + + } + + public static void MakeInvokeAndSendTx(CronBasicJob basicJob) + { + if (basicJob != null || basicJob.Wallet != null && basicJob.Sender != null) + try + { + if (ContractUtils.BuildInvokeMethod(basicJob.Contract, out var script) == false) + ConsoleHelper.Error($"Cron:Job[\"{basicJob.Name}\"]::\"Can not find method {basicJob.Contract.Method} with parameter count {basicJob.Contract.Params.Length}.\""); + else + { + var tx = basicJob.Wallet.MakeTransaction( + CronPlugin.NeoSystem.StoreView, + script, + basicJob.Sender, + basicJob.Signers, + maxGas: CronPluginSettings.Current.MaxGasInvoke); + + SignAndSendTx(basicJob.Wallet, tx); + } + } + catch (Exception ex) + { + ConsoleHelper.Error($"Cron:Job[\"{basicJob.Name}\"]::\"{ex.Message}\""); + } + } + + public static void SignAndSendTx(Wallet wallet, Transaction tx) + { + var context = new ContractParametersContext(CronPlugin.NeoSystem.StoreView, tx, CronPlugin.NeoSystem.Settings.Network); + if (wallet.Sign(context) && context.Completed) + { + tx.Witnesses = context.GetWitnesses(); + CronPlugin.NeoSystem.Blockchain.Tell(tx); + } + } +} diff --git a/src/Crontab/WalletUtils.cs b/src/Crontab/WalletUtils.cs deleted file mode 100644 index 716dd90..0000000 --- a/src/Crontab/WalletUtils.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (C) 2023 Christopher R Schuchardt -// -// The neo-cron-plugin is free software distributed under the -// MIT software license, see the accompanying file LICENSE in -// the main directory of the project for more details. - -using Akka.Actor; -using Neo.ConsoleService; -using Neo.Cryptography.ECC; -using Neo.Network.P2P.Payloads; -using Neo.Plugins.Crontab.Jobs; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.Wallets; -using System.Numerics; - -namespace Neo.Plugins.Crontab; - -internal static class WalletUtils -{ - public static void MakeTransferAndSendTx(CronTransferJob transferStep) - { - var asset = new AssetDescriptor(CronPlugin.NeoSystem.StoreView, CronPlugin.NeoSystem.Settings, transferStep.TokenHash); - var amount = new BigDecimal(transferStep.SendAmount, asset.Decimals); - - try - { - var tx = transferStep.Wallet.MakeTransaction(CronPlugin.NeoSystem.StoreView, new[] - { - new TransferOutput() - { - AssetId = transferStep.TokenHash, - Value = amount, - ScriptHash = transferStep.SendTo, - Data = transferStep.Comment, - } - }, transferStep.Sender, transferStep.Signers); - SignAndSendTx(transferStep.Wallet, tx); - } - catch (Exception ex) - { - ConsoleHelper.Error($"Cron:Job[\"{transferStep.Name}\"]::\"{ex.Message}\""); - } - - } - - public static void MakeInvokeAndSendTx(CronBasicJob basicStep) - { - if (basicStep != null || (basicStep.Wallet != null && basicStep.Sender != null)) - { - try - { - var tx = new Transaction() - { - Signers = new[] { new Signer() { Account = basicStep.Sender, Scopes = WitnessScope.CalledByEntry } }, - Attributes = Array.Empty(), - Witnesses = Array.Empty(), - }; - if (OnInvokeMethod(basicStep.Contract, tx) == false) - ConsoleHelper.Error($"Cron:Job[\"{basicStep.Name}\"]::\"Virtual machine invoke method failed.\""); - else - { - tx = basicStep.Wallet.MakeTransaction(CronPlugin.NeoSystem.StoreView, tx.Script, basicStep.Sender, tx.Signers, maxGas: CronPluginSettings.Current.MaxGasInvoke); - SignAndSendTx(basicStep.Wallet, tx); - } - } - catch (Exception ex) - { - ConsoleHelper.Error($"Cron:Job[\"{basicStep.Name}\"]::\"{ex.Message}\""); - } - } - } - - public static bool OnInvokeMethod(CronContract cronContract, Transaction tx) - { - var contract = NativeContract.ContractManagement.GetContract(CronPlugin.NeoSystem.StoreView, cronContract.ScriptHash); - if (contract == null) - return false; - else - { - if (contract.Manifest.Abi.GetMethod(cronContract.Method, cronContract.Params.Length) == null) - return false; - else - { - var args = cronContract.Params.Select(ConvertToContractParameter).ToArray(); - using var sb = new ScriptBuilder(); - if (args.Length > 0) - sb.EmitDynamicCall(cronContract.ScriptHash, cronContract.Method, args); - else - sb.EmitDynamicCall(cronContract.ScriptHash, cronContract.Method); - - tx.Script = sb.ToArray(); - - using var engine = ApplicationEngine.Run( - tx.Script, CronPlugin.NeoSystem.StoreView, - tx, settings: CronPlugin.NeoSystem.Settings, - gas: CronPluginSettings.Current.MaxGasInvoke); - - return engine.State != VMState.FAULT; - } - } - } - - public static void SignAndSendTx(Wallet wallet, Transaction tx) - { - var context = new ContractParametersContext(CronPlugin.NeoSystem.StoreView, tx, CronPlugin.NeoSystem.Settings.Network); - if (wallet.Sign(context) && context.Completed) - { - tx.Witnesses = context.GetWitnesses(); - CronPlugin.NeoSystem.Blockchain.Tell(tx); - } - } - - public static ContractParameter ConvertToContractParameter(CronJobContractParameterSettings parameterSettings) - { - return parameterSettings.Type.ToLowerInvariant() switch - { - "bytearray" => new() - { - Type = ContractParameterType.ByteArray, - Value = Convert.FromBase64String(parameterSettings.Value), - }, - "signature" => new() - { - Type = ContractParameterType.Signature, - Value = Convert.FromBase64String(parameterSettings.Value), - }, - "boolean" => new() - { - Type = ContractParameterType.Boolean, - Value = bool.Parse(parameterSettings.Value), - }, - "integer" => new() - { - Type = ContractParameterType.Integer, - Value = BigInteger.Parse(parameterSettings.Value), - }, - "string" => new() - { - Type = ContractParameterType.String, - Value = parameterSettings.Value, - }, - "hash160" => new() - { - Type = ContractParameterType.Hash160, - Value = UInt160.Parse(parameterSettings.Value), - }, - "hash256" => new() - { - Type = ContractParameterType.Hash256, - Value = UInt256.Parse(parameterSettings.Value), - }, - "publickey" => new() - { - Type = ContractParameterType.PublicKey, - Value = ECPoint.Parse(parameterSettings.Value, ECCurve.Secp256r1), - }, - _ => throw new NotSupportedException($"{parameterSettings.Type} is not supported.") - }; - } -} diff --git a/tests/Crontab.Test/Crontab.Test.csproj b/tests/Crontab.Test/Crontab.Test.csproj new file mode 100644 index 0000000..4ecaf3e --- /dev/null +++ b/tests/Crontab.Test/Crontab.Test.csproj @@ -0,0 +1,29 @@ + + + + net7.0 + enable + disable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tests/Crontab.Test/GlobalUsings.cs b/tests/Crontab.Test/GlobalUsings.cs new file mode 100644 index 0000000..465f360 --- /dev/null +++ b/tests/Crontab.Test/GlobalUsings.cs @@ -0,0 +1,7 @@ +// Copyright (C) 2023 Christopher R Schuchardt +// +// The neo-cron-plugin is free software distributed under the +// MIT software license, see the accompanying file LICENSE in +// the main directory of the project for more details. + +global using Xunit; diff --git a/tests/Crontab.Test/Helpers/UT_JobHelper.cs b/tests/Crontab.Test/Helpers/UT_JobHelper.cs new file mode 100644 index 0000000..8b11114 --- /dev/null +++ b/tests/Crontab.Test/Helpers/UT_JobHelper.cs @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Christopher R Schuchardt +// +// The neo-cron-plugin is free software distributed under the +// MIT software license, see the accompanying file LICENSE in +// the main directory of the project for more details. + +using NCrontab; +using Neo; +using Neo.Plugins.Crontab.Jobs; +using Neo.Plugins.Crontab.Settings; + +namespace Crontab.Test.Helpers; + +internal static class UT_JobHelper +{ + public static CronEntry CreateDummyJobEntry() + { + var settings = CreateDummySettings(); + var taskSchedule = CrontabSchedule.TryParse(settings.Expression); + return new(taskSchedule, CronBasicJob.Create(settings), settings); + } + + public static CronJobBasicSettings CreateDummySettings() => + new() + { + Name = "TestDummyJob", + Expression = "* * * * *", + Contract = new() + { + ScriptHash = UInt160.Zero.ToString(), + Method = "TestMethod", + Params = Array.Empty(), + }, + }; +} diff --git a/tests/Crontab.Test/UT_CronScheduler.cs b/tests/Crontab.Test/UT_CronScheduler.cs new file mode 100644 index 0000000..9a31a01 --- /dev/null +++ b/tests/Crontab.Test/UT_CronScheduler.cs @@ -0,0 +1,64 @@ +// Copyright (C) 2023 Christopher R Schuchardt +// +// The neo-cron-plugin is free software distributed under the +// MIT software license, see the accompanying file LICENSE in +// the main directory of the project for more details. + +using Crontab.Test.Helpers; +using Neo.Plugins.Crontab.Jobs; + +namespace Crontab.Test; + +public class UT_CronScheduler +{ + private readonly CronScheduler _cronScheduler; + + public UT_CronScheduler() + { + _cronScheduler = new(); + } + + [Fact] + public void Test_Job_Entries() + { + Test_TryAdd(); + + Thread.Sleep(3000); + + Test_TryRemove(); + } + + private void Test_TryAdd() + { + var dummyJobEntry = UT_JobHelper.CreateDummyJobEntry(); + + var result = _cronScheduler.TryAdd(dummyJobEntry, out var jobEntryId); + + Assert.True(result); + Assert.NotEqual(Guid.Empty, jobEntryId); + + var jobItem = new KeyValuePair(jobEntryId, dummyJobEntry); + Assert.Contains(jobItem, _cronScheduler.Entries); + } + + private void Test_TryRemove() + { + var jobEntryKvp = _cronScheduler.Entries.FirstOrDefault(); + + Assert.NotEqual(Guid.Empty, jobEntryKvp.Key); + Assert.NotNull(jobEntryKvp.Value); + + var doesExists = _cronScheduler.ContainsTask(jobEntryKvp.Value.Job); + + Assert.True(doesExists); + + var isRemoved = _cronScheduler.TryRemove(jobEntryKvp.Key, out var jobEntry); + + Assert.True(isRemoved); + Assert.NotNull(jobEntry); + + doesExists = _cronScheduler.ContainsTask(jobEntry.Job); + + Assert.False(doesExists); + } +}