From 311d20aba600076cf2fb9d88cb5dc7578fd9c850 Mon Sep 17 00:00:00 2001 From: Ravin Perera <33562092+ravinsp@users.noreply.github.com> Date: Mon, 20 Jul 2020 13:38:08 +0530 Subject: [PATCH] HP smart contract nodejs library. (#101) --- .../.gitignore | 0 .../rnd_contract.c | 0 examples/echo_contract/Dockerfile | 17 --- examples/echo_contract/contract.js | 48 ------- examples/hpclient/.gitignore | 1 - .../.gitignore | 0 .../.hp_client_keys | 0 .../file-client.js | 0 .../hp-client-lib.js | 0 .../package-lock.json | 0 .../{hpclient => nodejs_client}/package.json | 0 .../text-client.js | 0 .../.gitignore | 0 examples/nodejs_contract/echo_contract.js | 30 ++++ .../file_contract.js} | 0 examples/nodejs_contract/hp-contract-lib.js | 27 ++++ .../package-lock.json | 0 .../package.json | 0 examples/todo_contract/.gitignore | 2 - examples/todo_contract/DbModel.cs | 19 --- examples/todo_contract/HPHelper.cs | 68 --------- examples/todo_contract/HPModel.cs | 64 --------- .../20200114133142_InitialCreate.Designer.cs | 39 ----- .../20200114133142_InitialCreate.cs | 30 ---- .../Migrations/DataContextModelSnapshot.cs | 37 ----- examples/todo_contract/Program.cs | 136 ------------------ examples/todo_contract/ToDoContract.csproj | 19 --- src/sc.cpp | 3 +- test/local-cluster/cluster-create.sh | 11 +- test/vm-cluster/.gitignore | 3 +- test/vm-cluster/cluster.sh | 67 ++++++--- test/vm-cluster/setup-hp.sh | 21 ++- test/vm-cluster/setup-vm.sh | 40 +++--- 33 files changed, 154 insertions(+), 528 deletions(-) rename examples/{random_contract => c_contract}/.gitignore (100%) rename examples/{random_contract => c_contract}/rnd_contract.c (100%) delete mode 100644 examples/echo_contract/Dockerfile delete mode 100644 examples/echo_contract/contract.js delete mode 100644 examples/hpclient/.gitignore rename examples/{echo_contract => nodejs_client}/.gitignore (100%) rename examples/{hpclient => nodejs_client}/.hp_client_keys (100%) rename examples/{hpclient => nodejs_client}/file-client.js (100%) rename examples/{hpclient => nodejs_client}/hp-client-lib.js (100%) rename examples/{hpclient => nodejs_client}/package-lock.json (100%) rename examples/{hpclient => nodejs_client}/package.json (100%) rename examples/{hpclient => nodejs_client}/text-client.js (100%) rename examples/{file_contract => nodejs_contract}/.gitignore (100%) create mode 100644 examples/nodejs_contract/echo_contract.js rename examples/{file_contract/contract.js => nodejs_contract/file_contract.js} (100%) create mode 100644 examples/nodejs_contract/hp-contract-lib.js rename examples/{file_contract => nodejs_contract}/package-lock.json (100%) rename examples/{file_contract => nodejs_contract}/package.json (100%) delete mode 100644 examples/todo_contract/.gitignore delete mode 100644 examples/todo_contract/DbModel.cs delete mode 100644 examples/todo_contract/HPHelper.cs delete mode 100644 examples/todo_contract/HPModel.cs delete mode 100644 examples/todo_contract/Migrations/20200114133142_InitialCreate.Designer.cs delete mode 100644 examples/todo_contract/Migrations/20200114133142_InitialCreate.cs delete mode 100644 examples/todo_contract/Migrations/DataContextModelSnapshot.cs delete mode 100644 examples/todo_contract/Program.cs delete mode 100644 examples/todo_contract/ToDoContract.csproj diff --git a/examples/random_contract/.gitignore b/examples/c_contract/.gitignore similarity index 100% rename from examples/random_contract/.gitignore rename to examples/c_contract/.gitignore diff --git a/examples/random_contract/rnd_contract.c b/examples/c_contract/rnd_contract.c similarity index 100% rename from examples/random_contract/rnd_contract.c rename to examples/c_contract/rnd_contract.c diff --git a/examples/echo_contract/Dockerfile b/examples/echo_contract/Dockerfile deleted file mode 100644 index 5b33eb36..00000000 --- a/examples/echo_contract/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM hpcore:latest as hpcore-nodejs - -RUN apt-get update -RUN apt-get install -y build-essential -RUN apt-get install -y curl -RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - -RUN apt-get install -y nodejs - -FROM hpcore-nodejs - -RUN /hp/hpcore new /echo_contract -COPY ./examples/echo_contract/contract.js /echo_contract/bin/contract.js -RUN mv /echo_contract/cfg/hp.cfg /echo_contract/cfg/hp.json -RUN node -p "JSON.stringify({...require('/echo_contract/cfg/hp.json'), binary:'/usr/bin/node', binargs:'./bin/contract.js' }, null, 2)" > /echo_contract/cfg/hp.cfg -RUN rm /echo_contract/cfg/hp.json - -ENTRYPOINT ["/hp/hpcore", "run", "/echo_contract"] \ No newline at end of file diff --git a/examples/echo_contract/contract.js b/examples/echo_contract/contract.js deleted file mode 100644 index 35ad8ed0..00000000 --- a/examples/echo_contract/contract.js +++ /dev/null @@ -1,48 +0,0 @@ -process.on('uncaughtException', (err) => { - console.error('There was an uncaught error', err) -}) -const fs = require('fs') - -//console.log("===Sample contract started==="); -const hpargs = JSON.parse(fs.readFileSync(0, 'utf8')); -//console.log(hpargs); - -// We just save execution args as an example state file change. -if (!hpargs.readonly) - fs.appendFileSync("exects.txt", "ts:" + hpargs.ts + "\n"); - -Object.keys(hpargs.usrfd).forEach(function (key) { - const userfds = hpargs.usrfd[key]; - - if (userfds[0] != -1) { - const userinput = fs.readFileSync(userfds[0], 'utf8'); - - // Append user input to a state file if not in read only mode. - if (!hpargs.readonly) - fs.appendFileSync("userinputs.txt", userinput + "\n"); - - if (userinput == "ts") - fs.writeSync(userfds[1], fs.readFileSync("exects.txt")); - else - fs.writeSync(userfds[1], "Echoing: " + userinput); - } -}); - -if (!hpargs.readonly) { - - if (hpargs.nplfd[0] != -1) { - const nplinput = fs.readFileSync(hpargs.nplfd[0], 'utf8'); - console.log("Input received from peers:"); - console.log(nplinput); - fs.writeSync(hpargs.nplfd[1], "Echoing: " + nplinput); - } - - if (hpargs.hpfd[0] != -1) { - const hpinput = fs.readFileSync(hpargs.hpfd[0], 'utf8'); - console.log("Input received from hp:"); - console.log(hpinput); - fs.writeSync(hpargs.hpfd[1], "Echoing: " + hpinput); - } -} - -//console.log("===Sample contract ended==="); diff --git a/examples/hpclient/.gitignore b/examples/hpclient/.gitignore deleted file mode 100644 index b512c09d..00000000 --- a/examples/hpclient/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules \ No newline at end of file diff --git a/examples/echo_contract/.gitignore b/examples/nodejs_client/.gitignore similarity index 100% rename from examples/echo_contract/.gitignore rename to examples/nodejs_client/.gitignore diff --git a/examples/hpclient/.hp_client_keys b/examples/nodejs_client/.hp_client_keys similarity index 100% rename from examples/hpclient/.hp_client_keys rename to examples/nodejs_client/.hp_client_keys diff --git a/examples/hpclient/file-client.js b/examples/nodejs_client/file-client.js similarity index 100% rename from examples/hpclient/file-client.js rename to examples/nodejs_client/file-client.js diff --git a/examples/hpclient/hp-client-lib.js b/examples/nodejs_client/hp-client-lib.js similarity index 100% rename from examples/hpclient/hp-client-lib.js rename to examples/nodejs_client/hp-client-lib.js diff --git a/examples/hpclient/package-lock.json b/examples/nodejs_client/package-lock.json similarity index 100% rename from examples/hpclient/package-lock.json rename to examples/nodejs_client/package-lock.json diff --git a/examples/hpclient/package.json b/examples/nodejs_client/package.json similarity index 100% rename from examples/hpclient/package.json rename to examples/nodejs_client/package.json diff --git a/examples/hpclient/text-client.js b/examples/nodejs_client/text-client.js similarity index 100% rename from examples/hpclient/text-client.js rename to examples/nodejs_client/text-client.js diff --git a/examples/file_contract/.gitignore b/examples/nodejs_contract/.gitignore similarity index 100% rename from examples/file_contract/.gitignore rename to examples/nodejs_contract/.gitignore diff --git a/examples/nodejs_contract/echo_contract.js b/examples/nodejs_contract/echo_contract.js new file mode 100644 index 00000000..3f4a340f --- /dev/null +++ b/examples/nodejs_contract/echo_contract.js @@ -0,0 +1,30 @@ +const { HotPocketContract } = require("./hp-contract-lib"); +const fs = require('fs'); + +const hpc = new HotPocketContract(); + +//console.log("===Echo contract started==="); + +// We just save execution timestamp as an example state file change. +if (!hpc.readonly) + fs.appendFileSync("exects.txt", "ts:" + hpc.timestamp + "\n"); + +Object.keys(hpc.users).forEach(function (key) { + + const user = hpc.users[key]; + const inputBuf = user.readInput(); + if (inputBuf) { + const userInput = inputBuf.toString("utf8"); + + // Append user input to a state file if not in read only mode. + if (!hpc.readonly) + fs.appendFileSync("userinputs.txt", userInput + "\n"); + + if (userInput == "ts") + user.sendOutput(fs.readFileSync("exects.txt")); + else + user.sendOutput("Echoing: " + userInput); + } +}); + +//console.log("===Echo contract ended==="); diff --git a/examples/file_contract/contract.js b/examples/nodejs_contract/file_contract.js similarity index 100% rename from examples/file_contract/contract.js rename to examples/nodejs_contract/file_contract.js diff --git a/examples/nodejs_contract/hp-contract-lib.js b/examples/nodejs_contract/hp-contract-lib.js new file mode 100644 index 00000000..a0dc3d6a --- /dev/null +++ b/examples/nodejs_contract/hp-contract-lib.js @@ -0,0 +1,27 @@ +const fs = require('fs'); + +function HotPocketContract() { + const hpargs = JSON.parse(fs.readFileSync(0, 'utf8')); + this.readonly = hpargs.readonly; + this.timestamp = hpargs.ts; + this.users = {}; + + Object.keys(hpargs.usrfd).forEach((userPubKey) => { + const userfds = hpargs.usrfd[userPubKey]; + this.users[userPubKey] = new HotPocketChannel(userfds[0], userfds[1]); + }); +} + +function HotPocketChannel(infd, outfd) { + this.readInput = function () { + return infd == -1 ? null : fs.readFileSync(infd); + } + + this.sendOutput = function (output) { + fs.writeFileSync(outfd, output); + } +} + +module.exports = { + HotPocketContract +} \ No newline at end of file diff --git a/examples/file_contract/package-lock.json b/examples/nodejs_contract/package-lock.json similarity index 100% rename from examples/file_contract/package-lock.json rename to examples/nodejs_contract/package-lock.json diff --git a/examples/file_contract/package.json b/examples/nodejs_contract/package.json similarity index 100% rename from examples/file_contract/package.json rename to examples/nodejs_contract/package.json diff --git a/examples/todo_contract/.gitignore b/examples/todo_contract/.gitignore deleted file mode 100644 index 8d4a6c08..00000000 --- a/examples/todo_contract/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -bin -obj \ No newline at end of file diff --git a/examples/todo_contract/DbModel.cs b/examples/todo_contract/DbModel.cs deleted file mode 100644 index 74921604..00000000 --- a/examples/todo_contract/DbModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace ToDoContract -{ - public class DataContext : DbContext - { - public DbSet ToDoEntries { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder options) - => options.UseSqlite("Data Source=state/todo.db"); - } - - public class ToDoEntry - { - public int Id { get; set; } - public string Content { get; set; } - public string CreatedBy { get; set; } - } -} \ No newline at end of file diff --git a/examples/todo_contract/HPHelper.cs b/examples/todo_contract/HPHelper.cs deleted file mode 100644 index b925f4eb..00000000 --- a/examples/todo_contract/HPHelper.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Threading.Tasks; -using Mono.Unix.Native; -using Newtonsoft.Json; - -namespace HotPocket -{ - public static class HotPocketHelper - { - const int FD_READ_BUFFER_LEN = 1024; - - public static async Task GetContractArgsAsync() - { - using (var s = new StreamReader(Console.OpenStandardInput())) - { - var input = await s.ReadToEndAsync(); - var contractArgs = JsonConvert.DeserializeObject(input); - return contractArgs; - } - } - - public static string ReadStringFromFD(int fd) - { - return Encoding.UTF8.GetString(ReadBytesFromFD(fd)); - } - - public static void WriteStringToFD(int fd, string str) - { - WriteBytesToFD(fd, Encoding.UTF8.GetBytes(str)); - } - - public static unsafe byte[] ReadBytesFromFD(int fd) - { - // Keep reading the fd bytes and fill the memory stream until no more bytes. - using (var ms = new MemoryStream()) - { - int readbytes = 0; - - do - { - var buffer = new byte[FD_READ_BUFFER_LEN]; - - fixed (byte* p = buffer) - { - IntPtr ptr = (IntPtr)p; - readbytes = (int)Syscall.read(fd, ptr, FD_READ_BUFFER_LEN); - } - - ms.Write(buffer, 0, readbytes); - - } while (readbytes == FD_READ_BUFFER_LEN); - - return ms.ToArray(); - } - } - - public static unsafe void WriteBytesToFD(int fd, byte[] buffer) - { - fixed (byte* p = buffer) - { - IntPtr ptr = (IntPtr)p; - Syscall.write(fd, ptr, (ulong)buffer.Length); - } - } - } -} \ No newline at end of file diff --git a/examples/todo_contract/HPModel.cs b/examples/todo_contract/HPModel.cs deleted file mode 100644 index 2a514806..00000000 --- a/examples/todo_contract/HPModel.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace HotPocket -{ - public class ContractArgs - { - [JsonProperty("version")] - public string Version { get; set; } - - [JsonProperty("pubkey")] - public string PubKey { get; set; } - - [JsonProperty("ts")] - public string Timestamp { get; set; } - - [JsonProperty("hpfd")] - public IOPipe HotPocketPipe { get; set; } - - [JsonProperty("nplfd")] - public IOPipe NplPipe { get; set; } - - [JsonProperty("usrfd")] - public Dictionary UserPipes { get; set; } - - [JsonProperty("unl")] - public string[] Unl { get; set; } - } - - [JsonConverter(typeof(IOPipeJsonConverter))] - public class IOPipe - { - public int ReadFD { get; set; } - public int WriteFD { get; set; } - } - - public class IOPipeJsonConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return objectType == typeof(IOPipe); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.Null) - return null; - var array = JArray.Load(reader); - var pipe = (existingValue as IOPipe ?? new IOPipe()); - pipe.ReadFD = (int)array.ElementAtOrDefault(0); - pipe.WriteFD = (int)array.ElementAtOrDefault(1); - return pipe; - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var pipe = (IOPipe)value; - serializer.Serialize(writer, new[] { pipe.ReadFD, pipe.WriteFD }); - } - } -} \ No newline at end of file diff --git a/examples/todo_contract/Migrations/20200114133142_InitialCreate.Designer.cs b/examples/todo_contract/Migrations/20200114133142_InitialCreate.Designer.cs deleted file mode 100644 index f639f5fe..00000000 --- a/examples/todo_contract/Migrations/20200114133142_InitialCreate.Designer.cs +++ /dev/null @@ -1,39 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using ToDoContract; - -namespace ToDoContract.Migrations -{ - [DbContext(typeof(DataContext))] - [Migration("20200114133142_InitialCreate")] - partial class InitialCreate - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "3.1.0"); - - modelBuilder.Entity("ToDoContract.ToDoEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Content") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("ToDoEntries"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/examples/todo_contract/Migrations/20200114133142_InitialCreate.cs b/examples/todo_contract/Migrations/20200114133142_InitialCreate.cs deleted file mode 100644 index 3f32939f..00000000 --- a/examples/todo_contract/Migrations/20200114133142_InitialCreate.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace ToDoContract.Migrations -{ - public partial class InitialCreate : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "ToDoEntries", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Content = table.Column(nullable: true), - CreatedBy = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_ToDoEntries", x => x.Id); - }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "ToDoEntries"); - } - } -} diff --git a/examples/todo_contract/Migrations/DataContextModelSnapshot.cs b/examples/todo_contract/Migrations/DataContextModelSnapshot.cs deleted file mode 100644 index 09f9130f..00000000 --- a/examples/todo_contract/Migrations/DataContextModelSnapshot.cs +++ /dev/null @@ -1,37 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using ToDoContract; - -namespace ToDoContract.Migrations -{ - [DbContext(typeof(DataContext))] - partial class DataContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "3.1.0"); - - modelBuilder.Entity("ToDoContract.ToDoEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Content") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("ToDoEntries"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/examples/todo_contract/Program.cs b/examples/todo_contract/Program.cs deleted file mode 100644 index d2042089..00000000 --- a/examples/todo_contract/Program.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Newtonsoft.Json; -using HotPocket; -using System.Linq; - -namespace ToDoContract -{ - /* - * This is a simple multi-user ToDo list contract which uses sqlite database as storage. - * In order to run this .Net Core should be installed on the system. If using docker, - * mcr.microsoft.com/dotnet/core/sdk:3.1 docker image must be used. - * - * Produce deployable output with: dotnet publish -c Release - * - * User inputs can be submitted in the following format. - * Insert a new ToDo record: add - * Retrieve all records owned by user: get all - * Retrieve record by ID: get <id> - * Delete all records owned by user: delete all - * Delete record by ID: delete <id> - */ - public class Program - { - static async Task Main(string[] args) - { - Console.WriteLine("Starting .Net ToDo contract"); - - using (var dataContext = new DataContext()) - { - dataContext.Database.Migrate(); - } - - ContractArgs contractArgs = await HotPocketHelper.GetContractArgsAsync(); - - foreach (var user in contractArgs.UserPipes) - { - var pubkey = user.Key; - var pipe = user.Value; - - var input = HotPocketHelper.ReadStringFromFD(pipe.ReadFD); - if (string.IsNullOrEmpty(input)) - continue; - - var output = await HandleUserInputAsync(pubkey, input); - HotPocketHelper.WriteStringToFD(pipe.WriteFD, output); - } - } - - static async Task<string> HandleUserInputAsync(string userId, string input) - { - var parts = input.Trim().Split(' ', 2); - if (parts.Length < 2) - return "Invalid input format"; - - var command = parts[0].ToLower(); - var param = parts[1]; - - using (var dataContext = new DataContext()) - { - if (command == "add") // add new record. - { - var entry = new ToDoEntry - { - Content = param, - CreatedBy = userId - }; - dataContext.ToDoEntries.Add(entry); - - await dataContext.SaveChangesAsync(); - - return "Added entry with id " + entry.Id; - } - else if (command == "get" || command == "delete") - { - if (param == "all") // get/delete all records. - { - // Get all entries belonging to this user. - var entries = await dataContext.ToDoEntries.Where(e => e.CreatedBy == userId).OrderBy(e => e.Id).ToListAsync(); - - if (command == "get") - { - return JsonConvert.SerializeObject(entries.Select(e => $"ID-{e.Id}: {e.Content}")); - } - else - { - // Delete all records for this user. - dataContext.RemoveRange(entries); - await dataContext.SaveChangesAsync(); - return $"{entries.Count} record(s) deleted"; - - } - } - else // get/delete by ID. - { - int id = 0; - if (int.TryParse(param, out id)) - { - var entry = await dataContext.ToDoEntries.FirstOrDefaultAsync(e => e.Id == id); - if (entry == null) - { - return $"Record id {id} does not exist"; - } - else if (entry.CreatedBy != userId) - { - return $"You do not have permission for record id {id}"; - } - else - { - if (command == "get") - { - return entry.Content; - } - else - { - dataContext.Remove(entry); - await dataContext.SaveChangesAsync(); - return $"Record id {id} deleted"; - } - } - } - else - { - return "Invalid record id"; - } - } - } - else - { - return "Invalid command"; - } - } - } - } -} diff --git a/examples/todo_contract/ToDoContract.csproj b/examples/todo_contract/ToDoContract.csproj deleted file mode 100644 index 955a561e..00000000 --- a/examples/todo_contract/ToDoContract.csproj +++ /dev/null @@ -1,19 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <OutputType>Exe</OutputType> - <TargetFramework>netcoreapp3.1</TargetFramework> - <AllowUnsafeBlocks>true</AllowUnsafeBlocks> - </PropertyGroup> - - <ItemGroup> - <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0"> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - <PrivateAssets>all</PrivateAssets> - </PackageReference> - <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.0" /> - <PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" /> - <PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> - </ItemGroup> - -</Project> diff --git a/src/sc.cpp b/src/sc.cpp index d16bd135..49de4e48 100644 --- a/src/sc.cpp +++ b/src/sc.cpp @@ -176,7 +176,8 @@ namespace sc * "hpfd": [fd0, fd1], * "usrfd":{ "<pkhex>":[fd0, fd1], ... }, * "nplfd":[fd0, fd1], - * "unl":[ "pkhex", ... ] + * "unl":[ "pkhex", ... ], + * "readonly": <true|false> * } */ int write_contract_args(const execution_context &ctx) diff --git a/test/local-cluster/cluster-create.sh b/test/local-cluster/cluster-create.sh index 40d150c0..3c063e9b 100755 --- a/test/local-cluster/cluster-create.sh +++ b/test/local-cluster/cluster-create.sh @@ -47,12 +47,12 @@ do # Update contract config. node -p "JSON.stringify({...require('./tmp.json'), \ binary: '/usr/local/bin/node', \ - binargs: '/contract/bin/contract.js', \ + binargs: '/contract/bin/echo_contract.js', \ appbill: '', \ appbillargs: '', \ peerport: ${peerport}, \ pubport: ${pubport}, \ - roundtime: 1000, \ + roundtime: 2000, \ loglevel: 'debug', \ loggers:['console', 'file'] \ }, null, 2)" > hp.cfg @@ -65,9 +65,12 @@ do # Copy the contract executable and appbill. mkdir ./node$n/bin - cp ../../../examples/echo_contract/contract.js ./node$n/bin/contract.js + cp ../../../examples/nodejs_contract/{package.json,echo_contract.js,hp-contract-lib.js} ./node$n/bin/ cp ../bin/appbill ./node$n/bin/ - # cp -r ../../../examples/todo_contract/bin/Release/netcoreapp3.1/publish/* ./node$n/bin/ + + pushd ./node$n/bin > /dev/null 2>&1 + npm install + popd done # Function to generate JSON array string while skiping a given index. diff --git a/test/vm-cluster/.gitignore b/test/vm-cluster/.gitignore index 596199cf..ae791b63 100644 --- a/test/vm-cluster/.gitignore +++ b/test/vm-cluster/.gitignore @@ -1,3 +1,4 @@ cfg vmpass.txt -iplist.txt \ No newline at end of file +iplist.txt +hpfiles \ No newline at end of file diff --git a/test/vm-cluster/cluster.sh b/test/vm-cluster/cluster.sh index c0faebaf..df42c7fa 100755 --- a/test/vm-cluster/cluster.sh +++ b/test/vm-cluster/cluster.sh @@ -1,10 +1,15 @@ #!/bin/bash +# HotPocket VM cluster setup script. -# Usage example: ./cluster.sh run 1 +# Usage examples: +# ./cluster.sh new +# ./cluster.sh update +# ./cluster.sh run 1 # VM login password must exist in vmpass.txt +# All VMs must use same SSH password with username 'geveo' vmpass=$(cat vmpass.txt) -# List vm IP addresses of the cluster must exist in iplist.txt +# List of vm IP addresses/domain names of the cluster must exist in iplist.txt # (This list will be treated as the node numbers 1,2.3... from topmost IP to the bottom) readarray -t vmips < iplist.txt @@ -13,18 +18,19 @@ mode=$1 hpcore=$(realpath ../..) -if [ "$mode" = "new" ] || [ "$mode" = "update" ] || [ "$mode" = "run" ] || [ "$mode" = "check" ] || \ - [ "$mode" = "monitor" ] || [ "$mode" = "kill" ] || [ "$mode" = "reboot" ] || [ "$mode" = "ssh" ] || \ - [ "$mode" = "dns" ] || [ "$mode" = "ssl" ]; then +if [ "$mode" = "new" ] || [ "$mode" = "update" ] || [ "$mode" = "reconfig" ] || [ "$mode" = "run" ] || \ + [ "$mode" = "check" ] || [ "$mode" = "monitor" ] || [ "$mode" = "kill" ] || [ "$mode" = "reboot" ] || \ + [ "$mode" = "ssh" ] || [ "$mode" = "dns" ] || [ "$mode" = "ssl" ]; then echo "mode: $mode" else - echo "Invalid command. [ new | update | run <N> | check <N> | monitor <N> | kill <N> | reboot <N> | ssh <N> <custom command> | dns <N> <zerossl file> | ssl <N> ] expected." + echo "Invalid command. [ new | update | reconfig | run <N> | check <N> | monitor <N> | kill <N> | reboot <N> | ssh <N> <custom command> | dns <N> <zerossl file> | ssl <N> ] expected." exit 1 fi # Command modes: # new - Install hot pocket dependencies and hot pocket with example contracts to each vm. # update - Deploy updated hot pocket and example binaries into each vm. +# reconfig - Reconfigures the entire cluster using already uploaded HP binaries. # run - Run hot pocket of specified vm node. # check - Check hot pocket running status of specified vm node. # monitor - Monitor streaming hot pocket console output (if running) of specified vm node. @@ -37,7 +43,7 @@ fi if [ $mode = "run" ]; then let nodeid=$2-1 vmip=${vmips[$nodeid]} - sshpass -f vmpass.txt ssh geveo@$vmip 'nohup sudo ./hpcore run contract' + sshpass -f vmpass.txt ssh geveo@$vmip 'nohup sudo ~/hpfiles/bin/hpcore run ~/contract' sshpass -f vmpass.txt ssh geveo@$vmip 'tail -f nohup.out' exit 0 fi @@ -108,17 +114,19 @@ if [ $mode = "ssl" ]; then sshpass -f vmpass.txt scp ~/Downloads/$vmip/certs/* geveo@$vmip:~/contract/cfg/ rm -r ~/Downloads/$vmip + echo "Done" exit 0 fi -mkdir ./cfg > /dev/null 2>&1 - -for (( i=0; i<$vmcount; i++ )) -do - vmip=${vmips[i]} - let n=$i+1 - /bin/bash ./setup-vm.sh $mode $n $vmpass $vmip $hpcore & -done +# Run binary file setup for entire cluster. +if [ $mode = "new" ] || [ $mode = "update" ]; then + for (( i=0; i<$vmcount; i++ )) + do + vmip=${vmips[i]} + let n=$i+1 + /bin/bash ./setup-vm.sh $mode $n $vmpass $vmip $hpcore & + done +fi wait @@ -126,7 +134,22 @@ if [ $mode = "update" ]; then exit 0 fi -# Following code will only be executed in 'new' mode. +# All code below this will only execute in 'new' or 'reconfig' mode. +# Update all nodes hp.cfg files to be part of the same UNL cluster. + +if [ $mode = "reconfig" ]; then + mkdir ./cfg > /dev/null 2>&1 + for (( i=0; i<$vmcount; i++ )) + do + # Run hp setup script on the VM and download the generated hp.cfg + vmip=${vmips[i]} + let nodeid=$i+1 + sshpass -f vmpass.txt ssh geveo@$vmip '~/hpfiles/setup-hp.sh' + sshpass -f vmpass.txt scp geveo@$vmip:~/contract/cfg/hp.cfg ./cfg/node$nodeid.json + done +fi + +# Locally update values of download hp.cfg files. for (( i=0; i<$vmcount; i++ )) do @@ -172,9 +195,17 @@ do mypeers=$(joinarr peers $j) myunl=$(joinarr pubkeys $j) - node -p "JSON.stringify({...require('./cfg/node$n.json'),binary:'/usr/bin/node',binargs:'/home/geveo/contract.js',peers:${mypeers},unl:${myunl},loggers:['console', 'file']}, null, 2)" > ./cfg/node$n.cfg + node -p "JSON.stringify({...require('./cfg/node$n.json'), \ + binary:'/usr/bin/node', \ + binargs:'/home/geveo/hpfiles/nodejs_contract/echo_contract.js', \ + peers:${mypeers}, \ + unl:${myunl}, \ + roundtime: 2000, \ + loglevel: 'debug', \ + loggers:['console', 'file'] \ + }, null, 2)" > ./cfg/node$n.cfg - # Copy local cfg file back to remote vm. + # Upload local hp.cfg file back to remote vm. vmip=${vmips[j]} sshpass -f vmpass.txt scp ./cfg/node$n.cfg geveo@$vmip:~/contract/cfg/hp.cfg done diff --git a/test/vm-cluster/setup-hp.sh b/test/vm-cluster/setup-hp.sh index c0f75e66..b137eb3f 100755 --- a/test/vm-cluster/setup-hp.sh +++ b/test/vm-cluster/setup-hp.sh @@ -1,5 +1,14 @@ #!/bin/bash +if [[ ! -f /swapfile ]] +then + echo "Adding 5GB swap space..." + sudo fallocate -l 5G /swapfile + sudo chmod 600 /swapfile + sudo mkswap /swapfile + sudo swapon /swapfile +fi + if [ -x "$(command -v node)" ]; then echo "NodeJs already installed." else @@ -15,14 +24,18 @@ if [ -x "$(command -v fusermount3)" ]; then else echo "Installing FUSE and other shared libraries..." sudo apt-get -y install libgomp1 - sudo cp ./libfuse3.so.3 ./libb2.so.1 /usr/local/lib/ + sudo cp ~/hpfiles/bin/{libfuse3.so.3,libb2.so.1} /usr/local/lib/ sudo ldconfig - sudo cp ./fusermount3 /usr/local/bin/ + sudo cp ~/hpfiles/bin/fusermount3 /usr/local/bin/ fi sudo rm -r ~/contract > /dev/null 2>&1 -./hpcore new ./contract -pushd ./contract/cfg > /dev/null 2>&1 + +echo "Creating new contract directory..." +~/hpfiles/bin/hpcore new ~/contract + +pushd ~/contract/cfg > /dev/null 2>&1 +echo "Generating default ssl certs..." openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout tlskey.pem -out tlscert.pem \ -subj "/C=AU/ST=ST/L=L/O=O/OU=OU/CN=localhost/emailAddress=hp@example" > /dev/null 2>&1 popd > /dev/null 2>&1 diff --git a/test/vm-cluster/setup-vm.sh b/test/vm-cluster/setup-vm.sh index d4670a8f..41431f45 100755 --- a/test/vm-cluster/setup-vm.sh +++ b/test/vm-cluster/setup-vm.sh @@ -8,27 +8,27 @@ hpcore=$5 echo $nodeid. $vmip +# Copy required files to hpfiles dir. +mkdir -p hpfiles/{bin,nodejs_contract} + strip $hpcore/build/hpcore +cp $hpcore/build/hpcore hpfiles/bin/ +cp $hpcore/examples/nodejs_contract/{package.json,echo_contract.js,hp-contract-lib.js} hpfiles/nodejs_contract/ +if [ $mode = "new" ]; then + cp ../bin/{libfuse3.so.3,libb2.so.1,fusermount3,websocketd,websocat,hpfs} hpfiles/bin/ + cp ./setup-hp.sh hpfiles/ +fi + +echo "Uploading hp files..." +sshpass -f vmpass.txt scp -rp hpfiles geveo@$vmip:~/ +echo "Upload finished." if [ $mode = "new" ]; then - - sshpass -f vmpass.txt scp $hpcore/build/hpcore \ - $hpcore/examples/echo_contract/contract.js \ - ../bin/libfuse3.so.3 \ - ../bin/libb2.so.1 \ - ../bin/fusermount3 \ - ../bin/websocketd \ - ../bin/websocat \ - ../bin/hpfs \ - ./consensus-test-continuous.sh \ - ./setup-hp.sh \ - geveo@$vmip:~/ - - sshpass -f vmpass.txt ssh geveo@$vmip 'chmod 700 ~/consensus-test-continuous.sh && chmod 700 ~/setup-hp.sh && ~/setup-hp.sh' + # Run hp setup script on the VM and download the generated hp.cfg + sshpass -f vmpass.txt ssh geveo@$vmip '~/hpfiles/setup-hp.sh && cd ~/hpfiles/nodejs_contract && npm install' + sshpass -f vmpass.txt ssh geveo@$vmip 'echo sudo ~/hpfiles/bin/hpcore run ~/contract > ~/run.sh && sudo chmod +x ~/run.sh' + mkdir ./cfg > /dev/null 2>&1 sshpass -f vmpass.txt scp geveo@$vmip:~/contract/cfg/hp.cfg ./cfg/node$nodeid.json -else - sshpass -f vmpass.txt scp $hpcore/build/hpcore \ - $hpcore/examples/echo_contract/contract.js \ - ./consensus-test-continuous.sh \ - geveo@$vmip:~/ -fi \ No newline at end of file +fi + +rm -r hpfiles \ No newline at end of file