Skip to content
This repository was archived by the owner on Dec 7, 2023. It is now read-only.

node: Cli: Adding Script System for neo-cli #901

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
125 changes: 125 additions & 0 deletions neo-cli/CLI/MainService.Script.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (C) 2016-2023 The Neo Project.
//
// The neo-cli is free software distributed under the MIT software
// license, see the accompanying file LICENSE in the main directory of
// the project 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 Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Neo.ConsoleService;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Neo.SmartContract.Native;

namespace Neo.CLI
{
partial class MainService
{
/// <summary>
/// Create a new script file from the template
/// </summary>
/// <param name="path">Path of the new script file</param>
/// <example>new script script.cs</example>
[ConsoleCommand("script new", Category = "Command Script")]
private void OnCreatingNewScript(String path = null)
{
if (string.IsNullOrEmpty(path))
{
path = "script.cs";
}

if (!File.Exists(path))
{
ConsoleHelper.Info($"File {path} does not exist. Attempting to generate from template...");

if (!File.Exists("template.cs"))
{
ConsoleHelper.Error("Template file 'template.cs' does not exist. Unable to generate script.");
return;
}

File.Copy("template.cs", path);
ConsoleHelper.Info($"File {path} generated from template.");
}
else
{
ConsoleHelper.Info($"File {path} already exists. No action taken.");
}
}

/// <summary>
/// Execute the script file
/// </summary>
/// <param name="path">path of the script file, script.cs if not given</param>
/// <param name="watch"></param>
/// <example>run script script.cs true</example>
[ConsoleCommand("script run", Category = "Command Script")]
private async Task OnExecutingScript(String path = null, bool watch = false)
{
if (string.IsNullOrEmpty(path))
{
path = "script.cs";
}

if (!File.Exists(path))
{
ConsoleHelper.Error($"File {path} does not exist. Please create it using the 'new script' command.");
return;
}

ConsoleHelper.Info("Executing script...");
ConsoleHelper.Info("Path: " + path);

// Execute the script once initially
await ExecuteScript(path);

if (watch)
{
// Use FileSystemWatcher to watch the file for changes
using var watcher = new FileSystemWatcher();
watcher.Path = Path.GetDirectoryName(path)!;
watcher.Filter = Path.GetFileName(path);
watcher.NotifyFilter = NotifyFilters.LastWrite;

watcher.Changed += async (source, e) =>
{
ConsoleHelper.Info($"File {e.FullPath} changed. Re-executing script...");
await ExecuteScript(path);
};

watcher.EnableRaisingEvents = true;

ConsoleHelper.Info($"Watching for changes to {path}...");
ConsoleHelper.Info("Press any key to stop watching...");
Console.ReadKey();
}
}

private async Task ExecuteScript(string path)
{
await Task.Run(async () =>
{
try
{
await CSharpScript.EvaluateAsync(
await File.ReadAllTextAsync(path),
ScriptOptions.Default.WithImports("System", "System.Threading", "System.Linq").WithReferences(typeof(NeoSystem).Assembly, typeof(ScriptHelper.ScriptHelper).Assembly),
globals: this
);
ConsoleHelper.Info("Result: " + NativeContract.Contracts.ToList().Count);
}
catch (Exception e)
{
Console.Error.WriteLine($"{e}");
}
});
}

}
}
73 changes: 73 additions & 0 deletions neo-cli/ScriptHelper/ScriptHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (C) 2016-2023 The Neo Project.
//
// The neo-cli is free software distributed under the MIT software
// license, see the accompanying file LICENSE in the main directory of
// the project 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 System.Collections.Generic;
using System.Threading.Tasks;
using Akka.Actor;
using Neo.Ledger;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;

namespace Neo.ScriptHelper
{
public class ScriptHelper
{
private readonly NeoSystem _neoSystem;

public ScriptHelper(NeoSystem neoSystem)
{
_neoSystem = neoSystem;
Blockchain.Committing += OnCommitting;
Blockchain.Committed += OnCommitted;
}

private Dictionary<UInt256, TaskCompletionSource<Blockchain.ApplicationExecuted>> transactionCompletionSources
= new();

private TaskCompletionSource<Block> _blockStream = new();

public async Task<Blockchain.ApplicationExecuted> SendRawTransactionAsync(Transaction tx)
{
var tcs = new TaskCompletionSource<Blockchain.ApplicationExecuted>();

// Store the TaskCompletionSource for this transaction
transactionCompletionSources[tx.Hash] = tcs;

// Broadcast the transaction
_neoSystem.Blockchain.Tell(tx, ActorRefs.NoSender);

return await tcs.Task;
}

private void OnCommitting(NeoSystem system, Block block, DataCache snapshot, IReadOnlyList<Blockchain.ApplicationExecuted> applicationExecutedList)
{
foreach (var tx in applicationExecutedList)
{
if (!transactionCompletionSources.TryGetValue(tx.Transaction.Hash, out var tcs)) continue;
tcs.SetResult(tx);

transactionCompletionSources.Remove(tx.Transaction.Hash);
}
}

private void OnCommitted(NeoSystem system, Block block)
{
_blockStream.SetResult(block);
}

public async Task<Block> OnNewBlock()
{
var tcs = new TaskCompletionSource<Block>();
// Store the TaskCompletionSource for this transaction
_blockStream = tcs;
return await tcs.Task;
}
}
}
5 changes: 5 additions & 0 deletions neo-cli/neo-cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,16 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.8.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.Scripting" Version="4.8.0-1.final" />
<PackageReference Include="Neo" Version="3.6.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Neo.ConsoleService\Neo.ConsoleService.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Remove="template.cs" />
</ItemGroup>
</Project>
76 changes: 76 additions & 0 deletions neo-cli/template.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (C) 2016-2023 The Neo Project.
//
// The neo-cli is free software distributed under the MIT software
// license, see the accompanying file LICENSE in the main directory of
// the project 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 System;
using System.Linq;
using Neo;
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.Network.P2P.Payloads;
using Neo.Wallets;
using Neo.VM;
using Neo.ScriptHelper;

#if DEBUG
NeoSystem NeoSystem = new(null);
#endif

// Create an instance of ScriptHelper
ScriptHelper helper = new ScriptHelper(NeoSystem);

// loading key pair
var keyPair = new KeyPair(Wallet.GetPrivateKeyFromWIF("xxxxxxxxxxxxxxxxxxxxxxxx-your private key"));
var scriptHash = Contract.CreateSignatureContract(keyPair.PublicKey).ScriptHash;
Console.WriteLine($"ME: {scriptHash} {scriptHash.ToAddress(NeoSystem.Settings.AddressVersion)}");

// get snapshot
var snapshot = NeoSystem.GetSnapshot();
// get latest block index
var blockIndex = NativeContract.Ledger.CurrentIndex(snapshot);
// get latest block
var block = NativeContract.Ledger.GetBlock(snapshot, blockIndex);

// constructing transaction
var msg = @"XXX";
var sb = new ScriptBuilder();
sb.EmitPush(msg);
var transaction = new Transaction
{
Version = 0,
Nonce = (uint)new Random().NextInt64(),
SystemFee = 1000000,
NetworkFee = 0x3e500,
Attributes = new TransactionAttribute[],
Script = sb.ToArray(),
Signers = new Signer[] { new Signer { Account = scriptHash, Scopes = WitnessScope.CalledByEntry } },
ValidUntilBlock = blockIndex + 1,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plus NeoSystem.Settings.MaxValidUntilBlockIncrement?

};

// sign the transaction
var signature = transaction.Sign(keyPair, NeoSystem.Settings.Network);
var invocationScript = new byte[] { ((byte)OpCode.PUSHDATA1), 64 }.Concat(signature).ToArray();
var verificationScript = Contract.CreateSignatureContract(keyPair.PublicKey).Script;
transaction.Witnesses = new Witness[] { new Witness { InvocationScript = invocationScript, VerificationScript = verificationScript } };
Console.WriteLine($"HASH: {transaction.Hash}");
Console.WriteLine($"{transaction.Verify(NeoSystem.Settings, snapshot, new(), new Transaction[] { })}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe should be Debug.Assert?



var txRes = await helper.SendRawTransactionAsync(transaction);



// // get validators
// var validators = NativeContract.NEO.GetNextBlockValidators(snapshot, NeoSystem.Settings.ValidatorsCount);
// // get next primary
// UInt160 primary = Contract.CreateSignatureRedeemScript(validators[(block.PrimaryIndex + 1) % NeoSystem.Settings.ValidatorsCount]).ToScriptHash();
// Console.WriteLine($"NEXT SPEAKER: {primary.ToAddress(NeoSystem.Settings.AddressVersion)}");
//
// Console.WriteLine($"{NativeContract.NEO.BalanceOf(NeoSystem.GetSnapshot(), UInt160.Zero)}");