commit 3ef2f7f6584082967312fea25edf1b326f9bbdb9 Author: vms Date: Thu Oct 10 20:50:26 2019 +0300 decouple frank from the main repo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bbd951a --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Scala compiled +target/ + +# Python compiled +*.pyc + +# IntelliJ +.idea +.vscode +*.iml + +# MacOS folder metadata +.DS_Store + +# Text writing +*.scriv + +# Temp +docs/checklist.md + +# Node modules +node_modules +**/node_modules + +# Fluence CLI binary +tools/deploy/fluence + +# Contract typings and ABI +Network.d.ts +Network.json +Dashboard.d.ts diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..346dc6d --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,56 @@ +version = 2.0.1 + +docstrings = JavaDoc + +maxColumn = 120 + +align = some +align.tokens = [{code = "=>", owner = "Case"}, ":=", "%", "%%", "%%%"] + +assumeStandardLibraryStripMargin = true +includeCurlyBraceInSelectChains = false + +continuationIndent { + callSite = 2 + defnSite = 2 + extendSite = 4 +} + +danglingParentheses = true + +newlines { + alwaysBeforeTopLevelStatements = true + sometimesBeforeColonInMethodReturnType = true + penalizeSingleSelectMultiArgList = false + alwaysBeforeElseAfterCurlyIf = false + neverInResultType = false +} + +spaces { + afterKeywordBeforeParen = true +} + +binPack { + parentConstructors = true + literalArgumentLists = true +} + +optIn { + breaksInsideChains = false + breakChainOnFirstMethodDot = true + configStyleArguments = true +} + +runner { + optimizer { + forceConfigStyleOnOffset = 150 + forceConfigStyleMinArgCount = 2 + } +} + +rewrite { + rules = [ + SortImports + ] +} + diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..5f6f8c8 --- /dev/null +++ b/build.sbt @@ -0,0 +1,45 @@ +import SbtCommons._ + +name := "frank" + +commons + +/* Projects */ + +lazy val root = (project in file(".")) + .aggregate(`vm-scala`, `vm-rust`) + +lazy val `vm-rust` = (project in file("vm/src/main/rust/")) + .settings( + compileFrankVMSettings() + ) + +lazy val `vm-llamadb` = (project in file("vm/src/it/resources/llamadb")) + .settings( + downloadLlamadb() + ) + +lazy val `vm-scala` = (project in file("vm")) + .configs(IntegrationTest) + .settings(inConfig(IntegrationTest)(Defaults.itSettings): _*) + .settings( + commons, + libraryDependencies ++= Seq( + cats, + catsEffect, + ficus, + cryptoHashsign, + scalaTest, + scalaIntegrationTest, + mockito + ), + assemblyJarName in assembly := "frank.jar", + assemblyMergeStrategy in assembly := SbtCommons.mergeStrategy.value, + test in assembly := {}, + compile in Compile := (compile in Compile) + .dependsOn(compile in `vm-rust`).value, + test in IntegrationTest := (test in IntegrationTest) + .dependsOn(compile in `vm-llamadb`) + .value + ) + .enablePlugins(AutomateHeaderPlugin) diff --git a/project/SbtCommons.scala b/project/SbtCommons.scala new file mode 100644 index 0000000..caf94bc --- /dev/null +++ b/project/SbtCommons.scala @@ -0,0 +1,207 @@ +import de.heikoseeberger.sbtheader.HeaderPlugin.autoImport.headerLicense +import de.heikoseeberger.sbtheader.License +import org.scalafmt.sbt.ScalafmtPlugin.autoImport.scalafmtOnCompile +import sbt.Keys.{javaOptions, _} +import sbt.{Def, addCompilerPlugin, taskKey, _} +import sbtassembly.AssemblyPlugin.autoImport.assemblyMergeStrategy +import sbtassembly.{MergeStrategy, PathList} + +import scala.sys.process._ + +object SbtCommons { + + val scalaV = scalaVersion := "2.12.9" + + val kindProjector = Seq( + resolvers += Resolver.sonatypeRepo("releases"), + addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.10.0") + ) + + val commons = Seq( + scalaV, + version := "0.1.1", + fork in Test := true, + parallelExecution in Test := false, + fork in IntegrationTest := true, + parallelExecution in IntegrationTest := false, + organizationName := "Fluence Labs Limited", + organizationHomepage := Some(new URL("https://fluence.network")), + startYear := Some(2019), + licenses += ("Apache-2.0", new URL("https://www.apache.org/licenses/LICENSE-2.0.txt")), + headerLicense := Some(License.ALv2("2019", organizationName.value)), + resolvers += Resolver.bintrayRepo("fluencelabs", "releases"), + scalafmtOnCompile := true, + // see good explanation https://gist.github.com/djspiewak/7a81a395c461fd3a09a6941d4cd040f2 + scalacOptions ++= Seq("-Ypartial-unification", "-deprecation"), + javaOptions in Test ++= Seq( + "-XX:MaxMetaspaceSize=4G", + "-Xms4G", + "-Xmx4G", + "-Xss6M", + s"-Djava.library.path=${file("").getAbsolutePath}/vm/src/main/rust/target/release" + ), + javaOptions in IntegrationTest ++= Seq( + "-XX:MaxMetaspaceSize=4G", + "-Xms4G", + "-Xmx4G", + "-Xss6M", + s"-Djava.library.path=${file("").getAbsolutePath}/vm/src/main/rust/target/release" + ), + addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.0") + ) ++ kindProjector + + val mergeStrategy = Def.setting[String => MergeStrategy]({ + // a module definition fails compilation for java 8, just skip it + case PathList("module-info.class", xs @ _*) => MergeStrategy.first + case "META-INF/io.netty.versions.properties" => MergeStrategy.first + case x => + import sbtassembly.AssemblyPlugin.autoImport.assembly + val oldStrategy = (assemblyMergeStrategy in assembly).value + oldStrategy(x) + }: String => MergeStrategy) + + val rustToolchain = "nightly-2019-09-23" + + def installPrerequisites() = { + val installRust = s"curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $rustToolchain" + val installToolchain = s"~/.cargo/bin/rustup toolchain install $rustToolchain" + val installCross = "cargo install cross" + + //assert((installRust !) == 0, "Rust installation failed") + //assert((installToolchain !) == 0 , s"toolchain $rustToolchain installation failed") + (installCross !) + } + + def compileFrank() = { + val projectRoot = file("").getAbsolutePath + val frankFolder = s"$projectRoot/vm/src/main/rust" + val localCompileCmd = s"cargo +$rustToolchain build --manifest-path $frankFolder/Cargo.toml --release --lib" + val crossCompileCmd = s"cd $frankFolder ; cross build --target x86_64-unknown-linux-gnu --release --lib" + + println(crossCompileCmd) + assert((localCompileCmd !) == 0, "Frank VM native compilation failed") + assert((crossCompileCmd !) == 0, "Cross compilation to linux failed") + } + + def compileFrankVMSettings(): Seq[Def.Setting[_]] = + Seq( + publishArtifact := false, + test := (test in Test).dependsOn(compile).value, + compile := (compile in Compile) + .dependsOn(Def.task { + val log = streams.value.log + + log.info("Installing prerequisites for native part compilation") + installPrerequisites() + + log.info("Compiling Frank VM") + compileFrank() + }) + .value + ) + + def downloadLlamadb(): Seq[Def.Setting[_]] = + Seq( + publishArtifact := false, + test := (test in Test).dependsOn(compile).value, + compile := (compile in Compile) + .dependsOn(Def.task { + // by defaults, user.dir in sbt points to a submodule directory while in Idea to the project root + val resourcesPath = + if (System.getProperty("user.dir").endsWith("/vm")) + System.getProperty("user.dir") + "/src/it/resources/" + else + System.getProperty("user.dir") + "/vm/src/it/resources/" + + val log = streams.value.log + val llamadbUrl = "https://github.com/fluencelabs/llamadb-wasm/releases/download/0.1.2/llama_db.wasm" + val llamadbPreparedUrl = + "https://github.com/fluencelabs/llamadb-wasm/releases/download/0.1.2/llama_db_prepared.wasm" + + log.info(s"Dowloading llamadb from $llamadbUrl to $resourcesPath") + + // -nc prevents downloading if file already exists + val llamadbDownloadRet = s"wget -nc $llamadbUrl -O $resourcesPath/llama_db.wasm" ! + val llamadbPreparedDownloadRet = s"wget -nc $llamadbPreparedUrl -O $resourcesPath/llama_db_prepared.wasm" ! + + // wget returns 0 of file was downloaded and 1 if file already exists + assert(llamadbDownloadRet == 0 || llamadbDownloadRet == 1, s"Download failed: $llamadbUrl") + assert( + llamadbPreparedDownloadRet == 0 || llamadbPreparedDownloadRet == 1, + s"Download failed: $llamadbPreparedUrl" + ) + }) + .value + ) + + /* Common deps */ + + val catsVersion = "2.0.0" + val cats = "org.typelevel" %% "cats-core" % catsVersion + val catsEffectVersion = "2.0.0" + val catsEffect = "org.typelevel" %% "cats-effect" % catsEffectVersion + + val shapeless = "com.chuusai" %% "shapeless" % "2.3.3" + + val fs2Version = "1.0.4" + val fs2 = "co.fs2" %% "fs2-core" % fs2Version + val fs2rx = "co.fs2" %% "fs2-reactive-streams" % fs2Version + val fs2io = "co.fs2" %% "fs2-io" % fs2Version + + // functional wrapper around 'lightbend/config' + val ficus = "com.iheart" %% "ficus" % "1.4.5" + + val cryptoVersion = "0.0.9" + val cryptoHashsign = "one.fluence" %% "crypto-hashsign" % cryptoVersion + val cryptoJwt = "one.fluence" %% "crypto-jwt" % cryptoVersion + val cryptoCipher = "one.fluence" %% "crypto-cipher" % cryptoVersion + + val codecVersion = "0.0.5" + val codecCore = "one.fluence" %% "codec-core" % codecVersion + + val sttpVersion = "1.6.3" + val sttp = "com.softwaremill.sttp" %% "core" % sttpVersion + val sttpCirce = "com.softwaremill.sttp" %% "circe" % sttpVersion + val sttpFs2Backend = "com.softwaremill.sttp" %% "async-http-client-backend-fs2" % sttpVersion + val sttpCatsBackend = "com.softwaremill.sttp" %% "async-http-client-backend-cats" % sttpVersion + + val http4sVersion = "0.20.10" + val http4sDsl = "org.http4s" %% "http4s-dsl" % http4sVersion + val http4sServer = "org.http4s" %% "http4s-blaze-server" % http4sVersion + val http4sCirce = "org.http4s" %% "http4s-circe" % http4sVersion + + val circeVersion = "0.12.1" + val circeCore = "io.circe" %% "circe-core" % circeVersion + val circeGeneric = "io.circe" %% "circe-generic" % circeVersion + val circeGenericExtras = "io.circe" %% "circe-generic-extras" % circeVersion + val circeParser = "io.circe" %% "circe-parser" % circeVersion + val circeFs2 = "io.circe" %% "circe-fs2" % "0.11.0" + + val scodecBits = "org.scodec" %% "scodec-bits" % "1.1.9" + val scodecCore = "org.scodec" %% "scodec-core" % "1.11.3" + + val web3jVersion = "4.5.0" + val web3jCrypto = "org.web3j" % "crypto" % web3jVersion + val web3jCore = "org.web3j" % "core" % web3jVersion + + val toml = "com.electronwill.night-config" % "toml" % "3.4.2" + + val rocksDb = "org.rocksdb" % "rocksdbjni" % "5.17.2" + val levelDb = "org.iq80.leveldb" % "leveldb" % "0.12" + + val protobuf = "io.github.scalapb-json" %% "scalapb-circe" % "0.4.3" + val protobufUtil = "com.google.protobuf" % "protobuf-java-util" % "3.7.1" + + val bouncyCastle = "org.bouncycastle" % "bcprov-jdk15on" % "1.61" + + val asyncHttpClient = "org.asynchttpclient" % "async-http-client" % "2.8.1" + + /* Test deps*/ + val scalacheckShapeless = "com.github.alexarchambault" %% "scalacheck-shapeless_1.13" % "1.1.8" % Test + val catsTestkit = "org.typelevel" %% "cats-testkit" % catsVersion % Test + val disciplineScalaTest = "org.typelevel" %% "discipline-scalatest" % "1.0.0-M1" % Test + + val scalaTest = "org.scalatest" %% "scalatest" % "3.0.8" % Test + val scalaIntegrationTest = "org.scalatest" %% "scalatest" % "3.0.8" % IntegrationTest + val mockito = "org.mockito" % "mockito-core" % "2.21.0" % Test +} diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..c0bab04 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.2.8 diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 0000000..56995ce --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,9 @@ +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.0.1") + +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.2.0") + +addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.2") + +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") + +addSbtPlugin("ch.jodersky" % "sbt-jni" % "1.3.4") diff --git a/vm/src/it/resources/.gitignore b/vm/src/it/resources/.gitignore new file mode 100644 index 0000000..d5e82be --- /dev/null +++ b/vm/src/it/resources/.gitignore @@ -0,0 +1,2 @@ +# Downloaded test artifacts +*.wasm diff --git a/vm/src/it/resources/reference.conf b/vm/src/it/resources/reference.conf new file mode 100644 index 0000000..4a1303c --- /dev/null +++ b/vm/src/it/resources/reference.conf @@ -0,0 +1,60 @@ +# +# These settings describe VM configs for integrations test launches with different memory sizes. +# More info about each field meaning can be found in vm/src/main/resources/reference.conf. +# + +fluence.vm.client.4Mb { + + // 65536 * 64 = 4 Mb + memPagesCount: 64 + + loggerEnabled: true + + chunkSize: 4096 + + mainModuleConfig: { + allocateFunctionName: "allocate" + + deallocateFunctionName: "deallocate" + + invokeFunctionName: "invoke" + } +} + +fluence.vm.client.100Mb { + + // 65536 * 1600 = 100 Mb + memPagesCount: 1600 + + loggerEnabled: true + + chunkSize: 4096 + + mainModuleConfig: { + allocateFunctionName: "allocate" + + deallocateFunctionName: "deallocate" + + invokeFunctionName: "invoke" + } + +} + +fluence.vm.client.2Gb { + + // 65536 * 32767 ~ 2 Gb + memPagesCount: 12767 + + loggerEnabled: true + + chunkSize: 4096 + + mainModuleConfig: { + allocateFunctionName: "allocate" + + deallocateFunctionName: "deallocate" + + invokeFunctionName: "invoke" + } + +} diff --git a/vm/src/it/scala/fluence/vm/AppIntegrationTest.scala b/vm/src/it/scala/fluence/vm/AppIntegrationTest.scala new file mode 100644 index 0000000..bcbbedf --- /dev/null +++ b/vm/src/it/scala/fluence/vm/AppIntegrationTest.scala @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm + +import cats.data.EitherT +import cats.effect.IO +import org.scalatest.{Assertion, EitherValues, Matchers, OptionValues, WordSpec} + +trait AppIntegrationTest extends WordSpec with Matchers with OptionValues with EitherValues { + + protected def checkTestResult(result: InvocationResult, expectedString: String): Assertion = { + val resultAsString = new String(result.output) + resultAsString should startWith(expectedString) + } + + protected def compareArrays(first: Array[Byte], second: Array[Byte]): Assertion = + first.deep shouldBe second.deep + + implicit class EitherTValueReader[E <: Throwable, V](origin: EitherT[IO, E, V]) { + + def success(): V = + origin.value.unsafeRunSync() match { + case Left(e) => println(s"got error $e"); throw e + case Right(v) => v + } + + def failed(): E = + origin.value.unsafeRunSync().left.value + } + +} diff --git a/vm/src/it/scala/fluence/vm/LlamadbInstrumentedIntegrationTest.scala b/vm/src/it/scala/fluence/vm/LlamadbInstrumentedIntegrationTest.scala new file mode 100644 index 0000000..314b8d4 --- /dev/null +++ b/vm/src/it/scala/fluence/vm/LlamadbInstrumentedIntegrationTest.scala @@ -0,0 +1,115 @@ +/* + * Copyright 2018 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm + +import cats.data.NonEmptyList +import cats.effect.{IO, Timer} + +import scala.concurrent.ExecutionContext +import scala.language.{higherKinds, implicitConversions} +import fluence.vm.Utils.getModuleDirPrefix + +// TODO: to run this test from IDE It needs to download vm-llamadb project explicitly at first +// this test is separated from the main LlamadbIntegrationTest because gas price for each instruction could be changed +// and it would be difficult to update them in each test +class LlamadbInstrumentedIntegrationTest extends LlamadbIntegrationTestInterface { + + override val llamadbFilePath: String = getModuleDirPrefix() + + "/src/it/resources/llama_db_prepared.wasm" + + private implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global) + + "instrumented llamadb app" should { + + "be able to instantiate" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + state ← vm.computeVmState[IO].toVmError + } yield { + state should not be None + + }).success() + + } + + "be able to create table and insert to it" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + createResult ← createTestTable(vm) + + } yield { + checkTestResult(createResult, "rows inserted") + createResult.spentGas should equal(527599L) + + }).success() + + } + + "be able to select records" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + createResult ← createTestTable(vm) + emptySelectResult ← executeSql(vm, "SELECT * FROM Users WHERE name = 'unknown'") + selectAllResult ← executeSql(vm, "SELECT min(id), max(id), count(age), sum(age), avg(age) FROM Users") + explainResult ← executeSql(vm, "EXPLAIN SELECT id, name FROM Users") + + } yield { + checkTestResult(createResult, "rows inserted") + checkTestResult(emptySelectResult, "id, name, age") + checkTestResult( + selectAllResult, + "_0, _1, _2, _3, _4\n" + + "1, 4, 4, 98, 24.5" + ) + checkTestResult( + explainResult, + "query plan\n" + + "column names: (`id`, `name`)\n" + + "(scan `users` :source-id 0\n" + + " (yield\n" + + " (column-field :source-id 0 :column-offset 0)\n" + + " (column-field :source-id 0 :column-offset 1)))" + ) + + createResult.spentGas should equal(527599L) + emptySelectResult.spentGas should equal(370143L) + selectAllResult.spentGas should equal(754557L) + explainResult.spentGas should equal(387359L) + + }).success() + + } + + "be able to launch VM with 2 GiB memory and a lot of data inserts" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.2Gb") + _ ← createTestTable(vm) + + // trying to insert 30 times by ~200 KiB + _ = for (_ ← 1 to 30) yield { executeInsert(vm, 200) }.value.unsafeRunSync + insertResult ← executeInsert(vm, 1) + + } yield { + checkTestResult(insertResult, "rows inserted") + insertResult.spentGas should equal(1469167L) + + }).success() + } + + } + +} diff --git a/vm/src/it/scala/fluence/vm/LlamadbIntegrationTest.scala b/vm/src/it/scala/fluence/vm/LlamadbIntegrationTest.scala new file mode 100644 index 0000000..f52a5a9 --- /dev/null +++ b/vm/src/it/scala/fluence/vm/LlamadbIntegrationTest.scala @@ -0,0 +1,285 @@ +/* + * Copyright 2018 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm + +import cats.data.NonEmptyList +import cats.effect.{IO, Timer} + +import scala.concurrent.ExecutionContext +import scala.language.{higherKinds, implicitConversions} + +// TODO: to run this test from IDE It needs to download vm-llamadb project explicitly at first +class LlamadbIntegrationTest extends LlamadbIntegrationTestInterface { + + private implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global) + + "llamadb app" should { + + "be able to instantiate" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + state ← vm.computeVmState[IO].toVmError + } yield { + state should not be None + + }).success() + + } + + "be able to create table and insert to it" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + createResult ← createTestTable(vm) + + } yield { + checkTestResult(createResult, "rows inserted") + + }).success() + + } + + "be able to select records" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + createResult ← createTestTable(vm) + emptySelectResult ← executeSql(vm, "SELECT * FROM Users WHERE name = 'unknown'") + selectAllResult ← executeSql(vm, "SELECT min(id), max(id), count(age), sum(age), avg(age) FROM Users") + explainResult ← executeSql(vm, "EXPLAIN SELECT id, name FROM Users") + + } yield { + checkTestResult(createResult, "rows inserted") + checkTestResult(emptySelectResult, "id, name, age") + checkTestResult( + selectAllResult, + "_0, _1, _2, _3, _4\n" + + "1, 4, 4, 98, 24.5" + ) + checkTestResult( + explainResult, + "query plan\n" + + "column names: (`id`, `name`)\n" + + "(scan `users` :source-id 0\n" + + " (yield\n" + + " (column-field :source-id 0 :column-offset 0)\n" + + " (column-field :source-id 0 :column-offset 1)))" + ) + + }).success() + + } + + "be able to delete records and drop table" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + createResult1 ← createTestTable(vm) + deleteResult ← executeSql(vm, "DELETE FROM Users WHERE id = 1") + selectAfterDeleteTable ← executeSql(vm, "SELECT * FROM Users WHERE id = 1") + + truncateResult ← executeSql(vm, "TRUNCATE TABLE Users") + selectFromTruncatedTableResult ← executeSql(vm, "SELECT * FROM Users") + + createResult2 ← createTestTable(vm) + dropTableResult ← executeSql(vm, "DROP TABLE Users") + selectFromDroppedTableResult ← executeSql(vm, "SELECT * FROM Users") + + } yield { + checkTestResult(createResult1, "rows inserted") + checkTestResult(deleteResult, "rows deleted: 1") + checkTestResult(selectAfterDeleteTable, "id, name, age") + checkTestResult(truncateResult, "rows deleted: 3") + checkTestResult(selectFromTruncatedTableResult, "id, name, age") + checkTestResult(createResult2, "rows inserted") + checkTestResult(dropTableResult, "table was dropped") + checkTestResult(selectFromDroppedTableResult, "[Error] table does not exist: users") + + }).success() + + } + + "be able to manipulate with 2 tables and selects records with join" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + createResult ← createTestTable(vm) + createRoleResult ← executeSql(vm, "CREATE TABLE Roles(user_id INT, role VARCHAR(128))") + roleInsertResult ← executeSql( + vm, + "INSERT INTO Roles VALUES(1, 'Teacher'), (2, 'Student'), (3, 'Scientist'), (4, 'Writer')" + ) + selectWithJoinResult ← executeSql( + vm, + "SELECT u.name AS Name, r.role AS Role FROM Users u JOIN Roles r ON u.id = r.user_id WHERE r.role = 'Writer'" + ) + deleteResult ← executeSql( + vm, + "DELETE FROM Users WHERE id = (SELECT user_id FROM Roles WHERE role = 'Student')" + ) + updateResult ← executeSql( + vm, + "UPDATE Roles r SET r.role = 'Professor' WHERE r.user_id = " + + "(SELECT id FROM Users WHERE name = 'Sara')" + ) + + } yield { + checkTestResult(createResult, "rows inserted") + checkTestResult(createRoleResult, "table created") + checkTestResult(roleInsertResult, "rows inserted: 4") + checkTestResult( + selectWithJoinResult, + "name, role\n" + + "Tagless Final, Writer" + ) + checkTestResult(deleteResult, "rows deleted: 1") + checkTestResult(updateResult, "[Error] subquery must yield exactly one row") + + }).success() + + } + + "be able to operate with empty strings" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + _ ← executeSql(vm, "") + _ ← createTestTable(vm) + emptyQueryResult ← executeSql(vm, "") + + } yield { + checkTestResult( + emptyQueryResult, + "[Error] Expected SELECT, INSERT, CREATE, DELETE, TRUNCATE or EXPLAIN statement; got no more tokens" + ) + + }).success() + } + + "doesn't fail with incorrect queries" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + _ ← createTestTable(vm) + invalidQueryResult ← executeSql(vm, "SELECT salary FROM Users") + parserErrorResult ← executeSql(vm, "123") + incompatibleTypeResult ← executeSql(vm, "SELECT * FROM Users WHERE age = 'Bob'") + + } yield { + checkTestResult(invalidQueryResult, "[Error] column does not exist: salary") + checkTestResult( + parserErrorResult, + "[Error] Expected SELECT, INSERT, CREATE, DELETE, TRUNCATE or EXPLAIN statement; got Number(\"123\")" + ) + checkTestResult(incompatibleTypeResult, "[Error] 'Bob' cannot be cast to Integer { signed: true, bytes: 8 }") + + }).success() + } + + "be able to launch VM with 4 MiB memory and to insert a lot of data" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + _ ← createTestTable(vm) + + // allocate ~1 MiB memory + insertResult1 ← executeInsert(vm, 512) + insertResult2 ← executeInsert(vm, 512) + + } yield { + checkTestResult(insertResult1, "rows inserted") + checkTestResult(insertResult2, "rows inserted") + + }).success() + + } + + "be able to launch VM with 4 MiB memory and a lot of data inserts" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.4Mb") + _ ← createTestTable(vm) + + // trying to insert 1024 time by 1 KiB + _ = for (_ ← 1 to 1024) yield { executeInsert(vm, 1) }.value.unsafeRunSync + insertResult ← executeInsert(vm, 1) + + } yield { + checkTestResult(insertResult, "rows inserted") + + }).success() + + } + + "be able to launch VM with 100 MiB memory and to insert a lot of data" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.100Mb") + _ ← createTestTable(vm) + + // allocate ~30 MiB memory + insertResult1 ← executeInsert(vm, 15 * 1024) + insertResult2 ← executeInsert(vm, 15 * 1024) + + } yield { + checkTestResult(insertResult1, "rows inserted") + checkTestResult(insertResult2, "rows inserted") + + }).success() + } + + "be able to launch VM with 100 MiB memory and a lot of data inserts" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.100Mb") + _ ← createTestTable(vm) + + // trying to insert 30 time by ~100 KiB + _ = for (_ ← 1 to 30) yield { executeInsert(vm, 100) }.value.unsafeRunSync + insertResult ← executeInsert(vm, 1) + + } yield { + checkTestResult(insertResult, "rows inserted") + + }).success() + + } + + "be able to launch VM with 2 GiB memory and to allocate 256 MiB of continuously memory" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.2Gb") + _ ← executeSql(vm, "create table USERS(name varchar(" + 256 * 1024 * 1024 + "))") + + // trying to insert two records to ~256 MiB field + insertResult1 ← executeSql(vm, "insert into USERS values(\'" + "A" * 1024 + "\')") + insertResult2 ← executeSql(vm, "insert into USERS values(\'" + "A" * 1024 + "\')") + + } yield { + checkTestResult(insertResult1, "rows inserted") + checkTestResult(insertResult2, "rows inserted") + + }).success() + } + + "be able to launch VM with 2 GiB memory and a lot of data inserts" in { + (for { + vm ← WasmVm[IO](NonEmptyList.one(llamadbFilePath), "fluence.vm.client.2Gb") + _ ← createTestTable(vm) + + // trying to insert 30 time by ~200 KiB + _ = for (_ ← 1 to 30) yield { executeInsert(vm, 200) }.value.unsafeRunSync + insertResult ← executeInsert(vm, 1) + + } yield { + checkTestResult(insertResult, "rows inserted") + + }).success() + } + + } + +} diff --git a/vm/src/it/scala/fluence/vm/LlamadbIntegrationTestInterface.scala b/vm/src/it/scala/fluence/vm/LlamadbIntegrationTestInterface.scala new file mode 100644 index 0000000..df03184 --- /dev/null +++ b/vm/src/it/scala/fluence/vm/LlamadbIntegrationTestInterface.scala @@ -0,0 +1,59 @@ +/* + * Copyright 2018 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm + +import cats.data.EitherT +import cats.effect.IO +import fluence.vm.error.VmError +import org.scalatest.EitherValues +import fluence.vm.Utils.getModuleDirPrefix + +import scala.language.{higherKinds, implicitConversions} + +trait LlamadbIntegrationTestInterface extends AppIntegrationTest with EitherValues { + + protected val llamadbFilePath: String = getModuleDirPrefix() + + "/src/it/resources/llama_db.wasm" + + protected def executeSql(implicit vm: WasmVm, sql: String): EitherT[IO, VmError, InvocationResult] = + for { + result ← vm.invoke[IO](sql.getBytes()) + _ ← vm.computeVmState[IO].toVmError + } yield result + + protected def createTestTable(vm: WasmVm): EitherT[IO, VmError, InvocationResult] = + for { + _ ← executeSql(vm, "CREATE TABLE Users(id INT, name TEXT, age INT)") + insertResult ← executeSql( + vm, + "INSERT INTO Users VALUES(1, 'Monad', 23)," + + "(2, 'Applicative Functor', 19)," + + "(3, 'Free Monad', 31)," + + "(4, 'Tagless Final', 25)" + ) + } yield insertResult + + // inserts about (recordsCount KiB + const bytes) + protected def executeInsert(vm: WasmVm, recordsCount: Int): EitherT[IO, VmError, InvocationResult] = + for { + result ← executeSql( + vm, + "INSERT into USERS VALUES(1, 'A', 1)" + (",(1, \'" + "A" * 1024 + "\', 1)") * recordsCount + ) + } yield result + +} diff --git a/vm/src/main/resources/reference.conf b/vm/src/main/resources/reference.conf new file mode 100644 index 0000000..4092266 --- /dev/null +++ b/vm/src/main/resources/reference.conf @@ -0,0 +1,34 @@ +# +# These settings describe the reasonable defaults for WasmVm. +# + +fluence.vm.client { + + # To obtain deterministic execution, all Wasm memory is preallocated on the VM startup. + # This parameter defines count of Wasm pages that should be preallocated. Each page contains 65536 bytes of data, + # `65536 * 1600 ~ 100MB` + memPagesCount: 1600 + + # if true, allows Wasm code to use logging + loggerEnabled: true + + # Memory will be split by chunks to be able to build Merkle Tree on top of it. + # Size of memory in bytes must be dividable by chunkSize. + chunkSize: 4096 + + mainModuleConfig: { + # The main module name according to the conventions should be non set + # name: "main" + + # The name of function that should be called for allocation memory. This function + # is used for passing array of bytes to the main module. + allocateFunctionName: "allocate" + + # The name of function that should be called for deallocation of + # previously allocated memory by allocateFunction. + deallocateFunctionName: "deallocate" + + # The name of the main module handler function. + invokeFunctionName: "invoke" + } +} diff --git a/vm/src/main/rust/Cargo.lock b/vm/src/main/rust/Cargo.lock new file mode 100644 index 0000000..9aec554 --- /dev/null +++ b/vm/src/main/rust/Cargo.lock @@ -0,0 +1,1272 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "Frank" +version = "0.1.0" +dependencies = [ + "boolinator 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "exitfailure 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "jni 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.40.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pwasm-utils 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)", + "wasmer-runtime-core 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)", +] + +[[package]] +name = "aho-corasick" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayref" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ascii" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "atty" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bincode" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bindgen" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "which 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake2b_simd" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "boolinator" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cexpr" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clang-sys" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cmake" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "combine" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ascii 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cranelift-bforest" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-entity 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-codegen" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-bforest 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen-meta 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen-shared 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen-shared 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cranelift-entity" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cranelift-native" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-queue" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "env_logger" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "errno" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "error-chain" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "exitfailure" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "humantime" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "indexmap" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "jni" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "combine 3.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libloading" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lock_api" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memchr" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memoffset" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nix" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "page_size" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-wasm" +version = "0.40.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pwasm-utils" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.40.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quick-error" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "raw-cpuid" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon-core" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "regex" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-hash" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "same-file" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde-bench" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_bytes" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "target-lexicon" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termcolor" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "walkdir" +version = "2.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmer-clif-backend" +version = "0.7.0" +source = "git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering#ca0054f2a82e7d00494bae8b3043e760044cbb96" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-native 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-clif-fork-frontend 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-clif-fork-wasm 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)", + "wasmer-win-exception-handler 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)", + "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmer-clif-fork-frontend" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmer-clif-fork-wasm" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-clif-fork-frontend 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmer-runtime" +version = "0.7.0" +source = "git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering#ca0054f2a82e7d00494bae8b3043e760044cbb96" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-clif-backend 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)", + "wasmer-runtime-core 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)", +] + +[[package]] +name = "wasmer-runtime-core" +version = "0.7.0" +source = "git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering#ca0054f2a82e7d00494bae8b3043e760044cbb96" +dependencies = [ + "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmer-win-exception-handler" +version = "0.7.0" +source = "git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering#ca0054f2a82e7d00494bae8b3043e760044cbb96" +dependencies = [ + "bindgen 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmparser" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "which" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wincolor" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" +"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum ascii 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" +"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" +"checksum backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2" +"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" +"checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7" +"checksum bindgen 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebd71393f1ec0509b553aa012b9b58e81dadbdff7130bd3b8cba576e69b32f75" +"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" +"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" +"checksum boolinator 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" +"checksum cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +"checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af" +"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" +"checksum combine 3.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" +"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" +"checksum cranelift-bforest 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fff04f4ad82c9704a22e753c6268cc6a89add76f094b837cefbba1c665411451" +"checksum cranelift-codegen 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6ff4a221ec1b95df4b1d20a99fec4fe92a28bebf3a815f2eca72b26f9a627485" +"checksum cranelift-codegen-meta 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd47f665e2ee8f177b97d1f5ce2bd70f54d3b793abb26d92942bfaa4a381fe9f" +"checksum cranelift-codegen-shared 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05bb95945fd940bd5fc2616b063ce69e55de3d9449a32fa40f6bb99a927085bf" +"checksum cranelift-entity 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e8753f15d9bde04988834705d437b6f6e4b4da0527968b8d40d7342262d43052" +"checksum cranelift-native 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd16b58e95af9ee837218cf41e70306becc1fc7d7dada55dac42df5130a4a4ba" +"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" +"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" +"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" +"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" +"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" +"checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e" +"checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" +"checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" +"checksum exitfailure 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ff5bd832af37f366c6c194d813a11cd90ac484f124f079294f28e357ae40515" +"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" +"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" +"checksum indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3" +"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum jni 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e00f1fd30a82a801f8bf38bcb0895088a0013cde111acb713c0824edc372aa4" +"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" +"checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" +"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f89ef58b3d32420dbd1a43d2f38ae92f6239ef12bb556ab09ca55445f5a67242" +"checksum parity-wasm 0.40.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1e39faaa292a687ea15120b1ac31899b13586446521df6c149e46f1584671e0f" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" +"checksum pwasm-utils 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d473123ba135028544926f7aa6f34058d8bc6f120c4fcd3777f84af724280b3" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d" +"checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" +"checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" +"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" +"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" +"checksum serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd" +"checksum serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "45af0182ff64abaeea290235eb67da3825a576c5d53e642c4d5b652e12e6effc" +"checksum serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "11e410fde43e157d789fc290d26bc940778ad0fdd47836426fbac36573710dbb" +"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" +"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" +"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" +"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" +"checksum target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7975cb2c6f37d77b190bc5004a2bb015971464756fde9514651a525ada2a741a" +"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" +"checksum wasmer-clif-backend 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)" = "" +"checksum wasmer-clif-fork-frontend 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0cf2f552a9c1fda0555087170424bd8fedc63a079a97bb5638a4ef9b0d9656aa" +"checksum wasmer-clif-fork-wasm 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0073b512e1af5948d34be7944b74c747bbe735ccff2e2f28c26ed4c90725de8e" +"checksum wasmer-runtime 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)" = "" +"checksum wasmer-runtime-core 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)" = "" +"checksum wasmer-win-exception-handler 0.7.0 (git+http://github.com/fluencelabs/wasmer?branch=clif_jni_hardering)" = "" +"checksum wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5083b449454f7de0b15f131eee17de54b5a71dcb9adcf11df2b2f78fad0cd82" +"checksum which 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "240a31163872f7e8e49f35b42b58485e35355b07eb009d9f3686733541339a69" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" diff --git a/vm/src/main/rust/Cargo.toml b/vm/src/main/rust/Cargo.toml new file mode 100644 index 0000000..80fac6b --- /dev/null +++ b/vm/src/main/rust/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "Frank" +description = "Virtual machine based on Wasmer for the Fluence network" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" +license = "Apache-2.0" +keywords = ["fluence", "webassembly", "wasmer", "execution environment"] +categories = ["webassembly"] +repository = "https://github.com/fluencelabs/fluence/tree/master/vm/frank" +maintenance = { status = "actively-developed" } + +[lib] +name = "frank" +path = "src/lib.rs" +crate-type = ["cdylib"] + +[[bin]] +name = "frank" +path = "src/main.rs" + +[dependencies] +wasmer-runtime = {git = "http://github.com/fluencelabs/wasmer", branch = "clif_jni_hardering"} +wasmer-runtime-core = {git = "http://github.com/fluencelabs/wasmer", branch = "clif_jni_hardering"} +jni = "0.13.1" +failure = "0.1.5" +lazy_static = "1.4.0" +sha2 = "0.8.0" +clap = "2.33.0" +exitfailure = "0.5.1" +boolinator = "2.4.0" +parity-wasm = "0.40.3" +pwasm-utils = "0.11.0" + +[profile.release] +opt-level = 3 +debug = false +lto = true +debug-assertions = false +overflow-checks = false +panic = "abort" diff --git a/vm/src/main/rust/src/jni/exports.rs b/vm/src/main/rust/src/jni/exports.rs new file mode 100644 index 0000000..1471051 --- /dev/null +++ b/vm/src/main/rust/src/jni/exports.rs @@ -0,0 +1,143 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// Defines export functions that will be accessible from the Scala part. + +use crate::jni::jni_results::*; +use crate::vm::config::Config; +use crate::vm::errors::FrankError; +use crate::vm::frank::{Frank, FRANK}; +use crate::vm::frank_result::FrankResult; +use crate::vm::prepare::prepare_module; + +use jni::objects::{JClass, JObject, JString}; +use jni::sys::jbyteArray; +use jni::JNIEnv; +use sha2::digest::generic_array::GenericArray; +use std::fs; + +/// Initializes Frank virtual machine. +/// This method is exported to Scala. +/// +/// Arguments +/// +/// * env - corresponding java native interface +/// * _class - represents caller class +/// * module_path - a path to module that should be loaded to Frank virtual machine +/// * config - contains some configs that manage module loading and instantiation process +/// +/// Returns a object of RawInitializationResult case class +#[no_mangle] +pub extern "system" fn Java_fluence_vm_frank_FrankAdapter_initialize<'a>( + env: JNIEnv<'a>, + _class: JClass, + module_path: JString, + config: JObject, +) -> JObject<'a> { + fn initialize<'a>( + env: &JNIEnv<'a>, + module_path: JString, + config: JObject, + ) -> Result<(bool), FrankError> { + let module_path: String = env.get_string(module_path)?.into(); + let config = Config::new(&env, config)?; + + let wasm_code = fs::read(module_path)?; + let prepared_module = prepare_module(&wasm_code, &config)?; + let frank = Frank::new(&prepared_module, config)?; + + unsafe { FRANK = Some(Box::new(frank.0)) }; + + Ok(frank.1) + } + + match initialize(&env, module_path, config) { + Ok(expects_eths) => create_initialization_result(&env, None, expects_eths), + Err(err) => create_initialization_result(&env, Some(format!("{}", err)), false), + } +} + +/// Invokes the main module entry point function. +/// This method is exported to Scala. +/// +/// Arguments +/// +/// * env - corresponding java native interface +/// * _class - represents caller class +/// * fn_argument - an argument for thr main module entry point function +/// +/// Returns a object of RawInvocationResult case class +#[no_mangle] +pub extern "system" fn Java_fluence_vm_frank_FrankAdapter_invoke<'a>( + env: JNIEnv<'a>, + _class: JClass, + fn_argument: jbyteArray, +) -> JObject<'a> { + fn invoke(env: &JNIEnv, fn_argument: jbyteArray) -> Result { + let input_len = env.get_array_length(fn_argument)?; + let mut input = vec![0; input_len as _]; + env.get_byte_array_region(fn_argument, 0, input.as_mut_slice())?; + + // converts Vec to Vec without additional allocation + let u8_input = unsafe { + Vec::::from_raw_parts(input.as_mut_ptr() as *mut u8, input.len(), input.capacity()) + }; + std::mem::forget(input); + + unsafe { + match FRANK { + Some(ref mut vm) => Ok(vm.invoke(&u8_input)?), + None => Err(FrankError::FrankNotInitialized), + } + } + } + + match invoke(&env, fn_argument) { + Ok(result) => create_invocation_result(&env, None, result), + Err(err) => { + create_invocation_result(&env, Some(format!("{}", err)), FrankResult::default()) + } + } +} + +/// Computes hash of the internal VM state. +/// This method is exported to Scala. +/// +/// Arguments +/// +/// * env - corresponding java native interface +/// * _class - represents caller class +/// +/// Returns a object of RawStateComputationResult case class +#[no_mangle] +pub extern "system" fn Java_fluence_vm_frank_FrankAdapter_computeVmState<'a>( + env: JNIEnv<'a>, + _class: JClass, +) -> JObject<'a> { + unsafe { + match FRANK { + Some(ref mut vm) => { + let state = vm.compute_vm_state_hash(); + create_state_computation_result(&env, None, state) + } + None => create_state_computation_result( + &env, + Some(format!("{}", FrankError::FrankNotInitialized)), + GenericArray::default(), + ), + } + } +} diff --git a/vm/src/main/rust/src/jni/jni_results.rs b/vm/src/main/rust/src/jni/jni_results.rs new file mode 100644 index 0000000..8e626db --- /dev/null +++ b/vm/src/main/rust/src/jni/jni_results.rs @@ -0,0 +1,109 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// Defines functions used to construct result of the VM invocation for the Scala part. +/// Corresponding case classes could be found in vm/src/main/scala/fluence/vm/frank/result. +use crate::jni::option::*; +use crate::vm::frank_result::FrankResult; +use jni::objects::{JObject, JValue}; +use jni::JNIEnv; +use sha2::{digest::generic_array::GenericArray, digest::FixedOutput, Sha256}; + +/// Creates RawInitializationResult object for the Scala part. +pub fn create_initialization_result<'a>( + env: &JNIEnv<'a>, + error: Option, + expects_eths: bool, +) -> JObject<'a> { + let error_value = match error { + Some(err) => create_some_value(&env, err), + None => create_none_value(&env), + }; + + // public static RawInitializationResult apply(final Option error, final Boolean expects_eth) { + // return RawInitializationResult$.MODULE$.apply(var0); + // } + env.call_static_method( + "fluence/vm/frank/result/RawInitializationResult", + "apply", + "(Lscala/Option;Z)Lfluence/vm/frank/result/RawInitializationResult;", + &[error_value, JValue::from(expects_eths)], + ) + .expect("jni: couldn't allocate a new RawInitializationResult object") + .l() + .expect("jni: couldn't convert RawInitializationResult to a Java Object") +} + +/// Creates RawInvocationResult object for the Scala part. +pub fn create_invocation_result<'a>( + env: &JNIEnv<'a>, + error: Option, + result: FrankResult, +) -> JObject<'a> { + let error_value = match error { + Some(err) => create_some_value(&env, err), + None => create_none_value(&env), + }; + + // TODO: here we have 2 copying of result, first is from Wasm memory to a Vec, second is + // from the Vec to Java byte array. Optimization might be possible after memory refactoring. + let outcome = env.byte_array_from_slice(&result.outcome).unwrap(); + let outcome = JObject::from(outcome); + let spent_gas = JValue::from(result.spent_gas); + + // public static RawInvocationResult apply(final Option error, final byte[] output, final long spentGas) { + // return RawInvocationResult$.MODULE$.apply(var0, var1, var2); + // } + env.call_static_method( + "fluence/vm/frank/result/RawInvocationResult", + "apply", + "(Lscala/Option;[BJ)Lfluence/vm/frank/result/RawInvocationResult;", + &[error_value, JValue::from(outcome), spent_gas], + ) + .expect("jni: couldn't allocate a new RawInvocationResult object") + .l() + .expect("jni: couldn't convert RawInvocationResult to a Java Object") +} + +/// Creates RawStateComputationResult object for the Scala part. +pub fn create_state_computation_result<'a>( + env: &JNIEnv<'a>, + error: Option, + state: GenericArray::OutputSize>, +) -> JObject<'a> { + let error_value = match error { + Some(err) => create_some_value(&env, err), + None => create_none_value(&env), + }; + + let state = env + .byte_array_from_slice(state.as_slice()) + .expect("jni: couldn't allocate enough space for byte array"); + let state = JObject::from(state); + + // public static RawStateComputationResult apply(final Option error, final byte[] state) { + // return RawStateComputationResult$.MODULE$.apply(var0, var1); + // } + env.call_static_method( + "fluence/vm/frank/result/RawStateComputationResult", + "apply", + "(Lscala/Option;[B)Lfluence/vm/frank/result/RawStateComputationResult;", + &[error_value, JValue::from(state)], + ) + .expect("jni: couldn't allocate a new RawInvocationResult object") + .l() + .expect("jni: couldn't convert RawInvocationResult to a Java Object") +} diff --git a/vm/src/main/rust/src/jni/mod.rs b/vm/src/main/rust/src/jni/mod.rs new file mode 100644 index 0000000..20dca99 --- /dev/null +++ b/vm/src/main/rust/src/jni/mod.rs @@ -0,0 +1,19 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pub mod exports; +mod jni_results; +mod option; diff --git a/vm/src/main/rust/src/jni/option.rs b/vm/src/main/rust/src/jni/option.rs new file mode 100644 index 0000000..5dd2cae --- /dev/null +++ b/vm/src/main/rust/src/jni/option.rs @@ -0,0 +1,52 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use jni::objects::{JObject, JValue}; +/// Defines functions to construct the Scala Option[String] objects by calling the apply method: +/// +/// ``` +/// public static Option apply(final Object x) { +/// return Option$.MODULE$.apply(var0); +/// } +/// ``` +use jni::JNIEnv; + +/// creates Scala None value +pub fn create_none_value<'a>(env: &JNIEnv<'a>) -> JValue<'a> { + env.call_static_method( + "scala/Option", + "apply", + "(Ljava/lang/Object;)Lscala/Option;", + &[JValue::from(JObject::null())], + ) + .expect("jni: error while creating None object") +} + +/// creates Scala Some[String] value +pub fn create_some_value<'a>(env: &JNIEnv<'a>, value: String) -> JValue<'a> { + let value = env + .new_string(value) + .expect("jni: couldn't allocate new string"); + + let value = JObject::from(value); + env.call_static_method( + "scala/Option", + "apply", + "(Ljava/lang/Object;)Lscala/Option;", + &[JValue::from(value)], + ) + .expect("jni: couldn't allocate a Some object") +} diff --git a/vm/src/main/rust/src/lib.rs b/vm/src/main/rust/src/lib.rs new file mode 100644 index 0000000..e721600 --- /dev/null +++ b/vm/src/main/rust/src/lib.rs @@ -0,0 +1,28 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] + +mod jni; +mod vm; diff --git a/vm/src/main/rust/src/main.rs b/vm/src/main/rust/src/main.rs new file mode 100644 index 0000000..557776a --- /dev/null +++ b/vm/src/main/rust/src/main.rs @@ -0,0 +1,107 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] + +/// Command-line tool intended to test Frank VM. +mod jni; +mod vm; + +use crate::vm::config::Config; +use crate::vm::prepare::prepare_module; +use crate::vm::frank::Frank; + +use clap::{App, AppSettings, Arg, SubCommand}; +use exitfailure::ExitFailure; +use failure::err_msg; +use std::fs; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); +const AUTHORS: &str = env!("CARGO_PKG_AUTHORS"); +const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION"); + +const IN_MODULE_PATH: &str = "in-wasm-path"; +const INVOKE_ARG: &str = "arg"; + +fn prepare_args<'a, 'b>() -> [Arg<'a, 'b>; 2] { + [ + Arg::with_name(IN_MODULE_PATH) + .required(true) + .takes_value(true) + .short("i") + .help("path to the wasm file"), + Arg::with_name(INVOKE_ARG) + .required(true) + .takes_value(true) + .short("a") + .help("argument for the invoke function in the Wasm module"), + ] +} + +fn execute_wasm<'a, 'b>() -> App<'a, 'b> { + SubCommand::with_name("execute") + .about("Execute provided module on the Fluence Frank VM") + .args(&prepare_args()) +} + +fn main() -> Result<(), ExitFailure> { + let app = App::new("Fluence Frank Wasm execution environment for test purposes") + .version(VERSION) + .author(AUTHORS) + .about(DESCRIPTION) + .setting(AppSettings::ArgRequiredElseHelp) + .subcommand(execute_wasm()); + + match app.get_matches().subcommand() { + ("execute", Some(arg)) => { + let config = Box::new(Config::default()); + let in_module_path = arg.value_of(IN_MODULE_PATH).unwrap(); + let wasm_code = + fs::read(in_module_path).unwrap_or_else(|err| panic!(format!("{}", err))); + let wasm_code = prepare_module(&wasm_code, &config) + .map_err(|e| panic!(format!("{}", e))) + .unwrap(); + let invoke_arg = arg.value_of(INVOKE_ARG).unwrap(); + + let _ = Frank::new(&wasm_code, config) + .map_err(|err| panic!(format!("{}", err))) + .and_then(|mut executor| executor.0.invoke(invoke_arg.as_bytes())) + .map_err(|err| panic!(format!("{}", err))) + .map(|result| { + let outcome_copy = result.outcome.clone(); + match String::from_utf8(result.outcome) { + Ok(s) => println!("result: {}\nspent gas: {} ", s, result.spent_gas), + Err(_) => println!( + "result: {:?}\nspent gas: {} ", + outcome_copy, result.spent_gas + ), + } + }); + + Ok(()) + } + + c => Err(err_msg(format!("Unexpected command: {}", c.0)).into()), + } +} diff --git a/vm/src/main/rust/src/vm/config.rs b/vm/src/main/rust/src/vm/config.rs new file mode 100644 index 0000000..5126b0a --- /dev/null +++ b/vm/src/main/rust/src/vm/config.rs @@ -0,0 +1,137 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// Defines the Config struct that is similar to vm/src/main/scala/fluence/vm/config/VmConfig.scala. +use crate::vm::errors::FrankError; +use jni::objects::{JObject, JString}; +use jni::JNIEnv; + +#[derive(Clone, Debug, PartialEq)] +pub struct Config { + /// Count of Wasm memory pages that will be preallocated on the VM startup. + /// Each Wasm pages is 65536 bytes long. + pub mem_pages_count: i32, + + /// If true, registers the logger Wasm module with name 'logger'. + /// This functionality is just for debugging, and this module will be disabled in future. + pub logger_enabled: bool, + + /// Memory will be split by chunks to be able to build Merkle Tree on top of it. + /// Size of memory in bytes must be dividable by chunkSize. + pub chunk_size: i32, + + /// The name of the main module handler function. + pub invoke_function_name: String, + + /// The name of function that should be called for allocation memory. This function + /// is used for passing array of bytes to the main module. + pub allocate_function_name: String, + + /// The name of function that should be called for deallocation of + /// previously allocated memory by allocateFunction. + pub deallocate_function_name: String, +} + +impl Default for Config { + fn default() -> Self { + // some reasonable defaults + Self { + // 65536*1600 ~ 100 Mb + mem_pages_count: 1600, + invoke_function_name: "invoke".to_string(), + allocate_function_name: "allocate".to_string(), + deallocate_function_name: "deallocate".to_string(), + logger_enabled: true, + chunk_size: 4096, + } + } +} + +impl Config { + /// Creates a new config based on the supplied Scala object Config. + /// This config should have the following structure: + /// + /// ``` + /// case class MainModuleConfig( + /// name: Option[String], + /// allocateFunctionName: String, + /// deallocateFunctionName: String, + /// invokeFunctionName: String + /// ) + /// + /// case class VmConfig( + /// memPagesCount: Int, + /// loggerEnabled: Boolean, + /// chunkSize: Int, + /// mainModuleConfig: MainModuleConfig + /// ) + /// ``` + /// + pub fn new(env: &JNIEnv, config: JObject) -> Result, FrankError> { + let mem_pages_count = env.call_method(config, "memPagesCount", "()I", &[])?.i()?; + let logger_enabled = env.call_method(config, "loggerEnabled", "()Z", &[])?.z()?; + let chunk_size = env.call_method(config, "chunkSize", "()I", &[])?.i()?; + + let main_module_config = env + .call_method( + config, + "mainModuleConfig", + "()Lfluence/vm/config/MainModuleConfig;", + &[], + )? + .l()?; + + let allocate_function_name = env + .call_method( + main_module_config, + "allocateFunctionName", + "()Ljava/lang/String;", + &[], + )? + .l()?; + let deallocate_function_name = env + .call_method( + main_module_config, + "deallocateFunctionName", + "()Ljava/lang/String;", + &[], + )? + .l()?; + let invoke_function_name = env + .call_method( + main_module_config, + "invokeFunctionName", + "()Ljava/lang/String;", + &[], + )? + .l()?; + + // converts JObject to JString (without copying, just enum type changes) + let allocate_function_name = env.get_string(JString::from(allocate_function_name))?; + let deallocate_function_name = env.get_string(JString::from(deallocate_function_name))?; + let invoke_function_name = env.get_string(JString::from(invoke_function_name))?; + + Ok(Box::new(Self { + mem_pages_count, + logger_enabled, + chunk_size, + // and then finally to Rust String (requires one copy) + invoke_function_name: String::from(invoke_function_name), + allocate_function_name: String::from(allocate_function_name), + deallocate_function_name: String::from(deallocate_function_name), + })) + } +} diff --git a/vm/src/main/rust/src/vm/errors.rs b/vm/src/main/rust/src/vm/errors.rs new file mode 100644 index 0000000..ece88f0 --- /dev/null +++ b/vm/src/main/rust/src/vm/errors.rs @@ -0,0 +1,143 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use jni::errors::Error as JNIWrapperError; +use wasmer_runtime::error::{ + CallError, CompileError, CreationError, Error, ResolveError, RuntimeError, +}; + +// TODO: more errors to come (when preparation step will be completely landed) +/// Errors related to the preparation (instrumentation and so on) and compilation by Wasmer steps. +pub enum InitializationError { + /// Error that raises during compilation Wasm code by Wasmer. + WasmerCreationError(String), + + /// Error that raises during creation of some Wasm objects (like table and memory) by Wasmer. + WasmerCompileError(String), + + /// Error that raises on the preparation step. + PrepareError(String), +} + +pub enum FrankError { + /// Errors related to the preparation (instrumentation and so on) and compilation by Wasmer steps. + InstantiationError(String), + + /// Errors related to parameter passing from Java to Rust and back. + JNIError(String), + + /// Errors for I/O errors raising while opening a file. + IOError(String), + + /// This error type is produced by Wasmer during resolving a Wasm function. + WasmerResolveError(String), + + /// Error related to calling a main Wasm module. + WasmerInvokeError(String), + + /// Error indicates that smth really bad happened (like removing the global Frank state). + FrankNotInitialized, +} + +impl std::fmt::Display for InitializationError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + InitializationError::WasmerCompileError(msg) => write!(f, "{}", msg), + InitializationError::WasmerCreationError(msg) => write!(f, "{}", msg), + InitializationError::PrepareError(msg) => write!(f, "{}", msg), + } + } +} + +impl std::fmt::Display for FrankError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + FrankError::InstantiationError(msg) => write!(f, "InstantiationError: {}", msg), + FrankError::JNIError(msg) => write!(f, "JNIError: {}", msg), + FrankError::IOError(msg) => write!(f, "IOError: {}", msg), + FrankError::WasmerResolveError(msg) => write!(f, "WasmerResolveError: {}", msg), + FrankError::WasmerInvokeError(msg) => write!(f, "WasmerInvokeError: {}", msg), + FrankError::FrankNotInitialized => write!( + f, + "Attempt to use invoke virtual machine while it hasn't been initialized.\ + Please call the initialization method first." + ), + } + } +} + +impl From for InitializationError { + fn from(err: CreationError) -> Self { + InitializationError::WasmerCreationError(format!("{}", err)) + } +} + +impl From for InitializationError { + fn from(err: CompileError) -> Self { + InitializationError::WasmerCompileError(format!("{}", err)) + } +} + +impl From for InitializationError { + fn from(err: parity_wasm::elements::Error) -> Self { + InitializationError::PrepareError(format!("{}", err)) + } +} + +impl From for FrankError { + fn from(err: InitializationError) -> Self { + FrankError::InstantiationError(format!("{}", err)) + } +} + +impl From for FrankError { + fn from(err: JNIWrapperError) -> Self { + FrankError::JNIError(format!("{}", err)) + } +} + +impl From for FrankError { + fn from(err: CallError) -> Self { + match err { + CallError::Resolve(err) => FrankError::WasmerResolveError(format!("{}", err)), + CallError::Runtime(err) => FrankError::WasmerInvokeError(format!("{}", err)), + } + } +} + +impl From for FrankError { + fn from(err: ResolveError) -> Self { + FrankError::WasmerResolveError(format!("{}", err)) + } +} + +impl From for FrankError { + fn from(err: RuntimeError) -> Self { + FrankError::WasmerInvokeError(format!("{}", err)) + } +} + +impl From for FrankError { + fn from(err: Error) -> Self { + FrankError::WasmerInvokeError(format!("{}", err)) + } +} + +impl From for FrankError { + fn from(err: std::io::Error) -> Self { + FrankError::IOError(format!("{}", err)) + } +} diff --git a/vm/src/main/rust/src/vm/frank.rs b/vm/src/main/rust/src/vm/frank.rs new file mode 100644 index 0000000..7520eb6 --- /dev/null +++ b/vm/src/main/rust/src/vm/frank.rs @@ -0,0 +1,206 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{ + vm::modules::env_module::EnvModule, + vm::config::Config, + vm::errors::FrankError, + vm::frank_result::FrankResult, +}; + +use sha2::{digest::generic_array::GenericArray, digest::FixedOutput, Digest, Sha256}; +use std::ffi::c_void; +use wasmer_runtime::{func, imports, instantiate, Ctx, Func, Instance}; +use failure::_core::marker::PhantomData; +use wasmer_runtime_core::memory::ptr::{Array, WasmPtr}; + +pub struct Frank { + instance: &'static Instance, + + // It is safe to use unwrap() while calling these functions because Option is used here + // to allow partially initialization of the struct. And all Option fields will contain + // Some if invoking Frank::new is succeed. + allocate: Option>, + deallocate: Option>, + invoke: Option>, + + _tag: PhantomData<&'static Instance>, +} + +impl Drop for Frank { + // In normal situation this method should be called only while VM shutting down. + fn drop(&mut self) { + drop(self.allocate.as_ref()); + drop(self.deallocate.as_ref()); + drop(self.invoke.as_ref()); + drop(Box::from(self.instance)); + } +} + +// Waiting for new release of Wasmer with https://github.com/wasmerio/wasmer/issues/748. +// It will allow to use lazy_static here. thread_local isn't suitable in our case because +// it is difficult to guarantee that jni code will be called on the same thead context +// every time from the Scala part. +pub static mut FRANK: Option> = None; + +// A little hack: exporting functions with this name means that this module expects Ethereum blocks. +// Will be changed in the future. +const ETH_FUNC_NAME: &str = "expects_eth"; + +impl Frank { + /// Writes given value on the given address. + fn write_to_mem(&mut self, address: usize, value: &[u8]) -> Result<(), FrankError> { + let memory = self.instance.context().memory(0); + + for (byte_id, cell) in memory.view::()[address as usize..(address + value.len())] + .iter() + .enumerate() + { + cell.set(value[byte_id]); + } + + Ok(()) + } + + /// Reads given count of bytes from given address. + fn read_result_from_mem(&self, address: usize) -> Result, FrankError> { + let memory = self.instance.context().memory(0); + + let mut result_size: usize = 0; + + for (byte_id, cell) in memory.view::()[address..address + 4].iter().enumerate() { + result_size |= (cell.get() as usize) << (8 * byte_id); + } + + let mut result = Vec::::with_capacity(result_size); + for cell in memory.view()[(address + 4) as usize..(address + result_size + 4)].iter() { + result.push(cell.get()); + } + + Ok(result) + } + + /// Invokes a main module supplying byte array and expecting byte array with some outcome back. + pub fn invoke(&mut self, fn_argument: &[u8]) -> Result { + // renew the state of the registered environment module to track spent gas and eic + let env: &mut EnvModule = unsafe { &mut *(self.instance.context().data as *mut EnvModule) }; + env.renew_state(); + + // allocate memory for the given argument and write it to memory + let argument_len = fn_argument.len() as i32; + let argument_address = if argument_len != 0 { + let address = self.allocate.as_ref().unwrap().call(argument_len)?; + self.write_to_mem(address as usize, fn_argument)?; + address + } else { + 0 + }; + + // invoke a main module, read a result and deallocate it + let result_address = self + .invoke + .as_ref() + .unwrap() + .call(argument_address, argument_len)?; + let result = self.read_result_from_mem(result_address as _)?; + self.deallocate + .as_ref() + .unwrap() + .call(result_address, result.len() as i32)?; + + let state = env.get_state(); + Ok(FrankResult::new(result, state.0, state.1)) + } + + /// Computes the virtual machine state. + pub fn compute_vm_state_hash( + &mut self, + ) -> GenericArray::OutputSize> { + let mut hasher = Sha256::new(); + let memory = self.instance.context().memory(0); + + let wasm_ptr = WasmPtr::::new(0 as _); + let raw_mem = wasm_ptr + .deref(memory, 0, (memory.size().bytes().0 - 1) as _) + .expect("frank: internal error in compute_vm_state_hash"); + let raw_mem: &[u8] = unsafe { &*(raw_mem as *const [std::cell::Cell] as *const [u8]) }; + + hasher.input(raw_mem); + hasher.result() + } + + /// Creates a new virtual machine executor. + pub fn new(module: &[u8], config: Box) -> Result<(Self, bool), FrankError> { + let env_state = move || { + // allocate EnvModule on the heap + let env_module = EnvModule::new(); + let dtor = (|data: *mut c_void| unsafe { + drop(Box::from_raw(data as *mut EnvModule)); + }) as fn(*mut c_void); + + // and then release corresponding Box object obtaining the raw pointer + (Box::leak(env_module) as *mut EnvModule as *mut c_void, dtor) + }; + + let import_objects = imports! { + // this will enforce Wasmer to register EnvModule in the ctx.data field + env_state, + "logger" => { + "log_utf8_string" => func!(logger_log_utf8_string), + }, + "env" => { + "gas" => func!(update_gas_counter), + "eic" => func!(update_eic), + }, + }; + + let instance: &'static mut Instance = + Box::leak(Box::new(instantiate(module, &import_objects)?)); + let expects_eth = instance.func::<(), ()>(ETH_FUNC_NAME).is_ok(); + + Ok(( + Self { + instance, + allocate: Some(instance.func::<(i32), i32>(&config.allocate_function_name)?), + deallocate: Some( + instance.func::<(i32, i32), ()>(&config.deallocate_function_name)?, + ), + invoke: Some(instance.func::<(i32, i32), i32>(&config.invoke_function_name)?), + _tag: PhantomData, + }, + expects_eth, + )) + } +} + +// Prints utf8 string of the given size from the given offset. +fn logger_log_utf8_string(ctx: &mut Ctx, offset: i32, size: i32) { + let wasm_ptr = WasmPtr::::new(offset as _); + match wasm_ptr.get_utf8_string(ctx.memory(0), size as _) { + Some(msg) => print!("{}", msg), + None => print!("frank logger: incorrect UTF8 string's been supplied to logger"), + } +} + +fn update_gas_counter(ctx: &mut Ctx, spent_gas: i32) { + let env: &mut EnvModule = unsafe { &mut *(ctx.data as *mut EnvModule) }; + env.gas(spent_gas); +} + +fn update_eic(ctx: &mut Ctx, eic: i32) { + let env: &mut EnvModule = unsafe { &mut *(ctx.data as *mut EnvModule) }; + env.eic(eic); +} diff --git a/vm/src/main/rust/src/vm/frank_result.rs b/vm/src/main/rust/src/vm/frank_result.rs new file mode 100644 index 0000000..891576f --- /dev/null +++ b/vm/src/main/rust/src/vm/frank_result.rs @@ -0,0 +1,42 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#[derive(Clone, Debug, PartialEq)] +pub struct FrankResult { + pub outcome: Vec, + pub spent_gas: i64, + pub eic: i64, +} + +impl FrankResult { + pub fn new(outcome: Vec, spent_gas: i64, eic: i64) -> Self { + Self { + outcome, + spent_gas, + eic, + } + } +} + +impl Default for FrankResult { + fn default() -> Self { + Self { + outcome: Vec::new(), + spent_gas: 0, + eic: 0, + } + } +} diff --git a/vm/src/main/rust/src/vm/mod.rs b/vm/src/main/rust/src/vm/mod.rs new file mode 100644 index 0000000..dc78150 --- /dev/null +++ b/vm/src/main/rust/src/vm/mod.rs @@ -0,0 +1,22 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pub mod config; +pub mod errors; +pub mod frank; +pub mod frank_result; +pub mod prepare; +mod modules; diff --git a/vm/src/main/rust/src/vm/modules/env_module.rs b/vm/src/main/rust/src/vm/modules/env_module.rs new file mode 100644 index 0000000..74b5186 --- /dev/null +++ b/vm/src/main/rust/src/vm/modules/env_module.rs @@ -0,0 +1,61 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// Defines the environment module used for tracking execution state. +use std::ops::AddAssign; + +#[derive(Clone, Debug, PartialEq)] +pub struct EnvModule { + spent_gas: i64, + eic: i64, +} + +impl EnvModule { + pub fn new() -> Box { + Box::new(Self { + spent_gas: 0i64, + eic: 0i64, + }) + } + + pub fn gas(&mut self, gas: i32) { + // TODO: check for overflow + self.spent_gas.add_assign(i64::from(gas)); + } + + pub fn eic(&mut self, eic: i32) { + // TODO: check for overflow + self.eic.add_assign(i64::from(eic)); + } + + pub fn get_state(&self) -> (i64, i64) { + (self.spent_gas, self.eic) + } + + pub fn renew_state(&mut self) { + self.spent_gas = 0; + self.eic = 0; + } +} + +impl Default for EnvModule { + fn default() -> Self { + Self { + spent_gas: 0i64, + eic: 0i64, + } + } +} diff --git a/vm/src/main/rust/src/vm/modules/mod.rs b/vm/src/main/rust/src/vm/modules/mod.rs new file mode 100644 index 0000000..5578b81 --- /dev/null +++ b/vm/src/main/rust/src/vm/modules/mod.rs @@ -0,0 +1,18 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO: more modules to come +pub mod env_module; diff --git a/vm/src/main/rust/src/vm/prepare.rs b/vm/src/main/rust/src/vm/prepare.rs new file mode 100644 index 0000000..194b369 --- /dev/null +++ b/vm/src/main/rust/src/vm/prepare.rs @@ -0,0 +1,83 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Similar to +// https://github.com/paritytech/substrate/blob/master/srml/contracts/src/wasm/prepare.rs +// https://github.com/nearprotocol/nearcore/blob/master/runtime/near-vm-runner/src/prepare.rs + +use parity_wasm::builder; +use parity_wasm::elements; + +use crate::vm::config::Config; +use crate::vm::errors::InitializationError; +use parity_wasm::elements::{MemorySection, MemoryType, ResizableLimits}; + +struct ModulePreparator { + module: elements::Module, +} + +impl<'a> ModulePreparator { + fn init(module_code: &[u8]) -> Result { + let module = elements::deserialize_buffer(module_code)?; + + Ok(Self { module }) + } + + fn set_mem_pages_count(self, mem_pages_count: u32) -> Self { + let Self { mut module } = self; + + // At now, there is could be only one memory section, so + // it needs just to extract previous initial page count, delete existing memory section + let limits = match module.memory_section_mut() { + Some(section) => match section.entries_mut().pop() { + Some(entry) => *entry.limits(), + None => ResizableLimits::new(0 as _, Some(mem_pages_count)), + }, + None => ResizableLimits::new(0 as _, Some(mem_pages_count)), + }; + + let memory_entry = MemoryType::new(limits.initial(), Some(mem_pages_count)); + + let mut default_mem_section = MemorySection::default(); + + // and create a new one + module + .memory_section_mut() + .unwrap_or_else(|| &mut default_mem_section) + .entries_mut() + .push(memory_entry); + + let builder = builder::from_module(module); + + Self { + module: builder.build(), + } + } + + fn to_wasm(self) -> Result, InitializationError> { + elements::serialize(self.module).map_err(Into::into) + } +} + +/// Prepares a Wasm module: +/// - set memory page count +/// - TODO: instrument module with gas counter +/// - TODO: instrument module with eic +pub fn prepare_module(module: &[u8], config: &Config) -> Result, InitializationError> { + ModulePreparator::init(module)? + .set_mem_pages_count(config.mem_pages_count as _) + .to_wasm() +} diff --git a/vm/src/main/scala/fluence/vm/InvocationResult.scala b/vm/src/main/scala/fluence/vm/InvocationResult.scala new file mode 100644 index 0000000..bae6df9 --- /dev/null +++ b/vm/src/main/scala/fluence/vm/InvocationResult.scala @@ -0,0 +1,25 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm + +/** + * Represents result of the VM invocation. + * + * @param output the computed result by Frank VM + * @param spentGas spent gas by producing the output + */ +case class InvocationResult(output: Array[Byte], spentGas: Long) diff --git a/vm/src/main/scala/fluence/vm/Utils.scala b/vm/src/main/scala/fluence/vm/Utils.scala new file mode 100644 index 0000000..b2fc070 --- /dev/null +++ b/vm/src/main/scala/fluence/vm/Utils.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm + +object Utils { + + def getModuleDirPrefix(): String = + // getProperty could return different path depends on the run method (Idea or sbt) + if (System.getProperty("user.dir").endsWith("/vm")) + System.getProperty("user.dir") + else + System.getProperty("user.dir") + "/vm/" +} diff --git a/vm/src/main/scala/fluence/vm/WasmVm.scala b/vm/src/main/scala/fluence/vm/WasmVm.scala new file mode 100644 index 0000000..71753ab --- /dev/null +++ b/vm/src/main/scala/fluence/vm/WasmVm.scala @@ -0,0 +1,100 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm + +import cats.data.{EitherT, NonEmptyList} +import cats.effect.{LiftIO, Sync} +import cats.Monad +import com.typesafe.config.{Config, ConfigFactory} +import fluence.vm.config.VmConfig +import fluence.vm.error.{InitializationError, InvocationError, StateComputationError} +import fluence.vm.frank.{FrankAdapter, FrankWasmVm} +import scodec.bits.ByteVector + +import scala.language.higherKinds + +/** + * Virtual Machine api. + */ +trait WasmVm { + + /** + * Invokes Wasm ''function'' from specified Wasm ''module''. Each function receives and returns array of bytes. + * + * Note that, modules should be registered when VM started! + * + * @param fnArgument a Function arguments + * @tparam F a monad with an ability to absorb 'IO' + */ + def invoke[F[_]: LiftIO: Monad]( + fnArgument: Array[Byte] = Array.emptyByteArray + ): EitherT[F, InvocationError, InvocationResult] + + /** + * Returns hash of all significant inner state of this VM. This function calculates + * hashes for the state of each module and then concatenates them together. + * It's behaviour will change in future, till it looks like this: + * {{{ + * vmState = hash(hash(module1 state), hash(module2 state), ...)) + * }}} + * '''Note!''' It's very expensive operation, try to avoid frequent use. + */ + def computeVmState[F[_]: LiftIO: Monad]: EitherT[F, StateComputationError, ByteVector] + + /** + * Temporary way to pass a flag from userland (the WASM file) to the Node, denotes whether an app + * expects outer world to pass Ethereum blocks data into it. + * TODO move this flag to the Smart Contract + */ + val expectsEth: Boolean +} + +object WasmVm { + val javaLibPath: String = System.getProperty("java.library.path") + println(s"java.library.path = $javaLibPath") + + /** + * Main method factory for building VM. + * Compiles all files immediately by Asmble and returns VM implementation with eager module instantiation. + * + * @param inFiles input files in wasm or wast format + * @param configNamespace a path of config in 'lightbend/config terms, please see reference.conf + */ + def apply[F[_]: Sync]( + inFiles: NonEmptyList[String], + configNamespace: String = "fluence.vm.client", + conf: ⇒ Config = ConfigFactory.load() + ): EitherT[F, InitializationError, WasmVm] = + for { + // reading config + config ← VmConfig.readT[F](configNamespace, conf) + + vmRunnerInvoker <- EitherT.right(Sync[F].delay(new FrankAdapter())) + initializationResult <- EitherT.right(Sync[F].delay(vmRunnerInvoker.initialize(inFiles.head, config))) + + _ ← EitherT.cond( + initializationResult.error.isEmpty, + (), + InitializationError(initializationResult.error.get) + ) + + } yield new FrankWasmVm( + vmRunnerInvoker, + initializationResult.expectsEth + ) + +} diff --git a/vm/src/main/scala/fluence/vm/config/VmConfig.scala b/vm/src/main/scala/fluence/vm/config/VmConfig.scala new file mode 100644 index 0000000..f473205 --- /dev/null +++ b/vm/src/main/scala/fluence/vm/config/VmConfig.scala @@ -0,0 +1,68 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm.config + +import cats.data.EitherT +import cats.Monad +import cats.syntax.either._ +import com.typesafe.config.Config +import fluence.vm.error.InitializationError + +import scala.language.higherKinds + +/** + * Main module settings. + * + * @param name a name of the main module (None means absence of name section in a Wasm module) + * @param allocateFunctionName name of a function that should be called for allocation memory + * (used for passing complex data structures) + * @param deallocateFunctionName name of a function that should be called for deallocation + * of previously allocated memory + * @param invokeFunctionName name of main module handler function + */ +case class MainModuleConfig( + name: Option[String], + allocateFunctionName: String, + deallocateFunctionName: String, + invokeFunctionName: String +) + +/** + * WasmVm settings. + * + * @param memPagesCount the maximum count of memory pages when a module doesn't say + * @param loggerEnabled if set, registers the logger Wasm module with name 'logger' + * @param chunkSize a size of the memory chunks, that memory will be split into + * @param mainModuleConfig settings for the main module + */ +case class VmConfig( + memPagesCount: Int, + loggerEnabled: Boolean, + chunkSize: Int, + mainModuleConfig: MainModuleConfig +) + +object VmConfig { + import net.ceedubs.ficus.Ficus._ + import net.ceedubs.ficus.readers.ArbitraryTypeReader._ + + def readT[F[_]: Monad](namespace: String, conf: ⇒ Config): EitherT[F, InitializationError, VmConfig] = { + EitherT + .fromEither[F](Either.catchNonFatal(conf.getConfig(namespace).as[VmConfig])) + .leftMap(e ⇒ InitializationError("Unable to parse the virtual machine config " + e)) + } +} diff --git a/vm/src/main/scala/fluence/vm/error/VmError.scala b/vm/src/main/scala/fluence/vm/error/VmError.scala new file mode 100644 index 0000000..4696b9d --- /dev/null +++ b/vm/src/main/scala/fluence/vm/error/VmError.scala @@ -0,0 +1,55 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm.error + +import scala.util.control.NoStackTrace + +/** + * Base trait for errors occurred in Virtual machine. + * + * @param message detailed error message + * @param causedBy caught [[Throwable]], if any + */ +sealed abstract class VmError(val message: String, val causedBy: Option[Throwable]) + extends Throwable(message, causedBy.orNull, true, false) with NoStackTrace + +/** + * Corresponds to errors occurred during VM initialization. + * + * @param message detailed error message + * @param causedBy caught [[Throwable]], if any + */ +case class InitializationError(override val message: String, override val causedBy: Option[Throwable] = None) + extends VmError(message, causedBy) + +/** + * Corresponds to errors occurred during VM function invocation. + * + * @param message detailed error message + * @param causedBy caught [[Throwable]], if any + */ +case class InvocationError(override val message: String, override val causedBy: Option[Throwable] = None) + extends VmError(message, causedBy) + +/** + * Corresponds to errors occurred during computing VM state hash. + * + * @param message detailed error message + * @param causedBy caught [[Throwable]], if any + */ +case class StateComputationError(override val message: String, override val causedBy: Option[Throwable] = None) + extends VmError(message, causedBy) diff --git a/vm/src/main/scala/fluence/vm/frank/FrankAdapter.scala b/vm/src/main/scala/fluence/vm/frank/FrankAdapter.scala new file mode 100644 index 0000000..b70ffff --- /dev/null +++ b/vm/src/main/scala/fluence/vm/frank/FrankAdapter.scala @@ -0,0 +1,46 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm.frank +import fluence.vm.config.VmConfig +import fluence.vm.frank.result.{RawInitializationResult, RawInvocationResult, RawStateComputationResult} +import ch.jodersky.jni.nativeLoader + +/** + * Realizes connection to the virtual machine runner based on Wasmer through JNI. + */ +@nativeLoader("frank") +class FrankAdapter { + + /** + * Initializes execution environment with given file path. + * + * @param filePath path to a wasm file + */ + @native def initialize(filePath: String, config: VmConfig): RawInitializationResult + + /** + * Invokes main module handler. + * + * @param arg argument for invoked module + */ + @native def invoke(arg: Array[Byte]): RawInvocationResult + + /** + * Returns hash of all significant inner state of the VM. + */ + @native def computeVmState(): RawStateComputationResult +} diff --git a/vm/src/main/scala/fluence/vm/frank/FrankWasmVm.scala b/vm/src/main/scala/fluence/vm/frank/FrankWasmVm.scala new file mode 100644 index 0000000..17f0b44 --- /dev/null +++ b/vm/src/main/scala/fluence/vm/frank/FrankWasmVm.scala @@ -0,0 +1,65 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm.frank + +import cats.Monad +import cats.data.EitherT +import cats.syntax.either._ +import cats.effect.{IO, LiftIO} +import fluence.vm.{InvocationResult, WasmVm} +import scodec.bits.ByteVector +import fluence.vm.error.{InvocationError, StateComputationError} +import fluence.vm.frank.result.{RawInvocationResult, RawStateComputationResult} + +import scala.language.higherKinds + +/** + * Base implementation of [[WasmVm]] based on the Wasmer execution environment. + * + * '''Note!!! This implementation isn't thread-safe. The provision of calls + * linearization is the task of the caller side.''' + */ +class FrankWasmVm( + private val vmRunnerInvoker: FrankAdapter, + val expectsEth: Boolean +) extends WasmVm { + + override def invoke[F[_]: LiftIO: Monad]( + fnArgument: Array[Byte] + ): EitherT[F, InvocationError, InvocationResult] = + EitherT( + IO(vmRunnerInvoker.invoke(fnArgument)).attempt + .to[F] + ).leftMap(e ⇒ InvocationError(s"Frank invocation failed by exception. Cause: ${e.getMessage}", Some(e))) + .subflatMap { + case RawInvocationResult(Some(err), _, _) ⇒ + InvocationError(s"Frank invocation failed. Cause: $err").asLeft[InvocationResult] + case RawInvocationResult(None, output, spentGas) ⇒ + InvocationResult(output, spentGas).asRight[InvocationError] + } + + override def computeVmState[F[_]: LiftIO: Monad]: EitherT[F, StateComputationError, ByteVector] = + EitherT( + IO(vmRunnerInvoker.computeVmState()).attempt + .to[F] + ).leftMap(e ⇒ StateComputationError(s"Frank getting VM state failed. Cause: ${e.getMessage}", Some(e))).subflatMap { + case RawStateComputationResult(Some(err), _) ⇒ + StateComputationError(s"Frank invocation failed. Cause: $err").asLeft[ByteVector] + case RawStateComputationResult(None, state) ⇒ + ByteVector(state).asRight[StateComputationError] + } +} diff --git a/vm/src/main/scala/fluence/vm/frank/result/RawInitializationResult.scala b/vm/src/main/scala/fluence/vm/frank/result/RawInitializationResult.scala new file mode 100644 index 0000000..3b0e97c --- /dev/null +++ b/vm/src/main/scala/fluence/vm/frank/result/RawInitializationResult.scala @@ -0,0 +1,24 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm.frank.result + +/** + * Represents raw JNI result of FrankAdapter::initialize invoking. + * + * @param error represent various initialization errors, None - no error occurred + */ +final case class RawInitializationResult(error: Option[String], expectsEth: Boolean) diff --git a/vm/src/main/scala/fluence/vm/frank/result/RawInvocationResult.scala b/vm/src/main/scala/fluence/vm/frank/result/RawInvocationResult.scala new file mode 100644 index 0000000..003b513 --- /dev/null +++ b/vm/src/main/scala/fluence/vm/frank/result/RawInvocationResult.scala @@ -0,0 +1,26 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm.frank.result + +/** + * Represents raw JNI result of FrankAdapter::invoke invoking. + * + * @param error represents various invocation errors, None - no error occurred + * @param output the computed result by Frank VM, valid only if no error occurred (error == None) + * @param spentGas spent gas by producing the output, valid only if no error occurred (error == None) + */ +final case class RawInvocationResult(error: Option[String], output: Array[Byte], spentGas: Long) diff --git a/vm/src/main/scala/fluence/vm/frank/result/RawStateComputationResult.scala b/vm/src/main/scala/fluence/vm/frank/result/RawStateComputationResult.scala new file mode 100644 index 0000000..7855376 --- /dev/null +++ b/vm/src/main/scala/fluence/vm/frank/result/RawStateComputationResult.scala @@ -0,0 +1,25 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm.frank.result + +/** + * Represents raw JNI result of FrankAdapter::computeVmState invoking. + * + * @param error represent various initialization errors, None - no error occurred + * @param state computed state of Frank VM, valid only if no error occurred (error == None) + */ +final case class RawStateComputationResult(error: Option[String], state: Array[Byte]) diff --git a/vm/src/main/scala/fluence/vm/package.scala b/vm/src/main/scala/fluence/vm/package.scala new file mode 100644 index 0000000..d3cf413 --- /dev/null +++ b/vm/src/main/scala/fluence/vm/package.scala @@ -0,0 +1,41 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence +import cats.Functor +import cats.data.EitherT +import fluence.vm.error.VmError + +import scala.language.higherKinds + +package object vm { + + implicit class VmErrorMapper[F[_]: Functor, E <: VmError, T](eitherT: EitherT[F, E, T]) { + + def toVmError: EitherT[F, VmError, T] = { + eitherT.leftMap { e: VmError ⇒ + e + } + } + } + + object eitherT { + implicit class EitherTOps[F[_]: Functor, A, B](ef: F[Either[A, B]]) { + def eitherT: EitherT[F, A, B] = EitherT(ef) + } + } + +} diff --git a/vm/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/vm/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000..d65c74a --- /dev/null +++ b/vm/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1,2 @@ +mock-maker-inline +# allows to mock final classes from 'asmble' project for testing \ No newline at end of file diff --git a/vm/src/test/resources/wast/bad-allocation-function-f64.wast b/vm/src/test/resources/wast/bad-allocation-function-f64.wast new file mode 100644 index 0000000..68feb3e --- /dev/null +++ b/vm/src/test/resources/wast/bad-allocation-function-f64.wast @@ -0,0 +1,24 @@ +;; this example has "bad" allocation function that returns offset out of ByteBuffer limits as f64 + +(module + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result f64) + ;; returns floating-point f64 number instead od integer + (f64.const 200000000.12345) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + (func (export "invoke") (param $0 i32 ) (param $1 i32) (result i32) + ;; simply returns 10000 + (i32.const 10000) + ) + +) diff --git a/vm/src/test/resources/wast/bad-allocation-function-i64.wast b/vm/src/test/resources/wast/bad-allocation-function-i64.wast new file mode 100644 index 0000000..e6bfa50 --- /dev/null +++ b/vm/src/test/resources/wast/bad-allocation-function-i64.wast @@ -0,0 +1,25 @@ +;; this example has "bad" allocation function that returns offset out of ByteBuffer limits as i64 + +(module + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result i64) + ;; returns maximum value of signed 64-bit integer that wittingly exceeds maximum ByteBuffer size + ;; (and the address space limit on amd64 architecture) + (i64.const 9223372036854775807) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + (func (export "invoke") (param $0 i32 ) (param $1 i32) (result i32) + ;; simply returns 10000 + (i32.const 10000) + ) + +) diff --git a/vm/src/test/resources/wast/counter-copy.wast b/vm/src/test/resources/wast/counter-copy.wast new file mode 100644 index 0000000..8ce87da --- /dev/null +++ b/vm/src/test/resources/wast/counter-copy.wast @@ -0,0 +1,77 @@ +;; copy of simple counter module with different name + +(module $CounterCopyModule + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + ;; + ;; Initializes counter with zero value + ;; + (data (i32.const 12) "\00\00\00\00") + + ;; + ;; Increments variable in memory by one and returns it. + ;; + (func (export "invoke") (param $buffer i32) (param $size i32) (result i32) + (i32.store offset=12 + (i32.const 0) + (i32.add + (i32.load offset=12 (i32.const 0)) + (i32.const 1) + ) + ) + (call $putIntResult + (i32.load offset=12 (i32.const 0)) + ) + ) + + ;; int putIntResult(int result) { + ;; const int address = 1024*1024; + ;; + ;; globalBuffer[address] = 0; + ;; globalBuffer[address + 1] = 0; + ;; globalBuffer[address + 2] = 0; + ;; globalBuffer[address + 3] = 4; + ;; + ;; for(int i = 0; i < 4; ++i) { + ;; globalBuffer[address + 4 + i ] = ((result >> 8*i) & 0xFF); + ;; } + ;; + ;; return address; + ;; } + (func $putIntResult (param $result i32) (result i32) + (local $1 i32) + (local $2 i32) + (set_local $2 (i32.const 0)) + (i32.store offset=1048592 (i32.const 0) (i32.const 4)) + (set_local $1 (i32.const 1048596)) + (loop $label$0 + (i32.store8 + (get_local $1) + (i32.shr_u (get_local $result) (get_local $2)) + ) + (set_local $1 + (i32.add (get_local $1) (i32.const 1)) + ) + (br_if $label$0 + (i32.ne + (tee_local $2 (i32.add (get_local $2) (i32.const 8))) + (i32.const 32) + ) + ) + ) + (i32.const 1048592) + ) +) diff --git a/vm/src/test/resources/wast/counter.wast b/vm/src/test/resources/wast/counter.wast new file mode 100644 index 0000000..9dc4f34 --- /dev/null +++ b/vm/src/test/resources/wast/counter.wast @@ -0,0 +1,77 @@ +;; simple in-memory counter + +(module + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + ;; + ;; Initializes counter with zero value + ;; + (data (i32.const 12) "\00\00\00\00") + + ;; + ;; Increments variable in memory by one and returns it. + ;; + (func (export "invoke") (param $buffer i32) (param $size i32) (result i32) + (i32.store offset=12 + (i32.const 0) + (i32.add + (i32.load offset=12 (i32.const 0)) + (i32.const 1) + ) + ) + (call $putIntResult + (i32.load offset=12 (i32.const 0)) + ) + ) + + ;; int putIntResult(int result) { + ;; const int address = 1024*1024; + ;; + ;; globalBuffer[address] = 0; + ;; globalBuffer[address + 1] = 0; + ;; globalBuffer[address + 2] = 0; + ;; globalBuffer[address + 3] = 4; + ;; + ;; for(int i = 0; i < 4; ++i) { + ;; globalBuffer[address + 4 + i ] = ((result >> 8*i) & 0xFF); + ;; } + ;; + ;; return address; + ;; } + (func $putIntResult (param $result i32) (result i32) + (local $1 i32) + (local $2 i32) + (set_local $2 (i32.const 0)) + (i32.store offset=1048592 (i32.const 0) (i32.const 4)) + (set_local $1 (i32.const 1048596)) + (loop $label$0 + (i32.store8 + (get_local $1) + (i32.shr_u (get_local $result) (get_local $2)) + ) + (set_local $1 + (i32.add (get_local $1) (i32.const 1)) + ) + (br_if $label$0 + (i32.ne + (tee_local $2 (i32.add (get_local $2) (i32.const 8))) + (i32.const 32) + ) + ) + ) + (i32.const 1048592) + ) +) diff --git a/vm/src/test/resources/wast/incorrect-array-returning.wast b/vm/src/test/resources/wast/incorrect-array-returning.wast new file mode 100644 index 0000000..64c895b --- /dev/null +++ b/vm/src/test/resources/wast/incorrect-array-returning.wast @@ -0,0 +1,108 @@ +;; this example has some functions that recieve and put strings + +(module + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (data 0 (offset (i32.const 128)) "Hello from Fluence Labs!\00") + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + ;; puts 0x00FFFFFF as result size in memory at offset 1048592 and returns pointer to it + (func (export "invoke") (param $buffer i32) (param $size i32) (result i32) + (local $0 i32) + (set_local $0 (i32.const 1048592)) + + (i32.store8 + (get_local $0) + (i32.const 255) + ) + (i32.store8 + (i32.add (get_local $0) (i32.const 1)) + (i32.const 255) + ) + (i32.store8 + (i32.add (get_local $0) (i32.const 2)) + (i32.const 255) + ) + (i32.store8 + (i32.add (get_local $0) (i32.const 3)) + (i32.const 0) + ) + + (i32.const 1048592) + ) + + ;; int putStringResult(const char *string, int stringSize, int address) { + ;; + ;; globalBuffer[address] = (stringSize >> 24) & 0xFF; + ;; globalBuffer[address + 1] = (stringSize >> 16) & 0xFF; + ;; globalBuffer[address + 2] = (stringSize >> 8) & 0xFF; + ;; globalBuffer[address + 3] = stringSize & 0xFF; + ;; + ;; for(int i = 0; i < stringSize; ++i) { + ;; globalBuffer[address + 4 + i] = string[i]; + ;; } + ;; } + (func $putStringResult (param $string i32) (param $stringSize i32) (param $address i32) (result i32) + (local $3 i32) + (local $4 i32) + + (i32.store8 + (get_local $address) + (get_local $stringSize) + ) + (i32.store8 + (i32.add (get_local $address) (i32.const 1)) + (i32.shr_u (get_local $stringSize) (i32.const 8)) + ) + (i32.store8 + (i32.add (get_local $address) (i32.const 2)) + (i32.shr_u (get_local $stringSize) (i32.const 16)) + ) + (i32.store8 + (i32.add (get_local $address) (i32.const 3)) + (i32.shr_u (get_local $stringSize) (i32.const 24)) + ) + + (set_local $3 (get_local $address)) + (set_local $address (i32.add (get_local $address) (i32.const 4))) + + (loop $label$0 + ;; globalBuffer[address + 4 + i] = string[i]; + (i32.store8 + (get_local $address) + (i32.load8_u (get_local $string)) + ) + + ;; ++string + (set_local $string + (i32.add (get_local $string) (i32.const 1)) + ) + + ;; ++globalBuffer + (set_local $address + (i32.add (get_local $address) (i32.const 1)) + ) + + (br_if $label$0 + (i32.ne + (tee_local $4 (i32.add (get_local $4) (i32.const 1))) + (get_local $stringSize) + ) + ) + ) + + (get_local $3) + ) +) diff --git a/vm/src/test/resources/wast/mul.wast b/vm/src/test/resources/wast/mul.wast new file mode 100644 index 0000000..50fc431 --- /dev/null +++ b/vm/src/test/resources/wast/mul.wast @@ -0,0 +1,149 @@ +;; this example simply returns product of two integers + +(module $MulModule + + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + ;; int extractInt(const char *buffer, int begin, int end) { + ;; int value = 0; + ;; const int size = end - begin; + ;; + ;; // it is assumed that bytes already in little endian + ;; for(int byteIdx = 0; byteIdx < size; ++byteIdx) { + ;; value |= buffer[begin + byteIdx] << byteIdx * 8; + ;; } + ;; + ;; return value; + ;; } + (func $extractInt (param $buffer i32) (param $begin i32) (param $end i32) (result i32) + (local $3 i32) + (block $label$0 + (br_if $label$0 + (i32.lt_s + (tee_local $3 + (i32.sub (get_local $end) (get_local $begin)) + ) + (i32.const 1) + ) + ) + + (set_local $begin + (i32.add (get_local $buffer) (get_local $begin)) + ) + + (set_local $end (i32.const 0)) + (set_local $buffer (i32.const 0)) + (loop $label$1 + (set_local $buffer + (i32.or + (i32.shl + (i32.load8_s (get_local $begin)) + (get_local $end) + ) + (get_local $buffer) + ) + ) + (set_local $begin + (i32.add (get_local $begin) (i32.const 1)) + ) + (set_local $end + (i32.add (get_local $end) (i32.const 8)) + ) + (br_if $label$1 + (tee_local $3 + (i32.add (get_local $3) (i32.const -1)) + ) + ) + ) + (return (get_local $buffer)) + ) + (i32.const 0) + ) + + ;; int invoke(const char *buffer, int size) { + ;; if(size != 8) { + ;; return 0; + ;; } + ;; + ;; const int a = extractInt(buffer, 0, 4); + ;; const int b = extractInt(buffer, 4, 8); + ;; + ;; return a * b; + ;; } + (func (export "invoke") (param $buffer i32) (param $size i32) (result i32) + (local $2 i32) + (set_local $2 (i32.const 0)) + + (block $label$0 + (br_if $label$0 + (i32.ne (get_local $size) (i32.const 8)) + ) + (set_local $2 + (i32.mul + (call $extractInt + (get_local $buffer) + (i32.const 0) + (i32.const 4) + ) + (call $extractInt + (get_local $buffer) + (i32.const 4) + (i32.const 8) + ) + ) + ) + ) + (call $putIntResult (get_local $2)) + ) + + ;; int putIntResult(int result) { + ;; const int address = 1024*1024; + ;; + ;; globalBuffer[address] = 0; + ;; globalBuffer[address + 1] = 0; + ;; globalBuffer[address + 2] = 0; + ;; globalBuffer[address + 3] = 4; + ;; + ;; for(int i = 0; i < 4; ++i) { + ;; globalBuffer[address + 4 + i ] = ((result >> 8*i) & 0xFF); + ;; } + ;; + ;; return address; + ;; } + (func $putIntResult (param $result i32) (result i32) + (local $1 i32) + (local $2 i32) + (set_local $2 (i32.const 0)) + (i32.store offset=1048592 (i32.const 0) (i32.const 4)) + (set_local $1 (i32.const 1048596)) + (loop $label$0 + (i32.store8 + (get_local $1) + (i32.shr_u (get_local $result) (get_local $2)) + ) + (set_local $1 + (i32.add (get_local $1) (i32.const 1)) + ) + (br_if $label$0 + (i32.ne + (tee_local $2 (i32.add (get_local $2) (i32.const 8))) + (i32.const 32) + ) + ) + ) + (i32.const 1048592) + ) +) diff --git a/vm/src/test/resources/wast/no-getMemory.wast b/vm/src/test/resources/wast/no-getMemory.wast new file mode 100644 index 0000000..7731306 --- /dev/null +++ b/vm/src/test/resources/wast/no-getMemory.wast @@ -0,0 +1,20 @@ +;; this example has allocate/deallocate functions but doesn't have memory sections. +;; Asmble version 0.4.0 doesn't generate getMemory function in this case. + +(module + (func (export "allocate") (param $0 i32 ) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + (func (export "invoke") (param $0 i32) (param $1 i32) (result i32) + ;; simply returns 10000 + (i32.const 10000) + ) +) diff --git a/vm/src/test/resources/wast/no-invoke.wast b/vm/src/test/resources/wast/no-invoke.wast new file mode 100644 index 0000000..15be74f --- /dev/null +++ b/vm/src/test/resources/wast/no-invoke.wast @@ -0,0 +1,19 @@ +;; this example has allocate/deallocate functions but doesn't have invoke function. + +(module + + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) +) diff --git a/vm/src/test/resources/wast/simple-array-mutation.wast b/vm/src/test/resources/wast/simple-array-mutation.wast new file mode 100644 index 0000000..2a44e26 --- /dev/null +++ b/vm/src/test/resources/wast/simple-array-mutation.wast @@ -0,0 +1,117 @@ +;; this example adds 1 to each byte in supplied string + +(module + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + ;; char* invoke(const char *array, int arraySize) { + ;; for(int i = 0; i < stringSize; ++i) { + ;; ++array[i]; + ;; } + ;; + ;; return array; + ;; } + (func (export "invoke") (param $array i32) (param $arraySize i32) (result i32) + (local $arrayIdx i32) + (loop $label$0 + (i32.store8 + (i32.add (get_local $array) (get_local $arrayIdx)) + (i32.add + (i32.load8_s + (i32.add (get_local $array) (get_local $arrayIdx)) + ) + (i32.const 1) + ) + ) + (br_if $label$0 + (i32.ne + (tee_local $arrayIdx + (i32.add (get_local $arrayIdx) (i32.const 1)) + ) + (get_local $arraySize) + ) + ) + ) + + (call $putArrayResult + (get_local $array) + (get_local $arraySize) + (i32.const 1048592) + ) + ) + + ;; int putArrayResult(const char *string, int stringSize, int address) { + ;; + ;; globalBuffer[address] = (stringSize >> 24) & 0xFF; + ;; globalBuffer[address + 1] = (stringSize >> 16) & 0xFF; + ;; globalBuffer[address + 2] = (stringSize >> 8) & 0xFF; + ;; globalBuffer[address + 3] = stringSize & 0xFF; + ;; + ;; for(int i = 0; i < stringSize; ++i) { + ;; globalBuffer[address + 4 + i] = string[i]; + ;; } + ;; } + (func $putArrayResult (param $string i32) (param $stringSize i32) (param $address i32) (result i32) + (local $3 i32) + (local $4 i32) + + (i32.store8 + (get_local $address) + (get_local $stringSize) + ) + (i32.store8 + (i32.add (get_local $address) (i32.const 1)) + (i32.shr_u (get_local $stringSize) (i32.const 8)) + ) + (i32.store8 + (i32.add (get_local $address) (i32.const 2)) + (i32.shr_u (get_local $stringSize) (i32.const 16)) + ) + (i32.store8 + (i32.add (get_local $address) (i32.const 3)) + (i32.shr_u (get_local $stringSize) (i32.const 24)) + ) + + (set_local $3 (get_local $address)) + (set_local $address (i32.add (get_local $address) (i32.const 4))) + + (loop $label$0 + ;; globalBuffer[address + 4 + i] = string[i]; + (i32.store8 + (get_local $address) + (i32.load8_u (get_local $string)) + ) + + ;; ++string + (set_local $string + (i32.add (get_local $string) (i32.const 1)) + ) + + ;; ++globalBuffer + (set_local $address + (i32.add (get_local $address) (i32.const 1)) + ) + + (br_if $label$0 + (i32.ne + (tee_local $4 (i32.add (get_local $4) (i32.const 1))) + (get_local $stringSize) + ) + ) + ) + + (get_local $3) + ) +) diff --git a/vm/src/test/resources/wast/simple-array-returning.wast b/vm/src/test/resources/wast/simple-array-returning.wast new file mode 100644 index 0000000..2996a26 --- /dev/null +++ b/vm/src/test/resources/wast/simple-array-returning.wast @@ -0,0 +1,92 @@ +;; this example simply returns pointer to "Hello from Fluence Labs!\00" string + +(module + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (data 0 (offset (i32.const 128)) "Hello from Fluence Labs!\00") + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + ;; returns pointer to const string from memory + (func (export "invoke") (param $buffer i32) (param $bufferSize i32) (result i32) + (call $putArrayResult + (i32.const 128) + (i32.const 24) + (i32.const 1048592) + ) + ) + + ;; int putArrayResult(const char *string, int stringSize, int address) { + ;; + ;; globalBuffer[address] = (stringSize >> 24) & 0xFF; + ;; globalBuffer[address + 1] = (stringSize >> 16) & 0xFF; + ;; globalBuffer[address + 2] = (stringSize >> 8) & 0xFF; + ;; globalBuffer[address + 3] = stringSize & 0xFF; + ;; + ;; for(int i = 0; i < stringSize; ++i) { + ;; globalBuffer[address + 4 + i] = string[i]; + ;; } + ;; } + (func $putArrayResult (param $string i32) (param $stringSize i32) (param $address i32) (result i32) + (local $3 i32) + (local $4 i32) + + (i32.store8 + (get_local $address) + (get_local $stringSize) + ) + (i32.store8 + (i32.add (get_local $address) (i32.const 1)) + (i32.shr_u (get_local $stringSize) (i32.const 8)) + ) + (i32.store8 + (i32.add (get_local $address) (i32.const 2)) + (i32.shr_u (get_local $stringSize) (i32.const 16)) + ) + (i32.store8 + (i32.add (get_local $address) (i32.const 3)) + (i32.shr_u (get_local $stringSize) (i32.const 24)) + ) + + (set_local $3 (get_local $address)) + (set_local $address (i32.add (get_local $address) (i32.const 4))) + + (loop $label$0 + ;; globalBuffer[address + 4 + i] = string[i]; + (i32.store8 + (get_local $address) + (i32.load8_u (get_local $string)) + ) + + ;; ++string + (set_local $string + (i32.add (get_local $string) (i32.const 1)) + ) + + ;; ++globalBuffer + (set_local $address + (i32.add (get_local $address) (i32.const 1)) + ) + + (br_if $label$0 + (i32.ne + (tee_local $4 (i32.add (get_local $4) (i32.const 1))) + (get_local $stringSize) + ) + ) + ) + + (get_local $3) + ) +) diff --git a/vm/src/test/resources/wast/simple-string-passing.wast b/vm/src/test/resources/wast/simple-string-passing.wast new file mode 100644 index 0000000..273b976 --- /dev/null +++ b/vm/src/test/resources/wast/simple-string-passing.wast @@ -0,0 +1,90 @@ +;; this example calculates circular xor of supplied buffer + +(module + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + ;; int invoke(const char *buffer, int size) { + ;; int value = 0; + ;; + ;; for(int byteId = 0; byteId < size; ++byteId) { + ;; value ^= buffer[byteId]; + ;; } + ;; + ;; return value; + ;; } + (func (export "invoke") (param $buffer i32 ) (param $size i32) (result i32) + (local $value i32) + (set_local $value (i32.const 0) ) + (block $label$0 + (br_if $label$0 + (i32.lt_s (get_local $size) (i32.const 1) ) + ) + + (loop $label$1 + (set_local $value + (i32.xor (get_local $value) (i32.load8_s (get_local $buffer) ) ) + ) + (set_local $buffer + (i32.add (get_local $buffer) (i32.const 1) ) + ) + (br_if $label$1 + (tee_local $size + (i32.add (get_local $size) (i32.const -1) ) + ) + ) + ) + ) + (call $putIntResult (get_local $value)) + ) + + ;; int putIntResult(int result) { + ;; const int address = 1024*1024; + ;; + ;; globalBuffer[address] = 0; + ;; globalBuffer[address + 1] = 0; + ;; globalBuffer[address + 2] = 0; + ;; globalBuffer[address + 3] = 4; + ;; + ;; for(int i = 0; i < 4; ++i) { + ;; globalBuffer[address + 4 + i ] = ((result >> 8*i) & 0xFF); + ;; } + ;; + ;; return address; + ;; } + (func $putIntResult (param $result i32) (result i32) + (local $1 i32) + (local $2 i32) + (set_local $2 (i32.const 0)) + (i32.store offset=1048592 (i32.const 0) (i32.const 4)) + (set_local $1 (i32.const 1048596)) + (loop $label$0 + (i32.store8 + (get_local $1) + (i32.shr_u (get_local $result) (get_local $2)) + ) + (set_local $1 + (i32.add (get_local $1) (i32.const 1)) + ) + (br_if $label$0 + (i32.ne + (tee_local $2 (i32.add (get_local $2) (i32.const 8))) + (i32.const 32) + ) + ) + ) + (i32.const 1048592) + ) +) diff --git a/vm/src/test/resources/wast/sum-copy.wast b/vm/src/test/resources/wast/sum-copy.wast new file mode 100644 index 0000000..b22fb62 --- /dev/null +++ b/vm/src/test/resources/wast/sum-copy.wast @@ -0,0 +1,169 @@ +;; copy of sum module with the same module name + +(module $SumModule + + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + ;; int extractInt(const char *buffer, int begin, int end) { + ;; int value = 0; + ;; const int size = end - begin; + ;; + ;; // it is assumed that bytes already in little endian + ;; for(int byteIdx = 0; byteIdx < size; ++byteIdx) { + ;; value |= buffer[begin + byteIdx] << byteIdx * 8; + ;; } + ;; + ;; return value; + ;; } + (func $extractInt (param $buffer i32) (param $begin i32) (param $end i32) (result i32) + (local $3 i32) + (block $label$0 + (br_if $label$0 + (i32.lt_s + (tee_local $3 + (i32.sub (get_local $end) (get_local $begin)) + ) + (i32.const 1) + ) + ) + + (set_local $begin + (i32.add (get_local $buffer) (get_local $begin)) + ) + + (set_local $end (i32.const 0)) + (set_local $buffer (i32.const 0)) + (loop $label$1 + (set_local $buffer + (i32.or + (i32.shl + (i32.load8_s (get_local $begin)) + (get_local $end) + ) + (get_local $buffer) + ) + ) + (set_local $begin + (i32.add (get_local $begin) (i32.const 1)) + ) + (set_local $end + (i32.add (get_local $end) (i32.const 8)) + ) + (br_if $label$1 + (tee_local $3 + (i32.add (get_local $3) (i32.const -1)) + ) + ) + ) + (return (get_local $buffer)) + ) + (i32.const 0) + ) + + ;; int extractInt(const char *buffer, int begin, int end) { + ;; int value = 0; + ;; const int size = end - begin; + ;; + ;; // it is assumed that bytes already in little endian + ;; for(int byteIdx = 0; byteIdx < size; ++byteIdx) { + ;; value |= buffer[begin + byteIdx] << byteIdx * 8; + ;; } + ;; + ;; return value; + ;; } + (func $extractInt (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (block $label$0 + (br_if $label$0 + (i32.lt_s + (tee_local $3 + (i32.sub (get_local $2) (get_local $1)) + ) + (i32.const 1) + ) + ) + + (set_local $1 + (i32.add (get_local $0) (get_local $1)) + ) + + (set_local $2 (i32.const 0)) + (set_local $0 (i32.const 0)) + (loop $label$1 + (set_local $0 + (i32.or + (i32.shl + (i32.load8_s (get_local $1)) + (get_local $2) + ) + (get_local $0) + ) + ) + (set_local $1 + (i32.add (get_local $1) (i32.const 1)) + ) + (set_local $2 + (i32.add (get_local $2) (i32.const 8)) + ) + (br_if $label$1 + (tee_local $3 + (i32.add (get_local $3) (i32.const -1)) + ) + ) + ) + (return (get_local $0)) + ) + (i32.const 0) + ) + + ;; int putIntResult(int result) { + ;; const int address = 1024*1024; + ;; + ;; globalBuffer[address] = 0; + ;; globalBuffer[address + 1] = 0; + ;; globalBuffer[address + 2] = 0; + ;; globalBuffer[address + 3] = 4; + ;; + ;; for(int i = 0; i < 4; ++i) { + ;; globalBuffer[address + 4 + i ] = ((result >> 8*i) & 0xFF); + ;; } + ;; + ;; return address; + ;; } + (func $putIntResult (param $result i32) (result i32) + (local $1 i32) + (local $2 i32) + (set_local $2 (i32.const 0)) + (i32.store offset=1048592 (i32.const 0) (i32.const 4)) + (set_local $1 (i32.const 1048596)) + (loop $label$0 + (i32.store8 + (get_local $1) + (i32.shr_u (get_local $result) (get_local $2)) + ) + (set_local $1 + (i32.add (get_local $1) (i32.const 1)) + ) + (br_if $label$0 + (i32.ne + (tee_local $2 (i32.add (get_local $2) (i32.const 8))) + (i32.const 32) + ) + ) + ) + (i32.const 1048592) + ) +) diff --git a/vm/src/test/resources/wast/sum-with-trap.wast b/vm/src/test/resources/wast/sum-with-trap.wast new file mode 100644 index 0000000..2d3b6fd --- /dev/null +++ b/vm/src/test/resources/wast/sum-with-trap.wast @@ -0,0 +1,25 @@ +;; this example simply returns + +(module + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 10000) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + ;; int sum(int a, int b) { + ;; return a + b; + ;; } + (func (export "invoke") (param $0 i32) (param $1 i32) (result i32) + (unreachable) ;; unreachable: An instruction which always traps. + ) +) diff --git a/vm/src/test/resources/wast/sum.wast b/vm/src/test/resources/wast/sum.wast new file mode 100644 index 0000000..a71081e --- /dev/null +++ b/vm/src/test/resources/wast/sum.wast @@ -0,0 +1,149 @@ +;; this example compute sum of two given integers + +(module + + ;; force Asmble to use memory + (memory $0 20) + (export "memory" (memory $0)) + + (func (export "allocate") (param $0 i32) (result i32) + ;; just return constant offset in ByteBuffer + (i32.const 0) + ) + + (func (export "deallocate") (param $address i32) (param $size i32) (return) + ;; in this simple example deallocation function does nothing + (drop) + (drop) + ) + + ;; int extractInt(const char *buffer, int begin, int end) { + ;; int value = 0; + ;; const int size = end - begin; + ;; + ;; // it is assumed that bytes already in little endian + ;; for(int byteIdx = 0; byteIdx < size; ++byteIdx) { + ;; value |= buffer[begin + byteIdx] << byteIdx * 8; + ;; } + ;; + ;; return value; + ;; } + (func $extractInt (param $buffer i32) (param $begin i32) (param $end i32) (result i32) + (local $3 i32) + (block $label$0 + (br_if $label$0 + (i32.lt_s + (tee_local $3 + (i32.sub (get_local $end) (get_local $begin)) + ) + (i32.const 1) + ) + ) + + (set_local $begin + (i32.add (get_local $buffer) (get_local $begin)) + ) + + (set_local $end (i32.const 0)) + (set_local $buffer (i32.const 0)) + (loop $label$1 + (set_local $buffer + (i32.or + (i32.shl + (i32.load8_s (get_local $begin)) + (get_local $end) + ) + (get_local $buffer) + ) + ) + (set_local $begin + (i32.add (get_local $begin) (i32.const 1)) + ) + (set_local $end + (i32.add (get_local $end) (i32.const 8)) + ) + (br_if $label$1 + (tee_local $3 + (i32.add (get_local $3) (i32.const -1)) + ) + ) + ) + (return (get_local $buffer)) + ) + (i32.const 0) + ) + + ;; int invoke(const char *buffer, int size) { + ;; if(size != 8) { + ;; return 0; + ;; } + ;; + ;; const int a = extractInt(buffer, 0, 4); + ;; const int b = extractInt(buffer, 4, 8); + ;; + ;; return a + b; + ;; } + (func (export "invoke") (param $buffer i32) (param $size i32) (result i32) + (local $2 i32) + (set_local $2 (i32.const 0)) + + (block $label$0 + (br_if $label$0 + (i32.ne (get_local $size) (i32.const 8)) + ) + (set_local $2 + (i32.add + (call $extractInt + (get_local $buffer) + (i32.const 0) + (i32.const 4) + ) + (call $extractInt + (get_local $buffer) + (i32.const 4) + (i32.const 8) + ) + ) + ) + ) + (call $putIntResult (get_local $2)) + ) + + ;; int putIntResult(int result) { + ;; const int address = 1024*1024; + ;; + ;; globalBuffer[address] = 0; + ;; globalBuffer[address + 1] = 0; + ;; globalBuffer[address + 2] = 0; + ;; globalBuffer[address + 3] = 4; + ;; + ;; for(int i = 0; i < 4; ++i) { + ;; globalBuffer[address + 4 + i ] = ((result >> 8*i) & 0xFF); + ;; } + ;; + ;; return address; + ;; } + (func $putIntResult (param $result i32) (result i32) + (local $1 i32) + (local $2 i32) + (set_local $2 (i32.const 0)) + (i32.store offset=1048592 (i32.const 0) (i32.const 4)) + (set_local $1 (i32.const 1048596)) + (loop $label$0 + (i32.store8 + (get_local $1) + (i32.shr_u (get_local $result) (get_local $2)) + ) + (set_local $1 + (i32.add (get_local $1) (i32.const 1)) + ) + (br_if $label$0 + (i32.ne + (tee_local $2 (i32.add (get_local $2) (i32.const 8))) + (i32.const 32) + ) + ) + ) + (i32.const 1048592) + ) +) diff --git a/vm/src/test/scala/fluence/vm/TestUtils.scala b/vm/src/test/scala/fluence/vm/TestUtils.scala new file mode 100644 index 0000000..08c286d --- /dev/null +++ b/vm/src/test/scala/fluence/vm/TestUtils.scala @@ -0,0 +1,36 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm + +import cats.data.EitherT +import cats.effect.IO + +import scala.concurrent.duration.Duration +import scala.concurrent.duration._ + +object TestUtils { + + implicit class EitherTValueReader[E, V](origin: EitherT[IO, E, V]) { + + def success(timeout: Duration = 3.seconds): V = + origin.value.unsafeRunTimed(timeout).get.right.get + + def failed(timeout: Duration = 3.seconds): E = + origin.value.unsafeRunTimed(timeout).get.left.get + } + +} diff --git a/vm/src/test/scala/fluence/vm/WasmVmSpec.scala b/vm/src/test/scala/fluence/vm/WasmVmSpec.scala new file mode 100644 index 0000000..b18b465 --- /dev/null +++ b/vm/src/test/scala/fluence/vm/WasmVmSpec.scala @@ -0,0 +1,107 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fluence.vm + +import cats.data.{EitherT, NonEmptyList} +import cats.effect.{IO, Timer} +import fluence.vm.TestUtils._ +import fluence.vm.error.InitializationError +import org.scalatest.{Matchers, WordSpec} + +import scala.concurrent.ExecutionContext +import scala.language.implicitConversions + +class WasmVmSpec extends WordSpec with Matchers { + + implicit def error[E](either: EitherT[IO, E, _]): E = either.value.unsafeRunSync().left.get + + private implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global) + + "apply" should { + + "raise error" when { + + "config error" in { + val res = for { + vm <- WasmVm[IO](NonEmptyList.one("unknown file"), "wrong config namespace") + } yield vm + + val error = res.failed() + error shouldBe a[InitializationError] + error.getMessage should startWith("Unable to parse the virtual machine config") + } + + "file not found" in { + val res = for { + vm <- WasmVm[IO](NonEmptyList.one("unknown file")) + } yield vm + + val error = res.failed() + error shouldBe a[InitializationError] + error.getMessage should startWith("IOError: No such file or directory (os error 2)") + } + } + } + + "initialize Vm success" when { + "one module without name is provided" ignore { + val sumFile = getClass.getResource("/wast/sum.wast").getPath + + WasmVm[IO](NonEmptyList.one(sumFile)).success() + } + + "one module with name is provided" ignore { + // Mul modules have name + val mulFile = getClass.getResource("/wast/mul.wast").getPath + + WasmVm[IO](NonEmptyList.one(mulFile)).success() + } + + "two modules with different module names are provided" ignore { + val sumFile = getClass.getResource("/wast/sum.wast").getPath + val mulFile = getClass.getResource("/wast/mul.wast").getPath + + WasmVm[IO](NonEmptyList.of(mulFile, sumFile)).success() + } + + "two modules with functions with the same names are provided" ignore { + // module without name and with some functions with the same name ("allocate", "deallocate", "invoke", ...) + val sum1File = getClass.getResource("/wast/counter.wast").getPath + // module with name "Sum" and with some functions with the same name ("allocate", "deallocate", "invoke", ...) + val sum2File = getClass.getResource("/wast/mul.wast").getPath + + val res = for { + vm <- WasmVm[IO](NonEmptyList.of(sum1File, sum2File)) + } yield vm + + res.success() + } + + } + + "initialize Vm failed" when { + "two main modules provided" ignore { + // these modules both don't contain a name section + val sumFile = getClass.getResource("/wast/sum.wast").getPath + val mulFile = getClass.getResource("/wast/bad-allocation-function-i64.wast").getPath + + WasmVm[IO](NonEmptyList.of(mulFile, sumFile)).failed() + } + + } + +} diff --git a/vm/src/test/scala/fluence/vm/WasmerWasmVmSpec.scala b/vm/src/test/scala/fluence/vm/WasmerWasmVmSpec.scala new file mode 100644 index 0000000..5177390 --- /dev/null +++ b/vm/src/test/scala/fluence/vm/WasmerWasmVmSpec.scala @@ -0,0 +1,293 @@ +/* + * Copyright 2019 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO: Adapt tests for Wasmer + +package fluence.vm + +import java.nio.{ByteBuffer, ByteOrder} + +import cats.data.NonEmptyList +import cats.effect.{IO, Timer} +import fluence.vm.TestUtils._ +import fluence.vm.error.{InitializationError, InvocationError} +import org.scalatest.{Assertion, Matchers, WordSpec} + +import scala.concurrent.ExecutionContext +import scala.language.{higherKinds, implicitConversions} + +class WasmerWasmVmSpec extends WordSpec with Matchers { + + private implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global) + + /** + * By element comparision of arrays. + */ + private def compareArrays(first: Array[Byte], second: Array[Byte]): Assertion = + first.deep shouldBe second.deep + + /** + * Converts ints to byte array by supplied byte order. + * + * @param ints array of int + * @param byteOrder byte order that used for int converting + */ + private def intsToBytes( + ints: List[Int], + byteOrder: ByteOrder = ByteOrder.LITTLE_ENDIAN + ): ByteBuffer = { + val intBytesSize = 4 + val converter = ByteBuffer.allocate(intBytesSize * ints.length) + + converter.order(byteOrder) + ints.foreach(converter.putInt) + converter.flip() + converter + } + + "invoke" should { + "raise error" when { + + "trying to invoke when a module doesn't have one" ignore { + val noInvokeTestFile = getClass.getResource("/wast/no-invoke.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(noInvokeTestFile)) + result ← vm.invoke[IO]().toVmError + } yield result + val error = res.failed() + error shouldBe a[InitializationError] + error.getMessage should startWith("The main module must have functions with names") + } + + "trying to use Wasm memory when getMemory function isn't defined" ignore { + val noGetMemoryTestFile = getClass.getResource("/wast/no-getMemory.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(noGetMemoryTestFile)) + _ ← vm.invoke[IO]("test".getBytes()) + state ← vm.computeVmState[IO].toVmError + } yield state + + val error = res.failed() + error.getMessage should + startWith("Unable to initialize module=null") + error shouldBe a[InitializationError] + } + + "wasm code falls into the trap" ignore { + val sumTestFile = getClass.getResource("/wast/sum-with-trap.wast").getPath + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(sumTestFile)) + result ← vm.invoke[IO](fnArgument = intsToBytes(100 :: 13 :: Nil).array()).toVmError // Integer overflow + } yield result + val error = res.failed() + error shouldBe a[InvocationError] + error.getMessage should startWith("Function invoke with args:") + error.getMessage should include("was failed") + } + + "Wasm allocate function returns an incorrect i64 value" ignore { + val badAllocationFunctionFile = getClass.getResource("/wast/bad-allocation-function-i64.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(badAllocationFunctionFile)) + _ ← vm.invoke[IO]("test".getBytes()) + state ← vm.computeVmState[IO].toVmError + } yield state + + val error = res.failed() + error.getMessage shouldBe "Writing to -1 failed" + error shouldBe a[InvocationError] + } + + "Wasm allocate function returns an incorrect f64 value" ignore { + val badAllocationFunctionFile = getClass.getResource("/wast/bad-allocation-function-f64.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(badAllocationFunctionFile)) + result ← vm.invoke[IO]("test".getBytes()) + state ← vm.computeVmState[IO].toVmError + } yield state + + val error = res.failed() + error.getMessage shouldBe "Writing to 200000000 failed" + error shouldBe a[InvocationError] + } + + "trying to extract array with incorrect size from Wasm memory" ignore { + val incorrectArrayReturningTestFile = getClass.getResource("/wast/incorrect-array-returning.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(incorrectArrayReturningTestFile)) + result ← vm.invoke[IO]().toVmError + } yield result + + val error = res.failed() + error shouldBe a[InvocationError] + error.getMessage shouldBe "Reading from offset=1048596 16777215 bytes failed" + } + + } + } + + "invokes function success" when { + "run sum.wast" ignore { + val sumTestFile = getClass.getResource("/wast/sum.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(sumTestFile)) + result ← vm.invoke[IO](intsToBytes(100 :: 17 :: Nil).array()).toVmError + } yield { + compareArrays(result.output, Array[Byte](117, 0, 0, 0)) + } + + res.success() + } + + "run counter.wast" ignore { + val counterTestFile = getClass.getResource("/wast/counter.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(counterTestFile)) + get1 ← vm.invoke[IO]() // 0 -> 1; read 1 + get2 ← vm.invoke[IO]() // 1 -> 2; read 2 + get3 ← vm.invoke[IO]().toVmError // 2 -> 3; read 3 + } yield { + compareArrays(get1.output, Array[Byte](1, 0, 0, 0)) + compareArrays(get2.output, Array[Byte](2, 0, 0, 0)) + compareArrays(get3.output, Array[Byte](3, 0, 0, 0)) + } + + res.success() + } + + "run simple test with array passsing" ignore { + val simpleStringPassingTestFile = getClass.getResource("/wast/simple-string-passing.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(simpleStringPassingTestFile)) + value1 ← vm.invoke[IO]("test_argument".getBytes()) + value2 ← vm.invoke[IO]("XX".getBytes()) + value3 ← vm.invoke[IO]("XXX".getBytes()) + value4 ← vm.invoke[IO]("".getBytes()) // empty string + value5 ← vm.invoke[IO]("\"".getBytes()).toVmError // " string + } yield { + compareArrays(value1.output, Array[Byte](90, 0, 0, 0)) + compareArrays(value2.output, Array[Byte](0, 0, 0, 0)) + compareArrays(value3.output, Array[Byte]('X'.toByte, 0, 0, 0)) + compareArrays(value4.output, Array[Byte](0, 0, 0, 0)) // this Wasm example returns 0 on empty strings + compareArrays(value5.output, Array[Byte]('"'.toByte, 0, 0, 0)) + } + + res.success() + } + + "run simple test with array returning" ignore { + val simpleArrayPassingTestFile = getClass.getResource("/wast/simple-array-returning.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(simpleArrayPassingTestFile)) + value1 ← vm.invoke[IO]() + _ ← vm.computeVmState[IO].toVmError + } yield { + val stringValue = new String(value1.output) + stringValue shouldBe "Hello from Fluence Labs!" + } + + res.success() + } + + "run simple test with array mutation" ignore { + val simpleArrayMutationTestFile = getClass.getResource("/wast/simple-array-mutation.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(simpleArrayMutationTestFile)) + value1 ← vm.invoke[IO]("AAAAAAA".getBytes()) + state ← vm.computeVmState[IO].toVmError + } yield { + val stringValue = new String(value1.output) + stringValue shouldBe "BBBBBBB" + } + + res.success() + } + + } + + "getVmState" should { + "returns state" when { + "there is one module with memory present" ignore { + // the code in 'counter.wast' uses 'memory', instance for this module created with 'memory' field + val counterTestFile = getClass.getResource("/wast/counter.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.one(counterTestFile)) + get1 ← vm.invoke[IO]() // 0 -> 1; return 1 + state1 ← vm.computeVmState[IO] + get2 ← vm.invoke[IO]() // 1 -> 2; return 2 + + get3 ← vm.invoke[IO]() // 2 -> 3; return 3 + state2 ← vm.computeVmState[IO] + get4 ← vm.invoke[IO]().toVmError // 3 -> 4; return 4 + } yield { + compareArrays(get1.output, Array[Byte](1, 0, 0, 0)) + compareArrays(get2.output, Array[Byte](2, 0, 0, 0)) + compareArrays(get3.output, Array[Byte](3, 0, 0, 0)) + compareArrays(get4.output, Array[Byte](4, 0, 0, 0)) + + state1.size shouldBe 32 + state2.size shouldBe 32 + state1 should not be state2 + } + + res.success() + } + + "there are several modules present" ignore { + val counterTestFile = getClass.getResource("/wast/counter.wast").getPath + val counterCopyTestFile = getClass.getResource("/wast/counter-copy.wast").getPath + val mulTestFile = getClass.getResource("/wast/mul.wast").getPath + + val res = for { + vm ← WasmVm[IO](NonEmptyList.of(counterTestFile, counterCopyTestFile, mulTestFile)) + + get1 ← vm.invoke[IO]() // 0 -> 1; read 1 + _ ← vm.invoke[IO]() // 1 -> 2; read 2 + + state1 ← vm.computeVmState[IO] + + _ ← vm.invoke[IO]() // 2 -> 3 + get2 ← vm.invoke[IO]() // 3 -> 4 + + state2 ← vm.computeVmState[IO].toVmError + + } yield { + compareArrays(get1.output, Array[Byte](1, 0, 0, 0)) + compareArrays(get2.output, Array[Byte](4, 0, 0, 0)) + + state1.size shouldBe 32 + state2.size shouldBe 32 + state1 should not be state2 + } + + res.success() + } + + } + } + +}