diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e5fb08e6..93adf943 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,8 +20,13 @@ jobs: run: dotnet restore - name: Pack run: dotnet pack --output ./out --configuration Release --no-restore --verbosity normal + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: packages + path: ./out - name: Create Release - uses: marvinpinto/action-automatic-releases@v1.1.0 + uses: marvinpinto/action-automatic-releases@v1.1.1 with: repo_token: "${{ secrets.GITHUB_TOKEN }}" prerelease: ${{ contains(env.NUGET_PACKAGE_VERSION, '-preview') }} diff --git a/src/neoxp/Commands/BatchCommand.BatchFileCommands.cs b/src/neoxp/Commands/BatchCommand.BatchFileCommands.cs index 8ffad708..3ab5e351 100644 --- a/src/neoxp/Commands/BatchCommand.BatchFileCommands.cs +++ b/src/neoxp/Commands/BatchCommand.BatchFileCommands.cs @@ -1,5 +1,7 @@ +using System; using System.ComponentModel.DataAnnotations; using McMaster.Extensions.CommandLineUtils; +using Neo.Network.P2P.Payloads; namespace NeoExpress.Commands { @@ -60,6 +62,9 @@ internal class Deploy [Required] internal string Account { get; init; } = string.Empty; + [Option(Description = "Witness Scope to use for transaction signer (Default: CalledByEntry)")] + [AllowedValues(StringComparison.OrdinalIgnoreCase, "None", "CalledByEntry", "Global")] + internal WitnessScope WitnessScope { get; init; } = WitnessScope.CalledByEntry; } [Command("invoke")] @@ -72,6 +77,10 @@ internal class Invoke [Argument(1, Description = "Account to pay contract invocation GAS fee")] [Required] internal string Account { get; init; } = string.Empty; + + [Option(Description = "Witness Scope to use for transaction signer (Default: CalledByEntry)")] + [AllowedValues(StringComparison.OrdinalIgnoreCase, "None", "CalledByEntry", "Global")] + internal WitnessScope WitnessScope { get; init; } = WitnessScope.CalledByEntry; } } diff --git a/src/neoxp/Commands/BatchCommand.cs b/src/neoxp/Commands/BatchCommand.cs index 135637da..21b04a0c 100644 --- a/src/neoxp/Commands/BatchCommand.cs +++ b/src/neoxp/Commands/BatchCommand.cs @@ -106,6 +106,7 @@ await ContractCommand.Deploy.ExecuteAsync( fileSystem, cmd.Model.Contract, cmd.Model.Account, + cmd.Model.WitnessScope, writer).ConfigureAwait(false); break; } @@ -116,6 +117,7 @@ await ContractCommand.Invoke.ExecuteTxAsync( expressNode, cmd.Model.InvocationFile, cmd.Model.Account, + cmd.Model.WitnessScope, fileSystem, writer).ConfigureAwait(false); break; diff --git a/src/neoxp/Commands/ContractCommand.Deploy.cs b/src/neoxp/Commands/ContractCommand.Deploy.cs index d153ca45..1a0cd62b 100644 --- a/src/neoxp/Commands/ContractCommand.Deploy.cs +++ b/src/neoxp/Commands/ContractCommand.Deploy.cs @@ -3,6 +3,7 @@ using System.IO.Abstractions; using System.Threading.Tasks; using McMaster.Extensions.CommandLineUtils; +using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Manifest; @@ -30,6 +31,10 @@ public Deploy(IExpressChainManagerFactory chainManagerFactory, IFileSystem fileS [Required] internal string Account { get; init; } = string.Empty; + [Option(Description = "Witness Scope to use for transaction signer (Default: CalledByEntry)")] + [AllowedValues(StringComparison.OrdinalIgnoreCase, "None", "CalledByEntry", "Global")] + internal WitnessScope WitnessScope { get; init; } = WitnessScope.CalledByEntry; + [Option(Description = "Path to neo-express data file")] internal string Input { get; init; } = string.Empty; @@ -39,7 +44,7 @@ public Deploy(IExpressChainManagerFactory chainManagerFactory, IFileSystem fileS [Option(Description = "Output as JSON")] internal bool Json { get; init; } = false; - internal static async Task ExecuteAsync(IExpressChainManager chainManager, IExpressNode expressNode, IFileSystem fileSystem, string contract, string accountName, System.IO.TextWriter writer, bool json = false) + internal static async Task ExecuteAsync(IExpressChainManager chainManager, IExpressNode expressNode, IFileSystem fileSystem, string contract, string accountName, WitnessScope witnessScope, System.IO.TextWriter writer, bool json = false) { if (!chainManager.Chain.TryGetAccount(accountName, out var wallet, out var account, chainManager.ProtocolSettings)) { @@ -47,7 +52,7 @@ internal static async Task ExecuteAsync(IExpressChainManager chainManager, IExpr } var (nefFile, manifest) = await fileSystem.LoadContractAsync(contract).ConfigureAwait(false); - var txHash = await expressNode.DeployAsync(nefFile, manifest, wallet, account.ScriptHash).ConfigureAwait(false); + var txHash = await expressNode.DeployAsync(nefFile, manifest, wallet, account.ScriptHash, witnessScope).ConfigureAwait(false); await writer.WriteTxHashAsync(txHash, "Deployment", json).ConfigureAwait(false); } @@ -57,7 +62,7 @@ internal async Task OnExecuteAsync(IConsole console) { var (chainManager, _) = chainManagerFactory.LoadChain(Input); using var expressNode = chainManager.GetExpressNode(Trace); - await ExecuteAsync(chainManager, expressNode, fileSystem, Contract, Account, console.Out, Json).ConfigureAwait(false); + await ExecuteAsync(chainManager, expressNode, fileSystem, Contract, Account, WitnessScope, console.Out, Json).ConfigureAwait(false); return 0; } catch (Exception ex) diff --git a/src/neoxp/Commands/ContractCommand.Invoke.cs b/src/neoxp/Commands/ContractCommand.Invoke.cs index cd3b2f05..d9fea200 100644 --- a/src/neoxp/Commands/ContractCommand.Invoke.cs +++ b/src/neoxp/Commands/ContractCommand.Invoke.cs @@ -3,6 +3,7 @@ using System.IO.Abstractions; using System.Threading.Tasks; using McMaster.Extensions.CommandLineUtils; +using Neo.Network.P2P.Payloads; namespace NeoExpress.Commands { @@ -27,12 +28,13 @@ public Invoke(IExpressChainManagerFactory chainManagerFactory, IFileSystem fileS [Argument(1, Description = "Account to pay contract invocation GAS fee")] internal string Account { get; init; } = string.Empty; + [Option(Description = "Witness Scope to use for transaction signer (Default: CalledByEntry)")] + [AllowedValues(StringComparison.OrdinalIgnoreCase, "None", "CalledByEntry", "Global")] + internal WitnessScope WitnessScope { get; init; } = WitnessScope.CalledByEntry; + [Option(Description = "Invoke contract for results (does not cost GAS)")] internal bool Results { get; init; } = false; - [Option(Description = "Path to neo-express data file")] - internal string Input { get; init; } = string.Empty; - [Option("--gas|-g", CommandOptionType.SingleValue, Description = "Additional GAS to apply to the contract invocation")] internal decimal AdditionalGas { get; init; } = 0; @@ -42,7 +44,10 @@ public Invoke(IExpressChainManagerFactory chainManagerFactory, IFileSystem fileS [Option(Description = "Output as JSON")] internal bool Json { get; init; } = false; - internal static async Task ExecuteTxAsync(IExpressChainManager chainManager, IExpressNode expressNode, string invocationFile, string accountName, IFileSystem fileSystem, System.IO.TextWriter writer, bool json = false) + [Option(Description = "Path to neo-express data file")] + internal string Input { get; init; } = string.Empty; + + internal static async Task ExecuteTxAsync(IExpressChainManager chainManager, IExpressNode expressNode, string invocationFile, string accountName, WitnessScope witnessScope, IFileSystem fileSystem, System.IO.TextWriter writer, bool json = false) { if (!fileSystem.File.Exists(invocationFile)) { @@ -56,7 +61,7 @@ internal static async Task ExecuteTxAsync(IExpressChainManager chainManager, IEx var parser = await expressNode.GetContractParameterParserAsync(chainManager).ConfigureAwait(false); var script = await parser.LoadInvocationScriptAsync(invocationFile).ConfigureAwait(false); - var txHash = await expressNode.ExecuteAsync(wallet, account.ScriptHash, script).ConfigureAwait(false); + var txHash = await expressNode.ExecuteAsync(wallet, account.ScriptHash, witnessScope, script).ConfigureAwait(false); await writer.WriteTxHashAsync(txHash, "Deployment", json).ConfigureAwait(false); } @@ -108,7 +113,7 @@ internal async Task OnExecuteAsync(IConsole console) } else { - await ExecuteTxAsync(chainManager, expressNode, InvocationFile, Account, fileSystem, console.Out, Json); + await ExecuteTxAsync(chainManager, expressNode, InvocationFile, Account, WitnessScope, fileSystem, console.Out, Json); } return 0; diff --git a/src/neoxp/ExpressNodeExtensions.cs b/src/neoxp/ExpressNodeExtensions.cs index 331f8f7f..96659627 100644 --- a/src/neoxp/ExpressNodeExtensions.cs +++ b/src/neoxp/ExpressNodeExtensions.cs @@ -109,7 +109,7 @@ public static async Task TransferAsync(this IExpressNode expressNode, U var decimals = (byte)(results.Stack[0].GetInteger()); var value = quantity.AsT0.ToBigInteger(decimals); var script = asset.MakeScript("transfer", senderHash, receiverHash, value, null); - return await expressNode.ExecuteAsync(sender, senderHash, script).ConfigureAwait(false); + return await expressNode.ExecuteAsync(sender, senderHash, WitnessScope.CalledByEntry, script).ConfigureAwait(false); } else { @@ -135,11 +135,11 @@ public static async Task TransferAsync(this IExpressNode expressNode, U sb.EmitPush("transfer"); sb.EmitPush(asset); sb.EmitSysCall(ApplicationEngine.System_Contract_Call); - return await expressNode.ExecuteAsync(sender, senderHash, sb.ToArray()).ConfigureAwait(false); + return await expressNode.ExecuteAsync(sender, senderHash, WitnessScope.CalledByEntry, sb.ToArray()).ConfigureAwait(false); } } - public static async Task DeployAsync(this IExpressNode expressNode, NefFile nefFile, ContractManifest manifest, Wallet wallet, UInt160 accountHash) + public static async Task DeployAsync(this IExpressNode expressNode, NefFile nefFile, ContractManifest manifest, Wallet wallet, UInt160 accountHash, WitnessScope witnessScope) { // check for bad opcodes (logic borrowed from neo-cli LoadDeploymentScript) Neo.VM.Script script = nefFile.Script; @@ -163,7 +163,7 @@ public static async Task DeployAsync(this IExpressNode expressNode, Nef using var sb = new ScriptBuilder(); sb.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", nefFile.ToArray(), manifest.ToJson().ToString()); - return await expressNode.ExecuteAsync(wallet, accountHash, sb.ToArray()).ConfigureAwait(false); + return await expressNode.ExecuteAsync(wallet, accountHash, witnessScope, sb.ToArray()).ConfigureAwait(false); } public static async Task DesignateOracleRolesAsync(this IExpressNode expressNode, Wallet wallet, UInt160 accountHash, IEnumerable oracles) @@ -178,7 +178,7 @@ public static async Task DesignateOracleRolesAsync(this IExpressNode ex using var sb = new ScriptBuilder(); sb.EmitDynamicCall(NativeContract.RoleManagement.Hash, "designateAsRole", roleParam, oraclesParam); - return await expressNode.ExecuteAsync(wallet, accountHash, sb.ToArray()).ConfigureAwait(false); + return await expressNode.ExecuteAsync(wallet, accountHash, WitnessScope.CalledByEntry, sb.ToArray()).ConfigureAwait(false); } public static async Task GetOracleNodesAsync(this IExpressNode expressNode) diff --git a/src/neoxp/IExpressNode.cs b/src/neoxp/IExpressNode.cs index 94e449c4..bde5af91 100644 --- a/src/neoxp/IExpressNode.cs +++ b/src/neoxp/IExpressNode.cs @@ -19,7 +19,7 @@ internal interface IExpressNode : IDisposable ProtocolSettings ProtocolSettings { get; } Task CreateCheckpointAsync(string checkPointPath); - Task ExecuteAsync(Wallet wallet, UInt160 accountHash, Script script, decimal additionalGas = 0); + Task ExecuteAsync(Wallet wallet, UInt160 accountHash, WitnessScope witnessScope, Script script, decimal additionalGas = 0); Task SubmitTransactionAsync(Transaction tx); Task InvokeAsync(Script script); Task<(RpcNep17Balance balance, Nep17Contract contract)[]> GetBalancesAsync(UInt160 address); diff --git a/src/neoxp/Node/OfflineNode.cs b/src/neoxp/Node/OfflineNode.cs index 218b0cf9..7822e673 100644 --- a/src/neoxp/Node/OfflineNode.cs +++ b/src/neoxp/Node/OfflineNode.cs @@ -88,11 +88,11 @@ public async Task InvokeAsync(Neo.VM.Script script) return result; } - public async Task ExecuteAsync(Wallet wallet, UInt160 accountHash, Neo.VM.Script script, decimal additionalGas = 0) + public async Task ExecuteAsync(Wallet wallet, UInt160 accountHash, WitnessScope witnessScope, Neo.VM.Script script, decimal additionalGas = 0) { if (disposedValue) throw new ObjectDisposedException(nameof(OfflineNode)); - var signer = new Signer() { Account = accountHash, Scopes = WitnessScope.CalledByEntry }; + var signer = new Signer() { Account = accountHash, Scopes = witnessScope }; var tx = wallet.MakeTransaction(neoSystem.StoreView, script, accountHash, new[] { signer }); if (additionalGas > 0.0m) { diff --git a/src/neoxp/Node/OnlineNode.cs b/src/neoxp/Node/OnlineNode.cs index b599c866..abb4272d 100644 --- a/src/neoxp/Node/OnlineNode.cs +++ b/src/neoxp/Node/OnlineNode.cs @@ -37,9 +37,9 @@ public void Dispose() { } - public async Task ExecuteAsync(Wallet wallet, UInt160 accountHash, Script script, decimal additionalGas = 0) + public async Task ExecuteAsync(Wallet wallet, UInt160 accountHash, WitnessScope witnessScope, Script script, decimal additionalGas = 0) { - var signers = new[] { new Signer { Scopes = WitnessScope.CalledByEntry, Account = accountHash } }; + var signers = new[] { new Signer { Account = accountHash, Scopes = witnessScope } }; var factory = new TransactionManagerFactory(rpcClient); var tm = await factory.MakeTransactionAsync(script, signers).ConfigureAwait(false);