diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..98c300f49 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,25 @@ +# This matrix builds VSYS with various JDKs and will be expanded to cover many more cases. + +name: Build VSYS +on: [push,pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: install scala + run: | + echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | sudo tee /etc/apt/sources.list.d/sbt.list + echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | sudo tee /etc/apt/sources.list.d/sbt_old.list + curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo apt-key add + sudo apt-get update + sudo apt-get install sbt + + - uses: actions/checkout@v2 + + - name: Build and Package + run: sbt packageAll + + + diff --git a/.gitignore b/.gitignore index 5a7487430..88b53f3c4 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,9 @@ test/*/data/* src/test/**/.DS_Store *.sublime-* + +# metals +.metals/ +.bloop/ +.ammonite/ +project/metals.sbt diff --git a/build.sbt b/build.sbt index 417da3303..955fd766c 100644 --- a/build.sbt +++ b/build.sbt @@ -7,7 +7,7 @@ enablePlugins(sbtdocker.DockerPlugin, JavaServerAppPackaging, JDebPackaging, Sys name := "vsys" organization := "systems.v" -version := "0.3.1" +version := "0.4.2" scalaVersion in ThisBuild := "2.12.6" crossPaths := false publishArtifact in (Compile, packageDoc) := false diff --git a/project/Dependencies.scala b/project/Dependencies.scala index b026e79e5..def828fa7 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -46,7 +46,7 @@ object Dependencies { ) lazy val logging = Seq( - "ch.qos.logback" % "logback-classic" % "1.2.3", + "ch.qos.logback" % "logback-classic" % "1.2.9", "org.slf4j" % "slf4j-api" % "1.7.25", "org.slf4j" % "jul-to-slf4j" % "1.7.25", "net.logstash.logback" % "logstash-logback-encoder" % "4.11" diff --git a/project/plugins.sbt b/project/plugins.sbt index b66f6a985..3ce620453 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,6 @@ resolvers ++= Seq( - "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/", - "Artima Maven Repository" at "http://repo.artima.com/releases", + "Typesafe repository" at "https://repo.scala-sbt.org", + "Artima Maven Repository" at "https://repo.artima.com/releases", "JBoss" at "https://repository.jboss.org", Resolver.sbtPluginRepo("releases") ) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 33bbefb30..d59e01653 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -28,13 +28,13 @@ vsys { # In some cases, you may both set `decalred-address` and enable UPnP (e.g. when IGD can't reliably determine its # external IP address). In such cases the node will attempt to configure an IGD to pass traffic from external port # to `bind-address:port`. Please note, however, that this setup is not recommended. - # declared-address = "1.2.3.4:9923" + # declared-address = "1.2.3.4:9921" # Network address bind-address = "0.0.0.0" # Port number - port = 9923 + port = 9921 # Node name to send during handshake. Comment this string out to set random node name. # node-name = "default-node-name" @@ -43,7 +43,7 @@ vsys { # nonce = 0 # List of IP addresses of well known nodes. - known-peers = [] + known-peers = ["gemmer.vcoin.systems:9921","vnode.vcoin.systems:9921","gemmer.vos.systems:9921","vnode.vos.systems:9921"] # How long the information about peer stays in database after the last communication with it peers-data-residence-time = 1d @@ -110,8 +110,8 @@ vsys { # Min buffer size. Fast rollback is possible up to this value. minimum-in-memory-diff-blocks = 3600 - # Blockchain type. Could be TESTNET | MAINNET | CUSTOM. Default value is TESTNET. - type = TESTNET + # Blockchain type. Could be TESTNET | MAINNET | CUSTOM. Default value is MAINNET. + type = MAINNET # 'custom' section present only if CUSTOM blockchain type is set. It's impossible to overwrite predefined 'testnet' and 'mainnet' configurations. # custom { @@ -159,7 +159,8 @@ vsys { # !IMPORTANT if you change the settings below in state, rebuilding of state is required, which means you need to clean the data and resync the block at current version state { # turn on/off the state of transactions grouped by address and transaction type - tx-type-account-tx-ids = on + tx-type-account-tx-ids = off + tx-contract-tx-ids = off } } diff --git a/src/main/scala/vsys/Application.scala b/src/main/scala/vsys/Application.scala index 79005e79c..faf8f2372 100644 --- a/src/main/scala/vsys/Application.scala +++ b/src/main/scala/vsys/Application.scala @@ -153,6 +153,7 @@ class Application(val actorSystem: ActorSystem, val settings: VsysSettings) exte // on unexpected shutdown sys.addShutdownHook { network.shutdown() + peerDatabase.close() shutdown() } diff --git a/src/main/scala/vsys/api/http/BlocksApiRoute.scala b/src/main/scala/vsys/api/http/BlocksApiRoute.scala index e5810fcce..f4ebec941 100644 --- a/src/main/scala/vsys/api/http/BlocksApiRoute.scala +++ b/src/main/scala/vsys/api/http/BlocksApiRoute.scala @@ -1,7 +1,6 @@ package vsys.api.http import javax.ws.rs.Path - import akka.http.scaladsl.marshalling.ToResponseMarshallable import akka.http.scaladsl.server.Route import io.netty.channel.group.ChannelGroup @@ -13,7 +12,6 @@ import vsys.blockchain.state.ByteStr import vsys.blockchain.transaction.TransactionParser import vsys.network._ import vsys.settings.{CheckpointsSettings, RestAPISettings} - import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -31,7 +29,10 @@ case class BlocksApiRoute(settings: RestAPISettings, checkpointsSettings: Checkp } @Path("/address/{address}/{from}/{to}") - @ApiOperation(value = "Address", notes = "Get list of blocks generated by specified address", httpMethod = "GET") + @ApiOperation(value = "Address", notes = "Get a block list generated by a specified `address` and a block height range (`from`/`to`)", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the block list or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "from", value = "Start block height", required = true, dataType = "integer", paramType = "path"), new ApiImplicitParam(name = "to", value = "End block height", required = true, dataType = "integer", paramType = "path"), @@ -52,10 +53,13 @@ case class BlocksApiRoute(settings: RestAPISettings, checkpointsSettings: Checkp } @Path("/child/{signature}") - @ApiOperation(value = "Child", notes = "Get children of specified block", httpMethod = "GET") + @ApiOperation(value = "Child", notes = "Get children of a specified block by the base58-encoded `signature`", httpMethod = "GET") @ApiImplicitParams(Array( new ApiImplicitParam(name = "signature", value = "Base58-encoded signature", required = true, dataType = "string", paramType = "path") )) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the child block information or error") + )) def child: Route = (path("child" / Segment) & get) { encodedSignature => withBlock(history, encodedSignature) { block => complete(history.child(block).map(_.json + ("transaction count" -> Json.toJson(block.transactionData.length))).getOrElse[JsObject]( @@ -65,7 +69,7 @@ case class BlocksApiRoute(settings: RestAPISettings, checkpointsSettings: Checkp @Path("/delay/{signature}/{blockNum}") @ApiOperation(value = "Average delay", - notes = "Average delay in milliseconds between last `blockNum` blocks starting from block with `signature`", httpMethod = "GET") + notes = "Get the average delay in milliseconds between last `blockNum` blocks starting from block with `signature`", httpMethod = "GET", response = classOf[Long]) @ApiImplicitParams(Array( new ApiImplicitParam(name = "signature", value = "Base58-encoded signature", required = true, dataType = "string", paramType = "path"), new ApiImplicitParam(name = "blockNum", value = "Number of blocks to count delay", required = true, dataType = "string", paramType = "path") @@ -78,7 +82,7 @@ case class BlocksApiRoute(settings: RestAPISettings, checkpointsSettings: Checkp } @Path("/height/{signature}") - @ApiOperation(value = "Height", notes = "Get height of a block by its Base58-encoded signature", httpMethod = "GET") + @ApiOperation(value = "Height", notes = "Get the **block height** by its Base58-encoded `signature`", httpMethod = "GET", response = classOf[Int]) @ApiImplicitParams(Array( new ApiImplicitParam(name = "signature", value = "Base58-encoded signature", required = true, dataType = "string", paramType = "path") )) @@ -95,13 +99,16 @@ case class BlocksApiRoute(settings: RestAPISettings, checkpointsSettings: Checkp } @Path("/height") - @ApiOperation(value = "Height", notes = "Get blockchain height", httpMethod = "GET") + @ApiOperation(value = "Height", notes = "Get blockchain height", httpMethod = "GET", response=classOf[Int]) def height: Route = (path("height") & get) { complete(Json.obj("height" -> history.height())) } @Path("/at/{height}") - @ApiOperation(value = "At", notes = "Get block at specified height", httpMethod = "GET") + @ApiOperation(value = "At", notes = "Get block at specified `height`", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of a block details at a specified height or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "height", value = "Block height", required = true, dataType = "integer", paramType = "path") )) @@ -113,7 +120,10 @@ case class BlocksApiRoute(settings: RestAPISettings, checkpointsSettings: Checkp } @Path("/seq/{from}/{to}") - @ApiOperation(value = "Seq", notes = "Get block at specified heights", httpMethod = "GET") + @ApiOperation(value = "Seq", notes = "Get block at the specified height range (`from`/`to`)", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the block list or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "from", value = "Start block height", required = true, dataType = "integer", paramType = "path"), new ApiImplicitParam(name = "to", value = "End block height", required = true, dataType = "integer", paramType = "path") @@ -130,7 +140,10 @@ case class BlocksApiRoute(settings: RestAPISettings, checkpointsSettings: Checkp @Path("/last") - @ApiOperation(value = "Last", notes = "Get last block data", httpMethod = "GET") + @ApiOperation(value = "Last", notes = "Get the **last block** data", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the last block information or error") + )) def last: Route = (path("last") & get) { val height = history.height() val lastBlock = history.blockAt(height).get @@ -138,13 +151,19 @@ case class BlocksApiRoute(settings: RestAPISettings, checkpointsSettings: Checkp } @Path("/first") - @ApiOperation(value = "First", notes = "Get genesis block data", httpMethod = "GET") + @ApiOperation(value = "First", notes = "Get the **genesis block** data", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the first block details or error") + )) def first: Route = (path("first") & get) { complete(history.genesis.json + ("height" -> Json.toJson(1)) + ("transaction count" -> Json.toJson(history.genesis.transactionData.length))) } @Path("/signature/{signature}") - @ApiOperation(value = "Signature", notes = "Get block by a specified Base58-encoded signature", httpMethod = "GET") + @ApiOperation(value = "Signature", notes = "Get block by a specified Base58-encoded `signature`", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the block details or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "signature", value = "Base58-encoded signature", required = true, dataType = "string", paramType = "path") )) @@ -159,13 +178,13 @@ case class BlocksApiRoute(settings: RestAPISettings, checkpointsSettings: Checkp } @Path("/checkpoint") - @ApiOperation(value = "Checkpoint", notes = "Broadcast checkpoint of blocks", httpMethod = "POST") + @ApiOperation(value = "Checkpoint", notes = "Broadcast the checkpoint of blocks", httpMethod = "POST") @ApiImplicitParams(Array( new ApiImplicitParam(name = "message", value = "Checkpoint message", required = true, paramType = "body", dataType = "vsys.network.Checkpoint") )) @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json with response or error") + new ApiResponse(code = 200, message = "Successful Operation") )) def checkpoint: Route = (path("checkpoint") & post) { json[Checkpoint] { checkpoint => diff --git a/src/main/scala/vsys/api/http/NodeApiRoute.scala b/src/main/scala/vsys/api/http/NodeApiRoute.scala index 97ed5f1a9..339a4d822 100644 --- a/src/main/scala/vsys/api/http/NodeApiRoute.scala +++ b/src/main/scala/vsys/api/http/NodeApiRoute.scala @@ -24,9 +24,9 @@ case class NodeApiRoute(settings: RestAPISettings, } @Path("/version") - @ApiOperation(value = "Version", notes = "Get VSYS node version", httpMethod = "GET") + @ApiOperation(value = "Version", notes = "Get VSYS node version", httpMethod = "GET", response = classOf[String]) @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json VSYS node version") + new ApiResponse(code = 200, message = "Json response of VSYS node version") )) def version: Route = (get & path("version")) { complete(Json.obj("version" -> Constants.AgentName)) @@ -35,6 +35,9 @@ case class NodeApiRoute(settings: RestAPISettings, @Path("/stop") @ApiOperation(value = "Stop", notes = "Stop the node", httpMethod = "POST", authorizations = Array(new Authorization("api_key"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Successful Operation") + )) def stop: Route = (post & path("stop") & withAuth) { log.info("Request to stop application") application.shutdown() @@ -42,7 +45,10 @@ case class NodeApiRoute(settings: RestAPISettings, } @Path("/status") - @ApiOperation(value = "Status", notes = "Get status of the running core", httpMethod = "GET") + @ApiOperation(value = "Status", notes = "Get **status** of the running core", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the node status or error") + )) def status: Route = (get & path("status")) { val lastUpdated = history.lastBlock.get.timestamp complete( diff --git a/src/main/scala/vsys/api/http/PeersApiRoute.scala b/src/main/scala/vsys/api/http/PeersApiRoute.scala index e9bb9453c..e96fe2e50 100755 --- a/src/main/scala/vsys/api/http/PeersApiRoute.scala +++ b/src/main/scala/vsys/api/http/PeersApiRoute.scala @@ -29,9 +29,9 @@ case class PeersApiRoute( } @Path("/all") - @ApiOperation(value = "Peer list", notes = "Peer list", httpMethod = "GET") + @ApiOperation(value = "Peer list", notes = "Get the peer list", httpMethod = "GET") @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json with peer list or error") + new ApiResponse(code = 200, message = "Json response of a peer list or error") )) def allPeers: Route = (path("all") & get) { complete(Json.obj("peers" -> @@ -44,9 +44,9 @@ case class PeersApiRoute( } @Path("/connected") - @ApiOperation(value = "Connected peers list", notes = "Connected peers list", httpMethod = "GET") + @ApiOperation(value = "Connected peers list", notes = "Get the connected peers list", httpMethod = "GET") @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json with connected peers or error") + new ApiResponse(code = 200, message = "Json response of a connected peers list or error") )) def connectedPeers: Route = (path("connected") & get) { val peers = establishedConnections.values().stream().map[JsValue](pi => Json.obj( @@ -64,14 +64,16 @@ case class PeersApiRoute( @Path("/connect") @ApiOperation(value = "Connect to peer", notes = "Connect to peer", httpMethod = "POST", authorizations = Array(new Authorization("api_key"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Successful Operation") + )) @ApiImplicitParams(Array( new ApiImplicitParam( name = "body", - value = "Json with data", + value = "Peer host settings", required = true, paramType = "body", - dataType = "string", - defaultValue = "{\n\t\"host\":\"127.0.0.1\",\n\t\"port\":\"9084\"\n}" + dataType = "vsys.network.PeerNetworkConnection", ) )) def connect: Route = (path("connect") & post & withAuth) { @@ -84,10 +86,10 @@ case class PeersApiRoute( } @Path("/blacklisted") - @ApiOperation(value = "Blacklisted peers list", notes = "Blacklisted peers list", httpMethod = "GET") + @ApiOperation(value = "Blacklisted peers list", notes = "Get the blacklisted peers list", httpMethod = "GET") @ApiResponses( Array( - new ApiResponse(code = 200, message = "Json with blacklisted peers or error") + new ApiResponse(code = 200, message = "Json response of the blacklisted peers or error") )) def blacklistedPeers: Route = (path("blacklisted") & get) { complete( @@ -99,10 +101,10 @@ case class PeersApiRoute( } @Path("/suspended") - @ApiOperation(value = "Suspended peers list", notes = "Suspended peers list", httpMethod = "GET") + @ApiOperation(value = "Suspended peers list", notes = "Get the suspended peers list", httpMethod = "GET") @ApiResponses( Array( - new ApiResponse(code = 200, message = "JSON with suspended peers or error") + new ApiResponse(code = 200, message = "JSON response of a suspended peer list or error") )) def suspendedPeers: Route = (path("suspended") & get) { complete( @@ -115,7 +117,7 @@ case class PeersApiRoute( authorizations = Array(new Authorization("api_key"))) @ApiResponses( Array( - new ApiResponse(code = 200, message = "200") + new ApiResponse(code = 200, message = "Successful Operation") )) def clearBlacklist: Route = (path("clearblacklist") & post & withAuth) { peerDatabase.clearBlacklist() diff --git a/src/main/scala/vsys/api/http/TransactionsApiRoute.scala b/src/main/scala/vsys/api/http/TransactionsApiRoute.scala index 45fc2f516..794bc6cc9 100644 --- a/src/main/scala/vsys/api/http/TransactionsApiRoute.scala +++ b/src/main/scala/vsys/api/http/TransactionsApiRoute.scala @@ -1,18 +1,17 @@ package vsys.api.http import javax.ws.rs.Path - import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Route import io.swagger.annotations._ import play.api.libs.json._ -import vsys.account.Address +import vsys.account.{Account, Address} import vsys.blockchain.history.History import vsys.blockchain.state.ByteStr import vsys.blockchain.state.reader.StateReader import vsys.blockchain.transaction.lease.{LeaseCancelTransaction, LeaseTransaction} import vsys.blockchain.transaction.TransactionParser.TransactionType -import vsys.blockchain.transaction.{Transaction, ProcessedTransaction} +import vsys.blockchain.transaction.{ProcessedTransaction, Transaction} import vsys.blockchain.UtxPool import vsys.settings.{RestAPISettings, StateSettings} @@ -47,15 +46,16 @@ case class TransactionsApiRoute( @Path("/count") @ApiOperation(value = "Count", - notes = "Get count of transactions where specified address has been involved. *This is a custom api, you need to enable it in configuration file.*", - httpMethod = "GET") + notes = "Get count of transactions where specified address (wallet address or contract address) has been involved. *This is a custom api, you need to enable it in configuration file.*", + httpMethod = "GET", + response = classOf[Int]) @ApiImplicitParams(Array( - new ApiImplicitParam(name = "address", value = "wallet address ", required = true, dataType = "string", paramType = "query"), - new ApiImplicitParam(name = "txType", value = "transaction type", required = false, dataType = "integer", paramType = "query") + new ApiImplicitParam(name = "address", value = "Wallet address or contract address", required = true, dataType = "string", paramType = "query"), + new ApiImplicitParam(name = "txType", value = "Transaction type", required = false, dataType = "integer", paramType = "query") )) def transactionCount: Route = (path("count") & get) { parameters(('address, 'txType.?)) { (addressStr, txTypeStrOpt) => - Address.fromString(addressStr) match { + Account.fromString(addressStr) match { case Left(e) => complete(ApiError.fromValidationError(e)) case Right(a) => txTypeStrOpt match { @@ -85,17 +85,20 @@ case class TransactionsApiRoute( @Path("/list") @ApiOperation(value = "List", - notes = "Get list of transactions where specified address has been involved. *This is a custom api, you need to enable it in configuration file.*", + notes = "Get list of transactions where specified address (wallet address or contract address) has been involved. *This is a custom api, you need to enable it in configuration file.*", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response with a list of transactions or error") + )) @ApiImplicitParams(Array( - new ApiImplicitParam(name = "address", value = "wallet address ", required = true, dataType = "string", paramType = "query"), - new ApiImplicitParam(name = "txType", value = "transaction type ", required = false, dataType = "integer", paramType = "query"), + new ApiImplicitParam(name = "address", value = "Wallet address or contract address", required = true, dataType = "string", paramType = "query"), + new ApiImplicitParam(name = "txType", value = "Transaction type", required = false, dataType = "integer", paramType = "query"), new ApiImplicitParam(name = "limit", value = "Specified number of records to be returned", required = true, dataType = "integer", paramType = "query"), new ApiImplicitParam(name = "offset", value = "Specified number of records offset", required = false, dataType = "integer", paramType = "query") )) def transactionList: Route = (path("list") & get) { parameters(('address, 'txType.?, 'limit, 'offset ? "0")) { (addressStr, txTypeStrOpt, limitStr, offsetStr) => - Address.fromString(addressStr) match { + Account.fromString(addressStr) match { case Left(e) => complete(ApiError.fromValidationError(e)) case Right(a) => Exception.allCatch.opt(limitStr.toInt) match { @@ -130,15 +133,18 @@ case class TransactionsApiRoute( } @Path("/address/{address}/limit/{limit}") - @ApiOperation(value = "Address", notes = "Get list of transactions where specified address has been involved", httpMethod = "GET") + @ApiOperation(value = "Address", notes = "Get list of transactions where specified address (wallet address or contract address) has been involved", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response with a list of transactions or error") + )) @ApiImplicitParams(Array( - new ApiImplicitParam(name = "address", value = "Wallet address ", required = true, dataType = "string", paramType = "path"), + new ApiImplicitParam(name = "address", value = "Wallet address or contract address", required = true, dataType = "string", paramType = "path"), new ApiImplicitParam(name = "limit", value = "Specified number of records to be returned", required = true, dataType = "integer", paramType = "path") )) //remove after all related productions being updated def addressLimit: Route = (pathPrefix("address") & get) { pathPrefix(Segment) { address => - Address.fromString(address) match { + Account.fromString(address) match { case Left(e) => complete(ApiError.fromValidationError(e)) case Right(a) => pathPrefix("limit") { @@ -163,8 +169,11 @@ case class TransactionsApiRoute( } @Path("/activeLeaseList/{address}") - @ApiOperation(value = "Address", notes = "Get list of active lease transactions", httpMethod = "GET", + @ApiOperation(value = "Address", notes = "Get a list of **active lease transactions** by a specified `address`", httpMethod = "GET", authorizations = Array(new Authorization("api_key"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response with a list of active lease transactions or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Wallet address ", required = true, dataType = "string", paramType = "path"), )) @@ -187,7 +196,10 @@ case class TransactionsApiRoute( } @Path("/info/{id}") - @ApiOperation(value = "Info", notes = "Get transaction info", httpMethod = "GET") + @ApiOperation(value = "Info", notes = "Get transaction info by a specified transaction `id`", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of transaction info or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "id", value = "transaction id ", required = true, dataType = "string", paramType = "path") )) @@ -210,7 +222,10 @@ case class TransactionsApiRoute( } @Path("/unconfirmed") - @ApiOperation(value = "Unconfirmed", notes = "Get list of unconfirmed transactions", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the unconfirmed transactions list or error") + )) + @ApiOperation(value = "Unconfirmed", notes = "Get a list of unconfirmed transactions", httpMethod = "GET") def unconfirmed: Route = (pathPrefix("unconfirmed") & get) { pathEndOrSingleSlash { complete(JsArray(utxPool.all().map(txToExtendedJson))) @@ -218,13 +233,16 @@ case class TransactionsApiRoute( } @Path("/unconfirmed/size") - @ApiOperation(value = "Size of UTX pool", notes = "Get number of unconfirmed transactions in the UTX pool", httpMethod = "GET") + @ApiOperation(value = "Size of UTX pool", notes = "Get the number of transactions in the unconfirmed transactions(UTX) pool", httpMethod = "GET", response = classOf[Int]) def utxSize: Route = (pathPrefix("size") & get) { complete(Json.obj("size" -> JsNumber(utxPool.size))) } @Path("/unconfirmed/info/{id}") - @ApiOperation(value = "Transaction Info", notes = "Get transaction that is in the UTX", httpMethod = "GET") + @ApiOperation(value = "Transaction Info", notes = "Get the **unconfirmed transaction** by a specified transaction `id`", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the unconfirmed transaction info or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "id", value = "Transaction id ", required = true, dataType = "string", paramType = "path") )) diff --git a/src/main/scala/vsys/api/http/UtilsApiRoute.scala b/src/main/scala/vsys/api/http/UtilsApiRoute.scala index 2bce9bc4e..247037d92 100755 --- a/src/main/scala/vsys/api/http/UtilsApiRoute.scala +++ b/src/main/scala/vsys/api/http/UtilsApiRoute.scala @@ -28,10 +28,10 @@ case class UtilsApiRoute(timeService: Time, settings: RestAPISettings) extends A } @Path("/time") - @ApiOperation(value = "Time", notes = "Current Node time (UTC)", httpMethod = "GET") + @ApiOperation(value = "Time", notes = "Get the Current Node time (UTC)", httpMethod = "GET") @ApiResponses( Array( - new ApiResponse(code = 200, message = "Json with time or error") + new ApiResponse(code = 200, message = "Json response of the current node time or error") )) def time: Route = (path("time") & get) { val t = System.currentTimeMillis()*1000000L + System.nanoTime()%1000000L @@ -41,30 +41,32 @@ case class UtilsApiRoute(timeService: Time, settings: RestAPISettings) extends A @Path("/seed") @ApiOperation(value = "Seed", notes = "Generate random seed", httpMethod = "GET") @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json with peer list or error") + new ApiResponse(code = 200, message = "Json response of a random seed or error") )) def seedRoute: Route = (path("seed") & get) { complete(seed(DefaultSeedSize)) } @Path("/seed/{length}") - @ApiOperation(value = "Seed of specified length", notes = "Generate random seed of specified length", httpMethod = "GET") + @ApiOperation(value = "Seed of specified length", notes = "Generate random seed of specified `length`", httpMethod = "GET") @ApiImplicitParams(Array( new ApiImplicitParam(name = "length", value = "Seed length ", required = true, dataType = "integer", paramType = "path") )) - @ApiResponse(code = 200, message = "Json with error message") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of a random seed or error") + )) def length: Route = (path("seed" / IntNumber) & get) { length => if (length <= MaxSeedSize) complete(seed(length)) else complete(TooBigArrayAllocation) } @Path("/hash/secure") - @ApiOperation(value = "Hash", notes = "Return SecureCryptographicHash of specified message", httpMethod = "POST") + @ApiOperation(value = "Hash", notes = "Return a hash(SecureCryptographicHash) of a specified `message`", httpMethod = "POST") @ApiImplicitParams(Array( new ApiImplicitParam(name = "message", value = "Message to hash", required = true, paramType = "body", dataType = "string") )) @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json with error or json like {\"message\": \"your message\",\"hash\": \"your message hash\"}") + new ApiResponse(code = 200, message = "Successful Operation") )) def hashSecure: Route = (path("hash" / "secure") & post) { entity(as[String]) { message => @@ -73,12 +75,12 @@ case class UtilsApiRoute(timeService: Time, settings: RestAPISettings) extends A } @Path("/hash/fast") - @ApiOperation(value = "Hash", notes = "Return FastCryptographicHash of specified message", httpMethod = "POST") + @ApiOperation(value = "Hash", notes = "Return a hash(FastCryptographicHash) of a specified `message`", httpMethod = "POST") @ApiImplicitParams(Array( new ApiImplicitParam(name = "message", value = "Message to hash", required = true, paramType = "body", dataType = "string") )) @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json with error or json like {\"message\": \"your message\",\"hash\": \"your message hash\"}") + new ApiResponse(code = 200, message = "Successful Operation") )) def hashFast: Route = (path("hash" / "fast") & post) { entity(as[String]) { message => @@ -88,13 +90,13 @@ case class UtilsApiRoute(timeService: Time, settings: RestAPISettings) extends A @Path("/sign/{privateKey}") - @ApiOperation(value = "Hash", notes = "Return FastCryptographicHash of specified message", httpMethod = "POST") + @ApiOperation(value = "Hash", notes = "Return a hash(FastCryptographicHash) of a specified message with the `privateKey`", httpMethod = "POST") @ApiImplicitParams(Array( new ApiImplicitParam(name = "privateKey", value = "privateKey", required = true, paramType = "path", dataType = "string"), new ApiImplicitParam(name = "message", value = "Message to hash", required = true, paramType = "body", dataType = "string") )) @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json with error or json like {\"message\": \"your message\",\"hash\": \"your message hash\"}") + new ApiResponse(code = 200, message = "Successful Operation") )) def sign: Route = (path("sign" / Segment) & post) {pk => entity(as[String]) { message => diff --git a/src/main/scala/vsys/api/http/WalletApiRoute.scala b/src/main/scala/vsys/api/http/WalletApiRoute.scala index a48ae2929..090ef7245 100644 --- a/src/main/scala/vsys/api/http/WalletApiRoute.scala +++ b/src/main/scala/vsys/api/http/WalletApiRoute.scala @@ -17,6 +17,9 @@ case class WalletApiRoute(settings: RestAPISettings, wallet: Wallet) extends Api @Path("/seed") @ApiOperation(value = "Seed", notes = "Export wallet seed", httpMethod = "GET", authorizations = Array(new Authorization("api_key"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of a wallet seed or error") + )) def seed: Route = (path("wallet" / "seed") & get & withAuth) { complete(Json.obj("seed" -> wallet.seed)) } diff --git a/src/main/scala/vsys/api/http/addresses/AddressApiRoute.scala b/src/main/scala/vsys/api/http/addresses/AddressApiRoute.scala index f9056f179..1ae16ad58 100644 --- a/src/main/scala/vsys/api/http/addresses/AddressApiRoute.scala +++ b/src/main/scala/vsys/api/http/addresses/AddressApiRoute.scala @@ -32,8 +32,11 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } ~ root ~ create @Path("/{address}") - @ApiOperation(value = "Delete", notes = "Remove the account with address {address} from the wallet", httpMethod = "DELETE", + @ApiOperation(value = "Delete", notes = "Remove the `address` from the wallet", httpMethod = "DELETE", authorizations = Array(new Authorization("api_key"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Successful Operation") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path") )) @@ -50,14 +53,14 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/sign/{address}") - @ApiOperation(value = "Sign", notes = "Sign a message with a private key associated with {address}", httpMethod = "POST", + @ApiOperation(value = "Sign", notes = "Sign a `message` with a private key associated with an `address`", httpMethod = "POST", authorizations = Array(new Authorization("api_key"))) @ApiImplicitParams(Array( new ApiImplicitParam(name = "message", value = "Message to sign as a plain string", required = true, paramType = "body", dataType = "string"), new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path") )) @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json with error or json like {\"message\": \"Base58-encoded\",\"publickey\": \"Base58-encoded\", \"signature\": \"Base58-encoded\"}") + new ApiResponse(code = 200, message = "Successful Operation") )) def sign: Route = { path("sign" / Segment) { address => @@ -66,14 +69,14 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/signText/{address}") - @ApiOperation(value = "Sign", notes = "Sign a message with a private key associated with {address}", httpMethod = "POST", + @ApiOperation(value = "Sign", notes = "Sign a message with a private key associated with `address`", httpMethod = "POST", authorizations = Array(new Authorization("api_key"))) @ApiImplicitParams(Array( new ApiImplicitParam(name = "message", value = "Message to sign as a plain string", required = true, paramType = "body", dataType = "string"), new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path") )) @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json with error or json like {\"message\": \"plain text\",\"publickey\": \"Base58-encoded\", \"signature\": \"Base58-encoded\"}") + new ApiResponse(code = 200, message = "Successful Operation") )) def signText: Route = { path("signText" / Segment) { address => @@ -82,8 +85,11 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/verify/{address}") - @ApiOperation(value = "Verify", notes = "Check a signature of a message signed by an account", httpMethod = "POST", + @ApiOperation(value = "Verify", notes = "Check a signature of a base58-encoded message(`body`) signed by an `address`", httpMethod = "POST", authorizations = Array(new Authorization("api_key"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Successful Operation") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path"), new ApiImplicitParam( @@ -102,8 +108,11 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/verifyText/{address}") - @ApiOperation(value = "Verify text", notes = "Check a signature of a message signed by an account", httpMethod = "POST", + @ApiOperation(value = "Verify text", notes = "Check a signature of a plain message(`body`) signed by an `address`", httpMethod = "POST", authorizations = Array(new Authorization("api_key"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Successful Operation") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path"), new ApiImplicitParam( @@ -120,7 +129,7 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/balance/{address}") - @ApiOperation(value = "Balance", notes = "Account's balance", httpMethod = "GET") + @ApiOperation(value = "Balance", notes = "Get the available balance of a specified `address`", httpMethod = "GET", response = classOf[Balance]) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path") )) @@ -129,7 +138,7 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/balance/details/{address}") - @ApiOperation(value = "Details for balance", notes = "Account's balances", httpMethod = "GET") + @ApiOperation(value = "Details for balance", notes = "Get the balance details of a specified `address` including **effective, mintingAverage and available balance**.", httpMethod = "GET", response = classOf[BalanceDetails]) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path") )) @@ -140,7 +149,7 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/balance/{address}/{confirmations}") - @ApiOperation(value = "Confirmed balance", notes = "Balance of {address} after {confirmations}", httpMethod = "GET") + @ApiOperation(value = "Confirmed balance", notes = "Get the balance of an `address` after `confirmations`", httpMethod = "GET", response = classOf[Balance]) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path"), new ApiImplicitParam(name = "confirmations", value = "0", required = true, dataType = "integer", paramType = "path") @@ -153,7 +162,7 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta @Path("/effectiveBalance/{address}") - @ApiOperation(value = "Balance", notes = "Account's balance", httpMethod = "GET") + @ApiOperation(value = "Balance", notes = "Get the effective balance of a specified `address`", httpMethod = "GET", response = classOf[Balance]) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path") )) @@ -164,7 +173,7 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/effectiveBalance/{address}/{confirmations}") - @ApiOperation(value = "Confirmed balance", notes = "Balance of {address} after {confirmations}", httpMethod = "GET") + @ApiOperation(value = "Confirmed balance", notes = "Get the balance of an `address` after `confirmations`", httpMethod = "GET", response = classOf[Balance]) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path"), new ApiImplicitParam(name = "confirmations", value = "0", required = true, dataType = "integer", paramType = "path") @@ -178,8 +187,11 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/seed/{address}") - @ApiOperation(value = "Seed", notes = "Export seed value for the {address}", httpMethod = "GET", + @ApiOperation(value = "Seed", notes = "Export seed value for the `address`", httpMethod = "GET", authorizations = Array(new Authorization("api_key"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of a seed or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path") )) @@ -194,7 +206,7 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/validate/{address}") - @ApiOperation(value = "Validate", notes = "Check whether address {address} is valid or not", httpMethod = "GET") + @ApiOperation(value = "Validate", notes = "Check whether `address` is valid or not", httpMethod = "GET", response = classOf[Validity]) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path") )) @@ -203,6 +215,9 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of a addresses list or error") + )) @ApiOperation(value = "Addresses", notes = "Get wallet accounts addresses", httpMethod = "GET") def root: Route = (path("addresses") & get) { val accounts = wallet.privateKeyAccounts @@ -211,10 +226,13 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/seq/{from}/{to}") - @ApiOperation(value = "Seq", notes = "Get wallet accounts addresses", httpMethod = "GET") + @ApiOperation(value = "Seq", notes = "Get wallet accounts addresses by specified a start nonce index(`from`) and a end nonce index(`to`)", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of wallet addresses or error") + )) @ApiImplicitParams(Array( - new ApiImplicitParam(name = "from", value = "Start address", required = true, dataType = "integer", paramType = "path"), - new ApiImplicitParam(name = "to", value = "address", required = true, dataType = "integer", paramType = "path") + new ApiImplicitParam(name = "from", value = "nonce start index", required = true, dataType = "integer", paramType = "path"), + new ApiImplicitParam(name = "to", value = "nonce end index", required = true, dataType = "integer", paramType = "path") )) def seq: Route = { (path("seq" / IntNumber / IntNumber) & get) { case (start, end) => @@ -229,6 +247,9 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta } @Path("/") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Successful Operation") + )) @ApiOperation(value = "Create", notes = "Create a new account in the wallet(if it exists)", httpMethod = "POST", authorizations = Array(new Authorization("api_key"))) def create: Route = (path("addresses") & post) { @@ -318,7 +339,10 @@ case class AddressApiRoute(settings: RestAPISettings, wallet: Wallet, state: Sta new ApiImplicitParam(name = "publicKey", value = "Public key Base58-encoded", required = true, paramType = "path", dataType = "string") )) - @ApiOperation(value = "Address from Public Key", notes = "Generate a address from public key", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of an address or error") + )) + @ApiOperation(value = "Address from Public Key", notes = "Generate an address from a `publicKey`", httpMethod = "GET") def publicKey: Route = (path("publicKey" / Segment) & get) { publicKey => Base58.decode(publicKey) match { case Success(pubKeyBytes) => { diff --git a/src/main/scala/vsys/api/http/contract/ContractApiRoute.scala b/src/main/scala/vsys/api/http/contract/ContractApiRoute.scala index 9ff9a14b1..57814033c 100644 --- a/src/main/scala/vsys/api/http/contract/ContractApiRoute.scala +++ b/src/main/scala/vsys/api/http/contract/ContractApiRoute.scala @@ -27,11 +27,16 @@ import ContractApiRoute._ @Path("/contract") @Api(value = "/contract") -case class ContractApiRoute (settings: RestAPISettings, wallet: Wallet, utx: UtxPool, allChannels: ChannelGroup, time: Time, state: StateReader) +case class ContractApiRoute (settings: RestAPISettings, + wallet: Wallet, + utx: UtxPool, + allChannels: ChannelGroup, + time: Time, + state: StateReader) extends ApiRoute with BroadcastRoute { override val route = pathPrefix("contract") { - register ~ content ~ info ~ tokenInfo ~ balance ~ execute ~ tokenId ~ vBalance ~ getContractData + register ~ content ~ info ~ tokenInfo ~ balance ~ execute ~ tokenId ~ vBalance ~ getContractData ~ lastToken } @Path("/register") @@ -47,14 +52,13 @@ case class ContractApiRoute (settings: RestAPISettings, wallet: Wallet, utx: Utx required = true, paramType = "body", dataType = "vsys.api.http.contract.RegisterContractRequest", - defaultValue = "{\n\t\"sender\": \"3Mx2afTZ2KbRrLNbytyzTtXukZvqEB8SkW7\",\n\t\"contract\": \"contract\",\n\t\"data\":\"data\",\n\t\"description\":\"5VECG3ZHwy\",\n\t\"fee\": 100000,\n\t\"feeScale\": 100\n}" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def register: Route = processRequest("register", (t: RegisterContractRequest) => doBroadcast(TransactionFactory.registerContract(t, wallet, time))) @Path("/vBalance/{contractId}") - @ApiOperation(value = "Contract balance", notes = "Get contract account v balance associated with contract id.", httpMethod = "GET") + @ApiOperation(value = "Contract balance", notes = "Get contract account **v balance** associated with `contractId`.", httpMethod = "GET", response = classOf[Balance]) @ApiImplicitParams(Array( new ApiImplicitParam(name = "contractId", value = "Contract ID", required = true, dataType = "string", paramType = "path") )) @@ -70,8 +74,33 @@ case class ContractApiRoute (settings: RestAPISettings, wallet: Wallet, utx: Utx ))).getOrElse(InvalidContractAddress) } + @Path("/lastTokenIndex/{contractId}") + @ApiOperation(value = "Last Token Index", notes = "Token contract last token index", httpMethod = "Get") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response with Last Token Index or error") + )) + @ApiImplicitParams(Array( + new ApiImplicitParam(name = "contractId", value = "Contract ID", required = true, dataType = "string", paramType = "path") + )) + def lastToken: Route = (get & path("lastTokenIndex" / Segment)) { contractId => + ByteStr.decodeBase58(contractId) match { + case Success(id) if ContractAccount.fromString(contractId).isRight => { + val lastTokenIndex = state.contractTokens(id) - 1 + if (lastTokenIndex == -1) { + complete(CustomValidationError("No token generated in this contract")) + } else { + complete(TokenIndex(contractId, lastTokenIndex)) + } + } + case _ => complete(InvalidContractAddress) + } + } + @Path("/content/{contractId}") - @ApiOperation(value = "Contract content", notes = "Get contract content associated with a contract id.", httpMethod = "GET") + @ApiOperation(value = "Contract content", notes = "Get **contract content** associated with a `contractId`.", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the contract content or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "contractId", value = "Contract ID", required = true, dataType = "string", paramType = "path") )) @@ -86,18 +115,24 @@ case class ContractApiRoute (settings: RestAPISettings, wallet: Wallet, utx: Utx } } - @Path("data/{contractId}/{key}") - @ApiOperation(value = "Contract Data", notes = "Contract data by given contract ID and key (default numerical 0).", httpMethod = "Get", authorizations = Array(new Authorization("api_key"))) + @Path("/data/{contractId}/{key}") + @ApiOperation(value = "Contract Data", notes = "Get **contract data** by given `contractId` and `key` (default numerical 0).", httpMethod = "Get", authorizations = Array(new Authorization("api_key"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of contract data or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "contractId", value = "Contract Account", required = true, dataType = "string", paramType = "path"), new ApiImplicitParam(name = "key", value = "Key", required = true, dataType = "string", paramType = "path") )) - def getContractData: Route = (get & withAuth & path("data" / Segment / Segment)) { (contractId, key) => + def getContractData: Route = (get & path("data" / Segment / Segment)) { (contractId, key) => complete(dataJson(contractId, key)) } @Path("/info/{contractId}") - @ApiOperation(value = "Info", notes = "Get contract info associated with a contract id.", httpMethod = "GET") + @ApiOperation(value = "Info", notes = "Get **contract info** associated with a `contractId`.", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the contract info or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "contractId", value = "Contract ID", required = true, dataType = "string", paramType = "path") )) @@ -130,20 +165,32 @@ case class ContractApiRoute (settings: RestAPISettings, wallet: Wallet, utx: Utx } } - private def typeFromBytes(bytes: ByteStr): String = bytes match { - case ContractPermitted.contract.bytes => "TokenContractWithSplit" - case ContractPermitted.contractWithoutSplit.bytes => "TokenContract" - case ContractDepositWithdraw.contract.bytes => "DepositWithdrawContract" - case ContractDepositWithdrawProductive.contract.bytes => "ProductiveDepositWithdrawContract" - case ContractSystem.contract.bytes => "SystemContract" - case ContractLock.contract.bytes => "LockContract" - case ContractNonFungible.contract.bytes => "NonFungibleContract" - case ContractPaymentChannel.contract.bytes => "PaymentChannelContract" - case _ => "GeneralContract" - } + private val contractType: Map[ByteStr, String] = Map( + ContractPermitted.contract.bytes -> "TokenContractWithSplit", + ContractPermitted.contractWithoutSplit.bytes -> "TokenContract", + ContractSystem.contract.bytes -> "SystemContract", + ContractLock.contract.bytes -> "LockContract", + ContractNonFungible.contract.bytes -> "NonFungibleContract", + ContractPaymentChannel.contract.bytes -> "PaymentChannelContract", + ContractAtomicSwap.contract.bytes -> "AtomicSwapContract", + ContractVSwap.contract.bytes -> "VSwapContract", + ContractVOption.contract.bytes -> "VOptionContract", + ContractVStableSwap.contract.bytes -> "VStableSwapContract", + ContractVEscrow.contract.bytes -> "ESCROWContract", + ContractTokenV2.contractTokenWhiteList.bytes -> "TokenContractWithWhitelist", + ContractTokenV2.contractTokenBlackList.bytes -> "TokenContractWithBlacklist", + ContractNonFungibleV2.contractNFTWhitelist.bytes -> "NFTContractWithWhitelist", + ContractNonFungibleV2.contractNFTBlacklist.bytes -> "NFTContractWithBlacklist" + ) + + private def typeFromBytes(bytes: ByteStr): String = + contractType.getOrElse(bytes, "GeneralContract") @Path("/tokenInfo/{tokenId}") - @ApiOperation(value = "Token's Info", notes = "Token's info by given token", httpMethod = "Get") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of token info or error") + )) + @ApiOperation(value = "Token's Info", notes = "Get the **token's info** by given `tokenId`", httpMethod = "Get") @ApiImplicitParams(Array( new ApiImplicitParam(name = "tokenId", value = "Token ID", required = true, dataType = "string", paramType = "path") )) @@ -178,8 +225,11 @@ case class ContractApiRoute (settings: RestAPISettings, wallet: Wallet, utx: Utx } } - @Path("balance/{address}/{tokenId}") - @ApiOperation(value = "Token's balance", notes = "Account's balance by given token", httpMethod = "Get") + @Path("/balance/{address}/{tokenId}") + @ApiOperation(value = "Token's balance", notes = "Get the **balance** of a specified `tokenId` by a given `address`", httpMethod = "Get") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the token balance or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path"), new ApiImplicitParam(name = "tokenId", value = "Token ID", required = true, dataType = "string", paramType = "path") @@ -246,15 +296,17 @@ case class ContractApiRoute (settings: RestAPISettings, wallet: Wallet, utx: Utx value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.contract.ExecuteContractFunctionRequest", - defaultValue = "{\n\t\"sender\": \"3Mx2afTZ2KbRrLNbytyzTtXukZvqEB8SkW7\",\n\t\"contractId\": \"contractId\",\n\t\"funcIdx\": \"0\",\n\t\"data\":\"data\",\n\t\"description\":\"5VECG3ZHwy\",\n\t\"fee\": 100000,\n\t\"feeScale\": 100\n}" + dataType = "vsys.api.http.contract.ExecuteContractFunctionRequest" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def execute: Route = processRequest("execute", (t: ExecuteContractFunctionRequest) => doBroadcast(TransactionFactory.executeContractFunction(t, wallet, time))) - @Path("contractId/{contractId}/tokenIndex/{tokenIndex}") - @ApiOperation(value = "Token's Id", notes = "Token Id from contract Id and token index", httpMethod = "Get") + @Path("/contractId/{contractId}/tokenIndex/{tokenIndex}") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of a token id or error") + )) + @ApiOperation(value = "Token's Id", notes = "Get the **token Id** from the specified `contractId` and `tokenIndex`", httpMethod = "Get") @ApiImplicitParams(Array( new ApiImplicitParam(name = "contractId", value = "Contract ID", required = true, dataType = "string", paramType = "path"), new ApiImplicitParam(name = "tokenIndex", value = "Token Index", required = true, dataType = "integer", paramType = "path") @@ -283,7 +335,6 @@ case class ContractApiRoute (settings: RestAPISettings, wallet: Wallet, utx: Utx } } } - } object ContractApiRoute { @@ -292,4 +343,8 @@ object ContractApiRoute { implicit val balanceFormat: Format[Balance] = Json.format + case class TokenIndex(contractId: String, lastTokenIndex: Int) + + implicit val tokenIndexFormat: Format[TokenIndex] = Json.format + } \ No newline at end of file diff --git a/src/main/scala/vsys/api/http/contract/ContractBroadcastApiRoute.scala b/src/main/scala/vsys/api/http/contract/ContractBroadcastApiRoute.scala index a371c517f..6da8aa578 100644 --- a/src/main/scala/vsys/api/http/contract/ContractBroadcastApiRoute.scala +++ b/src/main/scala/vsys/api/http/contract/ContractBroadcastApiRoute.scala @@ -30,11 +30,10 @@ case class ContractBroadcastApiRoute(settings: RestAPISettings, value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.contract.SignedRegisterContractRequest", - defaultValue = "{\n\t\"contract\": \"contract\",\n\t\"data\":\"data\",\n\t\"description\":\"5VECG3ZHwy\",\n\t\"senderPublicKey\": \"11111\",\n\t\"fee\": 100000,\n\t\"feeScale\": 100,\"timestamp\": 12345678,\n\t\"signature\": \"asdasdasd\"\n}" + dataType = "vsys.api.http.contract.SignedRegisterContractRequest" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def signedRegister: Route = (path("register") & post) { json[SignedRegisterContractRequest] { contractReq => doBroadcast(contractReq.toTx) @@ -52,11 +51,10 @@ case class ContractBroadcastApiRoute(settings: RestAPISettings, value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.contract.SignedExecuteContractFunctionRequest", - defaultValue = "{\n\t\"contractId\": \"contractId\",\n\t\"funcIdx\": \"0\",\n\t\"data\":\"data\",\n\t\"description\":\"5VECG3ZHwy\",\n\t\"senderPublicKey\": \"11111\",\n\t\"fee\": 100000,\n\t\"feeScale\": 100,\"timestamp\": 12345678,\n\t\"signature\": \"asdasdasd\"\n}" + dataType = "vsys.api.http.contract.SignedExecuteContractFunctionRequest" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def signedExecute: Route = (path("execute") & post) { json[SignedExecuteContractFunctionRequest] { contractReq => doBroadcast(contractReq.toTx) diff --git a/src/main/scala/vsys/api/http/contract/SignedExecuteContractFunctionRequest.scala b/src/main/scala/vsys/api/http/contract/SignedExecuteContractFunctionRequest.scala index 86444d318..4d0a6a21e 100644 --- a/src/main/scala/vsys/api/http/contract/SignedExecuteContractFunctionRequest.scala +++ b/src/main/scala/vsys/api/http/contract/SignedExecuteContractFunctionRequest.scala @@ -21,9 +21,9 @@ case class SignedExecuteContractFunctionRequest(@ApiModelProperty(value = "Base5 functionData: String, @ApiModelProperty(value = "Base58 encoded attachment") attachment: Option[String], - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "30000000") fee: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "100") feeScale: Short, @ApiModelProperty(required = true) timestamp: Long, diff --git a/src/main/scala/vsys/api/http/contract/SignedRegisterContractRequest.scala b/src/main/scala/vsys/api/http/contract/SignedRegisterContractRequest.scala index 0588d57a0..0b25b515c 100644 --- a/src/main/scala/vsys/api/http/contract/SignedRegisterContractRequest.scala +++ b/src/main/scala/vsys/api/http/contract/SignedRegisterContractRequest.scala @@ -19,9 +19,9 @@ case class SignedRegisterContractRequest(@ApiModelProperty(value = "Base58 encod initData: String, @ApiModelProperty(value = "Description of contract") description: Option[String], - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "10000000000") fee: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "100") feeScale: Short, @ApiModelProperty(required = true) timestamp: Long, diff --git a/src/main/scala/vsys/api/http/database/DbApiRoute.scala b/src/main/scala/vsys/api/http/database/DbApiRoute.scala index 94e5b621f..53509fe83 100644 --- a/src/main/scala/vsys/api/http/database/DbApiRoute.scala +++ b/src/main/scala/vsys/api/http/database/DbApiRoute.scala @@ -39,10 +39,9 @@ case class DbApiRoute (settings: RestAPISettings, wallet: Wallet, utx: UtxPool, required = true, paramType = "body", dataType = "vsys.api.http.database.DbPutRequest", - defaultValue = "{\n\t\"dbKey\": \"name\",\n\t\"data\": \"dbdata\",\n\t\"dataType\": \"ByteArray\",\n\t\"sender\": \"3Mx2afTZ2KbRrLNbytyzTtXukZvqEB8SkW7\",\n\t\"fee\": 100000,\n\t\"feeScale\": 100\n}" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def putKV: Route = processRequest("put", (t: DbPutRequest) => doBroadcast(TransactionFactory.dbPut(t, wallet, time))) @Path("/broadcast/put") @@ -57,10 +56,9 @@ case class DbApiRoute (settings: RestAPISettings, wallet: Wallet, utx: UtxPool, required = true, paramType = "body", dataType = "vsys.api.http.database.SignedDbPutRequest", - defaultValue = "{\n\t\"dbKey\": \"name\",\n\t\"data\": \"dbdata\",\n\t\"dataType\": \"ByteArray\",\n\t\"sender\": \"3Mx2afTZ2KbRrLNbytyzTtXukZvqEB8SkW7\",\n\t\"fee\": 100000,\n\t\"feeScale\": 100\n\t\"timestamp\": 12345678,\n\t\"signature\": \"asdasdasd\"\n}" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def signPutKV: Route = (path("broadcast" / "put") & post) { json[SignedDbPutRequest] { signedDbPutReq => doBroadcast(signedDbPutReq.toTx) @@ -68,7 +66,10 @@ case class DbApiRoute (settings: RestAPISettings, wallet: Wallet, utx: UtxPool, } @Path("/get/{nameSpace}/{dbKey}") - @ApiOperation(value = "get", notes = "Get db entry", httpMethod = "GET") + @ApiOperation(value = "get", notes = "Get **db entries** by given `nameSpace` and `dbKey`", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of db entries or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "nameSpace", value = "Address", required = true, dataType = "string", paramType = "path"), new ApiImplicitParam(name = "dbKey", value = "dbKey", required = true, dataType = "string", paramType = "path") diff --git a/src/main/scala/vsys/api/http/database/SignedDbPutRequest.scala b/src/main/scala/vsys/api/http/database/SignedDbPutRequest.scala index 9e18e7b75..be54dec37 100644 --- a/src/main/scala/vsys/api/http/database/SignedDbPutRequest.scala +++ b/src/main/scala/vsys/api/http/database/SignedDbPutRequest.scala @@ -16,9 +16,9 @@ case class SignedDbPutRequest(@ApiModelProperty(value = "Base58 encoded sender p dataType: String, @ApiModelProperty(value = "data") data: String, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "100000000") fee: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "100") feeScale: Short, @ApiModelProperty(required = true) timestamp: Long, diff --git a/src/main/scala/vsys/api/http/debug/DebugApiRoute.scala b/src/main/scala/vsys/api/http/debug/DebugApiRoute.scala index 7dc1b3c21..08087de32 100644 --- a/src/main/scala/vsys/api/http/debug/DebugApiRoute.scala +++ b/src/main/scala/vsys/api/http/debug/DebugApiRoute.scala @@ -48,6 +48,9 @@ case class DebugApiRoute(settings: RestAPISettings, } @Path("/blocks/{howMany}") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the sizes and full hashes for last blocks or error") + )) @ApiOperation(value = "Blocks", notes = "Get sizes and full hashes for last blocks", httpMethod = "GET", authorizations = Array(new Authorization("api_key"))) @ApiImplicitParams(Array( @@ -70,7 +73,9 @@ case class DebugApiRoute(settings: RestAPISettings, @Path("/state") @ApiOperation(value = "State", notes = "Get current state", httpMethod = "GET", authorizations = Array(new Authorization("api_key"))) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json state"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the current state or error") + )) def state: Route = (path("state") & get & withAuth) { complete(stateReader.accountPortfolios .map { case (k, v) => @@ -82,7 +87,7 @@ case class DebugApiRoute(settings: RestAPISettings, @Path("/portfolios/{address}") @ApiOperation( value = "Portfolio", - notes = "Get current portfolio considering pessimistic transactions in the UTX pool", + notes = "Get current portfolio considering pessimistic transactions in the unconfirmed transactions(UTX) pool by a speicified `address`", httpMethod = "GET", authorizations = Array(new Authorization("api_key")) ) @@ -116,8 +121,11 @@ case class DebugApiRoute(settings: RestAPISettings, } @Path("/stateVsys/{height}") - @ApiOperation(value = "State at block", notes = "Get state at specified height", httpMethod = "GET", + @ApiOperation(value = "State at block", notes = "Get the **state** at a specified `height`", httpMethod = "GET", authorizations = Array(new Authorization("api_key"))) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the address/balance pairing or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "height", value = "height", required = true, dataType = "integer", paramType = "path") )) @@ -155,7 +163,7 @@ case class DebugApiRoute(settings: RestAPISettings, ) )) @ApiResponses(Array( - new ApiResponse(code = 200, message = "200 if success, 404 if there are no block at this height") + new ApiResponse(code = 200, message = "Successful Operation") )) def rollback: Route = withAuth { (path("rollback") & post) { @@ -171,9 +179,9 @@ case class DebugApiRoute(settings: RestAPISettings, } @Path("/info") - @ApiOperation(value = "State", notes = "All info you need to debug", httpMethod = "GET") + @ApiOperation(value = "State", notes = "Get all info you need to debug", httpMethod = "GET") @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json state") + new ApiResponse(code = 200, message = "Json response of the debug info or error") )) def info: Route = (path("info") & get & withAuth) { complete(Json.obj( @@ -184,7 +192,10 @@ case class DebugApiRoute(settings: RestAPISettings, @Path("/rollback-to/{signature}") - @ApiOperation(value = "Block signature", notes = "Rollback the state to the block with a given signature", httpMethod = "DELETE", + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Successful Operation") + )) + @ApiOperation(value = "Block signature", notes = "Rollback the state to the block with a given `signature`", httpMethod = "DELETE", authorizations = Array(new Authorization("api_key"))) @ApiImplicitParams(Array( new ApiImplicitParam(name = "signature", value = "Base58-encoded block signature", required = true, dataType = "string", paramType = "path") @@ -201,13 +212,13 @@ case class DebugApiRoute(settings: RestAPISettings, } @Path("/blacklist") - @ApiOperation(value = "Blacklist given peer", notes = "Moving peer to blacklist", httpMethod = "POST", + @ApiOperation(value = "Blacklist given peer", notes = "Moving peer to blacklist by a specified node `address`", httpMethod = "POST", authorizations = Array(new Authorization("api_key"))) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "IP address of node", required = true, dataType = "string", paramType = "body") )) @ApiResponses(Array( - new ApiResponse(code = 200, message = "200 if success, 404 if there are no peer with such address") + new ApiResponse(code = 200, message = "Successful Operation") )) def blacklist: Route = withAuth { (path("blacklist") & post) { diff --git a/src/main/scala/vsys/api/http/leasing/LeaseApiRoute.scala b/src/main/scala/vsys/api/http/leasing/LeaseApiRoute.scala index 01f5aecda..e11d29765 100644 --- a/src/main/scala/vsys/api/http/leasing/LeaseApiRoute.scala +++ b/src/main/scala/vsys/api/http/leasing/LeaseApiRoute.scala @@ -36,14 +36,16 @@ case class LeaseApiRoute(settings: RestAPISettings, wallet: Wallet, utx: UtxPool value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.leasing.LeaseRequest", - defaultValue = "{\n\t\"amount\": 100000000,\n\t\"recipient\": \"3NBsppTVpai9jq6agi9wXXrWhaMPPig48Aw\",\n\t\"sender\": \"3Mx2afTZ2KbRrLNbytyzTtXukZvqEB8SkW7\",\n\t\"fee\": 100000,\n\t\"feeScale\": 100\n}" + dataType = "vsys.api.http.leasing.LeaseRequest" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def lease: Route = processRequest("lease", (t: LeaseRequest) => doBroadcast(TransactionFactory.lease(t, wallet, time))) @Path("/cancel") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Successful Operation") + )) @ApiOperation(value = "Interrupt a lease", httpMethod = "POST", produces = "application/json", @@ -55,8 +57,7 @@ case class LeaseApiRoute(settings: RestAPISettings, wallet: Wallet, utx: UtxPool value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.leasing.LeaseCancelRequest", - defaultValue = "{\n\t\"sender\": \"3Myss6gmMckKYtka3cKCM563TBJofnxvfD7\",\n\t\"txId\": \"ABMZDPY4MyQz7kKNAevw5P9eNmRErMutJoV9UNeCtqRV\",\n\t\"fee\": 10000000,\n\t\"feeScale\": 100\n}" + dataType = "vsys.api.http.leasing.LeaseCancelRequest" ) )) def cancel: Route = processRequest("cancel", (t: LeaseCancelRequest) => doBroadcast(TransactionFactory.leaseCancel(t, wallet, time))) diff --git a/src/main/scala/vsys/api/http/leasing/LeaseBroadcastApiRoute.scala b/src/main/scala/vsys/api/http/leasing/LeaseBroadcastApiRoute.scala index eb04419f3..0dd74b67d 100644 --- a/src/main/scala/vsys/api/http/leasing/LeaseBroadcastApiRoute.scala +++ b/src/main/scala/vsys/api/http/leasing/LeaseBroadcastApiRoute.scala @@ -31,11 +31,10 @@ case class LeaseBroadcastApiRoute( value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.leasing.SignedLeaseRequest", - defaultValue = "{\n\t\"amount\": 100000000,\n\t\"recipient\": \"3NBsppTVpai9jq6agi9wXXrWhaMPPig48Aw\",\n\t\"senderPublicKey\": \"11111\",\n\t\"fee\": 100000,\n\t\"feeScale\": 100,\n\t\"timestamp\": 12345678,\n\t\"signature\": \"asdasdasd\"\n}" + dataType = "vsys.api.http.leasing.SignedLeaseRequest" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def signedLease: Route = (path("lease") & post) { json[SignedLeaseRequest] { leaseReq => doBroadcast(leaseReq.toTx) @@ -47,14 +46,16 @@ case class LeaseBroadcastApiRoute( httpMethod = "POST", produces = "application/json", consumes = "application/json") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Successful Operation") + )) @ApiImplicitParams(Array( new ApiImplicitParam( name = "body", value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.leasing.SignedLeaseCancelRequest", - defaultValue = "{\n\t\"sender\": \"3Myss6gmMckKYtka3cKCM563TBJofnxvfD7\",\n\t\"txId\": \"ABMZDPY4MyQz7kKNAevw5P9eNmRErMutJoV9UNeCtqRV\",\n\t\"fee\": 10000000,\n\t\"feeScale\": 100,\n\t\"timestamp\": 12345678,\n\t\"signature\": \"asdasdasd\"\n}" + dataType = "vsys.api.http.leasing.SignedLeaseCancelRequest" ) )) def signedLeaseCancel: Route = (path("cancel") & post) { diff --git a/src/main/scala/vsys/api/http/leasing/SignedLeaseCancelRequest.scala b/src/main/scala/vsys/api/http/leasing/SignedLeaseCancelRequest.scala index 30825330e..fcf204825 100644 --- a/src/main/scala/vsys/api/http/leasing/SignedLeaseCancelRequest.scala +++ b/src/main/scala/vsys/api/http/leasing/SignedLeaseCancelRequest.scala @@ -16,9 +16,9 @@ case class SignedLeaseCancelRequest(@ApiModelProperty(value = "Base58 encoded se timestamp: Long, @ApiModelProperty(required = true) signature: String, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "10000000") fee: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "100") feeScale: Short) extends BroadcastRequest { def toTx: Either[ValidationError, LeaseCancelTransaction] = for { _sender <- PublicKeyAccount.fromBase58String(senderPublicKey) @@ -37,4 +37,3 @@ case class SignedLeaseCancelRequest(@ApiModelProperty(value = "Base58 encoded se object SignedLeaseCancelRequest { implicit val leaseCancelRequestFormat: Format[SignedLeaseCancelRequest] = Json.format } - diff --git a/src/main/scala/vsys/api/http/leasing/SignedLeaseRequest.scala b/src/main/scala/vsys/api/http/leasing/SignedLeaseRequest.scala index 1c875941f..ca0ae6787 100644 --- a/src/main/scala/vsys/api/http/leasing/SignedLeaseRequest.scala +++ b/src/main/scala/vsys/api/http/leasing/SignedLeaseRequest.scala @@ -13,9 +13,9 @@ case class SignedLeaseRequest(@ApiModelProperty(value = "Base58 encoded sender p senderPublicKey: String, @ApiModelProperty(required = true) amount: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "10000000") fee: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "100") feeScale: Short, @ApiModelProperty(value = "Recipient address", required = true) recipient: String, diff --git a/src/main/scala/vsys/api/http/payment/PaymentApiRoute.scala b/src/main/scala/vsys/api/http/payment/PaymentApiRoute.scala index f443ba051..28d553ee1 100755 --- a/src/main/scala/vsys/api/http/payment/PaymentApiRoute.scala +++ b/src/main/scala/vsys/api/http/payment/PaymentApiRoute.scala @@ -23,7 +23,7 @@ case class PaymentApiRoute(settings: RestAPISettings, wallet: Wallet, utx: UtxPo @Path("/payment") @ApiOperation(value = "Send payment from wallet.", - notes = "Send payment from wallet to another wallet. Each call sends new payment.", + notes = "Send payment from wallet to another wallet.", httpMethod = "POST", produces = "application/json", consumes = "application/json", @@ -34,12 +34,11 @@ case class PaymentApiRoute(settings: RestAPISettings, wallet: Wallet, utx: UtxPo value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.payment.PaymentRequest", - defaultValue = "{\n\t\"amount\":400,\n\t\"fee\":1,\n\t\"feeScale\":100,\n\t\"sender\":\"senderId\",\n\t\"recipient\":\"recipientId\",\n\t\"attachment\":\"5VECG3ZHwy\"\n}" + dataType = "vsys.api.http.payment.PaymentRequest" ) )) @ApiResponses(Array( - new ApiResponse(code = 200, message = "Json with response or error") + new ApiResponse(code = 200, message = "Successful Operation") )) def payment: Route = (path("payment") & post & withAuth) { json[PaymentRequest] { p => @@ -57,11 +56,10 @@ case class PaymentApiRoute(settings: RestAPISettings, wallet: Wallet, utx: UtxPo value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.payment.SignedPaymentRequest", - defaultValue = "{\n\t\"timestamp\": 0,\n\t\"amount\":400,\n\t\"fee\":1,\n\t\"feeScale\":100,\n\t\"senderPublicKey\":\"senderPubKey\",\n\t\"recipient\":\"recipientId\",\n\t\"attachment\":\"5VECG3ZHwy\",\n\t\"signature\":\"sig\"\n}" + dataType = "vsys.api.http.payment.SignedPaymentRequest" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def broadcastPayment: Route = (path("broadcast" / "payment") & post) { json[SignedPaymentRequest] { payment => doBroadcast(payment.toTx) diff --git a/src/main/scala/vsys/api/http/payment/PaymentRequest.scala b/src/main/scala/vsys/api/http/payment/PaymentRequest.scala index 12dfb9e1c..8b2e80a83 100644 --- a/src/main/scala/vsys/api/http/payment/PaymentRequest.scala +++ b/src/main/scala/vsys/api/http/payment/PaymentRequest.scala @@ -6,9 +6,9 @@ import play.api.libs.functional.syntax._ case class PaymentRequest( @ApiModelProperty(required = true) amount: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "10000000") fee: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "100") feeScale: Short, @ApiModelProperty(value = "Base58 encoded sender", required = true) sender: String, diff --git a/src/main/scala/vsys/api/http/payment/SignedPaymentRequest.scala b/src/main/scala/vsys/api/http/payment/SignedPaymentRequest.scala index 0010dea98..409f1f9f6 100644 --- a/src/main/scala/vsys/api/http/payment/SignedPaymentRequest.scala +++ b/src/main/scala/vsys/api/http/payment/SignedPaymentRequest.scala @@ -8,14 +8,14 @@ import vsys.api.http.BroadcastRequest import vsys.blockchain.state.ByteStr import vsys.blockchain.transaction.{PaymentTransaction, ValidationError} -@ApiModel(value = "Signed Payment transaction") +@ApiModel(value = "SignedPaymentTransaction") case class SignedPaymentRequest( @ApiModelProperty(required = true) timestamp: Long, @ApiModelProperty(required = true) amount: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "10000000") fee: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "100") feeScale: Short, @ApiModelProperty(value = "Recipient address", required = true) recipient: String, diff --git a/src/main/scala/vsys/api/http/spos/SPOSApiRoute.scala b/src/main/scala/vsys/api/http/spos/SPOSApiRoute.scala index e8d1b3cb6..fc959f6f6 100644 --- a/src/main/scala/vsys/api/http/spos/SPOSApiRoute.scala +++ b/src/main/scala/vsys/api/http/spos/SPOSApiRoute.scala @@ -34,11 +34,10 @@ case class SPOSApiRoute(settings: RestAPISettings, wallet: Wallet, utx: UtxPool, value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.spos.ContendSlotsRequest", - defaultValue = "{\n\t\"slotId\": 0,\n\t\"sender\": \"3N4SMepbKXPRADdjfUwNYKdcZdMoVJGXQP5\",\n\t\"fee\": 10000000\n\t\"feeScale\": 100\n}" + dataType = "vsys.api.http.spos.ContendSlotsRequest" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def contend: Route = processRequest("contend", (t: ContendSlotsRequest) => doBroadcast(TransactionFactory.contendSlots(t, wallet, time))) @Path("/release") @@ -53,11 +52,10 @@ case class SPOSApiRoute(settings: RestAPISettings, wallet: Wallet, utx: UtxPool, value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.spos.ReleaseSlotsRequest", - defaultValue = "{\n\t\"slotId\": 0,\n\t\"sender\": \"3N4SMepbKXPRADdjfUwNYKdcZdMoVJGXQP5\",\n\t\"fee\": 100000\n\t\"feeScale\": 100\n}" + dataType = "vsys.api.http.spos.ReleaseSlotsRequest" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def release: Route = processRequest("release", (t: ReleaseSlotsRequest) => doBroadcast(TransactionFactory.releaseSlots(t, wallet, time))) } diff --git a/src/main/scala/vsys/api/http/spos/SPOSBroadcastApiRoute.scala b/src/main/scala/vsys/api/http/spos/SPOSBroadcastApiRoute.scala index 662dc38b2..31072b4c4 100644 --- a/src/main/scala/vsys/api/http/spos/SPOSBroadcastApiRoute.scala +++ b/src/main/scala/vsys/api/http/spos/SPOSBroadcastApiRoute.scala @@ -31,11 +31,10 @@ case class SPOSBroadcastApiRoute( value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.spos.SignedContendSlotsRequest", - defaultValue = "{\n\t\"slotId\": 0,\n\t\"senderPublicKey\": \"11111\",\n\t\"fee\": 100000\n\t\"feeScale\": 100\n\t\"timestamp\": 12345678,\n\t\"signature\": \"asdasdasd\"\n}" + dataType = "vsys.api.http.spos.SignedContendSlotsRequest" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def signedContend: Route = (path("contend") & post) { json[SignedContendSlotsRequest] { contendReq => doBroadcast(contendReq.toTx) @@ -53,11 +52,10 @@ case class SPOSBroadcastApiRoute( value = "Json with data", required = true, paramType = "body", - dataType = "vsys.api.http.spos.SignedReleaseSlotsRequest", - defaultValue = "{\n\t\"slotId\": 0,\n\t\"senderPublicKey\": \"11111\",\n\t\"fee\": 100000\n\t\"feeScale\": 100\n\t\"timestamp\": 12345678,\n\t\"signature\": \"asdasdasd\"\n}" + dataType = "vsys.api.http.spos.SignedReleaseSlotsRequest" ) )) - @ApiResponses(Array(new ApiResponse(code = 200, message = "Json with response or error"))) + @ApiResponses(Array(new ApiResponse(code = 200, message = "Successful Operation"))) def signRelease: Route = (path("release") & post) { json[SignedReleaseSlotsRequest] { releaseReq => doBroadcast(releaseReq.toTx) diff --git a/src/main/scala/vsys/api/http/spos/SignedContendSlotsRequest.scala b/src/main/scala/vsys/api/http/spos/SignedContendSlotsRequest.scala index 06aa16725..c3cb70d36 100644 --- a/src/main/scala/vsys/api/http/spos/SignedContendSlotsRequest.scala +++ b/src/main/scala/vsys/api/http/spos/SignedContendSlotsRequest.scala @@ -10,9 +10,9 @@ import vsys.blockchain.transaction.spos.ContendSlotsTransaction case class SignedContendSlotsRequest(@ApiModelProperty(value = "Base58 encoded sender public key", required = true) senderPublicKey: String, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "5000000000000") fee: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "100") feeScale: Short, @ApiModelProperty(required = true) slotId: Int, diff --git a/src/main/scala/vsys/api/http/spos/SignedReleaseSlotsRequest.scala b/src/main/scala/vsys/api/http/spos/SignedReleaseSlotsRequest.scala index 78de5c617..7277c0667 100644 --- a/src/main/scala/vsys/api/http/spos/SignedReleaseSlotsRequest.scala +++ b/src/main/scala/vsys/api/http/spos/SignedReleaseSlotsRequest.scala @@ -10,11 +10,11 @@ import vsys.blockchain.transaction.spos.ReleaseSlotsTransaction case class SignedReleaseSlotsRequest(@ApiModelProperty(value = "Base58 encoded sender public key", required = true) senderPublicKey: String, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "10000000") fee: Long, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "100") feeScale: Short, - @ApiModelProperty(required = true) + @ApiModelProperty(required = true, example = "0") slotId: Int, @ApiModelProperty(required = true) timestamp: Long, diff --git a/src/main/scala/vsys/api/http/spos/SposConsensusApiRoute.scala b/src/main/scala/vsys/api/http/spos/SposConsensusApiRoute.scala index 8dd3ad042..8f534e72d 100755 --- a/src/main/scala/vsys/api/http/spos/SposConsensusApiRoute.scala +++ b/src/main/scala/vsys/api/http/spos/SposConsensusApiRoute.scala @@ -25,10 +25,13 @@ case class SposConsensusApiRoute( } @Path("/generatingBalance/{address}") - @ApiOperation(value = "Generating balance", notes = "Account's generating balance(the same as balance atm)", httpMethod = "GET") + @ApiOperation(value = "Generating balance", notes = "Get Account's generating balance(the same as balance atm)", httpMethod = "GET") @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path") )) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json with error or response like {\"address\": \"your address\",\"balance\": \"your balance\"}") + )) def generatingBalance: Route = (path("generatingBalance" / Segment) & get) { address => Address.fromString(address) match { case Left(_) => complete(InvalidAddress) @@ -40,7 +43,10 @@ case class SposConsensusApiRoute( } @Path("/mintingAverageBalance/{address}") - @ApiOperation(value = "Minting average balance", notes = "Account's minting average balance", httpMethod = "GET") + @ApiOperation(value = "Minting average balance", notes = "Get the **minting average balance**(MAB) of an `address`", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of an address's MAB or error") + )) @ApiImplicitParams(Array( new ApiImplicitParam(name = "address", value = "Address", required = true, dataType = "string", paramType = "path") )) @@ -57,6 +63,9 @@ case class SposConsensusApiRoute( @Path("/allSlotsInfo") @ApiOperation(value = "Get all slots' info", notes = "Get all slots' information", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of all slots details or error") + )) def allSlotsInfo: Route = (path("allSlotsInfo") & get) { val h = state.height val ret = Json.arr(Json.obj("height" -> h)) ++ JsArray( @@ -84,10 +93,13 @@ case class SposConsensusApiRoute( } @Path("/slotInfo/{slotId}") - @ApiOperation(value = "Minting average balance with slot ID", notes = "Account of supernode's minting average balance", httpMethod = "GET") + @ApiOperation(value = "Minting average balance with slot ID", notes = "Get the supernode's minting average balance by specified `slotId`", httpMethod = "GET") @ApiImplicitParams(Array( new ApiImplicitParam(name = "slotId", value = "Slot Id", required = true, dataType = "integer", paramType = "path") )) + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json response of the minting average balance of a specified slot info or error") + )) def mintingAverageBalanceId: Route = (path("slotInfo" / IntNumber) & get) { slotId => state.slotAddress(slotId) match { case None if slotId >= 0 && slotId < fs.numOfSlots => @@ -111,7 +123,7 @@ case class SposConsensusApiRoute( } @Path("/mintTime/{blockId}") - @ApiOperation(value = "Mint time", notes = "Mint time of a block with specified id", httpMethod = "GET") + @ApiOperation(value = "Mint time", notes = "Mint time of a block with specified `blockId`", httpMethod = "GET", response = classOf[Long]) @ApiImplicitParams(Array( new ApiImplicitParam(name = "blockId", value = "Block id ", required = true, dataType = "string", paramType = "path") )) @@ -122,13 +134,16 @@ case class SposConsensusApiRoute( } @Path("/mintTime") - @ApiOperation(value = "Mint time last", notes = "Mint time of a last block", httpMethod = "GET") + @ApiOperation(value = "Mint time last", notes = "Mint time of a last block", httpMethod = "GET", response = classOf[Long]) def mintTime: Route = (path("mintTime") & get) { complete(Json.obj("mintTime" -> history.lastBlock.get.consensusData.mintTime)) } @Path("/algo") - @ApiOperation(value = "Consensus algo", notes = "Shows which consensus algo being using", httpMethod = "GET") + @ApiOperation(value = "Consensus algo", notes = "Shows which **consensus algo** are being used", httpMethod = "GET") + @ApiResponses(Array( + new ApiResponse(code = 200, message = "Json with error or response like {\"consensusAlgo\": \"current adopting algo\"}") + )) def algo: Route = (path("algo") & get) { complete(Json.obj("consensusAlgo" -> "supernode-proof-of-stake (SPoS)")) } diff --git a/src/main/scala/vsys/api/http/swagger/SwaggerDocService.scala b/src/main/scala/vsys/api/http/swagger/SwaggerDocService.scala index aeb35d441..dd4796393 100755 --- a/src/main/scala/vsys/api/http/swagger/SwaggerDocService.scala +++ b/src/main/scala/vsys/api/http/swagger/SwaggerDocService.scala @@ -7,11 +7,11 @@ import com.github.swagger.akka.SwaggerHttpService import vsys.Version import vsys.settings.RestAPISettings import io.swagger.util.{Json, Yaml} -import io.swagger.models.{Swagger, Scheme, Path} +import io.swagger.models.{Path, Scheme, Swagger} import io.swagger.models.auth.{ApiKeyAuthDefinition, In} import vsys.utils.ScorexLogging -import scala.collection.immutable.Map +import scala.collection.immutable.Map import scala.util.control.NonFatal import scala.collection.JavaConverters._ diff --git a/src/main/scala/vsys/blockchain/block/Block.scala b/src/main/scala/vsys/blockchain/block/Block.scala index 656d20cf7..b14753861 100755 --- a/src/main/scala/vsys/blockchain/block/Block.scala +++ b/src/main/scala/vsys/blockchain/block/Block.scala @@ -10,7 +10,7 @@ import vsys.blockchain.transaction._ import vsys.settings.GenesisSettings import vsys.utils.crypto.EllipticCurveImpl import vsys.utils.ScorexLogging - +import io.swagger.annotations._ import scala.util.{Failure, Try} case class Block(timestamp: Long, version: Byte, reference: ByteStr, signerData: SignerData, @@ -31,6 +31,7 @@ case class Block(timestamp: Long, version: Byte, reference: ByteStr, signerData: lazy val fee: Long = transactionData.map(_.transaction.transactionFee).sum + @ApiModelProperty(hidden= true) lazy val json: JsObject = versionField.json ++ timestampField.json ++ @@ -45,6 +46,7 @@ case class Block(timestamp: Long, version: Byte, reference: ByteStr, signerData: "blocksize" -> bytes.length ) + @ApiModelProperty(hidden= true) lazy val bytes: Array[Byte] = { val txBytesSize = transactionDataField.bytes.length val txBytes = Bytes.ensureCapacity(Ints.toByteArray(txBytesSize), 4, 0) ++ transactionDataField.bytes @@ -69,9 +71,11 @@ case class Block(timestamp: Long, version: Byte, reference: ByteStr, signerData: // set blockScore to minting Balance / 100000000 val sc = Math.max(consensusData.mintBalance / 100000000L, 1L) + @ApiModelProperty(hidden= true) lazy val blockScore: BigInt = BigInt(sc.toString) // destroy transaction fee + @ApiModelProperty(hidden= true) lazy val feesDistribution: Diff = Diff.empty override lazy val signatureValid: Boolean = EllipticCurveImpl.verify(signerData.signature.arr, bytesWithoutSignature, signerData.generator.publicKey) diff --git a/src/main/scala/vsys/blockchain/block/BlockField.scala b/src/main/scala/vsys/blockchain/block/BlockField.scala index 3310f39f4..37714171f 100755 --- a/src/main/scala/vsys/blockchain/block/BlockField.scala +++ b/src/main/scala/vsys/blockchain/block/BlockField.scala @@ -8,6 +8,7 @@ import vsys.account.PublicKeyAccount import vsys.blockchain.state.ByteStr import vsys.blockchain.transaction.{Transaction, ProcessedTransaction} import vsys.utils.serialization.{BytesSerializable, JsonSerializable} +import io.swagger.annotations._ /** * An abstraction of a part of a block, wrapping some data. The wrapper interface @@ -47,6 +48,7 @@ case class TransactionBlockField(override val name: String, override val value: override lazy val bytes: Array[Byte] = value.bytes } +@ApiModelProperty(hidden= true) case class SignerData(generator: PublicKeyAccount, signature: ByteStr) case class SignerDataBlockField(override val name: String, override val value: SignerData) diff --git a/src/main/scala/vsys/blockchain/contract/Contract.scala b/src/main/scala/vsys/blockchain/contract/Contract.scala index 61d81eb19..93c309b72 100644 --- a/src/main/scala/vsys/blockchain/contract/Contract.scala +++ b/src/main/scala/vsys/blockchain/contract/Contract.scala @@ -79,6 +79,22 @@ object Contract extends ScorexLogging { val LanguageVersionByteLength = 4 val LanguageCodeByte: Array[Byte] = Deser.serilizeString("vdds") val LanguageVersionByte: Array[Byte] = Ints.toByteArray(1) + val validContractBytesList: Seq[Array[Byte]] = Seq( + ContractPermitted.contract.bytes.arr, + ContractPermitted.contractWithoutSplit.bytes.arr, + ContractLock.contract.bytes.arr, + ContractNonFungible.contract.bytes.arr, + ContractPaymentChannel.contract.bytes.arr, + ContractAtomicSwap.contract.bytes.arr, + ContractVSwap.contract.bytes.arr, + ContractVOption.contract.bytes.arr, + ContractVStableSwap.contract.bytes.arr, + ContractVEscrow.contract.bytes.arr, + ContractTokenV2.contractTokenWhiteList.bytes.arr, + ContractTokenV2.contractTokenBlackList.bytes.arr, + ContractNonFungibleV2.contractNFTWhitelist.bytes.arr, + ContractNonFungibleV2.contractNFTBlacklist.bytes.arr + ) def buildContract(languageCode: Array[Byte], languageVersion: Array[Byte], trigger: Seq[Array[Byte]], descriptor: Seq[Array[Byte]], @@ -132,19 +148,25 @@ object Contract extends ScorexLogging { } } - def checkStateVar(stateVar: Array[Byte], dataType: DataType.Value): Boolean = - stateVar.length == 2 && dataType == DataType(stateVar(1)) + def checkStateVar(stateVar: Array[Byte]): Boolean = + stateVar.length == 2 + + def checkStateVar(stateVar: Array[Byte], dataType: DataType.DataTypeVal[_]): Boolean = + stateVar.length == 2 && DataType.check(dataType.id.toByte, stateVar(1)) + + def checkStateMap(stateMap: Array[Byte], keyDataType: DataType.DataTypeVal[_]): Boolean = + stateMap.length == 3 && DataType.check(keyDataType.id.toByte, stateMap(1)) - def checkStateMap(stateMap: Array[Byte], keyDataType: DataType.Value, valueDataType: DataType.Value): Boolean = - stateMap.length == 3 && keyDataType == DataType(stateMap(1)) && valueDataType == DataType(stateMap(2)) + def checkStateMap(stateMap: Array[Byte], keyDataType: DataType.DataTypeVal[_], valueDataType: DataType.DataTypeVal[_]): Boolean = + stateMap.length == 3 && DataType.check(keyDataType.id.toByte, stateMap(1)) && DataType.check(valueDataType.id.toByte, stateMap(2)) + + private def checkContractBytes(bytes: Array[Byte]): Boolean = { + validContractBytesList.forall(y => !(bytes sameElements y)) + } private def isByteArrayValid(bytes: Array[Byte], textual: Seq[Array[Byte]]): Boolean = { val textualStr = textualFromBytes(textual) - if (!(bytes sameElements ContractPermitted.contract.bytes.arr) && - !(bytes sameElements ContractPermitted.contractWithoutSplit.bytes.arr) && - !(bytes sameElements ContractLock.contract.bytes.arr) && - !(bytes sameElements ContractNonFungible.contract.bytes.arr) && - !(bytes sameElements ContractPaymentChannel.contract.bytes.arr)) { + if (checkContractBytes(bytes)) { log.warn(s"Illegal contract ${bytes.mkString(" ")}") false } else if (textualStr.isFailure || diff --git a/src/main/scala/vsys/blockchain/contract/ContractAtomicSwap.scala b/src/main/scala/vsys/blockchain/contract/ContractAtomicSwap.scala new file mode 100644 index 000000000..f2fbb7166 --- /dev/null +++ b/src/main/scala/vsys/blockchain/contract/ContractAtomicSwap.scala @@ -0,0 +1,152 @@ +package vsys.blockchain.contract + +import com.google.common.primitives.Ints +import vsys.blockchain.contract.ContractGen._ +import vsys.utils.serialization.Deser + +object ContractAtomicSwap { + lazy val contract: Contract = Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + Seq(initTrigger, depositTrigger, withdrawTrigger), + Seq(lockFunc, solvePuzzleFunc, expireWithdrawFunc), + Seq(makerStateVar.arr, tokenIdStateVar.arr), + Seq(balanceMap.arr, swapOwnerMap.arr, swapRecipientMap.arr, + swapPuzzleMap.arr, swapAmountMap.arr, swapExpiredTimeMap.arr, swapStatusMap.arr), + Seq(triggerTextual, descriptorTextual, stateVarTextual, stateMapTextual) + ).right.get + + // State Var + val stateVarName = List("maker", "tokenId") + val makerStateVar: StateVar = StateVar(0.toByte, DataType.Address.id.toByte) + val tokenIdStateVar: StateVar = StateVar(1.toByte, DataType.TokenId.id.toByte) + lazy val stateVarTextual: Array[Byte] = Deser.serializeArrays(stateVarName.map(x => Deser.serilizeString(x))) + + // State Map + val stateMapBalance = List("contractBalance", "userAddress", "balance") + val stateMapOwner = List("swapOwner", "swapId", "owner") + val stateMapRecipient = List("swapRecipient", "swapId", "recipient") + val stateMapPuzzle = List("swapPuzzle", "swapId", "puzzle") + val stateMapAmount = List("swapAmount", "swapId", "amount") + val stateMapExpiredTime = List("swapExpiredTime", "swapId", "expiredTime") + val stateMapStatus = List("swapStatus", "swapId", "status") + val balanceMap: StateMap = StateMap(0.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + val swapOwnerMap: StateMap = StateMap(1.toByte, DataType.ShortBytes.id.toByte, DataType.Address.id.toByte) + val swapRecipientMap: StateMap = StateMap(2.toByte, DataType.ShortBytes.id.toByte, DataType.Address.id.toByte) + val swapPuzzleMap: StateMap = StateMap(3.toByte, DataType.ShortBytes.id.toByte, DataType.ShortBytes.id.toByte) + val swapAmountMap: StateMap = StateMap(4.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val swapExpiredTimeMap: StateMap = StateMap(5.toByte, DataType.ShortBytes.id.toByte, DataType.Timestamp.id.toByte) + val swapStatusMap: StateMap = StateMap(6.toByte, DataType.ShortBytes.id.toByte, DataType.Boolean.id.toByte) + lazy val stateMapTextual: Array[Byte] = textualStateMap(Seq(stateMapBalance, stateMapOwner, stateMapRecipient, stateMapPuzzle, + stateMapAmount, stateMapExpiredTime, stateMapStatus)) + + // Initialization Trigger + val initId: Short = 0 + val initPara: Seq[String] = Seq("tokenId", + "signer") + val initDataType: Array[Byte] = Array(DataType.TokenId.id.toByte) + val initTriggerOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(1.toByte), + cdbvSet ++ Array(makerStateVar.index, 1.toByte), + cdbvSet ++ Array(tokenIdStateVar.index, 0.toByte) + ) + lazy val initTrigger: Array[Byte] = getFunctionBytes(initId, onInitTriggerType, nonReturnType, initDataType, initTriggerOpcs) + val initTextualBytes: Array[Byte] = textualFunc("init", Seq(), initPara) + + // Deposit Trigger + val depositId: Short = 1 + val depositPara: Seq[String] = Seq("depositor", "amount", "tokenId", + "contractTokenId") + val depositDataType: Array[Byte] = Array(DataType.Address.id.toByte, DataType.Amount.id.toByte, DataType.TokenId.id.toByte) + val depositTriggerOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + cdbvrGet ++ Array(tokenIdStateVar.index, 3.toByte), + assertEqual ++ Array(2.toByte, 3.toByte), + cdbvMapValAdd ++ Array(balanceMap.index, 0.toByte, 1.toByte) + ) + lazy val depositTrigger: Array[Byte] = getFunctionBytes(depositId, onDepositTriggerType, nonReturnType, depositDataType, depositTriggerOpcs) + val depositTextualBytes: Array[Byte] = textualFunc("deposit", Seq(), depositPara) + + // WithDraw Trigger + val withdrawId: Short = 2 + val withdrawPara: Seq[String] = Seq("withdrawer", "amount", "tokenId", + "contractTokenId") + val withdrawDataType: Array[Byte] = Array(DataType.Address.id.toByte, DataType.Amount.id.toByte, DataType.TokenId.id.toByte) + val withdrawTriggerOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + cdbvrGet ++ Array(tokenIdStateVar.index, 3.toByte), + assertEqual ++ Array(2.toByte, 3.toByte), + cdbvMapValMinus ++ Array(balanceMap.index, 0.toByte, 1.toByte) + ) + lazy val withdrawTrigger: Array[Byte] = getFunctionBytes(withdrawId, onWithDrawTriggerType, nonReturnType, withdrawDataType, withdrawTriggerOpcs) + val withdrawTextualBytes: Array[Byte] = textualFunc("withdraw", Seq(), withdrawPara) + + // Lock Function + val lockId: Short = 0 + val lockPara: Seq[String] = Seq("amount", "recipient", "puzzle", "expiredTime", + "caller", "txId", "valueTrue") + val lockDataType: Array[Byte] = Array(DataType.Amount.id.toByte, DataType.Address.id.toByte, DataType.ShortBytes.id.toByte, DataType.Timestamp.id.toByte) + val lockFunctionOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(4.toByte), + cdbvMapValMinus ++ Array(balanceMap.index, 4.toByte, 0.toByte), + loadTransactionId ++ Array(5.toByte), + cdbvMapSet ++ Array(swapOwnerMap.index, 5.toByte, 4.toByte), + cdbvMapSet ++ Array(swapRecipientMap.index, 5.toByte, 1.toByte), + cdbvMapSet ++ Array(swapPuzzleMap.index, 5.toByte, 2.toByte), + cdbvMapSet ++ Array(swapAmountMap.index, 5.toByte, 0.toByte), + cdbvMapSet ++ Array(swapExpiredTimeMap.index, 5.toByte, 3.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + cdbvMapSet ++ Array(swapStatusMap.index, 5.toByte, 6.toByte) + ) + lazy val lockFunc: Array[Byte] = getFunctionBytes(lockId, publicFuncType, nonReturnType, lockDataType, lockFunctionOpcs) + val lockTextualBytes: Array[Byte] = textualFunc("lock", Seq(), lockPara) + + // Solve Puzzle Function + val solvePuzzleId: Short = 1 + val solvePuzzlePara: Seq[String] = Seq("txId", "key", + "status", "recipient", "currentTime", "expiredTime", "res", "puzzle", "amount", "valueFalse") + val solvePuzzleDataType: Array[Byte] = Array(DataType.ShortBytes.id.toByte, DataType.ShortBytes.id.toByte) + val solvePuzzleFunctionOpcs: Seq[Array[Byte]] = Seq( + cdbvrMapGet ++ Array(swapStatusMap.index, 0.toByte, 2.toByte), + assertTrue ++ Array(2.toByte), + cdbvrMapGet ++ Array(swapRecipientMap.index, 0.toByte, 3.toByte), + assertCaller ++ Array(3.toByte), + loadTimestamp ++ Array(4.toByte), + cdbvrMapGet ++ Array(swapExpiredTimeMap.index, 0.toByte, 5.toByte), + compareGreater ++ Array(5.toByte, 4.toByte, 6.toByte), + assertTrue ++ Array(6.toByte), + cdbvrMapGet ++ Array(swapPuzzleMap.index, 0.toByte, 7.toByte), + assertHash ++ Array(7.toByte, 1.toByte), + cdbvrMapGet ++ Array(swapAmountMap.index, 0.toByte, 8.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(9.toByte), + cdbvMapSet ++ Array(swapStatusMap.index, 0.toByte, 9.toByte), + cdbvMapValAdd ++ Array(balanceMap.index, 3.toByte, 8.toByte) + ) + lazy val solvePuzzleFunc: Array[Byte] = getFunctionBytes(solvePuzzleId, publicFuncType, nonReturnType, solvePuzzleDataType, solvePuzzleFunctionOpcs) + val solvePuzzleTextualBytes: Array[Byte] = textualFunc("solvePuzzle", Seq(), solvePuzzlePara) + + // Expire Withdraw Function + val expireWithdrawId: Short = 2 + val expireWithdrawPara: Seq[String] = Seq("txId", + "status", "owner", "currentTime", "expiredTime", "res", "amount", "valueFalse") + val expireWithdrawDataType: Array[Byte] = Array(DataType.ShortBytes.id.toByte) + val expireWithdrawFunctionOpcs: Seq[Array[Byte]] = Seq( + cdbvrMapGet ++ Array(swapStatusMap.index, 0.toByte, 1.toByte), + assertTrue ++ Array(1.toByte), + cdbvrMapGet ++ Array(swapOwnerMap.index, 0.toByte, 2.toByte), + assertCaller ++ Array(2.toByte), + loadTimestamp ++ Array(3.toByte), + cdbvrMapGet ++ Array(swapExpiredTimeMap.index, 0.toByte, 4.toByte), + compareGreater ++ Array(3.toByte, 4.toByte, 5.toByte), + assertTrue ++ Array(5.toByte), + cdbvrMapGet ++ Array(swapAmountMap.index, 0.toByte, 6.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + cdbvMapSet ++ Array(swapStatusMap.index, 0.toByte, 7.toByte), + cdbvMapValAdd ++ Array(balanceMap.index, 2.toByte, 6.toByte) + ) + lazy val expireWithdrawFunc: Array[Byte] = getFunctionBytes(expireWithdrawId, publicFuncType, nonReturnType, expireWithdrawDataType, expireWithdrawFunctionOpcs) + val expireWithdrawTextualBytes: Array[Byte] = textualFunc("expireWithdraw", Seq(), expireWithdrawPara) + + // Gen Textual + lazy val triggerTextual: Array[Byte] = Deser.serializeArrays(Seq(initTextualBytes, depositTextualBytes, withdrawTextualBytes)) + lazy val descriptorTextual: Array[Byte] = Deser.serializeArrays(Seq(lockTextualBytes, solvePuzzleTextualBytes, expireWithdrawTextualBytes)) + +} \ No newline at end of file diff --git a/src/main/scala/vsys/blockchain/contract/ContractGen.scala b/src/main/scala/vsys/blockchain/contract/ContractGen.scala index 44985d357..163ab3aa1 100644 --- a/src/main/scala/vsys/blockchain/contract/ContractGen.scala +++ b/src/main/scala/vsys/blockchain/contract/ContractGen.scala @@ -25,10 +25,13 @@ object ContractGen { val cdbvMapSet = Array(3.toByte, 2.toByte) val cdbvMapValAdd = Array(3.toByte, 3.toByte) val cdbvMapValMinus = Array(3.toByte, 4.toByte) + val cdbvStateValAdd = Array(3.toByte, 5.toByte) + val cdbvStateValMinus = Array(3.toByte, 6.toByte) val cdbvrGet = Array(4.toByte, 1.toByte) val cdbvrMapGetOrDefault = Array(4.toByte, 2.toByte) val cdbvrMapGet = Array(4.toByte, 3.toByte) + val cdbvrGetOrDefault = Array(4.toByte, 4.toByte) val tdbNewToken = Array(5.toByte, 1.toByte) val tdbSplit = Array(5.toByte, 2.toByte) @@ -44,7 +47,14 @@ object ContractGen { val returnValue = Array(9.toByte, 1.toByte) - val compareGreater = Array(10.toByte, 1.toByte) + val compareGreaterEqual = Array(10.toByte, 1.toByte) + val compareGreater = Array(10.toByte, 2.toByte) + val compareLessEqual = Array(10.toByte, 3.toByte) + val compareLess = Array(10.toByte, 4.toByte) + val compareNumEqual = Array(10.toByte, 5.toByte) + val compareNumNotEqual = Array(10.toByte, 6.toByte) + val compareBytesEqual = Array(10.toByte, 7.toByte) + val compareBytesNotEqual = Array(10.toByte, 8.toByte) val basicAdd = Array(11.toByte, 1.toByte) val basicMinus = Array(11.toByte, 2.toByte) @@ -54,6 +64,15 @@ object ContractGen { val basicMax = Array(11.toByte, 6.toByte) val basicConcat = Array(11.toByte, 7.toByte) val basicConstantGet = Array(11.toByte, 8.toByte) + val basicSqrtBigint = Array(11.toByte, 9.toByte) + val basicConvert = Array(11.toByte, 10.toByte) + val basicAnd = Array(11.toByte, 11.toByte) + val basicOr = Array(11.toByte, 12.toByte) + val basicXor = Array(11.toByte, 13.toByte) + val basicNot = Array(11.toByte, 14.toByte) + + val conditionIf = Array(12.toByte, 1.toByte) + val conditionIfElse = Array(12.toByte, 2.toByte) sealed case class StateVar(index: Byte, dataType: Byte) { lazy val arr: Array[Byte] = Array(index, dataType) diff --git a/src/main/scala/vsys/blockchain/contract/ContractLock.scala b/src/main/scala/vsys/blockchain/contract/ContractLock.scala index 14d19678b..bab720fa9 100644 --- a/src/main/scala/vsys/blockchain/contract/ContractLock.scala +++ b/src/main/scala/vsys/blockchain/contract/ContractLock.scala @@ -64,7 +64,7 @@ object ContractLock { assertEqual ++ Array(2.toByte, 3.toByte), loadTimestamp ++ Array(4.toByte), cdbvrMapGetOrDefault ++ Array(lockTimeMap.index, 0.toByte, 5.toByte), - compareGreater ++ Array(4.toByte, 5.toByte, 6.toByte), + compareGreaterEqual ++ Array(4.toByte, 5.toByte, 6.toByte), assertTrue ++ Array(6.toByte), cdbvMapValMinus ++ Array(balanceMap.index, 0.toByte, 1.toByte) ) @@ -79,7 +79,7 @@ object ContractLock { val lockFunctionOpcs: Seq[Array[Byte]] = Seq( loadCaller ++ Array(1.toByte), cdbvrMapGetOrDefault ++ Array(lockTimeMap.index, 1.toByte, 2.toByte), - compareGreater ++ Array(0.toByte, 2.toByte, 3.toByte), + compareGreaterEqual ++ Array(0.toByte, 2.toByte, 3.toByte), assertTrue ++ Array(3.toByte), cdbvMapSet ++ Array(lockTimeMap.index, 1.toByte, 0.toByte) ) diff --git a/src/main/scala/vsys/blockchain/contract/ContractNonFungibleV2.scala b/src/main/scala/vsys/blockchain/contract/ContractNonFungibleV2.scala new file mode 100644 index 000000000..d4645fa55 --- /dev/null +++ b/src/main/scala/vsys/blockchain/contract/ContractNonFungibleV2.scala @@ -0,0 +1,227 @@ +package vsys.blockchain.contract + +import com.google.common.primitives.{Ints, Longs} +import vsys.blockchain.contract.ContractGen._ +import vsys.blockchain.state._ +import vsys.utils.serialization.Deser + +object ContractNonFungibleV2 { + lazy val contractNFTWhitelist: Contract = Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + Seq(initFunc), + Seq(supersedeFunc, issueFunc, updateListFunc, + sendWhitelistFunc, transferWhitelistFunc, depositWhitelistFunc, withdrawWhitelistFunc), + Seq(issuerStateVar.arr, makerStateVar.arr, regulatorStateVar.arr), + Seq(listMap.arr), + Seq(triggerTextual, descriptorWhitelistTextual, stateVarTextual, stateMapWhitelistTextual) + ).explicitGet() + + lazy val contractNFTBlacklist: Contract = Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + Seq(initFunc), + Seq(supersedeFunc, issueFunc, updateListFunc, + sendBlacklistFunc, transferBlacklistFunc, depositBlacklistFunc, withdrawBlacklistFunc), + Seq(issuerStateVar.arr, makerStateVar.arr, regulatorStateVar.arr), + Seq(listMap.arr), + Seq(triggerTextual, descriptorBlacklistTextual, stateVarTextual, stateMapBlacklistTextual) + ).explicitGet() + + // StateVar + val stateVarName = List("issuer", "maker", "regulator") + val issuerStateVar: StateVar = StateVar(0.toByte, DataType.Address.id.toByte) + val makerStateVar: StateVar = StateVar(1.toByte, DataType.Address.id.toByte) + val regulatorStateVar: StateVar = StateVar(2.toByte, DataType.Address.id.toByte) + lazy val stateVarTextual: Array[Byte] = Deser.serializeArrays(stateVarName.map(x => Deser.serilizeString(x))) + + // StateMap + val stateMapWhitelist = List("whitelist", "userAccount", "isInList") + val stateMapBlacklist = List("blacklist", "userAccount", "isInList") + val listMap: StateMap = StateMap(0.toByte, DataType.Account.id.toByte, DataType.Boolean.id.toByte) + lazy val stateMapWhitelistTextual: Array[Byte] = textualStateMap(Seq(stateMapWhitelist)) + lazy val stateMapBlacklistTextual: Array[Byte] = textualStateMap(Seq(stateMapBlacklist)) + + // initTrigger + val initId: Short = 0 + val initPara: Seq[String] = Seq( + "signer") + val initDataType: Array[Byte] = Array() + val initOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(0.toByte), + cdbvSet ++ Array(issuerStateVar.index, 0.toByte), + cdbvSet ++ Array(makerStateVar.index, 0.toByte), + cdbvSet ++ Array(regulatorStateVar.index, 0.toByte)) + lazy val initFunc: Array[Byte] = getFunctionBytes(initId, onInitTriggerType, nonReturnType, initDataType, initOpcs) + lazy val initFuncBytes: Array[Byte] = textualFunc("init", Seq(), initPara) + + // Functions + // Supersede + val supersedeId: Short = 0 + val supersedePara: Seq[String] = Seq("newIssuer", "newRegulator", + "maker") + val supersedeDataType: Array[Byte] = Array(DataType.Account.id.toByte, DataType.Account.id.toByte) + val supersedeOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(makerStateVar.index, 2.toByte), + assertSigner ++ Array(2.toByte), + cdbvSet ++ Array(issuerStateVar.index, 0.toByte), + cdbvSet ++ Array(regulatorStateVar.index, 1.toByte)) + lazy val supersedeFunc: Array[Byte] = getFunctionBytes(supersedeId, publicFuncType, nonReturnType, supersedeDataType, supersedeOpcs) + val supersedeFuncBytes: Array[Byte] = textualFunc("supersede", Seq(), supersedePara) + + // Issue + val issueId: Short = 1 + val issuePara: Seq[String] = Seq("tokenDescription", + "issuer", "amount", "tokens") + val issueDataType: Array[Byte] = Array(DataType.ShortText.id.toByte) + val issueOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(issuerStateVar.index, 1.toByte), + assertCaller ++ Array(1.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1), DataType.Amount).bytes ++ Array(2.toByte), + tdbNewToken ++ Array(2.toByte, 2.toByte, 0.toByte), + loadLastTokenIndex ++ Array(3.toByte), + tdbaDeposit ++ Array(1.toByte, 2.toByte, 3.toByte)) + lazy val issueFunc: Array[Byte] = getFunctionBytes(issueId, publicFuncType, nonReturnType, issueDataType, issueOpcs) + val issueFuncBytes: Array[Byte] = textualFunc("issue", Seq(), issuePara) + + // update list + val updateListId: Short = 2 + val updateListPara: Seq[String] = Seq("userAccount", "value", + "regulator") + val updateListDataType: Array[Byte] = Array(DataType.Account.id.toByte, DataType.Boolean.id.toByte) + val updateListOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(regulatorStateVar.index, 2.toByte), + assertCaller ++ Array(2.toByte), + cdbvMapSet ++ Array(listMap.index, 0.toByte, 1.toByte) + ) + lazy val updateListFunc: Array[Byte] = getFunctionBytes(updateListId, publicFuncType, nonReturnType, updateListDataType, updateListOpcs) + val updateListFuncBytes: Array[Byte] = textualFunc("updateList", Seq(), updateListPara) + + // send + val sendId: Short = 3 + val sendPara: Seq[String] = Seq("recipient", "tokenIndex", + "caller", "amount", "value", "isSenderInList", "isRecipientInList") + val sendDataType: Array[Byte] = Array(DataType.Account.id.toByte, DataType.Int32.id.toByte) + + // whitelist + val sendWhitelistOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(2.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1), DataType.Amount).bytes ++ Array(3.toByte) + ) ++ whitelistCheck(2.toByte, 0.toByte) ++ Seq( + tdbaTransfer ++ Array(2.toByte, 0.toByte, 3.toByte, 1.toByte) + ) + lazy val sendWhitelistFunc: Array[Byte] = getFunctionBytes(sendId, publicFuncType, nonReturnType, sendDataType, sendWhitelistOpcs) + val sendWhitelistFuncBytes: Array[Byte] = textualFunc("send", Seq(), sendPara) + + // blacklist + val sendBlacklistOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(2.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1), DataType.Amount).bytes ++ Array(3.toByte) + ) ++ blacklistCheck(2.toByte, 0.toByte) ++ Seq( + tdbaTransfer ++ Array(2.toByte, 0.toByte, 3.toByte, 1.toByte) + ) + lazy val sendBlacklistFunc: Array[Byte] = getFunctionBytes(sendId, publicFuncType, nonReturnType, sendDataType, sendBlacklistOpcs) + val sendBlacklistFuncBytes: Array[Byte] = textualFunc("send", Seq(), sendPara) + + // transfer + val transferId: Short = 4 + val transferPara: Seq[String] = Seq("sender", "recipient", "tokenIndex", + "amount", "value", "isSenderInList", "isRecipientInList") + val transferDataType: Array[Byte] = Array(DataType.Account.id.toByte, DataType.Account.id.toByte, DataType.Int32.id.toByte) + + // whitelist transfer + val transferWhitelistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1), DataType.Amount).bytes ++ Array(3.toByte) + ) ++ whitelistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 3.toByte, 2.toByte) + ) + lazy val transferWhitelistFunc: Array[Byte] = getFunctionBytes(transferId, publicFuncType, nonReturnType, transferDataType, transferWhitelistOpcs) + val transferWhitelistFuncBytes: Array[Byte] = textualFunc("transfer", Seq(), transferPara) + + // blacklist transfer + val transferBlacklistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1), DataType.Amount).bytes ++ Array(3.toByte) + ) ++ blacklistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 3.toByte, 2.toByte) + ) + lazy val transferBlacklistFunc: Array[Byte] = getFunctionBytes(transferId, publicFuncType, nonReturnType, transferDataType, transferBlacklistOpcs) + val transferBlacklistFuncBytes: Array[Byte] = textualFunc("transfer", Seq(), transferPara) + + // deposit + val depositId: Short = 5 + val depositPara: Seq[String] = Seq("sender", "smart", "tokenIndex", + "amount", "value", "isSenderInList", "isRecipientInList") + val depositDataType: Array[Byte] = Array(DataType.Account.id.toByte, DataType.ContractAccount.id.toByte, DataType.Int32.id.toByte) + + // whitelist deposit + val depositWhitelistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1), DataType.Amount).bytes ++ Array(3.toByte) + ) ++ whitelistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 3.toByte, 2.toByte) + ) + lazy val depositWhitelistFunc: Array[Byte] = getFunctionBytes(depositId, publicFuncType, nonReturnType, depositDataType, depositWhitelistOpcs) + val depositWhitelistFuncBytes: Array[Byte] = textualFunc("deposit", Seq(), depositPara) + + // blacklist deposit + val depositBlacklistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1), DataType.Amount).bytes ++ Array(3.toByte) + ) ++ blacklistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 3.toByte, 2.toByte) + ) + lazy val depositBlacklistFunc: Array[Byte] = getFunctionBytes(depositId, publicFuncType, nonReturnType, depositDataType, depositBlacklistOpcs) + val depositBlacklistFuncBytes: Array[Byte] = textualFunc("deposit", Seq(), depositPara) + + // withdraw + val withdrawId: Short = 6 + val withdrawPara: Seq[String] = Seq("smart", "recipient", "tokenIndex", + "amount", "value", "isSenderInList", "isRecipientInList") + val withdrawDataType: Array[Byte] = Array(DataType.ContractAccount.id.toByte, DataType.Account.id.toByte, DataType.Int32.id.toByte) + + // whitelist withdraw + val withdrawWhitelistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(1.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1), DataType.Amount).bytes ++ Array(3.toByte) + ) ++ whitelistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 3.toByte, 2.toByte) + ) + lazy val withdrawWhitelistFunc: Array[Byte] = getFunctionBytes(withdrawId, publicFuncType, nonReturnType, withdrawDataType, withdrawWhitelistOpcs) + val withdrawWhitelistFuncBytes: Array[Byte] = textualFunc("withdraw", Seq(), withdrawPara) + + // blacklist withdraw + val withdrawBlacklistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(1.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1), DataType.Amount).bytes ++ Array(3.toByte) + ) ++ blacklistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 3.toByte, 2.toByte) + ) + lazy val withdrawBlacklistFunc: Array[Byte] = getFunctionBytes(withdrawId, publicFuncType, nonReturnType, withdrawDataType, withdrawBlacklistOpcs) + val withdrawBlacklistFuncBytes: Array[Byte] = textualFunc("withdraw", Seq(), withdrawPara) + + private def whitelistCheck(sender: Byte, recipient: Byte): Seq[Array[Byte]] = + Seq( + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(4.toByte), + cdbvrMapGetOrDefault ++ Array(listMap.index, sender, 5.toByte), + assertEqual ++ Array(5.toByte, 4.toByte), + cdbvrMapGetOrDefault ++ Array(listMap.index, recipient, 6.toByte), + assertEqual ++ Array(6.toByte, 4.toByte), + ) + + private def blacklistCheck(sender: Byte, recipient: Byte): Seq[Array[Byte]] = + Seq( + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(4.toByte), + cdbvrMapGetOrDefault ++ Array(listMap.index, sender, 5.toByte), + assertEqual ++ Array(5.toByte, 4.toByte), + cdbvrMapGetOrDefault ++ Array(listMap.index, recipient, 6.toByte), + assertEqual ++ Array(6.toByte, 4.toByte) + ) + + // textual + lazy val triggerTextual: Array[Byte] = Deser.serializeArrays(Seq(initFuncBytes)) + lazy val descriptorWhitelistTextual: Array[Byte] = Deser.serializeArrays(Seq(supersedeFuncBytes, issueFuncBytes, + updateListFuncBytes, sendWhitelistFuncBytes, transferWhitelistFuncBytes, + depositWhitelistFuncBytes, withdrawWhitelistFuncBytes)) + lazy val descriptorBlacklistTextual: Array[Byte] = Deser.serializeArrays(Seq(supersedeFuncBytes, issueFuncBytes, + updateListFuncBytes, sendBlacklistFuncBytes, transferBlacklistFuncBytes, + depositBlacklistFuncBytes, withdrawBlacklistFuncBytes)) + +} diff --git a/src/main/scala/vsys/blockchain/contract/ContractPaymentChannel.scala b/src/main/scala/vsys/blockchain/contract/ContractPaymentChannel.scala index 3a60f2c59..288939e93 100644 --- a/src/main/scala/vsys/blockchain/contract/ContractPaymentChannel.scala +++ b/src/main/scala/vsys/blockchain/contract/ContractPaymentChannel.scala @@ -117,7 +117,7 @@ object ContractPaymentChannel { cdbvrMapGet ++ Array(channelStatusMap.index, 0.toByte, 3.toByte), assertTrue ++ Array(3.toByte), cdbvrMapGet ++ Array(channelExpirationTimeMap.index, 0.toByte, 4.toByte), - compareGreater ++ Array(1.toByte, 4.toByte, 5.toByte), + compareGreaterEqual ++ Array(1.toByte, 4.toByte, 5.toByte), assertTrue ++ Array(5.toByte), cdbvMapSet ++ Array(channelExpirationTimeMap.index, 0.toByte, 1.toByte) ) @@ -169,7 +169,7 @@ object ContractPaymentChannel { assertCaller ++ Array(1.toByte), loadTimestamp ++ Array(2.toByte), cdbvrMapGet ++ Array(channelExpirationTimeMap.index, 0.toByte, 3.toByte), - compareGreater ++ Array(2.toByte, 3.toByte, 4.toByte), + compareGreaterEqual ++ Array(2.toByte, 3.toByte, 4.toByte), assertTrue ++ Array(4.toByte), cdbvrMapGetOrDefault ++ Array(accumulatedLoadMap.index, 0.toByte, 5.toByte), cdbvrMapGetOrDefault ++ Array(accumulatedPaymentMap.index, 0.toByte, 6.toByte), @@ -191,16 +191,16 @@ object ContractPaymentChannel { assertCaller ++ Array(3.toByte), loadTimestamp ++ Array(4.toByte), cdbvrMapGet ++ Array(channelExpirationTimeMap.index, 0.toByte, 5.toByte), - compareGreater ++ Array(5.toByte, 4.toByte, 6.toByte), + compareGreaterEqual ++ Array(5.toByte, 4.toByte, 6.toByte), assertTrue ++ Array(6.toByte), cdbvrMapGet ++ Array(channelCreatorPublicKeyMap.index, 0.toByte, 7.toByte), basicConcat ++ Array(0.toByte, 1.toByte, 8.toByte), assertSig ++ Array(8.toByte, 2.toByte, 7.toByte), cdbvrMapGetOrDefault ++ Array(accumulatedPaymentMap.index, 0.toByte, 9.toByte), - compareGreater ++ Array(1.toByte, 9.toByte, 10.toByte), + compareGreaterEqual ++ Array(1.toByte, 9.toByte, 10.toByte), assertTrue ++ Array(10.toByte), cdbvrMapGetOrDefault ++ Array(accumulatedLoadMap.index, 0.toByte, 11.toByte), - compareGreater ++ Array(11.toByte, 1.toByte, 12.toByte), + compareGreaterEqual ++ Array(11.toByte, 1.toByte, 12.toByte), assertTrue ++ Array(12.toByte), basicMinus ++ Array(1.toByte, 9.toByte, 13.toByte), cdbvMapValAdd ++ Array(accumulatedPaymentMap.index, 0.toByte, 13.toByte), diff --git a/src/main/scala/vsys/blockchain/contract/ContractPermitted.scala b/src/main/scala/vsys/blockchain/contract/ContractPermitted.scala index fb3aebb16..702fc8df3 100644 --- a/src/main/scala/vsys/blockchain/contract/ContractPermitted.scala +++ b/src/main/scala/vsys/blockchain/contract/ContractPermitted.scala @@ -96,7 +96,7 @@ object ContractPermitted { cdbvrGet ++ Array(issuerStateVar.index, 1.toByte), assertCaller ++ Array(1.toByte), tdbSplit ++ Array(0.toByte)) - lazy val splitFunc: Array[Byte] = getFunctionBytes(splitId, publicFuncType, nonReturnType, destroyDataType, splitOpcs) + lazy val splitFunc: Array[Byte] = getFunctionBytes(splitId, publicFuncType, nonReturnType, splitDataType, splitOpcs) val splitFuncBytes: Array[Byte] = textualFunc("split", Seq(), splitPara) //send diff --git a/src/main/scala/vsys/blockchain/contract/ContractTokenV2.scala b/src/main/scala/vsys/blockchain/contract/ContractTokenV2.scala new file mode 100644 index 000000000..206133487 --- /dev/null +++ b/src/main/scala/vsys/blockchain/contract/ContractTokenV2.scala @@ -0,0 +1,236 @@ +package vsys.blockchain.contract + +import com.google.common.primitives.Ints +import vsys.blockchain.contract.ContractGen._ +import vsys.blockchain.state._ +import vsys.utils.serialization.Deser + +object ContractTokenV2 { + lazy val contractTokenWhiteList: Contract = Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + Seq(initFunc), + Seq(supersedeFunc, issueFunc, destroyFunc, updateListFunc, + sendWhitelistFunc, transferWhitelistFunc, depositWhitelistFunc, withdrawWhitelistFunc, + totalSupplyFunc, maxSupplyFunc, balanceOfFunc, getIssuerFunc, getRegulatorFunc), + Seq(issuerStateVar.arr, makerStateVar.arr, regulatorStateVar.arr), + Seq(listMap.arr), + Seq(triggerTextual, descriptorWhitelistTextual, stateVarTextual, whitelistStateMapTextual) + ).explicitGet() + + lazy val contractTokenBlackList: Contract = Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + Seq(initFunc), + Seq(supersedeFunc, issueFunc, destroyFunc, updateListFunc, + sendBlacklistFunc, transferBlacklistFunc, depositBlacklistFunc, withdrawBlacklistFunc, + totalSupplyFunc, maxSupplyFunc, balanceOfFunc, getIssuerFunc, getRegulatorFunc), + Seq(issuerStateVar.arr, makerStateVar.arr, regulatorStateVar.arr), + Seq(listMap.arr), + Seq(triggerTextual, descriptorBlacklistTextual, stateVarTextual, blacklistStateMapTextual) + ).explicitGet() + + // StateVar + val stateVarName = List("issuer", "maker", "regulator") + val issuerStateVar: StateVar = StateVar(0.toByte, DataType.Address.id.toByte) + val makerStateVar: StateVar = StateVar(1.toByte, DataType.Address.id.toByte) + val regulatorStateVar: StateVar = StateVar(2.toByte, DataType.Address.id.toByte) + lazy val stateVarTextual: Array[Byte] = Deser.serializeArrays(stateVarName.map(x => Deser.serilizeString(x))) + + // State Map + val stateMapWhitelist = List("whitelist", "userAccount", "isInList") + val stateMapBlacklist = List("blacklist", "userAccount", "isInList") + val listMap: StateMap = StateMap(0.toByte, DataType.Account.id.toByte, DataType.Boolean.id.toByte) + lazy val whitelistStateMapTextual: Array[Byte] = textualStateMap(Seq(stateMapWhitelist)) + lazy val blacklistStateMapTextual: Array[Byte] = textualStateMap(Seq(stateMapBlacklist)) + + // initTrigger + val initId: Short = 0 + val initPara: Seq[String] = Seq("max", "unity", "tokenDescription", + "signer") + val initDataType: Array[Byte] = Array(DataType.Amount.id.toByte, DataType.Amount.id.toByte, DataType.ShortText.id.toByte) + val initOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(3.toByte), + cdbvSet ++ Array(issuerStateVar.index, 3.toByte), + cdbvSet ++ Array(makerStateVar.index, 3.toByte), + cdbvSet ++ Array(regulatorStateVar.index, 3.toByte), + tdbNewToken ++ Array(0.toByte, 1.toByte, 2.toByte)) + lazy val initFunc: Array[Byte] = getFunctionBytes(initId, onInitTriggerType, nonReturnType, initDataType, initOpcs) + lazy val initFuncBytes: Array[Byte] = textualFunc("init", Seq(), initPara) + + // Functions + // Supersede + val supersedeId: Short = 0 + val supersedeIdWithoutSplit: Short = 0 + val supersedePara: Seq[String] = Seq("newIssuer", "newRegulator", + "maker") + val supersedeDataType: Array[Byte] = Array(DataType.Account.id.toByte, DataType.Account.id.toByte) + val supersedeOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(makerStateVar.index, 2.toByte), + assertSigner ++ Array(2.toByte), + cdbvSet ++ Array(issuerStateVar.index, 0.toByte), + cdbvSet ++ Array(regulatorStateVar.index, 1.toByte)) + lazy val supersedeFunc: Array[Byte] = getFunctionBytes(supersedeId, publicFuncType, nonReturnType, supersedeDataType, supersedeOpcs) + val supersedeFuncBytes: Array[Byte] = textualFunc("supersede", Seq(), supersedePara) + + // Issue + lazy val issueFunc: Array[Byte] = ContractPermitted.issueFunc + val issueFuncBytes: Array[Byte] = ContractPermitted.issueFuncBytes + + // Destroy + lazy val destroyFunc: Array[Byte] = ContractPermitted.destroyFunc + val destroyFuncBytes: Array[Byte] = ContractPermitted.destroyFuncBytes + + // Update List + val updateListId: Short = 3 + val updateListPara: Seq[String] = Seq("userAccount", "value", + "regulator") + val updateListDataType: Array[Byte] = Array(DataType.Account.id.toByte, DataType.Boolean.id.toByte) + val updateListOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(regulatorStateVar.index, 2.toByte), + assertCaller ++ Array(2.toByte), + cdbvMapSet ++ Array(listMap.index, 0.toByte, 1.toByte) + ) + lazy val updateListFunc: Array[Byte] = getFunctionBytes(updateListId, publicFuncType, nonReturnType, updateListDataType, updateListOpcs) + val updateListFuncBytes: Array[Byte] = textualFunc("updateList", Seq(), updateListPara) + + private def whitelistCheck(sender: Byte, recipient: Byte): Seq[Array[Byte]] = + Seq( + cdbvrMapGetOrDefault ++ Array(listMap.index, sender, 3.toByte), + assertTrue ++ Array(3.toByte), + cdbvrMapGetOrDefault ++ Array(listMap.index, recipient, 4.toByte), + assertTrue ++ Array(4.toByte) + ) + + private def blacklistCheck(sender: Byte, recipient: Byte): Seq[Array[Byte]] = + Seq( + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(3.toByte), + cdbvrMapGetOrDefault ++ Array(listMap.index, sender, 4.toByte), + assertEqual ++ Array(4.toByte, 3.toByte), + cdbvrMapGetOrDefault ++ Array(listMap.index, recipient, 5.toByte), + assertEqual ++ Array(5.toByte, 3.toByte) + ) + + // Send + val sendId: Short = 4 + val sendDataType: Array[Byte] = Array(DataType.Account.id.toByte, DataType.Amount.id.toByte) + + // Whitelist + val sendWhitelistPara: Seq[String] = Seq("recipient", "amount", + "caller", "isSenderInList", "isRecipientInList") + val sendWhitelistOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(2.toByte)) ++ whitelistCheck(2.toByte, 0.toByte) ++ Seq( + tdbaTransfer ++ Array(2.toByte, 0.toByte, 1.toByte) + ) + lazy val sendWhitelistFunc: Array[Byte] = getFunctionBytes(sendId, publicFuncType, nonReturnType, sendDataType, sendWhitelistOpcs) + val sendWhitelistFuncBytes: Array[Byte] = textualFunc("send", Seq(), sendWhitelistPara) + + // Blacklist + val sendBlacklistPara: Seq[String] = Seq("recipient", "amount", + "caller", "valueFalse", "isSenderInList", "isRecipientInList") + val sendBlacklistOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(2.toByte)) ++ blacklistCheck(2.toByte, 0.toByte) ++ Seq( + tdbaTransfer ++ Array(2.toByte, 0.toByte, 1.toByte) + ) + lazy val sendBlacklistFunc: Array[Byte] = getFunctionBytes(sendId, publicFuncType, nonReturnType, sendDataType, sendBlacklistOpcs) + val sendBlacklistFuncBytes: Array[Byte] = textualFunc("send", Seq(), sendBlacklistPara) + + // Transfer + val transferId: Short = 5 + val transferDataType: Array[Byte] = Array(DataType.Account.id.toByte, DataType.Account.id.toByte, DataType.Amount.id.toByte) + + // Whitelist + val transferWhitelistPara: Seq[String] = Seq("sender", "recipient", "amount", + "isSenderInList", "isRecipientInList") + val transferWhitelistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte)) ++ whitelistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 2.toByte)) + lazy val transferWhitelistFunc: Array[Byte] = getFunctionBytes(transferId, publicFuncType, nonReturnType, transferDataType, transferWhitelistOpcs) + val transferWhitelistFuncBytes: Array[Byte] = textualFunc("transfer", Seq(), transferWhitelistPara) + + // Blacklist + val transferBlacklistPara: Seq[String] = Seq("sender", "recipient", "amount", + "valueFalse", "isSenderInList", "isRecipientInList") + val transferBlacklistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte)) ++ blacklistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 2.toByte)) + lazy val transferBlacklistFunc: Array[Byte] = getFunctionBytes(transferId, publicFuncType, nonReturnType, transferDataType, transferBlacklistOpcs) + val transferBlacklistFuncBytes: Array[Byte] = textualFunc("transfer", Seq(), transferBlacklistPara) + + // Deposit + val depositId: Short = 6 + val depositDataType: Array[Byte] = Array(DataType.Account.id.toByte, DataType.ContractAccount.id.toByte, DataType.Amount.id.toByte) + + // Whitelist + val depositWhitelistPara: Seq[String] = Seq("sender", "smart", "amount", + "isSenderInList", "isRecipientInList") + val depositWhitelistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte)) ++ whitelistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 2.toByte)) + lazy val depositWhitelistFunc: Array[Byte] = getFunctionBytes(depositId, publicFuncType, nonReturnType, depositDataType, depositWhitelistOpcs) + val depositWhitelistFuncBytes: Array[Byte] = textualFunc("deposit", Seq(), depositWhitelistPara) + + // Blacklist + val depositBlacklistPara: Seq[String] = Seq("sender", "smart", "amount", + "valueFalse", "isSenderInList", "isRecipientInList") + val depositBlacklistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte)) ++ blacklistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 2.toByte)) + lazy val depositBlacklistFunc: Array[Byte] = getFunctionBytes(depositId, publicFuncType, nonReturnType, depositDataType, depositBlacklistOpcs) + val depositBlacklistFuncBytes: Array[Byte] = textualFunc("deposit", Seq(), depositBlacklistPara) + + // Withdraw + val withdrawId: Short = 7 + val withdrawDataType: Array[Byte] = Array(DataType.ContractAccount.id.toByte, DataType.Account.id.toByte, DataType.Amount.id.toByte) + + // Whitelist + val withdrawWhitelistPara: Seq[String] = Seq("smart", "recipient", "amount", + "isSenderInList", "isRecipientInList") + val withdrawWhitelistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(1.toByte)) ++ whitelistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 2.toByte)) + lazy val withdrawWhitelistFunc: Array[Byte] = getFunctionBytes(withdrawId, publicFuncType, nonReturnType, withdrawDataType, withdrawWhitelistOpcs) + val withdrawWhitelistFuncBytes: Array[Byte] = textualFunc("withdraw", Seq(), withdrawWhitelistPara) + + // Blacklist + val withdrawBlacklistPara: Seq[String] = Seq("smart", "recipient", "amount", + "valueFalse", "isSenderInList", "isRecipientInList") + val withdrawBlacklistOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(1.toByte)) ++ blacklistCheck(0.toByte, 1.toByte) ++ Seq( + tdbaTransfer ++ Array(0.toByte, 1.toByte, 2.toByte)) + lazy val withdrawBlacklistFunc: Array[Byte] = getFunctionBytes(withdrawId, publicFuncType, nonReturnType, withdrawDataType, withdrawBlacklistOpcs) + val withdrawBlacklistFuncBytes: Array[Byte] = textualFunc("withdraw", Seq(), withdrawBlacklistPara) + + // TotalSupply + lazy val totalSupplyFunc: Array[Byte] = ContractPermitted.totalSupplyFunc + val totalSupplyFuncBytes: Array[Byte] = ContractPermitted.totalSupplyFuncBytes + + // MaxSupply + lazy val maxSupplyFunc: Array[Byte] = ContractPermitted.maxSupplyFunc + val maxSupplyFuncBytes: Array[Byte] = ContractPermitted.maxSupplyFuncBytes + + // BalanceOf + lazy val balanceOfFunc: Array[Byte] = ContractPermitted.balanceOfFunc + val balanceOfFuncBytes: Array[Byte] = ContractPermitted.balanceOfFuncBytes + + // GetIssuer + lazy val getIssuerFunc: Array[Byte] = ContractPermitted.getIssuerFunc + val getIssuerFuncBytes: Array[Byte] = ContractPermitted.getIssuerFuncBytes + + // GetRegulator + val getRegulatorId: Short = 12 + val getRegulatorPara: Seq[String] = Seq( + "regulator") + val getRegulatorDataType: Array[Byte] = Array() + val getRegulatorOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(regulatorStateVar.index, 0.toByte), + returnValue ++ Array(0.toByte)) + lazy val getRegulatorFunc: Array[Byte] = getFunctionBytes(getRegulatorId, publicFuncType, Array(DataType.Account.id.toByte), getRegulatorDataType, getRegulatorOpcs) + val getRegulatorFuncBytes: Array[Byte] = textualFunc("getRegulator", Seq("regulator"), getRegulatorPara) + + // Textual + lazy val triggerTextual: Array[Byte] = Deser.serializeArrays(Seq(initFuncBytes)) + lazy val descriptorWhitelistTextual: Array[Byte] = Deser.serializeArrays(Seq(supersedeFuncBytes, issueFuncBytes, + destroyFuncBytes, updateListFuncBytes, sendWhitelistFuncBytes, transferWhitelistFuncBytes, depositWhitelistFuncBytes, withdrawWhitelistFuncBytes, + totalSupplyFuncBytes, maxSupplyFuncBytes, balanceOfFuncBytes, getIssuerFuncBytes, getRegulatorFuncBytes)) + lazy val descriptorBlacklistTextual: Array[Byte] = Deser.serializeArrays(Seq(supersedeFuncBytes, issueFuncBytes, + destroyFuncBytes, updateListFuncBytes, sendBlacklistFuncBytes, transferBlacklistFuncBytes, depositBlacklistFuncBytes, withdrawBlacklistFuncBytes, + totalSupplyFuncBytes, maxSupplyFuncBytes, balanceOfFuncBytes, getIssuerFuncBytes, getRegulatorFuncBytes)) + +} diff --git a/src/main/scala/vsys/blockchain/contract/ContractVEscrow.scala b/src/main/scala/vsys/blockchain/contract/ContractVEscrow.scala new file mode 100644 index 000000000..bd20aafa9 --- /dev/null +++ b/src/main/scala/vsys/blockchain/contract/ContractVEscrow.scala @@ -0,0 +1,489 @@ +package vsys.blockchain.contract + +import com.google.common.primitives.{Ints, Longs} +import vsys.blockchain.contract.ContractGen._ +import vsys.blockchain.state._ +import vsys.utils.serialization.Deser + +object ContractVEscrow { + lazy val contract: Contract = Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + Seq(initTrigger, depositTrigger, withdrawTrigger), // Triggers + Seq(supersedeFunc, createFunc, recipientDepositFunc, judgeDepositFunc, payerCancelFunc, recipientCancelFunc, judgeCancelFunc, + submitWorkFunc, approveWorkFunc, applyToJudgeFunc, judgeFunc, submitPenaltyFunc, + payerRefundFunc, recipientRefundFunc, collectFunc), + stateVarSeq, // StateVars + stateMapSeq, // StateMaps + Seq(triggerTextual, descriptorTextual, stateVarTextual, stateMapTextual) + ).explicitGet() + + // State Var + val stateVarName = List("maker", "judge", "tokenId", "duration", "judgeDuration") + val makerStateVar: StateVar = StateVar(0.toByte, DataType.Address.id.toByte) + val judgeStateVar: StateVar = StateVar(1.toByte, DataType.Address.id.toByte) + val tokenIdStateVar: StateVar = StateVar(2.toByte, DataType.TokenId.id.toByte) + val durationStateVar: StateVar = StateVar(3.toByte, DataType.Timestamp.id.toByte) + val judgeDurationStateVar: StateVar = StateVar(4.toByte, DataType.Timestamp.id.toByte) + lazy val stateVarSeq = Seq(makerStateVar.arr, judgeStateVar.arr, tokenIdStateVar.arr, + durationStateVar.arr, judgeDurationStateVar.arr) + lazy val stateVarTextual: Array[Byte] = Deser.serializeArrays(stateVarName.map(x => Deser.serilizeString(x))) + + // State Map + val stateMapContractBalance = List("contractBalance", "userAddress", "balance") + val stateMapOrderPayer = List("orderPayer", "orderId", "payer") + val stateMapOrderRecipient = List("orderRecipient", "orderId", "recipient") + val stateMapOrderAmount = List("orderAmount", "orderId", "amount") + val stateMapOrderRecipientDeposit = List("orderRecipientDeposit", "orderId", "repDeposit") + val stateMapOrderJudgeDeposit = List("orderJudgeDeposit", "orderId", "judgeDeposit") + val stateMapOrderFee = List("orderFee", "orderId", "fee") + val stateMapOrderRecipientAmount = List("orderRecipientAmount", "orderId", "repAmount") + val stateMapOrderRefund = List("orderRefund", "orderId", "refund") + val stateMapOrderRecipientRefund = List("orderRecipientRefund", "orderId", "repRefund") + val stateMapOrderExpirationTime = List("orderExpirationTime", "orderId", "expirationTime") + val stateMapOrderStatus = List("orderStatus", "orderId", "status") + val stateMapOrderRepDepositStatus = List("orderRepDepositStatus", "orderId", "repDepositStatus") + val stateMapOrderJudgeDepositStatus = List("orderJudgeDepositStatus", "orderId", "judgeDepositStatus") + val stateMapOrderSubmitStatus = List("orderSubmitStatus", "orderId", "submitStatus") + val stateMapOrderJudgeStatus = List("orderJudgeStatus", "orderId", "judgeStatus") + val stateMapOrderRepLockedAmount = List("orderRepLockedAmount", "orderId", "repLockedAmount") + val stateMapOrderJudgeLockedAmount = List("orderJudgeLockedAmount", "orderId", "judgeLockedAmount") + val contractBalanceMap: StateMap = StateMap(0.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + val orderPayerMap: StateMap = StateMap(1.toByte, DataType.ShortBytes.id.toByte, DataType.Address.id.toByte) + val orderRecipientMap: StateMap = StateMap(2.toByte, DataType.ShortBytes.id.toByte, DataType.Address.id.toByte) + val orderAmountMap: StateMap = StateMap(3.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val orderRecipientDepositMap: StateMap = StateMap(4.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val orderJudgeDepositMap: StateMap = StateMap(5.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val orderFeeMap: StateMap = StateMap(6.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val orderRecipientAmountMap: StateMap = StateMap(7.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val orderRefundMap: StateMap = StateMap(8.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val orderRecipientRefundMap: StateMap = StateMap(9.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val orderExpirationTimeMap: StateMap = StateMap(10.toByte, DataType.ShortBytes.id.toByte, DataType.Timestamp.id.toByte) + val orderStatusMap: StateMap = StateMap(11.toByte, DataType.ShortBytes.id.toByte, DataType.Boolean.id.toByte) + val orderRepDepositStatusMap: StateMap = StateMap(12.toByte, DataType.ShortBytes.id.toByte, DataType.Boolean.id.toByte) + val orderJudgeDepositStatusMap: StateMap = StateMap(13.toByte, DataType.ShortBytes.id.toByte, DataType.Boolean.id.toByte) + val orderSubmitStatusMap: StateMap = StateMap(14.toByte, DataType.ShortBytes.id.toByte, DataType.Boolean.id.toByte) + val orderJudgeStatusMap: StateMap = StateMap(15.toByte, DataType.ShortBytes.id.toByte, DataType.Boolean.id.toByte) + val orderRepLockedAmountMap: StateMap = StateMap(16.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val orderJudgeLockedAmountMap: StateMap = StateMap(17.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + + lazy val stateMapSeq = Seq(contractBalanceMap.arr, orderPayerMap.arr, orderRecipientMap.arr, orderAmountMap.arr, + orderRecipientDepositMap.arr, orderJudgeDepositMap.arr, orderFeeMap.arr, + orderRecipientAmountMap.arr, orderRefundMap.arr, orderRecipientRefundMap.arr, + orderExpirationTimeMap.arr, orderStatusMap.arr, orderRepDepositStatusMap.arr, orderJudgeDepositStatusMap.arr, + orderSubmitStatusMap.arr, orderJudgeStatusMap.arr, orderRepLockedAmountMap.arr, orderJudgeLockedAmountMap.arr) + lazy val stateMapTextual: Array[Byte] = textualStateMap( + Seq(stateMapContractBalance, stateMapOrderPayer, stateMapOrderRecipient, stateMapOrderAmount, + stateMapOrderRecipientDeposit, stateMapOrderJudgeDeposit, stateMapOrderFee, stateMapOrderRecipientAmount, + stateMapOrderRefund, stateMapOrderRecipientRefund, stateMapOrderExpirationTime, stateMapOrderStatus, + stateMapOrderRepDepositStatus, stateMapOrderJudgeDepositStatus, stateMapOrderSubmitStatus, stateMapOrderJudgeStatus, + stateMapOrderRepLockedAmount, stateMapOrderJudgeLockedAmount)) + + val commonDataType: Array[Byte] = Array(DataType.ShortBytes.id.toByte) + + // Initialization Trigger + val initId: Short = 0 + val initPara: Seq[String] = Seq("tokenId", "duration", "judgeDuration", + "signer") + val initDataType: Array[Byte] = Array(DataType.TokenId.id.toByte, DataType.Timestamp.id.toByte, DataType.Timestamp.id.toByte) + val initTriggerOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(3.toByte), + cdbvSet ++ Array(makerStateVar.index, 3.toByte), + cdbvSet ++ Array(judgeStateVar.index, 3.toByte), + cdbvSet ++ Array(tokenIdStateVar.index, 0.toByte), + cdbvSet ++ Array(durationStateVar.index, 1.toByte), + cdbvSet ++ Array(judgeDurationStateVar.index, 2.toByte) + ) + lazy val initTrigger: Array[Byte] = getFunctionBytes(initId, onInitTriggerType, nonReturnType, initDataType, initTriggerOpcs) + val initTextualBytes: Array[Byte] = textualFunc("init", Seq(), initPara) + + // Deposit Trigger + val depositId: Short = 1 + val depositPara: Seq[String] = Seq("depositor", "amount", "tokenId", + "contractTokenId") + val depositDataType: Array[Byte] = Array(DataType.Address.id.toByte, DataType.Amount.id.toByte, DataType.TokenId.id.toByte) + val depositTriggerOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + cdbvrGet ++ Array(tokenIdStateVar.index, 3.toByte), + assertEqual ++ Array(2.toByte, 3.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 0.toByte, 1.toByte) + ) + lazy val depositTrigger: Array[Byte] = getFunctionBytes(depositId, onDepositTriggerType, nonReturnType, depositDataType, depositTriggerOpcs) + val depositTextualBytes: Array[Byte] = textualFunc("deposit", Seq(), depositPara) + + // Withdraw Trigger + val withdrawId: Short = 2 + val withdrawPara: Seq[String] = Seq("withdrawer", "amount", "tokenId", + "contractTokenId") + val withdrawDataType: Array[Byte] = Array(DataType.Address.id.toByte, DataType.Amount.id.toByte, DataType.TokenId.id.toByte) + val withdrawTriggerOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + cdbvrGet ++ Array(tokenIdStateVar.index, 3.toByte), + assertEqual ++ Array(2.toByte, 3.toByte), + cdbvMapValMinus ++ Array(contractBalanceMap.index, 0.toByte, 1.toByte) + ) + lazy val withdrawTrigger: Array[Byte] = getFunctionBytes(withdrawId, onWithDrawTriggerType, nonReturnType, withdrawDataType, withdrawTriggerOpcs) + val withdrawTextualBytes: Array[Byte] = textualFunc("withdraw", Seq(), withdrawPara) + + // Functions + // Supersede + val supersedeId: Short = 0 + val supersedePara: Seq[String] = Seq("newJudge", + "maker") + val supersedeDataType: Array[Byte] = Array(DataType.Account.id.toByte) + val supersedeOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(makerStateVar.index, 1.toByte), + assertSigner ++ Array(1.toByte), + cdbvSet ++ Array(judgeStateVar.index, 0.toByte)) + lazy val supersedeFunc: Array[Byte] = getFunctionBytes(supersedeId, publicFuncType, nonReturnType, supersedeDataType, supersedeOpcs) + val supersedeTextualBytes: Array[Byte] = textualFunc("supersede", Seq(), supersedePara) + + // Create Order Function + val createId: Short = 1 + val createPara: Seq[String] = Seq("recipient", "amount", "repDeposit", "judgeDeposit", "fee", "refund", "expirationTime") ++ + Seq("caller", "orderId", "repAmount", "needToDeposit", "totalDeposit", "repRefund", + "valueTrue", "valueFalse", "amountZero") + val createDataType: Array[Byte] = Array(DataType.Address.id.toByte) ++ + Array.fill[Byte](5)(DataType.Amount.id.toByte) ++ + Array(DataType.Timestamp.id.toByte) + val createOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(7.toByte), + loadTransactionId ++ Array(8.toByte), + cdbvMapValMinus ++ Array(contractBalanceMap.index, 7.toByte, 1.toByte), + cdbvMapSet ++ Array(orderPayerMap.index, 8.toByte, 7.toByte), + cdbvMapSet ++ Array(orderRecipientMap.index, 8.toByte, 0.toByte), + cdbvMapSet ++ Array(orderAmountMap.index, 8.toByte, 1.toByte), + cdbvMapSet ++ Array(orderRecipientDepositMap.index, 8.toByte, 2.toByte), + cdbvMapSet ++ Array(orderJudgeDepositMap.index, 8.toByte, 3.toByte), + cdbvMapSet ++ Array(orderFeeMap.index, 8.toByte, 4.toByte), + basicMinus ++ Array(1.toByte, 4.toByte, 9.toByte), + cdbvMapSet ++ Array(orderRecipientAmountMap.index, 8.toByte, 9.toByte), + basicAdd ++ Array(2.toByte, 3.toByte, 10.toByte), + basicAdd ++ Array(10.toByte, 1.toByte, 11.toByte), + basicMinus ++ Array(11.toByte, 5.toByte, 12.toByte), + cdbvMapSet ++ Array(orderRefundMap.index, 8.toByte, 5.toByte), + cdbvMapSet ++ Array(orderRecipientRefundMap.index, 8.toByte, 12.toByte), + cdbvMapSet ++ Array(orderExpirationTimeMap.index, 8.toByte, 6.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(13.toByte), + cdbvMapSet ++ Array(orderStatusMap.index, 8.toByte, 13.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(14.toByte), + cdbvMapSet ++ Array(orderRepDepositStatusMap.index, 8.toByte, 14.toByte), + cdbvMapSet ++ Array(orderJudgeDepositStatusMap.index, 8.toByte, 14.toByte), + cdbvMapSet ++ Array(orderSubmitStatusMap.index, 8.toByte, 14.toByte), + cdbvMapSet ++ Array(orderJudgeStatusMap.index, 8.toByte, 14.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(0L), DataType.Amount).bytes ++ Array(15.toByte), + cdbvMapSet ++ Array(orderRepLockedAmountMap.index, 8.toByte, 15.toByte), + cdbvMapSet ++ Array(orderJudgeLockedAmountMap.index, 8.toByte, 15.toByte) + ) + lazy val createFunc: Array[Byte] = getFunctionBytes(createId, publicFuncType, nonReturnType, createDataType, createOpcs) + val createTextualBytes: Array[Byte] = textualFunc("create", Seq(), createPara) + + // Order Deposit Common + private def depositCommonOpcs(isCallerJudge: Boolean, orderDepositStatusIndex: Byte, orderDepositAmountIndex: Byte, orderLockedAmountIndex: Byte): Seq[Array[Byte]] = { + val tmp :Seq[Array[Byte]] = Seq( + assertCaller ++ Array(1.toByte), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, 2.toByte), + assertTrue ++ Array(2.toByte), + cdbvrMapGet ++ Array(orderDepositStatusIndex, 0.toByte, 3.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(4.toByte), + assertEqual ++ Array(3.toByte, 4.toByte), + cdbvrMapGet ++ Array(orderDepositAmountIndex, 0.toByte, 5.toByte), + cdbvMapValMinus ++ Array(contractBalanceMap.index, 1.toByte, 5.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + cdbvMapSet ++ Array(orderDepositStatusIndex, 0.toByte, 6.toByte), + cdbvMapSet ++ Array(orderLockedAmountIndex, 0.toByte, 5.toByte) + ) + if (isCallerJudge) { + Seq(cdbvrGet ++ Array(judgeStateVar.index, 1.toByte)) ++ tmp + } else { + Seq(cdbvrMapGet ++ Array(orderRecipientMap.index, 0.toByte, 1.toByte)) ++ tmp + } + } + + // Recipient Deposit Function + val recipientDepositId: Short = 2 + val recipientDepositPara: Seq[String] = Seq("orderId") ++ + Seq("recipient", "orderStatus", "depositStatus", "valueFalse", "amount", "valueTrue") + val recipientDepositOpcs: Seq[Array[Byte]] = depositCommonOpcs(false, orderRepDepositStatusMap.index, + orderRecipientDepositMap.index, orderRepLockedAmountMap.index) + lazy val recipientDepositFunc: Array[Byte] = getFunctionBytes(recipientDepositId, publicFuncType, nonReturnType, commonDataType, recipientDepositOpcs) + val recipientDepositTextualBytes: Array[Byte] = textualFunc("recipientDeposit", Seq(), recipientDepositPara) + + // Judge Deposit Function + val judgeDepositId: Short = 3 + val judgeDepositPara: Seq[String] = Seq("orderId") ++ + Seq("judge", "orderStatus", "depositStatus", "valueFalse", "amount", "valueTrue") + val judgeDepositOpcs: Seq[Array[Byte]] = depositCommonOpcs(true, orderJudgeDepositStatusMap.index, + orderJudgeDepositMap.index, orderJudgeLockedAmountMap.index) + lazy val judgeDepositFunc: Array[Byte] = getFunctionBytes(judgeDepositId, publicFuncType, nonReturnType, commonDataType, judgeDepositOpcs) + val judgeDepositTextualBytes: Array[Byte] = textualFunc("judgeDeposit", Seq(), judgeDepositPara) + + // Order Cancel Common + val cancelCommonPara: Seq[String] = Seq("orderId") ++ + Seq("payer", "recipient", "judge", "orderStatus", "repDepositStatus", "judgeDepositStatus", + "depositStatus", "valueFalse", "amount", "recipientAmount", "judgeAmount") + private def cancelCommonOpcs(callerIndex: Byte): Seq[Array[Byte]] = { + Seq( + cdbvrMapGet ++ Array(orderPayerMap.index, 0.toByte, 1.toByte), + cdbvrMapGet ++ Array(orderRecipientMap.index, 0.toByte, 2.toByte), + cdbvrGet ++ Array(judgeStateVar.index, 3.toByte), + assertCaller ++ Array(callerIndex), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, 4.toByte), + assertTrue ++ Array(4.toByte), + cdbvrMapGet ++ Array(orderRepDepositStatusMap.index, 0.toByte, 5.toByte), + cdbvrMapGet ++ Array(orderJudgeDepositStatusMap.index, 0.toByte, 6.toByte), + basicAnd ++ Array(5.toByte, 6.toByte, 7.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(8.toByte), + assertEqual ++ Array(7.toByte, 8.toByte), + cdbvrMapGet ++ Array(orderAmountMap.index, 0.toByte, 9.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 1.toByte, 9.toByte), + cdbvrMapGet ++ Array(orderRepLockedAmountMap.index, 0.toByte, 10.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 2.toByte, 10.toByte), + cdbvrMapGet ++ Array(orderJudgeLockedAmountMap.index, 0.toByte, 11.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 3.toByte, 11.toByte), + cdbvMapSet ++ Array(orderStatusMap.index, 0.toByte, 8.toByte) + ) + } + + // Payer Cancel Function + val payerCancelId: Short = 4 + val payerCancelOpcs: Seq[Array[Byte]] = cancelCommonOpcs(1.toByte) + lazy val payerCancelFunc: Array[Byte] = getFunctionBytes(payerCancelId, publicFuncType, nonReturnType, commonDataType, payerCancelOpcs) + val payerCancelTextualBytes: Array[Byte] = textualFunc("payerCancel", Seq(), cancelCommonPara) + + // Recipient Cancel Function + val recipientCancelId: Short = 5 + val recipientCancelOpcs: Seq[Array[Byte]] = cancelCommonOpcs(2.toByte) + lazy val recipientCancelFunc: Array[Byte] = getFunctionBytes(recipientCancelId, publicFuncType, nonReturnType, commonDataType, recipientCancelOpcs) + val recipientCancelTextualBytes: Array[Byte] = textualFunc("recipientCancel", Seq(), cancelCommonPara) + + // Judge Cancel Function + val judgeCancelId: Short = 6 + val judgeCancelOpcs: Seq[Array[Byte]] = cancelCommonOpcs(3.toByte) + lazy val judgeCancelFunc: Array[Byte] = getFunctionBytes(judgeCancelId, publicFuncType, nonReturnType, commonDataType, judgeCancelOpcs) + val judgeCancelTextualBytes: Array[Byte] = textualFunc("judgeCancel", Seq(), cancelCommonPara) + + private def timestampCheck(startIndex: Int, isValid: Boolean): Seq[Array[Byte]] = { + val a: Byte = if (isValid) (startIndex + 1).toByte else startIndex.toByte + val b: Byte = if (isValid) startIndex.toByte else (startIndex + 1).toByte + Seq( + loadTimestamp ++ Array(startIndex.toByte), + cdbvrMapGet ++ Array(orderExpirationTimeMap.index, 0.toByte, (startIndex + 1).toByte), + compareGreater ++ Array(a, b, (startIndex + 2).toByte), + assertTrue ++Array((startIndex + 2).toByte), + ) + } + + // Submit Work Function + val submitWorkId: Short = 7 + val submitWorkPara: Seq[String] = Seq("orderId") ++ + Seq("recipient", "orderStatus", "repDepositStatus", "judgeDepositStatus", "depositStatus", + "currentTime", "expirationTime", "isValidTime", "valueFalse", "submitStatus", "duration", + "time", "updateTime", "valueTrue") + val submitWorkOpcs: Seq[Array[Byte]] = Seq( + cdbvrMapGet ++ Array(orderRecipientMap.index, 0.toByte, 1.toByte), + assertCaller ++ Array(1.toByte), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, 2.toByte), + assertTrue ++ Array(2.toByte), + cdbvrMapGet ++ Array(orderRepDepositStatusMap.index, 0.toByte, 3.toByte), + cdbvrMapGet ++ Array(orderJudgeDepositStatusMap.index, 0.toByte, 4.toByte), + basicAnd ++ Array(3.toByte, 4.toByte, 5.toByte), + assertTrue ++ Array(5.toByte)) ++ timestampCheck(6, true) ++ Seq( + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(9.toByte), + cdbvrMapGet ++ Array(orderSubmitStatusMap.index, 0.toByte, 10.toByte), + assertEqual ++ Array(9.toByte, 10.toByte), + cdbvrGet ++ Array(durationStateVar.index, 11.toByte), + basicAdd ++ Array(6.toByte, 11.toByte, 12.toByte), + basicMax ++ Array(12.toByte, 7.toByte, 13.toByte), + cdbvMapSet ++ Array(orderExpirationTimeMap.index, 0.toByte, 13.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(14.toByte), + cdbvMapSet ++ Array(orderSubmitStatusMap.index, 0.toByte, 14.toByte) + ) + lazy val submitWorkFunc: Array[Byte] = getFunctionBytes(submitWorkId, publicFuncType, nonReturnType, commonDataType, submitWorkOpcs) + val submitWorkTextualBytes: Array[Byte] = textualFunc("submitWork", Seq(), submitWorkPara) + + // Approve Work Function + val approveWorkId: Short = 8 + val approveWorkPara: Seq[String] = Seq("orderId") ++ + Seq("payer", "orderStatus", "isSubmit", "currentTime", "expirationTime", "isValidTime", + "recipient", "judge", "workAmount", "recipientLocked", "recipientAmount", + "fee", "judgeLocked", "judgeAmount", "valueFalse") + val approveWorkOpcs: Seq[Array[Byte]] = Seq( + cdbvrMapGet ++ Array(orderPayerMap.index, 0.toByte, 1.toByte), + assertCaller ++ Array(1.toByte), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, 2.toByte), + assertTrue ++ Array(2.toByte), + cdbvrMapGet ++ Array(orderSubmitStatusMap.index, 0.toByte, 3.toByte), + assertTrue ++ Array(3.toByte)) ++ timestampCheck(4, true) ++ Seq( + cdbvrMapGet ++ Array(orderRecipientMap.index, 0.toByte, 7.toByte), + cdbvrGet ++ Array(judgeStateVar.index, 8.toByte), + cdbvrMapGet ++ Array(orderRecipientAmountMap.index, 0.toByte, 9.toByte), + cdbvrMapGet ++ Array(orderRepLockedAmountMap.index, 0.toByte, 10.toByte), + basicAdd ++ Array(9.toByte, 10.toByte, 11.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 7.toByte, 11.toByte), + cdbvrMapGet ++ Array(orderFeeMap.index, 0.toByte, 12.toByte), + cdbvrMapGet ++ Array(orderJudgeLockedAmountMap.index, 0.toByte, 13.toByte), + basicAdd ++ Array(12.toByte, 13.toByte, 14.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 8.toByte, 14.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(15.toByte), + cdbvMapSet ++ Array(orderStatusMap.index, 0.toByte, 15.toByte) + ) + lazy val approveWorkFunc: Array[Byte] = getFunctionBytes(approveWorkId, publicFuncType, nonReturnType, commonDataType, approveWorkOpcs) + val approveWorkTextualBytes: Array[Byte] = textualFunc("approveWork", Seq(), approveWorkPara) + + // Apply to Judge Function + val applyToJudgeId: Short = 9 + val applyToJudgePara: Seq[String] = Seq("orderId") ++ + Seq("payer", "orderStatus", "isSubmit", "currentTime", "expirationTime", "isValidTime", + "judgeStatus", "valueFalse", "judgeDuration", "time", "updateTime", "valueTrue") + val applyToJudgeOpcs: Seq[Array[Byte]] = Seq( + cdbvrMapGet ++ Array(orderPayerMap.index, 0.toByte, 1.toByte), + assertCaller ++ Array(1.toByte), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, 2.toByte), + assertTrue ++ Array(2.toByte), + cdbvrMapGet ++ Array(orderSubmitStatusMap.index, 0.toByte, 3.toByte), + assertTrue ++ Array(3.toByte)) ++ timestampCheck(4, true) ++ Seq( + cdbvrMapGet ++ Array(orderJudgeStatusMap.index, 0.toByte, 7.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(8.toByte), + assertEqual ++ Array(7.toByte, 8.toByte), + cdbvrGet ++ Array(judgeDurationStateVar.index, 9.toByte), + basicAdd ++ Array(4.toByte, 9.toByte, 10.toByte), + basicMax ++ Array(10.toByte, 5.toByte, 11.toByte), + cdbvMapSet ++ Array(orderExpirationTimeMap.index, 0.toByte, 11.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(12.toByte), + cdbvMapSet ++ Array(orderJudgeStatusMap.index, 0.toByte, 12.toByte) + ) + lazy val applyToJudgeFunc: Array[Byte] = getFunctionBytes(applyToJudgeId, publicFuncType, nonReturnType, commonDataType, applyToJudgeOpcs) + val applyToJudgeTextualBytes: Array[Byte] = textualFunc("applyToJudge", Seq(), applyToJudgePara) + + // Judge Function + val judgeId: Short = 10 + val judgePara: Seq[String] = Seq("orderId", "payerAmount", "recipientAmount") ++ + Seq("judge", "orderStatus", "currentTime", "expirationTime", "isValidTime", + "judgeStatus", "payToRep", "recipientLocked", "totalToPay", "totalArrange", + "payer", "recipient", "fee", "judgeLocked", "judgeAmount", "valueFalse") + val judgeDataType: Array[Byte] = Array(DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte, DataType.Amount.id.toByte) + val judgeOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(judgeStateVar.index, 3.toByte), + assertCaller ++ Array(3.toByte), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, 4.toByte), + assertTrue ++ Array(4.toByte)) ++ timestampCheck(5, true) ++ Seq( + cdbvrMapGet ++ Array(orderJudgeStatusMap.index, 0.toByte, 8.toByte), + assertTrue ++ Array(8.toByte), + cdbvrMapGet ++ Array(orderRecipientAmountMap.index, 0.toByte, 9.toByte), + cdbvrMapGet ++ Array(orderRepLockedAmountMap.index, 0.toByte, 10.toByte), + basicAdd ++ Array(9.toByte, 10.toByte, 11.toByte), + basicAdd ++ Array(1.toByte, 2.toByte, 12.toByte), + assertEqual ++ Array(11.toByte, 12.toByte), + cdbvrMapGet ++ Array(orderPayerMap.index, 0.toByte, 13.toByte), + cdbvrMapGet ++ Array(orderRecipientMap.index, 0.toByte, 14.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 13.toByte, 1.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 14.toByte, 2.toByte), + cdbvrMapGet ++ Array(orderFeeMap.index, 0.toByte, 15.toByte), + cdbvrMapGet ++ Array(orderJudgeLockedAmountMap.index, 0.toByte, 16.toByte), + basicAdd ++ Array(15.toByte, 16.toByte, 17.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 3.toByte, 17.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(18.toByte), + cdbvMapSet ++ Array(orderStatusMap.index, 0.toByte, 18.toByte) + ) + lazy val judgeFunc: Array[Byte] = getFunctionBytes(judgeId, publicFuncType, nonReturnType, judgeDataType, judgeOpcs) + val judgeTextualBytes: Array[Byte] = textualFunc("judge", Seq(), judgePara) + + // Submit Penalty Function + val submitPenaltyId: Short = 11 + val submitPenaltyPara: Seq[String] = Seq("orderId") ++ + Seq("payer", "orderStatus", "valueFalse", "isSubmit", + "currentTime", "expirationTime", "isExpired", + "judge", "workAmount", "recipientLocked", "penaltyAmount", + "fee", "judgeLocked", "judgeAmount") + val submitPenaltyOpcs: Seq[Array[Byte]] = Seq( + cdbvrMapGet ++ Array(orderPayerMap.index, 0.toByte, 1.toByte), + assertCaller ++ Array(1.toByte), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, 2.toByte), + assertTrue ++ Array(2.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(3.toByte), + cdbvrMapGet ++ Array(orderSubmitStatusMap.index, 0.toByte, 4.toByte), + assertEqual ++ Array(3.toByte, 4.toByte)) ++ timestampCheck(5, false) ++ Seq( + cdbvrGet ++ Array(judgeStateVar.index, 8.toByte), + cdbvrMapGet ++ Array(orderRecipientAmountMap.index, 0.toByte, 9.toByte), + cdbvrMapGet ++ Array(orderRepLockedAmountMap.index, 0.toByte, 10.toByte), + basicAdd ++ Array(9.toByte, 10.toByte, 11.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 1.toByte, 11.toByte), + cdbvrMapGet ++ Array(orderFeeMap.index, 0.toByte, 12.toByte), + cdbvrMapGet ++ Array(orderJudgeLockedAmountMap.index, 0.toByte, 13.toByte), + basicAdd ++ Array(12.toByte, 13.toByte, 14.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 8.toByte, 14.toByte), + cdbvMapSet ++ Array(orderStatusMap.index, 0.toByte, 3.toByte) + ) + lazy val submitPenaltyFunc: Array[Byte] = getFunctionBytes(submitPenaltyId, publicFuncType, nonReturnType, commonDataType, submitPenaltyOpcs) + val submitPenaltyTextualBytes: Array[Byte] = textualFunc("submitPenalty", Seq(), submitPenaltyPara) + + // Order Refund Common + val refundCommonPara: Seq[String] = Seq("orderId") ++ + Seq("payer", "recipient", "orderStatus", "judgeStatus", "currentTime", "expirationTime", "isExpired", + "recipientRefund", "payerRefund", "valueFalse") + private def refundCommonOpcs(callerIndex: Byte): Seq[Array[Byte]] = { + Seq( + cdbvrMapGet ++ Array(orderPayerMap.index, 0.toByte, 1.toByte), + cdbvrMapGet ++ Array(orderRecipientMap.index, 0.toByte, 2.toByte), + assertCaller ++ Array(callerIndex), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, 3.toByte), + assertTrue ++ Array(3.toByte), + cdbvrMapGet ++ Array(orderJudgeStatusMap.index, 0.toByte, 4.toByte), + assertTrue ++ Array(4.toByte)) ++ timestampCheck(5, false) ++ Seq( + cdbvrMapGet ++ Array(orderRecipientRefundMap.index, 0.toByte, 8.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 2.toByte, 8.toByte), + cdbvrMapGet ++ Array(orderRefundMap.index, 0.toByte, 9.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 1.toByte, 9.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(10.toByte), + cdbvMapSet ++ Array(orderStatusMap.index, 0.toByte, 10.toByte) + ) + } + + // Payer Refund Function + val payerRefundId: Short = 12 + val payerRefundOpcs: Seq[Array[Byte]] = refundCommonOpcs(1.toByte) + lazy val payerRefundFunc: Array[Byte] = getFunctionBytes(payerRefundId, publicFuncType, nonReturnType, commonDataType, payerRefundOpcs) + val payerRefundTextualBytes: Array[Byte] = textualFunc("payerRefund", Seq(), refundCommonPara) + + // Payer Refund Function + val recipientRefundId: Short = 13 + val recipientRefundOpcs: Seq[Array[Byte]] = refundCommonOpcs(2.toByte) + lazy val recipientRefundFunc: Array[Byte] = getFunctionBytes(recipientRefundId, publicFuncType, nonReturnType, commonDataType, recipientRefundOpcs) + val recipientRefundTextualBytes: Array[Byte] = textualFunc("recipientRefund", Seq(), refundCommonPara) + + // Collect Function + val collectId: Short = 14 + val collectPara: Seq[String] = Seq("orderId") ++ + Seq("recipient", "orderStatus", "isSubmit", "valueFalse", "judgeStatus", + "currentTime", "expirationTime", "isExpired", + "judge", "workAmount", "recipientLocked", "recipientAmount", "fee", "judgeLocked", "judgeAmount") + val collectOpcs: Seq[Array[Byte]] = Seq( + cdbvrMapGet ++ Array(orderRecipientMap.index, 0.toByte, 1.toByte), + assertCaller ++ Array(1.toByte), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, 2.toByte), + assertTrue ++ Array(2.toByte), + cdbvrMapGet ++ Array(orderSubmitStatusMap.index, 0.toByte, 3.toByte), + assertTrue ++ Array(3.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(4.toByte), + cdbvrMapGet ++ Array(orderJudgeStatusMap.index, 0.toByte, 5.toByte), + assertEqual ++ Array(4.toByte, 5.toByte)) ++ timestampCheck(6, false) ++ Seq( + cdbvrGet ++ Array(judgeStateVar.index, 9.toByte), + cdbvrMapGet ++ Array(orderRecipientAmountMap.index, 0.toByte, 10.toByte), + cdbvrMapGet ++ Array(orderRepLockedAmountMap.index, 0.toByte, 11.toByte), + basicAdd ++ Array(10.toByte, 11.toByte, 12.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 1.toByte, 12.toByte), + cdbvrMapGet ++ Array(orderFeeMap.index, 0.toByte, 13.toByte), + cdbvrMapGet ++ Array(orderJudgeLockedAmountMap.index, 0.toByte, 14.toByte), + basicAdd ++ Array(13.toByte, 14.toByte, 15.toByte), + cdbvMapValAdd ++ Array(contractBalanceMap.index, 9.toByte, 15.toByte), + cdbvMapSet ++ Array(orderStatusMap.index, 0.toByte, 4.toByte) + ) + lazy val collectFunc: Array[Byte] = getFunctionBytes(collectId, publicFuncType, nonReturnType, commonDataType, collectOpcs) + val collectTextualBytes: Array[Byte] = textualFunc("collect", Seq(), collectPara) + + // Textual + lazy val triggerTextual: Array[Byte] = Deser.serializeArrays(Seq(initTextualBytes, depositTextualBytes, withdrawTextualBytes)) + lazy val descriptorTextual: Array[Byte] = Deser.serializeArrays(Seq(supersedeTextualBytes, createTextualBytes, recipientDepositTextualBytes, + judgeDepositTextualBytes, payerCancelTextualBytes, recipientCancelTextualBytes, judgeCancelTextualBytes, submitWorkTextualBytes, + approveWorkTextualBytes, applyToJudgeTextualBytes, judgeTextualBytes, submitPenaltyTextualBytes, + payerRefundTextualBytes, recipientRefundTextualBytes, collectTextualBytes)) +} \ No newline at end of file diff --git a/src/main/scala/vsys/blockchain/contract/ContractVOption.scala b/src/main/scala/vsys/blockchain/contract/ContractVOption.scala new file mode 100644 index 000000000..21af4914c --- /dev/null +++ b/src/main/scala/vsys/blockchain/contract/ContractVOption.scala @@ -0,0 +1,360 @@ +package vsys.blockchain.contract + +import com.google.common.primitives.{Ints, Longs} +import vsys.blockchain.contract.ContractGen._ +import vsys.blockchain.state._ +import vsys.utils.serialization.Deser + +object ContractVOption { + lazy val contract: Contract = Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + Seq(initTrigger, depositTrigger, withdrawTrigger), // Triggers + Seq(supersedeFunc, activateFunc, mintFunc, unlockFunc, executeFunc, collectFunc), // Functions + stateVarSeq, // StateVars + stateMapSeq, // StateMaps + Seq(triggerTextual, descriptorTextual, stateVarTextual, stateMapTextual) // Textual + ).explicitGet() + + // State Var + val stateVarName = List("maker", "baseTokenId", "targetTokenId", "optionTokenId", "proofTokenId", "executeTime", + "executeDeadline", "optionStatus", "maxIssueNum", "reservedOption", "reservedProof", + "price", "priceUnit", "tokenLocked", "tokenCollected") + val makerStateVar: StateVar = StateVar(0.toByte, DataType.Address.id.toByte) + val baseTokenIdStateVar: StateVar = StateVar(1.toByte, DataType.TokenId.id.toByte) + val targetTokenIdStateVar: StateVar = StateVar(2.toByte, DataType.TokenId.id.toByte) + val optionTokenIdStateVar: StateVar = StateVar(3.toByte, DataType.TokenId.id.toByte) + val proofTokenIdStateVar: StateVar = StateVar(4.toByte, DataType.TokenId.id.toByte) + val executeTimeStateVar: StateVar = StateVar(5.toByte, DataType.Timestamp.id.toByte) + val executeDeadlineStateVar: StateVar = StateVar(6.toByte, DataType.Timestamp.id.toByte) + val optionStatusStateVar: StateVar = StateVar(7.toByte, DataType.Boolean.id.toByte) + val maxIssueNumStateVar: StateVar = StateVar(8.toByte, DataType.Amount.id.toByte) + val reservedOptionStateVar: StateVar = StateVar(9.toByte, DataType.Amount.id.toByte) + val reservedProofStateVar: StateVar = StateVar(10.toByte, DataType.Amount.id.toByte) + val priceStateVar: StateVar = StateVar(11.toByte, DataType.Amount.id.toByte) + val priceUnitStateVar: StateVar = StateVar(12.toByte, DataType.Amount.id.toByte) + val tokenLockedStateVar: StateVar = StateVar(13.toByte, DataType.Amount.id.toByte) + val tokenCollectedStateVar: StateVar = StateVar(14.toByte, DataType.Amount.id.toByte) + lazy val stateVarSeq = Seq(makerStateVar.arr, baseTokenIdStateVar.arr, targetTokenIdStateVar.arr, + optionTokenIdStateVar.arr, proofTokenIdStateVar.arr, + executeTimeStateVar.arr, executeDeadlineStateVar.arr, optionStatusStateVar.arr, + maxIssueNumStateVar.arr, reservedOptionStateVar.arr, reservedProofStateVar.arr, + priceStateVar.arr, priceUnitStateVar.arr, tokenLockedStateVar.arr, tokenCollectedStateVar.arr) + lazy val stateVarTextual: Array[Byte] = Deser.serializeArrays(stateVarName.map(x => Deser.serilizeString(x))) + + // State Map + val stateMapBaseTokenBalance = List("baseTokenBalance", "userAddress", "balance") + val stateMapTargetTokenBalance = List("targetTokenBalance", "userAddress", "balance") + val stateMapOptionTokenBalance = List("optionTokenBalance", "userAddress", "balance") + val stateMapProofTokenBalance = List("proofTokenBalance", "userAddress", "balance") + val baseTokenBalanceMap: StateMap = StateMap(0.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + val targetTokenBalanceMap: StateMap = StateMap(1.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + val optionTokenBalanceMap: StateMap = StateMap(2.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + val proofTokenBalanceMap: StateMap = StateMap(3.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + + lazy val stateMapSeq = Seq(baseTokenBalanceMap.arr, targetTokenBalanceMap.arr, optionTokenBalanceMap.arr, proofTokenBalanceMap.arr) + lazy val stateMapTextual: Array[Byte] = textualStateMap(Seq(stateMapBaseTokenBalance, stateMapTargetTokenBalance, + stateMapOptionTokenBalance, stateMapProofTokenBalance)) + + // Initialization Trigger + val initId: Short = 0 + val initPara: Seq[String] = Seq("baseTokenId", "targetTokenId", "optionTokenId", "proofTokenId", "executeTime", "executeDeadline") ++ + Seq("signer", "optionStatus") + val initDataType: Array[Byte] = Array(DataType.TokenId.id.toByte, DataType.TokenId.id.toByte,DataType.TokenId.id.toByte, + DataType.TokenId.id.toByte, DataType.Timestamp.id.toByte, DataType.Timestamp.id.toByte) + val initTriggerOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(6.toByte), + cdbvSet ++ Array(makerStateVar.index, 6.toByte), + cdbvSet ++ Array(baseTokenIdStateVar.index, 0.toByte), + cdbvSet ++ Array(targetTokenIdStateVar.index, 1.toByte), + cdbvSet ++ Array(optionTokenIdStateVar.index, 2.toByte), + cdbvSet ++ Array(proofTokenIdStateVar.index, 3.toByte), + cdbvSet ++ Array(executeTimeStateVar.index, 4.toByte), + cdbvSet ++ Array(executeDeadlineStateVar.index, 5.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + cdbvSet ++ Array(optionStatusStateVar.index, 7.toByte) + ) + lazy val initTrigger: Array[Byte] = getFunctionBytes(initId, onInitTriggerType, nonReturnType, initDataType, initTriggerOpcs) + val initTextualBytes: Array[Byte] = textualFunc("init", Seq(), initPara) + + // Deposit Trigger + val depositId: Short = 1 + val depositPara: Seq[String] = Seq("depositor", "amount", "tokenId") ++ + Seq("baseTokenId", "targetTokenId", "optionTokenId", "proofTokenId", "isValidTokenId", + "isBaseToken", "valueFalse", "baseTokenIfBlock", "isTargetToken", "targetTokenIfBlock", + "isOptionToken", "optionTokenIfBlock", "isProofToken", "proofTokenIfBlock") + val depositDataType: Array[Byte] = Array(DataType.Address.id.toByte, DataType.Amount.id.toByte, DataType.TokenId.id.toByte) + val depositTriggerOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + cdbvrGet ++ Array(baseTokenIdStateVar.index, 3.toByte), + cdbvrGet ++ Array(targetTokenIdStateVar.index, 4.toByte), + cdbvrGet ++ Array(optionTokenIdStateVar.index, 5.toByte), + cdbvrGet ++ Array(proofTokenIdStateVar.index, 6.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + compareBytesEqual ++ Array(2.toByte, 3.toByte, 8.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(9.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + assertEqual ++ Array(7.toByte, 9.toByte), + cdbvMapValAdd ++ Array(baseTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(10.toByte), + compareBytesEqual ++ Array(2.toByte, 4.toByte, 11.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + assertEqual ++ Array(7.toByte, 9.toByte), + cdbvMapValAdd ++ Array(targetTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(12.toByte), + compareBytesEqual ++ Array(2.toByte, 5.toByte, 13.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + assertEqual ++ Array(7.toByte, 9.toByte), + cdbvMapValAdd ++ Array(optionTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(14.toByte), + compareBytesEqual ++ Array(2.toByte, 6.toByte, 15.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + assertEqual ++ Array(7.toByte, 9.toByte), + cdbvMapValAdd ++ Array(proofTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(16.toByte), + conditionIf ++ Array(8.toByte, 10.toByte), + conditionIf ++ Array(11.toByte, 12.toByte), + conditionIf ++ Array(13.toByte, 14.toByte), + conditionIf ++ Array(15.toByte, 16.toByte), + assertTrue ++ Array(7.toByte) + ) + lazy val depositTrigger: Array[Byte] = getFunctionBytes(depositId, onDepositTriggerType, nonReturnType, depositDataType, depositTriggerOpcs) + val depositTextualBytes: Array[Byte] = textualFunc("deposit", Seq(), depositPara) + + // WithDraw Trigger + val withdrawId: Short = 2 + val withdrawPara: Seq[String] = Seq("withdrawer", "amount", "tokenId") ++ + Seq("baseTokenId", "targetTokenId", "optionTokenId", "proofTokenId", "isValidTokenId", + "isBaseToken", "baseTokenIfBlock", "isTargetToken", "targetTokenIfBlock", + "isOptionToken", "optionTokenIfBlock", "isProofToken", "proofTokenIfBlock") + val withdrawDataType: Array[Byte] = Array(DataType.Address.id.toByte, DataType.Amount.id.toByte, DataType.TokenId.id.toByte) + val withdrawTriggerOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + cdbvrGet ++ Array(baseTokenIdStateVar.index, 3.toByte), + cdbvrGet ++ Array(targetTokenIdStateVar.index, 4.toByte), + cdbvrGet ++ Array(optionTokenIdStateVar.index, 5.toByte), + cdbvrGet ++ Array(proofTokenIdStateVar.index, 6.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + compareBytesEqual ++ Array(2.toByte, 3.toByte, 8.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + cdbvMapValMinus ++ Array(baseTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(9.toByte), + compareBytesEqual ++ Array(2.toByte, 4.toByte, 10.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + cdbvMapValMinus ++ Array(targetTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(11.toByte), + compareBytesEqual ++ Array(2.toByte, 5.toByte, 12.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + cdbvMapValMinus ++ Array(optionTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(13.toByte), + compareBytesEqual ++ Array(2.toByte, 6.toByte, 14.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + cdbvMapValMinus ++ Array(proofTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(15.toByte), + conditionIf ++ Array(8.toByte, 9.toByte), + conditionIf ++ Array(10.toByte, 11.toByte), + conditionIf ++ Array(12.toByte, 13.toByte), + conditionIf ++ Array(14.toByte, 15.toByte), + assertTrue ++ Array(7.toByte) + ) + lazy val withdrawTrigger: Array[Byte] = getFunctionBytes(withdrawId, onWithDrawTriggerType, nonReturnType, withdrawDataType, withdrawTriggerOpcs) + val withdrawTextualBytes: Array[Byte] = textualFunc("withdraw", Seq(), withdrawPara) + + // Functions + // Supersede + val supersedeId: Short = 0 + val supersedePara: Seq[String] = Seq("newOwner", + "maker") + val supersedeDataType: Array[Byte] = Array(DataType.Account.id.toByte) + val supersedeOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(makerStateVar.index, 1.toByte), + assertSigner ++ Array(1.toByte), + cdbvSet ++ Array(makerStateVar.index, 0.toByte)) + lazy val supersedeFunc: Array[Byte] = getFunctionBytes(supersedeId, publicFuncType, nonReturnType, supersedeDataType, supersedeOpcs) + val supersedeTextualBytes: Array[Byte] = textualFunc("supersede", Seq(), supersedePara) + + // Activate Option + val activateId: Short = 1 + val activatePara: Seq[String] = Seq("maxIssueNum", "price", "priceUnit") ++ + Seq("maker", "optionStatus", "valueFalse", "amountZero", "isValidIssueNum", + "isValidPrice", "isValidPriceUnit", "optionTokenNum", "proofTokenNum", "valueTrue") + val activateDataType: Array[Byte] = Array(DataType.Amount.id.toByte, DataType.Amount.id.toByte, DataType.Amount.id.toByte) + val activateOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(makerStateVar.index, 3.toByte), + assertCaller ++ Array(3.toByte), + cdbvrGet ++ Array(optionStatusStateVar.index, 4.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + assertEqual ++ Array(4.toByte, 5.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(0L), DataType.Amount).bytes ++ Array(6.toByte), + compareGreater ++ Array(0.toByte, 6.toByte, 7.toByte), + assertTrue ++ Array(7.toByte), + compareGreater ++ Array(1.toByte, 6.toByte, 8.toByte), + assertTrue ++ Array(8.toByte), + compareGreater ++ Array(2.toByte, 6.toByte, 9.toByte), + assertTrue ++ Array(9.toByte), + cdbvrMapGetOrDefault ++ Array(optionTokenBalanceMap.index, 3.toByte, 10.toByte), + cdbvrMapGetOrDefault ++ Array(proofTokenBalanceMap.index, 3.toByte, 11.toByte), + assertEqual ++ Array(10.toByte, 0.toByte), + assertEqual ++ Array(11.toByte, 0.toByte), + cdbvMapValMinus ++ Array(optionTokenBalanceMap.index, 3.toByte, 0.toByte), + cdbvMapValMinus ++ Array(proofTokenBalanceMap.index, 3.toByte, 0.toByte), + cdbvSet ++ Array(maxIssueNumStateVar.index, 0.toByte), + cdbvStateValAdd ++ Array(reservedOptionStateVar.index, 0.toByte), + cdbvStateValAdd ++ Array(reservedProofStateVar.index, 0.toByte), + cdbvSet ++ Array(priceStateVar.index, 1.toByte), + cdbvSet ++ Array(priceUnitStateVar.index, 2.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(12.toByte), + cdbvSet ++ Array(optionStatusStateVar.index, 12.toByte) + ) + lazy val activateFunc: Array[Byte] = getFunctionBytes(activateId, publicFuncType, nonReturnType, activateDataType, activateOpcs) + val activateTextualBytes: Array[Byte] = textualFunc("activate", Seq(), activatePara) + + // Common Option Code + val commonOptionPara: Seq[String] = Seq("amount") ++ + Seq("caller", "OptionStatus", "currentTime") + val commonOptionDataType: Array[Byte] = Array(DataType.Amount.id.toByte) + val commonOptionOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(1.toByte), + cdbvrGet ++ Array(optionStatusStateVar.index, 2.toByte), + assertTrue ++ Array(2.toByte), + loadTimestamp ++ Array(3.toByte) + ) + + // Mint Option + val mintId: Short = 2 + val mintPara: Seq[String] = commonOptionPara ++ + Seq("executeTime", "isValidTime") + val mintOpcs: Seq[Array[Byte]] = commonOptionOpcs ++ Seq( + cdbvrGet ++ Array(executeTimeStateVar.index, 4.toByte), + compareGreater ++ Array(4.toByte, 3.toByte, 5.toByte), + assertTrue ++ Array(5.toByte), + cdbvMapValMinus ++ Array(targetTokenBalanceMap.index, 1.toByte, 0.toByte), + cdbvStateValMinus ++ Array(reservedOptionStateVar.index, 0.toByte), + cdbvStateValMinus ++ Array(reservedProofStateVar.index, 0.toByte), + cdbvStateValAdd ++ Array(tokenLockedStateVar.index, 0.toByte), + cdbvMapValAdd ++ Array(optionTokenBalanceMap.index, 1.toByte, 0.toByte), + cdbvMapValAdd ++ Array(proofTokenBalanceMap.index, 1.toByte, 0.toByte) + ) + lazy val mintFunc: Array[Byte] = getFunctionBytes( mintId, publicFuncType, nonReturnType, commonOptionDataType, mintOpcs) + val mintTextualBytes: Array[Byte] = textualFunc("mint", Seq(), mintPara) + + // Unlock Option + val unlockId: Short = 3 + val unlockPara: Seq[String] = commonOptionPara ++ + Seq("executeDeadline", "isValidTime") + val unlockOpcs: Seq[Array[Byte]] = commonOptionOpcs ++ Seq( + cdbvrGet ++ Array(executeDeadlineStateVar.index, 4.toByte), + compareGreater ++ Array(4.toByte, 3.toByte, 5.toByte), + assertTrue ++ Array(5.toByte), + cdbvMapValMinus ++ Array(optionTokenBalanceMap.index, 1.toByte, 0.toByte), + cdbvMapValMinus ++ Array(proofTokenBalanceMap.index, 1.toByte, 0.toByte), + cdbvStateValAdd ++ Array(reservedOptionStateVar.index, 0.toByte), + cdbvStateValAdd ++ Array(reservedProofStateVar.index, 0.toByte), + cdbvStateValMinus ++ Array(tokenLockedStateVar.index, 0.toByte), + cdbvMapValAdd ++ Array(targetTokenBalanceMap.index, 1.toByte, 0.toByte) + ) + lazy val unlockFunc: Array[Byte] = getFunctionBytes( unlockId, publicFuncType, nonReturnType, commonOptionDataType, unlockOpcs) + val unlockTextualBytes: Array[Byte] = textualFunc("unlock", Seq(), unlockPara) + + // Execute Option + val executeId: Short = 4 + val executePara: Seq[String] = commonOptionPara ++ + Seq("executeDeadline", "isBeforeDeadline", "executeTime", "isValidToExecute", + "price", "priceUnit", "bigIntType", "amountBigInt", "priceBigInt", "priceUnitBigInt", + "costWithUnitBigInt", "costBigInt", "amountType", "cost", "amountOne", "fixedCost") + val executeOpcs: Seq[Array[Byte]] = commonOptionOpcs ++ Seq( + cdbvrGet ++ Array(executeDeadlineStateVar.index, 4.toByte), + compareGreater ++ Array(4.toByte, 3.toByte, 5.toByte), + assertTrue ++ Array(5.toByte), + cdbvrGet ++ Array(executeTimeStateVar.index, 6.toByte), + compareGreaterEqual ++ Array(3.toByte, 6.toByte, 7.toByte), + assertTrue ++ Array(7.toByte), + cdbvMapValMinus ++ Array(optionTokenBalanceMap.index, 1.toByte, 0.toByte), + cdbvStateValAdd ++ Array(reservedOptionStateVar.index, 0.toByte), + cdbvrGet ++ Array(priceStateVar.index, 8.toByte), + cdbvrGet ++ Array(priceUnitStateVar.index, 9.toByte), + basicConstantGet ++ DataEntry(Array(DataType.BigInteger.id.toByte), DataType.DataTypeObj).bytes ++ Array(10.toByte), + basicConvert ++ Array(0.toByte, 10.toByte, 11.toByte), + basicConvert ++ Array(8.toByte, 10.toByte, 12.toByte), + basicConvert ++ Array(9.toByte, 10.toByte, 13.toByte), + basicMultiply ++ Array(11.toByte, 12.toByte, 14.toByte), + basicDivide ++ Array(14.toByte, 13.toByte, 15.toByte), + basicConstantGet ++ DataEntry(Array(DataType.Amount.id.toByte), DataType.DataTypeObj).bytes ++ Array(16.toByte), + basicConvert ++ Array(15.toByte, 16.toByte, 17.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1L), DataType.Amount).bytes ++ Array(18.toByte), + basicAdd ++ Array(17.toByte, 18.toByte, 19.toByte), + cdbvMapValMinus ++ Array(baseTokenBalanceMap.index, 1.toByte, 19.toByte), + cdbvStateValAdd ++ Array(tokenCollectedStateVar.index, 19.toByte), + cdbvStateValMinus ++ Array(tokenLockedStateVar.index, 0.toByte), + cdbvMapValAdd ++ Array(targetTokenBalanceMap.index, 1.toByte, 0.toByte) + ) + lazy val executeFunc: Array[Byte] = getFunctionBytes( executeId, publicFuncType, nonReturnType, commonOptionDataType, executeOpcs) + val executeTextualBytes: Array[Byte] = textualFunc("execute", Seq(), executePara) + + val collectId: Short = 5 + val collectPara: Seq[String] = commonOptionPara ++ + Seq("executeDeadline", "isValidTime", "maxIssue", "reservedProof", "isValidCollect", "uncollectedProof", + "bigIntType", "baseNumber", "baseNumberBigInt", "amountBigInt", "uncollectedProofBigInt", + "baseNumerator", "baseCollectBigInt", "amountType", "baseCollect", "targetNumber", "targetNumberBigInt", + "targetNumerator", "targetCollectBigInt", "targetCollect") + val collectOpcs: Seq[Array[Byte]] = commonOptionOpcs ++ Seq( + cdbvrGet ++ Array(executeDeadlineStateVar.index, 4.toByte), + compareGreater ++ Array(3.toByte, 4.toByte, 5.toByte), + assertTrue ++ Array(5.toByte), + cdbvrGet ++ Array(maxIssueNumStateVar.index, 6.toByte), + cdbvrGetOrDefault ++ Array(reservedProofStateVar.index, 7.toByte), + compareGreater ++ Array(6.toByte, 7.toByte, 8.toByte), + assertTrue ++ Array(8.toByte), + basicMinus ++ Array(6.toByte, 7.toByte, 9.toByte), + cdbvMapValMinus ++ Array(proofTokenBalanceMap.index, 1.toByte, 0.toByte), + cdbvStateValAdd ++ Array(reservedProofStateVar.index, 0.toByte), + basicConstantGet ++ DataEntry(Array(DataType.BigInteger.id.toByte), DataType.DataTypeObj).bytes ++ Array(10.toByte), + cdbvrGetOrDefault ++ Array(tokenCollectedStateVar.index, 11.toByte), + basicConvert ++ Array(11.toByte, 10.toByte, 12.toByte), + basicConvert ++ Array(0.toByte, 10.toByte, 13.toByte), + basicConvert ++ Array(9.toByte, 10.toByte, 14.toByte), + basicMultiply ++ Array(12.toByte, 13.toByte, 15.toByte), + basicDivide ++ Array(15.toByte, 14.toByte, 16.toByte), + basicConstantGet ++ DataEntry(Array(DataType.Amount.id.toByte), DataType.DataTypeObj).bytes ++ Array(17.toByte), + basicConvert ++ Array(16.toByte, 17.toByte, 18.toByte), + cdbvrGetOrDefault ++ Array(tokenLockedStateVar.index, 19.toByte), + basicConvert ++ Array(19.toByte, 10.toByte, 20.toByte), + basicMultiply ++ Array(20.toByte, 13.toByte, 21.toByte), + basicDivide ++ Array(21.toByte, 14.toByte, 22.toByte), + basicConvert ++ Array(22.toByte, 17.toByte, 23.toByte), + cdbvStateValMinus ++ Array(tokenCollectedStateVar.index, 18.toByte), + cdbvMapValAdd ++ Array(baseTokenBalanceMap.index, 1.toByte, 18.toByte), + cdbvStateValMinus ++ Array(tokenLockedStateVar.index, 23.toByte), + cdbvMapValAdd ++ Array(targetTokenBalanceMap.index, 1.toByte, 23.toByte) + ) + lazy val collectFunc: Array[Byte] = getFunctionBytes( collectId, publicFuncType, nonReturnType, commonOptionDataType, collectOpcs) + val collectTextualBytes: Array[Byte] = textualFunc("collect", Seq(), collectPara) + + // Textual + lazy val triggerTextual: Array[Byte] = Deser.serializeArrays(Seq(initTextualBytes, depositTextualBytes, withdrawTextualBytes)) + lazy val descriptorTextual: Array[Byte] = Deser.serializeArrays(Seq(supersedeTextualBytes, activateTextualBytes, mintTextualBytes, + unlockTextualBytes, executeTextualBytes, collectTextualBytes)) + +} \ No newline at end of file diff --git a/src/main/scala/vsys/blockchain/contract/ContractVStableSwap.scala b/src/main/scala/vsys/blockchain/contract/ContractVStableSwap.scala new file mode 100644 index 000000000..f75c914e4 --- /dev/null +++ b/src/main/scala/vsys/blockchain/contract/ContractVStableSwap.scala @@ -0,0 +1,347 @@ +package vsys.blockchain.contract + +import com.google.common.primitives.{Ints, Longs} +import vsys.blockchain.contract.ContractGen._ +import vsys.blockchain.state._ +import vsys.utils.serialization.Deser + +object ContractVStableSwap { + lazy val contract: Contract = Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + Seq(initTrigger, depositTrigger, withdrawTrigger), // Triggers + Seq(supersedeFunc, setOrderFunc, updateFunc, orderDepositFunc, orderWithdrawFunc, closeFunc, + swapBaseToTargetFunc, swapTargetToBaseFunc), // Functions + stateVarSeq, // StateVars + stateMapSeq, // StateMaps + Seq(triggerTextual, descriptorTextual, stateVarTextual, stateMapTextual) // Textual + ).explicitGet() + + // State Var + val stateVarName = List("maker", "baseTokenId", "targetTokenId", "maxOrderPerUser", "unitPriceBase", "unitPriceTarget") + val makerStateVar: StateVar = StateVar(0.toByte, DataType.Address.id.toByte) + val baseTokenIdStateVar: StateVar = StateVar(1.toByte, DataType.TokenId.id.toByte) + val targetTokenIdStateVar: StateVar = StateVar(2.toByte, DataType.TokenId.id.toByte) + val maxOrderPerUserStateVar: StateVar = StateVar(3.toByte, DataType.Amount.id.toByte) + val unitPriceBaseStateVar: StateVar = StateVar(4.toByte, DataType.Amount.id.toByte) + val unitPriceTargetStateVar: StateVar = StateVar(5.toByte, DataType.Amount.id.toByte) + lazy val stateVarSeq = Seq(makerStateVar.arr, baseTokenIdStateVar.arr, targetTokenIdStateVar.arr, maxOrderPerUserStateVar.arr, + unitPriceBaseStateVar.arr, unitPriceTargetStateVar.arr) + lazy val stateVarTextual: Array[Byte] = Deser.serializeArrays(stateVarName.map(x => Deser.serilizeString(x))) + + // State Map + val stateMapBaseTokenBalance = List("baseTokenBalance", "userAddress", "balance") + val stateMapTargetTokenBalance = List("targetTokenBalance", "userAddress", "balance") + val stateMapUserOrders = List("userOrders", "userAddress", "orderNum") + val stateMapOrderOwner = List("orderOwner", "orderId", "owner") + val stateMapFeeBase = List("feeBase", "orderId", "fee") + val stateMapFeeTarget = List("feeTarget", "orderId", "fee") + val stateMapMinBase = List("minBase", "orderId", "amount") + val stateMapMaxBase = List("maxBase", "orderId", "amount") + val stateMapMinTarget = List("minTarget", "orderId", "amount") + val stateMapMaxTarget = List("maxTarget", "orderId", "amount") + val stateMapPriceBase = List("priceBase", "orderId", "price") + val stateMapPriceTarget = List("priceTarget", "orderId", "price") + val stateMapBaseTokenLocked = List("baseTokenLocked", "orderId", "amount") + val stateMapTargetTokenLocked = List("targetTokenLocked", "orderId", "amount") + val stateMapOrderStatus = List("orderStatus", "orderId", "status") + val baseTokenBalanceMap: StateMap = StateMap(0.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + val targetTokenBalanceMap: StateMap = StateMap(1.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + val userOrdersMap: StateMap = StateMap(2.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + val orderOwnerMap: StateMap = StateMap(3.toByte, DataType.ShortBytes.id.toByte, DataType.Address.id.toByte) + val feeBaseMap: StateMap = StateMap(4.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val feeTargetMap: StateMap = StateMap(5.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val minBaseMap: StateMap = StateMap(6.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val maxBaseMap: StateMap = StateMap(7.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val minTargetMap: StateMap = StateMap(8.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val maxTargetMap: StateMap = StateMap(9.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val priceBaseMap: StateMap = StateMap(10.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val priceTargetMap: StateMap = StateMap(11.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val baseTokenLockedMap: StateMap = StateMap(12.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val targetTokenLockedMap: StateMap = StateMap(13.toByte, DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte) + val orderStatusMap: StateMap = StateMap(14.toByte, DataType.ShortBytes.id.toByte, DataType.Boolean.id.toByte) + + lazy val stateMapSeq = Seq(baseTokenBalanceMap.arr, targetTokenBalanceMap.arr, userOrdersMap.arr, orderOwnerMap.arr, + feeBaseMap.arr, feeTargetMap.arr, minBaseMap.arr, maxBaseMap.arr, + minTargetMap.arr, maxTargetMap.arr, priceBaseMap.arr, priceTargetMap.arr, + baseTokenLockedMap.arr, targetTokenLockedMap.arr, orderStatusMap.arr) + lazy val stateMapTextual: Array[Byte] = textualStateMap( + Seq(stateMapBaseTokenBalance, stateMapTargetTokenBalance, stateMapUserOrders, stateMapOrderOwner, + stateMapFeeBase, stateMapFeeTarget, stateMapMinBase, stateMapMaxBase, stateMapMinTarget, + stateMapMaxTarget, stateMapPriceBase, stateMapPriceTarget, stateMapBaseTokenLocked, stateMapTargetTokenLocked, stateMapOrderStatus)) + + // Initialization Trigger + val initId: Short = 0 + val initPara: Seq[String] = Seq("baseTokenId", "targetTokenId", "maxOrderPerUser", "unitPriceBase", "unitPriceTarget") ++ + Seq("signer") + val initDataType: Array[Byte] = Array(DataType.TokenId.id.toByte, DataType.TokenId.id.toByte, DataType.Amount.id.toByte, + DataType.Amount.id.toByte, DataType.Amount.id.toByte) + val initTriggerOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(5.toByte), + cdbvSet ++ Array(makerStateVar.index, 5.toByte), + cdbvSet ++ Array(baseTokenIdStateVar.index, 0.toByte), + cdbvSet ++ Array(targetTokenIdStateVar.index, 1.toByte), + cdbvSet ++ Array(maxOrderPerUserStateVar.index, 2.toByte), + cdbvSet ++ Array(unitPriceBaseStateVar.index, 3.toByte), + cdbvSet ++ Array(unitPriceTargetStateVar.index, 4.toByte) + ) + lazy val initTrigger: Array[Byte] = getFunctionBytes(initId, onInitTriggerType, nonReturnType, initDataType, initTriggerOpcs) + val initTextualBytes: Array[Byte] = textualFunc("init", Seq(), initPara) + + // Deposit Trigger + val depositId: Short = 1 + val depositPara: Seq[String] = Seq("depositor", "amount", "tokenId") ++ + Seq("baseTokenId", "targetTokenId", "isValidTokenId", + "isBaseToken", "valueFalse", "baseTokenIfBlock", "isTargetToken", "targetTokenIfBlock") + val depositDataType: Array[Byte] = Array(DataType.Address.id.toByte, DataType.Amount.id.toByte, DataType.TokenId.id.toByte) + val depositTriggerOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + cdbvrGet ++ Array(baseTokenIdStateVar.index, 3.toByte), + cdbvrGet ++ Array(targetTokenIdStateVar.index, 4.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + compareBytesEqual ++ Array(2.toByte, 3.toByte, 6.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(7.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + assertEqual ++ Array(5.toByte, 7.toByte), + cdbvMapValAdd ++ Array(baseTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(8.toByte), + compareBytesEqual ++ Array(2.toByte, 4.toByte, 9.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + assertEqual ++ Array(5.toByte, 7.toByte), + cdbvMapValAdd ++ Array(targetTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(10.toByte), + conditionIf ++ Array(6.toByte, 8.toByte), + conditionIf ++ Array(9.toByte, 10.toByte), + assertTrue ++ Array(5.toByte) + ) + lazy val depositTrigger: Array[Byte] = getFunctionBytes(depositId, onDepositTriggerType, nonReturnType, depositDataType, depositTriggerOpcs) + val depositTextualBytes: Array[Byte] = textualFunc("deposit", Seq(), depositPara) + + // Withdraw Trigger + val withdrawId: Short = 2 + val withdrawPara: Seq[String] = Seq("withdrawer", "amount", "tokenId") ++ + Seq("baseTokenId", "targetTokenId", "isValidTokenId", + "isBaseToken", "baseTokenIfBlock", "isTargetToken", "targetTokenIfBlock") + val withdrawDataType: Array[Byte] = Array(DataType.Address.id.toByte, DataType.Amount.id.toByte, DataType.TokenId.id.toByte) + val withdrawTriggerOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + cdbvrGet ++ Array(baseTokenIdStateVar.index, 3.toByte), + cdbvrGet ++ Array(targetTokenIdStateVar.index, 4.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + compareBytesEqual ++ Array(2.toByte, 3.toByte, 6.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + cdbvMapValMinus ++ Array(baseTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(7.toByte), + compareBytesEqual ++ Array(2.toByte, 4.toByte, 8.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + cdbvMapValMinus ++ Array(targetTokenBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(9.toByte), + conditionIf ++ Array(6.toByte, 7.toByte), + conditionIf ++ Array(8.toByte, 9.toByte), + assertTrue ++ Array(5.toByte) + ) + lazy val withdrawTrigger: Array[Byte] = getFunctionBytes(withdrawId, onWithDrawTriggerType, nonReturnType, withdrawDataType, withdrawTriggerOpcs) + val withdrawTextualBytes: Array[Byte] = textualFunc("withdraw", Seq(), withdrawPara) + + // Functions + // Supersede + val supersedeId: Short = 0 + val supersedePara: Seq[String] = Seq("newOwner", + "maker") + val supersedeDataType: Array[Byte] = Array(DataType.Account.id.toByte) + val supersedeOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(makerStateVar.index, 1.toByte), + assertSigner ++ Array(1.toByte), + cdbvSet ++ Array(makerStateVar.index, 0.toByte)) + lazy val supersedeFunc: Array[Byte] = getFunctionBytes(supersedeId, publicFuncType, nonReturnType, supersedeDataType, supersedeOpcs) + val supersedeTextualBytes: Array[Byte] = textualFunc("supersede", Seq(), supersedePara) + + // Set Order + val setOrderId: Short = 1 + val setOrderPara: Seq[String] = Seq("feeBase", "feeTarget", "minBase", "maxBase", "minTarget", "maxTarget", "priceBase", + "priceTarget", "baseDeposit", "targetDeposit") ++ + Seq("caller", "maxOrderPerUser", "userOrders", "isValidOrderNum", "orderId", "amountOne", "valueTrue") + val setOrderDataType: Array[Byte] = Array.fill[Byte](10)(DataType.Amount.id.toByte) + val setOrderOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(10.toByte), + cdbvrGet ++ Array(maxOrderPerUserStateVar.index, 11.toByte), + cdbvrMapGetOrDefault ++ Array(userOrdersMap.index, 10.toByte, 12.toByte), + compareGreater ++ Array(11.toByte, 12.toByte, 13.toByte), + assertTrue ++ Array(13.toByte), + loadTransactionId ++ Array(14.toByte), + cdbvMapValMinus ++ Array(baseTokenBalanceMap.index, 10.toByte, 8.toByte), + cdbvMapValMinus ++ Array(targetTokenBalanceMap.index, 10.toByte, 9.toByte), + cdbvMapSet ++ Array(orderOwnerMap.index, 14.toByte, 10.toByte), + cdbvMapSet ++ Array(feeBaseMap.index, 14.toByte, 0.toByte), + cdbvMapSet ++ Array(feeTargetMap.index, 14.toByte, 1.toByte), + cdbvMapSet ++ Array(minBaseMap.index, 14.toByte, 2.toByte), + cdbvMapSet ++ Array(maxBaseMap.index, 14.toByte, 3.toByte), + cdbvMapSet ++ Array(minTargetMap.index, 14.toByte, 4.toByte), + cdbvMapSet ++ Array(maxTargetMap.index, 14.toByte, 5.toByte), + cdbvMapSet ++ Array(priceBaseMap.index, 14.toByte, 6.toByte), + cdbvMapSet ++ Array(priceTargetMap.index, 14.toByte, 7.toByte), + cdbvMapValAdd ++ Array(baseTokenLockedMap.index, 14.toByte, 8.toByte), + cdbvMapValAdd ++ Array(targetTokenLockedMap.index, 14.toByte, 9.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1L), DataType.Amount).bytes ++ Array(15.toByte), + cdbvMapValAdd ++ Array(userOrdersMap.index, 10.toByte, 15.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(16.toByte), + cdbvMapSet ++ Array(orderStatusMap.index, 14.toByte, 16.toByte) + ) + lazy val setOrderFunc: Array[Byte] = getFunctionBytes(setOrderId, publicFuncType, nonReturnType, setOrderDataType, setOrderOpcs) + val setOrderTextualBytes: Array[Byte] = textualFunc("setOrder", Seq(), setOrderPara) + + private def commonCheckOpcs(ownerIndex: Byte, statusIndex: Byte): Seq[Array[Byte]] = Seq( + cdbvrMapGet ++ Array(orderOwnerMap.index, 0.toByte, ownerIndex), + assertCaller ++ Array(ownerIndex), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, statusIndex), + assertTrue ++ Array(statusIndex) + ) + + // Update Order + val updateId: Short = 2 + val updatePara: Seq[String] = Seq("orderId", "feeBase", "feeTarget", "minBase", "maxBase", "minTarget", "maxTarget", + "priceBase", "priceTarget") ++ + Seq("owner", "status") + val updateDataType: Array[Byte] = Array(DataType.ShortBytes.id.toByte) ++ Array.fill[Byte](8)(DataType.Amount.id.toByte) + val updateOpcs: Seq[Array[Byte]] = commonCheckOpcs(9.toByte, 10.toByte) ++ Seq( + cdbvMapSet ++ Array(feeBaseMap.index, 0.toByte, 1.toByte), + cdbvMapSet ++ Array(feeTargetMap.index, 0.toByte, 2.toByte), + cdbvMapSet ++ Array(minBaseMap.index, 0.toByte, 3.toByte), + cdbvMapSet ++ Array(maxBaseMap.index, 0.toByte, 4.toByte), + cdbvMapSet ++ Array(minTargetMap.index, 0.toByte, 5.toByte), + cdbvMapSet ++ Array(maxTargetMap.index, 0.toByte, 6.toByte), + cdbvMapSet ++ Array(priceBaseMap.index, 0.toByte, 7.toByte), + cdbvMapSet ++ Array(priceTargetMap.index, 0.toByte, 8.toByte) + ) + lazy val updateFunc: Array[Byte] = getFunctionBytes(updateId, publicFuncType, nonReturnType, updateDataType, updateOpcs) + val updateTextualBytes: Array[Byte] = textualFunc("update", Seq(), updatePara) + + // Order Deposit + val orderDepositId: Short = 3 + val orderDepositPara: Seq[String] = Seq("orderId", "baseDeposit", "targetDeposit") ++ + Seq("owner", "status") + val orderDepositDataType: Array[Byte] = Array(DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte, DataType.Amount.id.toByte) + val orderDepositOpcs: Seq[Array[Byte]] = commonCheckOpcs(3.toByte, 4.toByte) ++ Seq( + cdbvMapValMinus ++ Array(baseTokenBalanceMap.index, 3.toByte, 1.toByte), + cdbvMapValMinus ++ Array(targetTokenBalanceMap.index, 3.toByte, 2.toByte), + cdbvMapValAdd ++ Array(baseTokenLockedMap.index, 0.toByte, 1.toByte), + cdbvMapValAdd ++ Array(targetTokenLockedMap.index, 0.toByte, 2.toByte) + ) + lazy val orderDepositFunc: Array[Byte] = getFunctionBytes(orderDepositId, publicFuncType, nonReturnType, orderDepositDataType, orderDepositOpcs) + val orderDepositTextualBytes: Array[Byte] = textualFunc("orderDeposit", Seq(), orderDepositPara) + + // Order Withdraw + val orderWithdrawId: Short = 4 + val orderWithdrawPara: Seq[String] = Seq("orderId", "baseWithdraw", "targetWithdraw") ++ + Seq("owner", "status") + val orderWithdrawDataType: Array[Byte] = Array(DataType.ShortBytes.id.toByte, DataType.Amount.id.toByte, DataType.Amount.id.toByte) + val orderWithdrawOpcs: Seq[Array[Byte]] = commonCheckOpcs(3.toByte, 4.toByte) ++ Seq( + cdbvMapValMinus ++ Array(baseTokenLockedMap.index, 0.toByte, 1.toByte), + cdbvMapValMinus ++ Array(targetTokenLockedMap.index, 0.toByte, 2.toByte), + cdbvMapValAdd ++ Array(baseTokenBalanceMap.index, 3.toByte, 1.toByte), + cdbvMapValAdd ++ Array(targetTokenBalanceMap.index, 3.toByte, 2.toByte) + ) + lazy val orderWithdrawFunc: Array[Byte] = getFunctionBytes(orderWithdrawId, publicFuncType, nonReturnType, orderWithdrawDataType, orderWithdrawOpcs) + val orderWithdrawTextualBytes: Array[Byte] = textualFunc("orderWithdraw", Seq(), orderWithdrawPara) + + // Order Close + val closeId: Short = 5 + val closePara: Seq[String] = Seq("orderId") ++ + Seq("owner", "status", "baseWithdraw", "targetWithdraw", "amountOne", "valueFalse") + val closeDataType: Array[Byte] = Array(DataType.ShortBytes.id.toByte) + val closeOpcs: Seq[Array[Byte]] = commonCheckOpcs(1.toByte, 2.toByte) ++ Seq( + cdbvrMapGetOrDefault ++ Array(baseTokenLockedMap.index, 0.toByte, 3.toByte), + cdbvrMapGetOrDefault ++ Array(targetTokenLockedMap.index, 0.toByte, 4.toByte), + cdbvMapValMinus ++ Array(baseTokenLockedMap.index, 0.toByte, 3.toByte), + cdbvMapValMinus ++ Array(targetTokenLockedMap.index, 0.toByte, 4.toByte), + cdbvMapValAdd ++ Array(baseTokenBalanceMap.index, 1.toByte, 3.toByte), + cdbvMapValAdd ++ Array(targetTokenBalanceMap.index, 1.toByte, 4.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(1L), DataType.Amount).bytes ++ Array(5.toByte), + cdbvMapValMinus ++ Array(userOrdersMap.index, 1.toByte, 5.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + cdbvMapSet ++ Array(orderStatusMap.index, 0.toByte, 6.toByte) + ) + lazy val closeFunc: Array[Byte] = getFunctionBytes(closeId, publicFuncType, nonReturnType, closeDataType, closeOpcs) + val closeTextualBytes: Array[Byte] = textualFunc("close", Seq(), closePara) + + // Swap Common + val swapCommonPara: Seq[String] = Seq("orderId", "amount", "fee", "price", "deadline") ++ + Seq("caller", "status", "lastBlockTime", "isValidTime", "feeInOrder", "priceInOrder", + "minAmount", "isGreaterThanMin", "maxAmount", "isLessThanMax", + "bigIntType", "unitPrice", "unitBigInt", "amountBigInt", "priceBigInt", + "mul", "amountWithoutFeeBigInt", "amountType", "amountWithoutFee", "swapAmount", + "amountZero", "isValidSwapAmount") + val swapCommonDataType: Array[Byte] = Array(DataType.ShortBytes.id.toByte) ++ + Array.fill[Byte](3)(DataType.Amount.id.toByte) ++ + Array(DataType.Timestamp.id.toByte) + private def swapCommonOpcs(feeIndex: Byte, priceIndex: Byte, minValue: Byte, maxValue: Byte, unitPriceIndex: Byte): Seq[Array[Byte]] = { + Seq( + loadCaller ++ Array(5.toByte), + cdbvrMapGet ++ Array(orderStatusMap.index, 0.toByte, 6.toByte), + assertTrue ++ Array(6.toByte), + loadTimestamp ++ Array(7.toByte), + compareGreaterEqual ++ Array(4.toByte, 7.toByte, 8.toByte), + assertTrue ++ Array(8.toByte), + cdbvrMapGet ++ Array(feeIndex, 0.toByte, 9.toByte), + assertEqual ++ Array(2.toByte, 9.toByte), + cdbvrMapGet ++ Array(priceIndex, 0.toByte, 10.toByte), + assertEqual ++ Array(3.toByte, 10.toByte), + cdbvrMapGet ++ Array(minValue, 0.toByte, 11.toByte), + compareGreaterEqual ++ Array(1.toByte, 11.toByte, 12.toByte), + assertTrue ++ Array(12.toByte), + cdbvrMapGet ++ Array(maxValue, 0.toByte, 13.toByte), + compareLessEqual ++ Array(1.toByte, 13.toByte, 14.toByte), + assertTrue ++ Array(14.toByte), + basicConstantGet ++ DataEntry(Array(DataType.BigInteger.id.toByte), DataType.DataTypeObj).bytes ++ Array(15.toByte), + cdbvrGet ++ Array(unitPriceIndex, 16.toByte), + basicConvert ++ Array(16.toByte, 15.toByte, 17.toByte), + basicConvert ++ Array(1.toByte, 15.toByte, 18.toByte), + basicConvert ++ Array(3.toByte, 15.toByte, 19.toByte), + basicMultiply ++ Array(18.toByte, 19.toByte, 20.toByte), + basicDivide ++ Array(20.toByte, 17.toByte, 21.toByte), + basicConstantGet ++ DataEntry(Array(DataType.Amount.id.toByte), DataType.DataTypeObj).bytes ++ Array(22.toByte), + basicConvert ++ Array(21.toByte, 22.toByte, 23.toByte), + basicMinus ++ Array(23.toByte, 2.toByte, 24.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(0L), DataType.Amount).bytes ++ Array(25.toByte), + compareGreater ++ Array(24.toByte, 25.toByte, 26.toByte), + assertTrue ++ Array(26.toByte)) + } + + // Swap + val swapBaseToTargetId: Short = 6 + val swapBaseToTargetOpcs: Seq[Array[Byte]] = swapCommonOpcs(feeBaseMap.index, priceBaseMap.index, minBaseMap.index, maxBaseMap.index, unitPriceBaseStateVar.index) ++ Seq( + cdbvMapValMinus ++ Array(baseTokenBalanceMap.index, 5.toByte, 1.toByte), + cdbvMapValMinus ++ Array(targetTokenLockedMap.index, 0.toByte, 24.toByte), + cdbvMapValAdd ++ Array(baseTokenLockedMap.index, 0.toByte, 1.toByte), + cdbvMapValAdd ++ Array(targetTokenBalanceMap.index, 5.toByte, 24.toByte) + ) + lazy val swapBaseToTargetFunc: Array[Byte] = getFunctionBytes(swapBaseToTargetId, publicFuncType, nonReturnType, swapCommonDataType, swapBaseToTargetOpcs) + val swapBaseToTargetTextualBytes: Array[Byte] = textualFunc("swapBaseToTarget", Seq(), swapCommonPara) + + val swapTargetToBaseId: Short = 7 + val swapTargetToBaseOpcs: Seq[Array[Byte]] = swapCommonOpcs(feeTargetMap.index, priceTargetMap.index, minTargetMap.index, maxTargetMap.index, unitPriceTargetStateVar.index) ++ Seq( + cdbvMapValMinus ++ Array(targetTokenBalanceMap.index, 5.toByte, 1.toByte), + cdbvMapValMinus ++ Array(baseTokenLockedMap.index, 0.toByte, 24.toByte), + cdbvMapValAdd ++ Array(targetTokenLockedMap.index, 0.toByte, 1.toByte), + cdbvMapValAdd ++ Array(baseTokenBalanceMap.index, 5.toByte, 24.toByte) + ) + lazy val swapTargetToBaseFunc: Array[Byte] = getFunctionBytes(swapTargetToBaseId, publicFuncType, nonReturnType, swapCommonDataType, swapTargetToBaseOpcs) + val swapTargetToBaseTextualBytes: Array[Byte] = textualFunc("swapTargetToBase", Seq(), swapCommonPara) + + // Textual + lazy val triggerTextual: Array[Byte] = Deser.serializeArrays(Seq(initTextualBytes, depositTextualBytes, withdrawTextualBytes)) + lazy val descriptorTextual: Array[Byte] = Deser.serializeArrays(Seq(supersedeTextualBytes, setOrderTextualBytes, updateTextualBytes, + orderDepositTextualBytes, orderWithdrawTextualBytes, closeTextualBytes, swapBaseToTargetTextualBytes, swapTargetToBaseTextualBytes)) + + +} \ No newline at end of file diff --git a/src/main/scala/vsys/blockchain/contract/ContractVSwap.scala b/src/main/scala/vsys/blockchain/contract/ContractVSwap.scala new file mode 100644 index 000000000..a354e4293 --- /dev/null +++ b/src/main/scala/vsys/blockchain/contract/ContractVSwap.scala @@ -0,0 +1,516 @@ +package vsys.blockchain.contract + +import com.google.common.primitives.{Ints, Longs} +import vsys.blockchain.contract.ContractGen._ +import vsys.blockchain.state._ +import vsys.utils.serialization.Deser + +object ContractVSwap { + lazy val contract: Contract = Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + Seq(initTrigger, depositTrigger, withdrawTrigger), // Triggers + Seq(supersedeFunc, setSwapFunc, addLiquidityFunc, removeLiquidityFunc, swapTokenForExactBaseTokenFunc, + swapExactTokenForBaseTokenFunc, swapTokenForExactTargetTokenFunc, swapExactTokenForTargetTokenFunc), // Functions + stateVarSeq, // StateVars + stateMapSeq, // StateMaps + Seq(triggerTextual, descriptorTextual, stateVarTextual, stateMapTextual) // Textual + ).explicitGet() + + // State Var + val stateVarName = List("maker", "tokenAId", "tokenBId", "tokenLiquidityId", "swapStatus", "minimumLiquidity", + "tokenAReserved", "tokenBReserved", "totalSupply", "liquidityTokenLeft") + val makerStateVar: StateVar = StateVar(0.toByte, DataType.Address.id.toByte) + val tokenAIdStateVar: StateVar = StateVar(1.toByte, DataType.TokenId.id.toByte) + val tokenBIdStateVar: StateVar = StateVar(2.toByte, DataType.TokenId.id.toByte) + val tokenLiquidityIdStateVar: StateVar = StateVar(3.toByte, DataType.TokenId.id.toByte) + val swapStatusStateVar: StateVar = StateVar(4.toByte, DataType.Boolean.id.toByte) + val minimumLiquidityStateVar: StateVar = StateVar(5.toByte, DataType.Amount.id.toByte) + val tokenAReservedStateVar: StateVar = StateVar(6.toByte, DataType.Amount.id.toByte) + val tokenBReservedStateVar: StateVar = StateVar(7.toByte, DataType.Amount.id.toByte) + val totalSupplyStateVar: StateVar = StateVar(8.toByte, DataType.Amount.id.toByte) + val liquidityTokenLeftStateVar: StateVar = StateVar(9.toByte, DataType.Amount.id.toByte) + lazy val stateVarSeq = Seq(makerStateVar.arr, tokenAIdStateVar.arr, tokenBIdStateVar.arr, tokenLiquidityIdStateVar.arr, + swapStatusStateVar.arr, minimumLiquidityStateVar.arr, tokenAReservedStateVar.arr, + tokenBReservedStateVar.arr, totalSupplyStateVar.arr, liquidityTokenLeftStateVar.arr) + lazy val stateVarTextual: Array[Byte] = Deser.serializeArrays(stateVarName.map(x => Deser.serilizeString(x))) + + // State Map + val stateMapTokenABalance = List("tokenABalance", "userAddress", "balance") + val stateMapTokenBBalance = List("tokenBBalance", "userAddress", "balance") + val stateMapLiquidityBalance = List("liquidityBalance", "userAddress", "balance") + val tokenABalanceMap: StateMap = StateMap(0.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + val tokenBBalanceMap: StateMap = StateMap(1.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + val liquidityBalanceMap: StateMap = StateMap(2.toByte, DataType.Address.id.toByte, DataType.Amount.id.toByte) + lazy val stateMapSeq = Seq(tokenABalanceMap.arr, tokenBBalanceMap.arr, liquidityBalanceMap.arr) + lazy val stateMapTextual: Array[Byte] = textualStateMap(Seq(stateMapTokenABalance, stateMapTokenBBalance, stateMapLiquidityBalance)) + + // Initialization Trigger + val initId: Short = 0 + val initPara: Seq[String] = Seq("tokenAId", "tokenBId", "liquidityTokenId", "minimumLiquidity") ++ + Seq("signer", "swapStatus") + val initDataType: Array[Byte] = Array(DataType.TokenId.id.toByte, DataType.TokenId.id.toByte, DataType.TokenId.id.toByte, DataType.Amount.id.toByte) + val initTriggerOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(4.toByte), + cdbvSet ++ Array(makerStateVar.index, 4.toByte), + cdbvSet ++ Array(tokenAIdStateVar.index, 0.toByte), + cdbvSet ++ Array(tokenBIdStateVar.index, 1.toByte), + cdbvSet ++ Array(tokenLiquidityIdStateVar.index, 2.toByte), + cdbvSet ++ Array(minimumLiquidityStateVar.index, 3.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + cdbvSet ++ Array(swapStatusStateVar.index, 5.toByte) + ) + lazy val initTrigger: Array[Byte] = getFunctionBytes(initId, onInitTriggerType, nonReturnType, initDataType, initTriggerOpcs) + val initTextualBytes: Array[Byte] = textualFunc("init", Seq(), initPara) + + // Deposit Trigger + val depositId: Short = 1 + val depositPara: Seq[String] = Seq("depositor", "amount", "tokenId") ++ + Seq("tokenAId", "tokenBId", "tokenLiquidityId", "isValidTokenId", + "isTokenA", "valueFalse", "tokenAIfBlock", "isTokenB", "tokenBIfBlock", + "isTokenLiquidity", "tokenLiquidityIfBlock") + val depositDataType: Array[Byte] = Array(DataType.Address.id.toByte, DataType.Amount.id.toByte, DataType.TokenId.id.toByte) + val depositTriggerOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + cdbvrGet ++ Array(tokenAIdStateVar.index, 3.toByte), + cdbvrGet ++ Array(tokenBIdStateVar.index, 4.toByte), + cdbvrGet ++ Array(tokenLiquidityIdStateVar.index, 5.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + compareBytesEqual ++ Array(2.toByte, 3.toByte, 7.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(8.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + assertEqual ++ Array(6.toByte, 8.toByte), + cdbvMapValAdd ++ Array(tokenABalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(9.toByte), + compareBytesEqual ++ Array(2.toByte, 4.toByte, 10.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + assertEqual ++ Array(6.toByte, 8.toByte), + cdbvMapValAdd ++ Array(tokenBBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(11.toByte), + compareBytesEqual ++ Array(2.toByte, 5.toByte, 12.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + assertEqual ++ Array(6.toByte, 8.toByte), + cdbvMapValAdd ++ Array(liquidityBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(13.toByte), + conditionIf ++ Array(7.toByte, 9.toByte), + conditionIf ++ Array(10.toByte, 11.toByte), + conditionIf ++ Array(12.toByte, 13.toByte), + assertTrue ++ Array(6.toByte) + ) + lazy val depositTrigger: Array[Byte] = getFunctionBytes(depositId, onDepositTriggerType, nonReturnType, depositDataType, depositTriggerOpcs) + val depositTextualBytes: Array[Byte] = textualFunc("deposit", Seq(), depositPara) + + // WithDraw Trigger + val withdrawId: Short = 2 + val withdrawPara: Seq[String] = Seq("withdrawer", "amount", "tokenId") ++ + Seq("tokenAId", "tokenBId", "tokenLiquidityId", "isValidTokenId", + "isTokenA", "tokenAIfBlock", "isTokenB", "tokenBIfBlock", + "isTokenLiquidity", "tokenLiquidityIfBlock") + val withdrawDataType: Array[Byte] = Array(DataType.Address.id.toByte, DataType.Amount.id.toByte, DataType.TokenId.id.toByte) + val withdrawTriggerOpcs: Seq[Array[Byte]] = Seq( + assertCaller ++ Array(0.toByte), + cdbvrGet ++ Array(tokenAIdStateVar.index, 3.toByte), + cdbvrGet ++ Array(tokenBIdStateVar.index, 4.toByte), + cdbvrGet ++ Array(tokenLiquidityIdStateVar.index, 5.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + compareBytesEqual ++ Array(2.toByte, 3.toByte, 7.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + cdbvMapValMinus ++ Array(tokenABalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(8.toByte), + compareBytesEqual ++ Array(2.toByte, 4.toByte, 9.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + cdbvMapValMinus ++ Array(tokenBBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(10.toByte), + compareBytesEqual ++ Array(2.toByte, 5.toByte, 11.toByte), + basicConstantGet ++ DataEntry(genFunctionOpcs( + Seq( + cdbvMapValMinus ++ Array(liquidityBalanceMap.index, 0.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(6.toByte), + ) + ), DataType.OpcBlock).bytes ++ Array(12.toByte), + conditionIf ++ Array(7.toByte, 8.toByte), + conditionIf ++ Array(9.toByte, 10.toByte), + conditionIf ++ Array(11.toByte, 12.toByte), + assertTrue ++ Array(6.toByte) + ) + lazy val withdrawTrigger: Array[Byte] = getFunctionBytes(withdrawId, onWithDrawTriggerType, nonReturnType, withdrawDataType, withdrawTriggerOpcs) + val withdrawTextualBytes: Array[Byte] = textualFunc("withdraw", Seq(), withdrawPara) + + // Functions + // Supersede + val supersedeId: Short = 0 + val supersedePara: Seq[String] = Seq("newOwner", + "maker") + val supersedeDataType: Array[Byte] = Array(DataType.Account.id.toByte) + val supersedeOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(makerStateVar.index, 1.toByte), + assertSigner ++ Array(1.toByte), + cdbvSet ++ Array(makerStateVar.index, 0.toByte)) + lazy val supersedeFunc: Array[Byte] = getFunctionBytes(supersedeId, publicFuncType, nonReturnType, supersedeDataType, supersedeOpcs) + val supersedeTextualBytes: Array[Byte] = textualFunc("supersede", Seq(), supersedePara) + + // Set Swap + val setSwapId: Short = 1 + val setSwapPara: Seq[String] = Seq("amountADesired", "amountBDesired") ++ + Seq("maker", "swapStatus", "valueFalse", "amountZero", "isValidAmountADesired", "isValidAmountBDesired", + "bigIntType", "amountADesiredBigInt", "amountBDesiredBigInt", "valueK", "initLiquidityBigInt", + "amountType", "initLiquidity", "minimumLiquidity", "isValidInitLiquidity", "liquidity", + "maxLiquidity", "isValidLiquidity", "liquidityLeft", "valueTrue") + val setSwapDataType: Array[Byte] = Array(DataType.Amount.id.toByte, DataType.Amount.id.toByte) + val setSwapFunctionOpcs: Seq[Array[Byte]] = Seq( + cdbvrGet ++ Array(makerStateVar.index, 2.toByte), + assertCaller ++ Array(2.toByte), + cdbvrGet ++ Array(swapStatusStateVar.index, 3.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(4.toByte), + assertEqual ++ Array(3.toByte, 4.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(0L), DataType.Amount).bytes ++ Array(5.toByte), + compareGreater ++ Array(0.toByte, 5.toByte, 6.toByte), + assertTrue ++ Array(6.toByte), + compareGreater ++ Array(1.toByte, 5.toByte, 7.toByte), + assertTrue ++ Array(7.toByte), + cdbvMapValMinus ++ Array(tokenABalanceMap.index, 2.toByte, 0.toByte), + cdbvMapValMinus ++ Array(tokenBBalanceMap.index, 2.toByte, 1.toByte), + basicConstantGet ++ DataEntry(Array(DataType.BigInteger.id.toByte), DataType.DataTypeObj).bytes ++ Array(8.toByte), + basicConvert ++ Array(0.toByte, 8.toByte, 9.toByte), + basicConvert ++ Array(1.toByte, 8.toByte, 10.toByte), + basicMultiply ++ Array(9.toByte, 10.toByte, 11.toByte), + basicSqrtBigint ++ Array(11.toByte, 12.toByte), + basicConstantGet ++ DataEntry(Array(DataType.Amount.id.toByte), DataType.DataTypeObj).bytes ++ Array(13.toByte), + basicConvert ++ Array(12.toByte, 13.toByte, 14.toByte), + cdbvrGet ++ Array(minimumLiquidityStateVar.index, 15.toByte), + compareGreater ++ Array(14.toByte, 15.toByte, 16.toByte), + assertTrue ++ Array(16.toByte), + basicMinus ++ Array(14.toByte, 15.toByte, 17.toByte), + cdbvrMapGetOrDefault ++ Array(liquidityBalanceMap.index, 2.toByte, 18.toByte), + compareGreater ++ Array(18.toByte, 14.toByte, 19.toByte), + assertTrue ++ Array(19.toByte), + cdbvMapValMinus ++ Array(liquidityBalanceMap.index, 2.toByte, 18.toByte), + cdbvMapValAdd ++ Array(liquidityBalanceMap.index, 2.toByte, 17.toByte), + basicMinus ++ Array(18.toByte, 14.toByte, 20.toByte), + cdbvStateValAdd ++ Array(liquidityTokenLeftStateVar.index, 20.toByte), + cdbvStateValAdd ++ Array(totalSupplyStateVar.index, 14.toByte), + cdbvStateValAdd ++ Array(tokenAReservedStateVar.index, 0.toByte), + cdbvStateValAdd ++ Array(tokenBReservedStateVar.index, 1.toByte), + basicConstantGet ++ DataEntry(Array(1.toByte), DataType.Boolean).bytes ++ Array(21.toByte), + cdbvSet ++ Array(swapStatusStateVar.index, 21.toByte) + ) + lazy val setSwapFunc: Array[Byte] = getFunctionBytes(setSwapId, publicFuncType, nonReturnType, setSwapDataType, setSwapFunctionOpcs) + val setSwapTextualBytes: Array[Byte] = textualFunc("setSwap", Seq(), setSwapPara) + + // Add Liquidity + val addLiquidityId: Short = 2 + val addLiquidityPara: Seq[String] = Seq("amountADesired", "amountBDesired", "amountAMin", "amountBMin", "deadline") ++ + Seq("caller", "swapStatus", "currentTime", "isValidTime", "amountZero", + "reserveA", "isValidReserveA", "reserveB", "isValidReserveB", + "bigIntType", "reserveABigInt", "reserveBBigInt", "amountADesiredBigInt", "amountBDesiredBigInt", + "mulA", "amountBGet", "mulB", "amountAGet", "amountType", "amountABigInt", "amountA", "isValidAmountA", + "amountBBigInt", "amountB", "isValidAmountB", "totalSupply", "totalSupplyBigInt", + "mulSupplyA", "mulSupplyB", "liquidityABigInt", "liquidityBBigInt", "liquidityBigInt", + "liquidity", "liquidityLeft", "isValidLiquidity") + val addLiquidityDataType: Array[Byte] = Array(DataType.Amount.id.toByte, DataType.Amount.id.toByte, + DataType.Amount.id.toByte, DataType.Amount.id.toByte, DataType.Timestamp.id.toByte) + val addLiquidityFunctionOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(5.toByte), + cdbvrGet ++ Array(swapStatusStateVar.index, 6.toByte), + assertTrue ++ Array(6.toByte), + loadTimestamp ++ Array(7.toByte), + compareGreaterEqual ++ Array(4.toByte, 7.toByte, 8.toByte), + assertTrue ++ Array(8.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(0L), DataType.Amount).bytes ++ Array(9.toByte), + cdbvrGetOrDefault ++ Array(tokenAReservedStateVar.index, 10.toByte), + compareGreater ++ Array(10.toByte, 9.toByte, 11.toByte), + assertTrue ++ Array(11.toByte), + cdbvrGetOrDefault ++ Array(tokenBReservedStateVar.index, 12.toByte), + compareGreater ++ Array(12.toByte, 9.toByte, 13.toByte), + assertTrue ++ Array(13.toByte), + basicConstantGet ++ DataEntry(Array(DataType.BigInteger.id.toByte), DataType.DataTypeObj).bytes ++ Array(14.toByte), + basicConvert ++ Array(10.toByte, 14.toByte, 15.toByte), + basicConvert ++ Array(12.toByte, 14.toByte, 16.toByte), + basicConvert ++ Array(0.toByte, 14.toByte, 17.toByte), + basicConvert ++ Array(1.toByte, 14.toByte, 18.toByte), + basicMultiply ++ Array(17.toByte, 16.toByte, 19.toByte), + basicDivide ++ Array(19.toByte, 15.toByte, 20.toByte), + basicMultiply ++ Array(18.toByte, 15.toByte, 21.toByte), + basicDivide ++ Array(21.toByte, 16.toByte, 22.toByte), + basicConstantGet ++ DataEntry(Array(DataType.Amount.id.toByte), DataType.DataTypeObj).bytes ++ Array(23.toByte), + basicMin ++ Array(22.toByte, 17.toByte, 24.toByte), + basicConvert ++ Array(24.toByte, 23.toByte, 25.toByte), + compareGreaterEqual ++ Array(25.toByte, 2.toByte, 26.toByte), + assertTrue ++ Array(26.toByte), + basicMin ++ Array(20.toByte, 18.toByte, 27.toByte), + basicConvert ++ Array(27.toByte, 23.toByte, 28.toByte), + compareGreaterEqual ++ Array(28.toByte, 3.toByte, 29.toByte), + assertTrue ++ Array(29.toByte), + cdbvMapValMinus ++ Array(tokenABalanceMap.index, 5.toByte, 25.toByte), + cdbvMapValMinus ++ Array(tokenBBalanceMap.index, 5.toByte, 28.toByte), + cdbvrGetOrDefault ++ Array(totalSupplyStateVar.index, 30.toByte), + basicConvert ++ Array(30.toByte, 14.toByte, 31.toByte), + basicMultiply ++ Array(24.toByte, 31.toByte, 32.toByte), + basicMultiply ++ Array(27.toByte, 31.toByte, 33.toByte), + basicDivide ++ Array(32.toByte, 15.toByte, 34.toByte), + basicDivide ++ Array(33.toByte, 16.toByte, 35.toByte), + basicMin ++ Array(34.toByte, 35.toByte, 36.toByte), + basicConvert ++ Array(36.toByte, 23.toByte, 37.toByte), + cdbvrGetOrDefault ++ Array(liquidityTokenLeftStateVar.index, 38.toByte), + compareGreaterEqual ++ Array(38.toByte, 37.toByte, 39.toByte), + assertTrue ++ Array(39.toByte), + cdbvStateValMinus ++ Array(liquidityTokenLeftStateVar.index, 37.toByte), + cdbvStateValAdd ++ Array(totalSupplyStateVar.index, 37.toByte), + cdbvStateValAdd ++ Array(tokenAReservedStateVar.index, 25.toByte), + cdbvStateValAdd ++ Array(tokenBReservedStateVar.index, 28.toByte), + cdbvMapValAdd ++ Array(liquidityBalanceMap.index, 5.toByte, 37.toByte) + ) + lazy val addLiquidityFunc: Array[Byte] = getFunctionBytes(addLiquidityId, publicFuncType, nonReturnType, + addLiquidityDataType, addLiquidityFunctionOpcs) + val addLiquidityTextualBytes: Array[Byte] = textualFunc("addLiquidity", Seq(), addLiquidityPara) + + // Remove Liquidity + val removeLiquidityId: Short = 3 + val removeLiquidityPara: Seq[String] = Seq("liquidity", "amountAMin", "amountBMin", "deadline") ++ + Seq("caller", "swapStatus", "currentTime", "isValidTime", "reserveA", "reserveB", + "bigIntType", "liquidityBigInt", "reserveABigInt", "reserveBBigInt", + "totalSupply", "amountZero", "isValidTotalSupply", "totalSupplyBigInt", + "mulA", "amountABigInt", "mulB", "amountBBigInt", "amountType", "amountA", "amountB", + "isValidAmountA", "isValidAmountB") + val removeLiquidityDataType: Array[Byte] = Array(DataType.Amount.id.toByte, DataType.Amount.id.toByte, + DataType.Amount.id.toByte, DataType.Timestamp.id.toByte) + val removeLiquidityFunctionOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(4.toByte), + cdbvrGet ++ Array(swapStatusStateVar.index, 5.toByte), + assertTrue ++ Array(5.toByte), + loadTimestamp ++ Array(6.toByte), + compareGreaterEqual ++ Array(3.toByte, 6.toByte, 7.toByte), + assertTrue ++ Array(7.toByte), + cdbvMapValMinus ++ Array(liquidityBalanceMap.index, 4.toByte, 0.toByte), + cdbvrGetOrDefault ++ Array(tokenAReservedStateVar.index, 8.toByte), + cdbvrGetOrDefault ++ Array(tokenBReservedStateVar.index, 9.toByte), + basicConstantGet ++ DataEntry(Array(DataType.BigInteger.id.toByte), DataType.DataTypeObj).bytes ++ Array(10.toByte), + basicConvert ++ Array(0.toByte, 10.toByte, 11.toByte), + basicConvert ++ Array(8.toByte, 10.toByte, 12.toByte), + basicConvert ++ Array(9.toByte, 10.toByte, 13.toByte), + cdbvrGetOrDefault ++ Array(totalSupplyStateVar.index, 14.toByte), + basicConstantGet ++ DataEntry(Longs.toByteArray(0L), DataType.Amount).bytes ++ Array(15.toByte), + compareGreater ++ Array(14.toByte, 15.toByte, 16.toByte), + assertTrue ++ Array(16.toByte), + basicConvert ++ Array(14.toByte, 10.toByte, 17.toByte), + basicMultiply ++ Array(11.toByte, 12.toByte, 18.toByte), + basicDivide ++ Array(18.toByte, 17.toByte, 19.toByte), + basicMultiply ++ Array(11.toByte, 13.toByte, 20.toByte), + basicDivide ++ Array(20.toByte, 17.toByte, 21.toByte), + basicConstantGet ++ DataEntry(Array(DataType.Amount.id.toByte), DataType.DataTypeObj).bytes ++ Array(22.toByte), + basicConvert ++ Array(19.toByte, 22.toByte, 23.toByte), + basicConvert ++ Array(21.toByte, 22.toByte, 24.toByte), + compareGreaterEqual ++ Array(23.toByte, 1.toByte, 25.toByte), + assertTrue ++ Array(25.toByte), + compareGreaterEqual ++ Array(24.toByte, 2.toByte, 26.toByte), + assertTrue ++ Array(26.toByte), + cdbvStateValMinus ++ Array(totalSupplyStateVar.index, 0.toByte), + cdbvStateValMinus ++ Array(tokenAReservedStateVar.index, 23.toByte), + cdbvStateValMinus ++ Array(tokenBReservedStateVar.index, 24.toByte), + cdbvMapValAdd ++ Array(tokenABalanceMap.index, 4.toByte, 23.toByte), + cdbvMapValAdd ++ Array(tokenBBalanceMap.index, 4.toByte, 24.toByte), + cdbvStateValAdd ++ Array(liquidityTokenLeftStateVar.index, 0.toByte) + ) + lazy val removeLiquidityFunc: Array[Byte] = getFunctionBytes(removeLiquidityId, publicFuncType, nonReturnType, + removeLiquidityDataType, removeLiquidityFunctionOpcs) + val removeLiquidityTextualBytes: Array[Byte] = textualFunc("removeLiquidity", Seq(), removeLiquidityPara) + + // Common Swap Code + val commonSwapPara: Seq[String] = Seq("caller", "swapStatus", "currentTime", "isValidTime") + val swapDataType: Array[Byte] = Array(DataType.Amount.id.toByte, DataType.Amount.id.toByte, DataType.Timestamp.id.toByte) + val commonSwapFunctionOpcs: Seq[Array[Byte]] = Seq( + loadCaller ++ Array(3.toByte), + cdbvrGet ++ Array(swapStatusStateVar.index, 4.toByte), + assertTrue ++ Array(4.toByte), + loadTimestamp ++ Array(5.toByte), + compareGreaterEqual ++ Array(2.toByte, 5.toByte, 6.toByte), + assertTrue ++ Array(6.toByte), + ) + + private def commonSwapLoaded(checkIndex: Byte): Seq[Array[Byte]] = { + Seq( + basicConstantGet ++ DataEntry(Longs.toByteArray(0L), DataType.Amount).bytes ++ Array(7.toByte), + compareGreater ++ Array(checkIndex, 7.toByte, 8.toByte), + assertTrue ++ Array(8.toByte), + cdbvrGetOrDefault ++ Array(tokenAReservedStateVar.index, 9.toByte), + cdbvrGetOrDefault ++ Array(tokenBReservedStateVar.index, 10.toByte), + basicConstantGet ++ DataEntry(Array(DataType.BigInteger.id.toByte), DataType.DataTypeObj).bytes ++ Array(11.toByte), + basicConvert ++ Array(checkIndex, 11.toByte, 12.toByte), + basicConvert ++ Array(9.toByte, 11.toByte, 13.toByte), + basicConvert ++ Array(10.toByte, 11.toByte, 14.toByte), + ) + } + + private def swapKValueCheck(updatedReserveA: Byte, updatedReserveB: Byte, value1000: Byte, + reservedABigInt: Byte, reservedBBigInt: Byte, updateK: Byte): Seq[Array[Byte]] = { + Seq( + basicMultiply ++ Array(updatedReserveA, updatedReserveB, updateK), + basicMultiply ++ Array(reservedABigInt, reservedBBigInt, (updateK + 1).toByte), + basicMultiply ++ Array(value1000, value1000, (updateK + 2).toByte), + basicMultiply ++ Array((updateK + 1).toByte, (updateK + 2).toByte, (updateK + 3).toByte), + compareGreaterEqual ++ Array(updateK, (updateK + 3).toByte, (updateK + 4).toByte), + assertTrue ++ Array((updateK + 4).toByte), + ) + } + + val swapTokenForExactBaseTokenId: Short = 4 + val swapTokenForExactBaseTokenPara: Seq[String] = Seq("amountOut", "amountInMax", "deadline") ++ + commonSwapPara ++ + Seq("amountZero", "isValidAmountOut", "reserveA", "reserveB", + "bigIntType", "amountOutBigInt", "reserveABigInt", "reserveBBigInt", + "value1000", "mulValue", "numerator", "value997", "subValue", "denominator", + "valueOne", "amountInBigIntPre", "amountInBigInt", "amountType", + "amountIn", "isValidAmountIn", "updatedReserveA", "amountInWithOutFee", + "updatedReserveB", "updatedK", "KValue", "valueSquare", "oldK", "isValidUpdatedK") + val swapTokenForExactBaseTokenFunctionOpcs: Seq[Array[Byte]] = commonSwapFunctionOpcs ++ commonSwapLoaded(0.toByte) ++ Seq( + basicConstantGet ++ DataEntry.create(BigInt(1000).toByteArray, DataType.BigInteger).right.get.bytes ++ Array(15.toByte), + basicMultiply ++ Array(14.toByte, 15.toByte, 16.toByte), + basicMultiply ++ Array(16.toByte, 12.toByte, 17.toByte), + basicConstantGet ++ DataEntry.create(BigInt(997).toByteArray, DataType.BigInteger).right.get.bytes ++ Array(18.toByte), + basicMinus ++ Array(13.toByte, 12.toByte, 19.toByte), + basicMultiply ++ Array(19.toByte, 18.toByte, 20.toByte), + basicConstantGet ++ DataEntry.create(BigInt(1).toByteArray, DataType.BigInteger).right.get.bytes ++ Array(21.toByte), + basicDivide ++ Array(17.toByte, 20.toByte, 22.toByte), + basicAdd ++ Array(22.toByte, 21.toByte, 23.toByte), + basicConstantGet ++ DataEntry(Array(DataType.Amount.id.toByte), DataType.DataTypeObj).bytes ++ Array(24.toByte), + basicConvert ++ Array(23.toByte, 24.toByte, 25.toByte), + compareGreaterEqual ++ Array(1.toByte, 25.toByte, 26.toByte), + assertTrue ++ Array(26.toByte), + basicMultiply ++ Array(19.toByte, 15.toByte, 27.toByte), + basicMultiply ++ Array(23.toByte, 18.toByte, 28.toByte), + basicAdd ++ Array(16.toByte, 28.toByte, 29.toByte)) ++ + swapKValueCheck(27.toByte, 29.toByte, 15.toByte, 13.toByte, 14.toByte, 30.toByte) ++ Seq( + // minus may put directly after load reserved token value A and B + cdbvStateValMinus ++ Array(tokenAReservedStateVar.index, 0.toByte), + cdbvMapValMinus ++ Array(tokenBBalanceMap.index, 3.toByte, 25.toByte), + cdbvStateValAdd ++ Array(tokenBReservedStateVar.index, 25.toByte), + cdbvMapValAdd ++ Array(tokenABalanceMap.index, 3.toByte, 0.toByte) + ) + lazy val swapTokenForExactBaseTokenFunc: Array[Byte] = getFunctionBytes(swapTokenForExactBaseTokenId, publicFuncType, nonReturnType, + swapDataType, swapTokenForExactBaseTokenFunctionOpcs) + val swapTokenForExactBaseTokenTextualBytes: Array[Byte] = textualFunc("swapTokenForExactBaseToken", Seq(), swapTokenForExactBaseTokenPara) + + val swapExactTokenForBaseTokenId: Short = 5 + val swapExactTokenForBaseTokenPara: Seq[String] = Seq("amountOutMin", "amountIn", "deadline") ++ + commonSwapPara ++ + Seq("amountZero", "isValidAmountIn", "reserveA", "reserveB", + "bigIntType", "amountInBigInt", "reserveABigInt", "reserveBBigInt", + "value997", "amountInWithOutFee", "value1000", "mulValue", + "numerator", "denominator", "amountOutBigInt", "amountType", "amountOut", + "isValidAmountOut", "subValue", "updatedReserveA", "updatedReserveB", + "updatedK", "KValue", "valueSquare","oldK", "isValidUpdatedK") + val swapExactTokenForBaseTokenFunctionOpcs: Seq[Array[Byte]] = commonSwapFunctionOpcs ++ commonSwapLoaded(1.toByte) ++ Seq( + basicConstantGet ++ DataEntry.create(BigInt(997).toByteArray, DataType.BigInteger).right.get.bytes ++ Array(15.toByte), + basicMultiply ++ Array(12.toByte, 15.toByte, 16.toByte), + basicConstantGet ++ DataEntry.create(BigInt(1000).toByteArray, DataType.BigInteger).right.get.bytes ++ Array(17.toByte), + basicMultiply ++ Array(14.toByte, 17.toByte, 18.toByte), + basicMultiply ++ Array(13.toByte, 16.toByte, 19.toByte), + basicAdd ++ Array(18.toByte, 16.toByte, 20.toByte), + basicDivide ++ Array(19.toByte, 20.toByte, 21.toByte), + basicConstantGet ++ DataEntry(Array(DataType.Amount.id.toByte), DataType.DataTypeObj).bytes ++ Array(22.toByte), + basicConvert ++ Array(21.toByte, 22.toByte, 23.toByte), + compareGreaterEqual ++ Array(23.toByte, 0.toByte, 24.toByte), + assertTrue ++ Array(24.toByte), + basicMinus ++ Array(13.toByte, 21.toByte, 25.toByte), + basicMultiply ++ Array(25.toByte, 17.toByte, 26.toByte), + basicAdd ++ Array(18.toByte, 16.toByte, 27.toByte)) ++ + swapKValueCheck(26.toByte, 27.toByte, 17.toByte, 13.toByte, 14.toByte, 28.toByte) ++ Seq( + cdbvStateValMinus ++ Array(tokenAReservedStateVar.index, 23.toByte), + cdbvMapValMinus ++ Array(tokenBBalanceMap.index, 3.toByte, 1.toByte), + cdbvStateValAdd ++ Array(tokenBReservedStateVar.index, 1.toByte), + cdbvMapValAdd ++ Array(tokenABalanceMap.index, 3.toByte, 23.toByte) + ) + lazy val swapExactTokenForBaseTokenFunc: Array[Byte] = getFunctionBytes(swapExactTokenForBaseTokenId, publicFuncType, nonReturnType, + swapDataType, swapExactTokenForBaseTokenFunctionOpcs) + val swapExactTokenForBaseTokenTextualBytes: Array[Byte] = textualFunc("swapExactTokenForBaseToken", Seq(), swapExactTokenForBaseTokenPara) + + val swapTokenForExactTargetTokenId: Short = 6 + val swapTokenForExactTargetTokenPara: Seq[String] = Seq("amountOut", "amountInMax", "deadline") ++ + commonSwapPara ++ + Seq("amountZero", "isValidAmountOut", "reserveA", "reserveB", + "bigIntType", "amountOutBigInt", "reserveABigInt", "reserveBBigInt", + "value1000", "mulValue", "numerator", "value997", "subValue", "denominator", + "valueOne", "amountInBigIntPre", "amountInBigInt", "amountType", + "amountIn", "isValidAmountIn", "updatedReserveB", "amountInWithOutFee", + "updatedReserveA", "updatedK", "KValue", "valueSquare", "oldK", "isValidUpdatedK") + val swapTokenForExactTargetTokenFunctionOpcs: Seq[Array[Byte]] = commonSwapFunctionOpcs ++ commonSwapLoaded(0.toByte) ++ Seq( + basicConstantGet ++ DataEntry.create(BigInt(1000).toByteArray, DataType.BigInteger).right.get.bytes ++ Array(15.toByte), + basicMultiply ++ Array(13.toByte, 15.toByte, 16.toByte), + basicMultiply ++ Array(16.toByte, 12.toByte, 17.toByte), + basicConstantGet ++ DataEntry.create(BigInt(997).toByteArray, DataType.BigInteger).right.get.bytes ++ Array(18.toByte), + basicMinus ++ Array(14.toByte, 12.toByte, 19.toByte), + basicMultiply ++ Array(19.toByte, 18.toByte, 20.toByte), + basicConstantGet ++ DataEntry.create(BigInt(1).toByteArray, DataType.BigInteger).right.get.bytes ++ Array(21.toByte), + basicDivide ++ Array(17.toByte, 20.toByte, 22.toByte), + basicAdd ++ Array(22.toByte, 21.toByte, 23.toByte), + basicConstantGet ++ DataEntry(Array(DataType.Amount.id.toByte), DataType.DataTypeObj).bytes ++ Array(24.toByte), + basicConvert ++ Array(23.toByte, 24.toByte, 25.toByte), + compareGreaterEqual ++ Array(1.toByte, 25.toByte, 26.toByte), + assertTrue ++ Array(26.toByte), + basicMultiply ++ Array(19.toByte, 15.toByte, 27.toByte), + basicMultiply ++ Array(23.toByte, 18.toByte, 28.toByte), + basicAdd ++ Array(16.toByte, 28.toByte, 29.toByte)) ++ + swapKValueCheck(29.toByte, 27.toByte, 15.toByte, 13.toByte, 14.toByte, 30.toByte) ++ Seq( + // minus may put directly after load reserved token value A and B + cdbvStateValMinus ++ Array(tokenBReservedStateVar.index, 0.toByte), + cdbvMapValMinus ++ Array(tokenABalanceMap.index, 3.toByte, 25.toByte), + cdbvStateValAdd ++ Array(tokenAReservedStateVar.index, 25.toByte), + cdbvMapValAdd ++ Array(tokenBBalanceMap.index, 3.toByte, 0.toByte) + ) + lazy val swapTokenForExactTargetTokenFunc: Array[Byte] = getFunctionBytes(swapTokenForExactTargetTokenId, publicFuncType, nonReturnType, + swapDataType, swapTokenForExactTargetTokenFunctionOpcs) + val swapTokenForExactTargetTokenTextualBytes: Array[Byte] = textualFunc("swapTokenForExactTargetToken", Seq(), swapTokenForExactTargetTokenPara) + + val swapExactTokenForTargetTokenId: Short = 7 + val swapExactTokenForTargetTokenPara: Seq[String] = Seq("amountOutMin", "amountIn", "deadline") ++ + commonSwapPara ++ + Seq("amountZero", "isValidAmountIn", "reserveA", "reserveB", + "bigIntType", "amountInBigInt", "reserveABigInt", "reserveBBigInt", + "value997", "amountInWithOutFee", "value1000", "mulValue", + "numerator", "denominator", "amountOutBigInt", "amountType", "amountOut", + "isValidAmountOut", "subValue", "updatedReserveB", "updatedReserveA", + "updatedK", "KValue", "valueSquare","oldK", "isValidUpdatedK") + val swapExactTokenForTargetTokenFunctionOpcs: Seq[Array[Byte]] = commonSwapFunctionOpcs ++ commonSwapLoaded(1.toByte) ++ Seq( + basicConstantGet ++ DataEntry.create(BigInt(997).toByteArray, DataType.BigInteger).right.get.bytes ++ Array(15.toByte), + basicMultiply ++ Array(12.toByte, 15.toByte, 16.toByte), + basicConstantGet ++ DataEntry.create(BigInt(1000).toByteArray, DataType.BigInteger).right.get.bytes ++ Array(17.toByte), + basicMultiply ++ Array(13.toByte, 17.toByte, 18.toByte), + basicMultiply ++ Array(14.toByte, 16.toByte, 19.toByte), + basicAdd ++ Array(18.toByte, 16.toByte, 20.toByte), + basicDivide ++ Array(19.toByte, 20.toByte, 21.toByte), + basicConstantGet ++ DataEntry(Array(DataType.Amount.id.toByte), DataType.DataTypeObj).bytes ++ Array(22.toByte), + basicConvert ++ Array(21.toByte, 22.toByte, 23.toByte), + compareGreaterEqual ++ Array(23.toByte, 0.toByte, 24.toByte), + assertTrue ++ Array(24.toByte), + basicMinus ++ Array(14.toByte, 21.toByte, 25.toByte), + basicMultiply ++ Array(25.toByte, 17.toByte, 26.toByte), + basicAdd ++ Array(18.toByte, 16.toByte, 27.toByte)) ++ + swapKValueCheck(27.toByte, 26.toByte, 17.toByte, 13.toByte, 14.toByte, 28.toByte) ++ Seq( + cdbvStateValMinus ++ Array(tokenBReservedStateVar.index, 23.toByte), + cdbvMapValMinus ++ Array(tokenABalanceMap.index, 3.toByte, 1.toByte), + cdbvStateValAdd ++ Array(tokenAReservedStateVar.index, 1.toByte), + cdbvMapValAdd ++ Array(tokenBBalanceMap.index, 3.toByte, 23.toByte) + ) + lazy val swapExactTokenForTargetTokenFunc: Array[Byte] = getFunctionBytes(swapExactTokenForTargetTokenId, publicFuncType, nonReturnType, + swapDataType, swapExactTokenForTargetTokenFunctionOpcs) + val swapExactTokenForTargetTokenTextualBytes: Array[Byte] = textualFunc("swapExactTokenForTargetToken", Seq(), swapExactTokenForTargetTokenPara) + + // Textual + lazy val triggerTextual: Array[Byte] = Deser.serializeArrays(Seq(initTextualBytes, depositTextualBytes, withdrawTextualBytes)) + lazy val descriptorTextual: Array[Byte] = Deser.serializeArrays(Seq(supersedeTextualBytes, setSwapTextualBytes, addLiquidityTextualBytes, removeLiquidityTextualBytes, + swapTokenForExactBaseTokenTextualBytes, swapExactTokenForBaseTokenTextualBytes, swapTokenForExactTargetTokenTextualBytes, swapExactTokenForTargetTokenTextualBytes)) + +} \ No newline at end of file diff --git a/src/main/scala/vsys/blockchain/contract/DataEntry.scala b/src/main/scala/vsys/blockchain/contract/DataEntry.scala index bcc9f5ecb..aacd30651 100644 --- a/src/main/scala/vsys/blockchain/contract/DataEntry.scala +++ b/src/main/scala/vsys/blockchain/contract/DataEntry.scala @@ -1,43 +1,25 @@ package vsys.blockchain.contract -import com.google.common.primitives.{Bytes, Ints, Longs, Shorts} -import play.api.libs.json.{JsObject, JsValue, Json} +import com.google.common.primitives.{Shorts, Bytes, Longs, Ints} +import play.api.libs.json.{JsObject, Json} import scorex.crypto.encode.Base58 -import vsys.account.{Address, AddressScheme, ContractAccount, PublicKeyAccount} -import vsys.account.ContractAccount.ChecksumLength -import vsys.blockchain.state.ByteStr -import vsys.blockchain.transaction.TransactionParser.{AmountLength, KeyLength, TimestampLength} import vsys.blockchain.transaction.ValidationError import vsys.blockchain.transaction.ValidationError.InvalidDataEntry -import vsys.utils.crypto.hash.SecureCryptographicHash._ +import vsys.blockchain.contract.DataType._ +import scala.language.implicitConversions import scala.util.Success case class DataEntry(data: Array[Byte], - dataType: DataType.Value) { + dataType: DataType.DataTypeVal[_]) { lazy val bytes: Array[Byte] = Array(dataType.id.asInstanceOf[Byte]) ++ data lazy val json: JsObject = Json.obj( - "data" -> toJson(data, dataType), + "data" -> dataType.jsonifierB(data), "type" -> dataType ) - private def toJson(d: Array[Byte], t: DataType.Value): JsValue = { - t match { - case DataType.PublicKey => Json.toJson(PublicKeyAccount(d).address) - case DataType.Address => Json.toJson(Address.fromBytes(d).right.get.address) - case DataType.Amount => Json.toJson(Longs.fromByteArray(d)) - case DataType.Int32 => Json.toJson(Ints.fromByteArray(d)) - case DataType.ShortText => Json.toJson(Base58.encode(d)) - case DataType.ContractAccount => Json.toJson(ContractAccount.fromBytes(d).right.get.address) - case DataType.TokenId => Json.toJson(ByteStr(d).base58) - case DataType.Timestamp => Json.toJson(Longs.fromByteArray(d)) - case DataType.Boolean => Json.toJson(if (d(0) == 1.toByte) "True" else "False") - case DataType.ShortBytes => Json.toJson(Base58.encode(d)) - } - } - override def equals(obj: Any): Boolean = obj match { case a: DataEntry => bytes sameElements a.bytes case _ => false @@ -49,28 +31,19 @@ case class DataEntry(data: Array[Byte], object DataEntry { - private def scheme = AddressScheme.current + def create(data: Array[Byte], dataType: DataType.DataTypeVal[_]): Either[ValidationError, DataEntry] = + if (dataType.lenFixed || dataType == DataType.Account) doCreate(data, dataType) + else doCreate(DataType.arrayShortLengthToByteArray(data) ++ data, dataType) - val maxShortTextLength = 140 - val maxShortBytesLength = 255 - - def create(data: Array[Byte], dataType: DataType.Value): Either[ValidationError, DataEntry] = { - dataType match { - case DataType.ShortText if checkDataType(Shorts.toByteArray(data.length.toShort) ++ data, dataType) => Right(DataEntry(Shorts.toByteArray(data.length.toShort) ++ data, dataType)) - case DataType.ShortBytes if checkDataType(Shorts.toByteArray(data.length.toShort) ++ data, dataType) => Right(DataEntry(Shorts.toByteArray(data.length.toShort) ++ data, dataType)) - case _ if checkDataType(data, dataType) => Right(DataEntry(data, dataType)) - case _ => Left(InvalidDataEntry) - } - } + // length unfixed data array should start with 2 bytes which indicates length of content of data + private def doCreate(data: Array[Byte], dataType: DataType.DataTypeVal[_]): Either[ValidationError, DataEntry] = + if (dataType.validator(data)) Right(DataEntry(data, dataType)) else Left(InvalidDataEntry) def fromBytes(bytes: Array[Byte]): Either[ValidationError, DataEntry] = { - if (bytes.length == 0 || DataType.fromByte(bytes(0)).isEmpty) - Left(InvalidDataEntry) - else - DataType.fromByte(bytes(0)) match { - case Some(DataType.ShortText) => create(bytes.slice(3, bytes.length), DataType(bytes(0))) - case Some(DataType.ShortBytes) => create(bytes.slice(3, bytes.length), DataType(bytes(0))) - case _ => create(bytes.tail, DataType(bytes(0))) + + if (bytes.length == 0) Left(InvalidDataEntry) else DataType.fromByte(bytes(0)) match { + case Some(dt) => doCreate(bytes.tail, dt) + case _ => Left(InvalidDataEntry) } } @@ -81,29 +54,20 @@ object DataEntry { } } - def parseArraySize(bytes: Array[Byte], position: Int): Either[ValidationError, (DataEntry, Int)] = { - DataType.fromByte(bytes(position)) match { - case Some(DataType.PublicKey) if checkDataType(bytes.slice(position + 1, position + 1 + KeyLength), DataType.PublicKey) => - Right((DataEntry(bytes.slice(position + 1, position + 1 + KeyLength), DataType.PublicKey), position + 1 + KeyLength)) - case Some(DataType.Address) if checkDataType(bytes.slice(position + 1, position + 1 + Address.AddressLength), DataType.Address) => - Right((DataEntry(bytes.slice(position + 1, position + 1 + Address.AddressLength), DataType.Address), position + 1 + Address.AddressLength)) - case Some(DataType.Amount) if checkDataType(bytes.slice(position + 1, position + 1 + AmountLength), DataType.Amount) => - Right((DataEntry(bytes.slice(position + 1, position + 1 + AmountLength), DataType.Amount), position + 1 + AmountLength)) - case Some(DataType.Int32) if checkDataType(bytes.slice(position + 1, position + 1 + 4), DataType.Int32) => - Right((DataEntry(bytes.slice(position + 1, position + 1 + 4), DataType.Int32), position + 1 + 4)) - case Some(DataType.ShortText) if checkDataType(bytes.slice(position + 1, position + 3 + Shorts.fromByteArray(bytes.slice(position + 1, position + 3))), DataType.ShortText) => - Right((DataEntry(bytes.slice(position + 1, position + 3 + Shorts.fromByteArray(bytes.slice(position + 1, position + 3))), DataType.ShortText), position + 3 + Shorts.fromByteArray(bytes.slice(position + 1, position + 3)))) - case Some(DataType.ContractAccount) if checkDataType(bytes.slice(position + 1, position + 1 + ContractAccount.AddressLength), DataType.ContractAccount) => - Right((DataEntry(bytes.slice(position + 1, position + 1 + ContractAccount.AddressLength), DataType.ContractAccount), position + 1 + ContractAccount.AddressLength)) - case Some(DataType.TokenId) if checkDataType(bytes.slice(position + 1, position + 1 + ContractAccount.TokenAddressLength), DataType.TokenId) => - Right((DataEntry(bytes.slice(position + 1, position + 1 + ContractAccount.TokenAddressLength), DataType.TokenId), position + 1 + ContractAccount.TokenAddressLength)) - case Some(DataType.Timestamp) if checkDataType(bytes.slice(position + 1, position + 1 + TimestampLength), DataType.Timestamp) => - Right((DataEntry(bytes.slice(position + 1, position + 1 + TimestampLength), DataType.Timestamp), position + 1 + TimestampLength)) - case Some(DataType.Boolean) if checkDataType(bytes.slice(position + 1, position + 1 + 1), DataType.Boolean) => - Right((DataEntry(bytes.slice(position + 1, position + 1 + 1), DataType.Boolean), position + 1 + 1)) - case Some(DataType.ShortBytes) if checkDataType(bytes.slice(position + 1, position + 3 + Shorts.fromByteArray(bytes.slice(position + 1, position + 3))), DataType.ShortBytes) => - Right((DataEntry(bytes.slice(position + 1, position + 3 + Shorts.fromByteArray(bytes.slice(position + 1, position + 3))), DataType.ShortBytes), position + 3 + Shorts.fromByteArray(bytes.slice(position + 1, position + 3)))) - case _ => Left(InvalidDataEntry) + private def parseArray(bytes: Array[Byte]): Either[ValidationError, (DataEntry, Array[Byte])] = { + bytes.headOption match { + case Some(b) => + DataType.fromByte(b) match { + case Some(dt: DataType.DataTypeVal[_]) if (dt.lenFixed && dt.validator(bytes.drop(1).take(dt.maxLen))) => + Right((DataEntry(bytes.drop(1).take(dt.maxLen), dt), bytes.drop(1 + dt.maxLen))) + case Some(dt: DataType.DataTypeVal[_]) if (!dt.lenFixed && dt.maxLen <= Short.MaxValue && bytes.length >= 3 + && dt.validator(bytes.drop(1).take(2 + Shorts.fromByteArray(bytes.drop(1).take(2))))) => + Right((DataEntry(bytes.drop(1).take(2 + Shorts.fromByteArray(bytes.drop(1).take(2))), dt), + bytes.drop(3 + Shorts.fromByteArray(bytes.drop(1).take(2))))) + case _ => Left(InvalidDataEntry) + } + case _ => + Left(InvalidDataEntry) } } @@ -111,54 +75,29 @@ object DataEntry { Shorts.toByteArray(ds.length.toShort) ++ Bytes.concat(ds.map(_.bytes): _*) } - def right(structure: (Seq[DataEntry], Int)): Either[ValidationError, (Seq[DataEntry], Int)] = Right(structure) - def parseArrays(bytes: Array[Byte]): Either[ValidationError, Seq[DataEntry]] = { - val length = Shorts.fromByteArray(bytes.slice(0, 2)) - (0 until length).foldLeft(right((Seq.empty[DataEntry], 2))) { - case (accPos, _) => accPos.flatMap(ap => parseArraySize(bytes, ap._2) match { - case Right((arr, nextPos)) => Right((ap._1 :+ arr, nextPos)) + val length = Shorts.fromByteArray(bytes.take(2)) + (0 until length).foldLeft(Right((Seq.empty[DataEntry], bytes.drop(2))): Either[ValidationError, (Seq[DataEntry], Array[Byte])]) { + case (accPos, _) => accPos.flatMap(ap => parseArray(ap._2) match { + case Right((arr, suffix)) => Right((ap._1 :+ arr, suffix)) case Left(l) => Left(l) }) - } match { case Right((acc, _)) => Right(acc) case Left(l) => Left(l) } } - private def checkDataType(data: Array[Byte], dataType: DataType.Value): Boolean = dataType match { - case DataType.PublicKey => data.length == KeyLength - case DataType.Address => Address.fromBytes(data).isRight - case DataType.Amount => data.length == AmountLength && Longs.fromByteArray(data) >= 0 - case DataType.Int32 => data.length == 4 && Ints.fromByteArray(data) >= 0 - case DataType.ShortText => Shorts.fromByteArray(data.slice(0, 2)) + 2 == data.length && data.length <= 2 + maxShortTextLength - case DataType.ContractAccount => ContractAccount.fromBytes(data).isRight - case DataType.TokenId => isTokenIdValid(data) - case DataType.Timestamp => data.length == TimestampLength - case DataType.Boolean => data.length == 1 && (data(0) == 1.toByte || data(0) == 0.toByte) - case DataType.ShortBytes => Shorts.fromByteArray(data.slice(0, 2)) + 2 == data.length && data.length <= 2 + maxShortBytesLength - case _ => false - } + object ConvertHelper { - private def isTokenIdValid(addressBytes: Array[Byte]): Boolean = { - val version = addressBytes.head - val network = addressBytes.tail.head - if (version != ContractAccount.TokenAddressVersion) { - false - } else if (network != scheme.chainId) { - false - } else { - if (addressBytes.length != ContractAccount.TokenAddressLength) - false - else { - val checkSum = addressBytes.takeRight(ChecksumLength) - val checkSumGenerated = calcCheckSum(addressBytes.dropRight(ChecksumLength)) - checkSum.sameElements(checkSumGenerated) - } - } - } + implicit def int2Byte(x:Int): Byte = x.toByte + + implicit def dataEntry2Int(x: DataEntry): Int = Ints.fromByteArray(x.data) - private def calcCheckSum(withoutChecksum: Array[Byte]): Array[Byte] = hash(withoutChecksum).take(ChecksumLength) + implicit def dataEntry2BigInt(x: DataEntry): BigInt = BigInteger.deserializer(x.data) + implicit def dataEntry2Long(x: DataEntry): Long = Longs.fromByteArray(x.data) + + implicit def boolDataEntry2Byte(x: DataEntry): Byte = x.data(0) + } } diff --git a/src/main/scala/vsys/blockchain/contract/DataType.scala b/src/main/scala/vsys/blockchain/contract/DataType.scala index 4b13ff4b6..9867ca5ad 100644 --- a/src/main/scala/vsys/blockchain/contract/DataType.scala +++ b/src/main/scala/vsys/blockchain/contract/DataType.scala @@ -1,27 +1,171 @@ package vsys.blockchain.contract +import vsys.blockchain.transaction.TransactionParser.{AmountLength, KeyLength, TimestampLength} +import play.api.libs.json.{JsValue, Json} +import com.google.common.primitives.{Ints, Longs, Shorts} +import vsys.account.{AddressScheme, PublicKeyAccount} +import scorex.crypto.encode.Base58 +import vsys.account.ContractAccount.ChecksumLength +import vsys.blockchain.state.ByteStr +import vsys.utils.crypto.hash.SecureCryptographicHash._ +import scala.util.Try + object DataType extends Enumeration { - val PublicKey = Value(1) - val Address = Value(2) - val Amount = Value(3) - val Int32 = Value(4) - val ShortText = Value(5) - val ContractAccount = Value(6) - val Account = Value(7) - val TokenId = Value(8) - val Timestamp = Value(9) - val Boolean = Value(10) - val ShortBytes = Value(11) - val Balance = Value(12) - - def fromByte(b: Byte): Option[DataType.Value] = { - if (b < DataType.PublicKey.id || b > DataType.ShortBytes.id) - None - else - Some(DataType(b)) + + val MaxShortTextLength = 140 + val MaxShortBytesLength = 255 + val MaxOpcBlockLength = 255 + val MaxBigIntLength = 255 // less than 2048 bits + + sealed case class DataTypeVal[T](dataType: Int, lenFixed: Boolean, maxLen: Int, + deserializer: Array[Byte] => T, // take bytes WITH length prefix if length not fixed + serializer: T => Array[Byte], // returns WITHOUT length prefix + jsonifier: T => JsValue, + private val extValidator: Array[Byte] => Boolean) extends Val(dataType) { + + def *(n: Int): Int = n * dataType + + val jsonifierB: Array[Byte] => JsValue = b => jsonifier(deserializer(b)) + + // check length and other limits + val validator: Array[Byte] => Boolean = data => (((lenFixed && data.length == maxLen) + || (!lenFixed && data.length >= 2 && Shorts.fromByteArray(data.take(2)) == data.drop(2).length && data.drop(2).length <= maxLen)) + && extValidator(data)) } - private def check(a: Byte, b: Byte): Boolean = { + + val DataTypeObj = DataTypeVal[DataTypeVal[_]](0, + lenFixed = true, + maxLen = 1, + deserializer = x => fromByte(x(0)).get, + serializer = v => Array(v.id.toByte), + jsonifier = v => Json.toJson(v.toString), + extValidator = x => fromByte(x(0)).nonEmpty) + + val PublicKey = DataTypeVal[PublicKeyAccount](1, + lenFixed = true, + maxLen = KeyLength, + deserializer = PublicKeyAccount(_), + serializer = p => p.publicKey, + jsonifier = p => Json.toJson(Base58.encode(p.publicKey)), + extValidator = _ => true) + + val Address = DataTypeVal[vsys.account.Address](2, + lenFixed = true, + maxLen = vsys.account.Address.AddressLength, + deserializer = vsys.account.Address.fromBytes(_).right.get, + serializer = a => a.bytes.arr, + jsonifier = a => Json.toJson(a.address), + extValidator = vsys.account.Address.fromBytes(_).isRight) + + val Amount = DataTypeVal[Long](3, + lenFixed = true, + maxLen = AmountLength, + deserializer = Longs.fromByteArray(_), + serializer = Longs.toByteArray(_), + jsonifier = Json.toJson(_), + extValidator = Longs.fromByteArray(_) >= 0) + + val Int32 = DataTypeVal[Int](4, + lenFixed = true, + maxLen = 4, + deserializer = Ints.fromByteArray(_), + serializer = Ints.toByteArray(_), + jsonifier = Json.toJson(_), + extValidator = Ints.fromByteArray(_) >= 0) + + val ShortText = DataTypeVal[String](5, + lenFixed = false, + maxLen = MaxShortTextLength, + deserializer = b => new String(b.drop(2)), + serializer = s => s.getBytes, + jsonifier = Json.toJson(_), + extValidator = _ => true) + + val ContractAccount = DataTypeVal[vsys.account.ContractAccount](6, + lenFixed = true, + maxLen = vsys.account.ContractAccount.AddressLength, + deserializer = vsys.account.ContractAccount.fromBytes(_).right.get, + serializer = a => a.bytes.arr, + jsonifier = a => Json.toJson(a.address), + extValidator = vsys.account.ContractAccount.fromBytes(_).isRight) + + val Account = DataTypeVal[vsys.account.Account](7, + lenFixed = false, + maxLen = (vsys.account.Address.AddressLength).max(vsys.account.ContractAccount.AddressLength), + deserializer = vsys.account.Account.fromBytes(_, 0).right.get._1, + serializer = a => a.bytes.arr, + jsonifier = a => Json.toJson(a.bytes.arr), + extValidator = _ => false) // unsupported type for now. vsys.account.Account.fromBytes(_, 0).isRight) + + val TokenId = DataTypeVal[ByteStr](8, + lenFixed = true, + maxLen = vsys.account.ContractAccount.TokenAddressLength, + deserializer = ByteStr(_), + serializer = b => b.arr, + jsonifier = b => Json.toJson(b.base58), + extValidator = addressBytes => { + // length is already ensured here + def scheme = AddressScheme.current + val version = addressBytes.head + val network = addressBytes.tail.head + val checkSum1 = addressBytes.takeRight(ChecksumLength) + val checkSum2 = hash((addressBytes.dropRight(ChecksumLength))).take(ChecksumLength) + // addr version && net id && checksum + version == vsys.account.ContractAccount.TokenAddressVersion && network == scheme.chainId && checkSum1.sameElements(checkSum2) + }) + + val Timestamp = DataTypeVal[Long](9, + lenFixed = true, + maxLen = TimestampLength, + deserializer = Longs.fromByteArray(_), + serializer = Longs.toByteArray(_), + jsonifier = Json.toJson(_), + extValidator = Longs.fromByteArray(_) >= 0) + + val Boolean = DataTypeVal[Boolean](10, + lenFixed = true, + maxLen = 1, + deserializer = b => b.head == 1, + serializer = b => Array((if(b) 1 else 0).toByte), + jsonifier = b => Json.toJson(b.toString), + extValidator = b => (b(0) == 1 || b(0) == 0)) + + val ShortBytes = DataTypeVal[Array[Byte]](11, + lenFixed = false, + maxLen = MaxShortBytesLength, + deserializer = b => b.drop(2), + serializer = b => b, + jsonifier = b => Json.toJson(Base58.encode(b)), + extValidator = _ => true) + + val Balance = DataTypeVal[Long](12, + lenFixed = true, + maxLen = AmountLength, + deserializer = Longs.fromByteArray(_), + serializer = Longs.toByteArray(_), + jsonifier = Json.toJson(_), + extValidator = _ => false) // unsupported type for now. Longs.fromByteArray(_) >= 0) + + val OpcBlock = DataTypeVal[Array[Byte]](13, + lenFixed = false, + maxLen = MaxOpcBlockLength, + deserializer = b => b.drop(2), + serializer = b => b, + jsonifier = b => Json.toJson(Base58.encode(b)), + extValidator = _ => true) + + val BigInteger = DataTypeVal[BigInt](14, + lenFixed = false, + maxLen = MaxBigIntLength, + deserializer = b => BigInt(b.drop(2)), + serializer = i => i.toByteArray, + jsonifier = i => Json.toJson(i.toString), + extValidator = b => b.length > 2) + + def fromByte(b: Byte): Option[DataTypeVal[_]] = Try(DataType(b).asInstanceOf[DataTypeVal[_]]).toOption + + def check(a: Byte, b: Byte): Boolean = { if (a == b) true else if (a == DataType.Account.id) b == DataType.Address.id || b == DataType.ContractAccount.id else if (b == DataType.Account.id) check(b, a) @@ -32,4 +176,6 @@ object DataType extends Enumeration { paraTypes.length == dataTypes.length && (paraTypes, dataTypes).zipped.forall { case (a, b) => check(a, b) } } + def arrayShortLengthToByteArray(a: Array[_]) = Shorts.toByteArray(a.length.toShort) + } diff --git a/src/main/scala/vsys/blockchain/state/Diff.scala b/src/main/scala/vsys/blockchain/state/Diff.scala index 1883e9670..f1b9d08b9 100644 --- a/src/main/scala/vsys/blockchain/state/Diff.scala +++ b/src/main/scala/vsys/blockchain/state/Diff.scala @@ -89,7 +89,7 @@ object Diff { contractTokens: Map[ByteStr, Int] = Map.empty, tokenDB: Map[ByteStr, Array[Byte]] = Map.empty, tokenAccountBalance: Map[ByteStr, Long] = Map.empty, - relatedAddress: Map[Address, Boolean] = Map.empty, + relatedAddress: Map[Account, Boolean] = Map.empty, dbEntries: Map[ByteStr, Entry] = Map.empty, leaseState: Map[ByteStr, Boolean] = Map.empty): Diff = Diff( transactions = Map((tx.id, (height, ProcessedTransaction(txStatus, chargedFee, tx), (portfolios.keys ++ relatedAddress.keys).toSet))), diff --git a/src/main/scala/vsys/blockchain/state/StateWriter.scala b/src/main/scala/vsys/blockchain/state/StateWriter.scala index 18f2ed7ae..3ee1d9ad1 100644 --- a/src/main/scala/vsys/blockchain/state/StateWriter.scala +++ b/src/main/scala/vsys/blockchain/state/StateWriter.scala @@ -1,8 +1,8 @@ package vsys.blockchain.state import java.util.concurrent.locks.ReentrantReadWriteLock - import cats.implicits._ +import vsys.account.Address import vsys.blockchain.state.reader.StateReaderImpl import vsys.utils.ScorexLogging import vsys.settings.StateSettings @@ -45,7 +45,7 @@ class StateWriterImpl(p: StateStorage, synchronizationToken: ReentrantReadWriteL } measureSizeLog("accountTransactionIds")(blockDiff.txsDiff.accountTransactionIds) { - _.foreach { case (acc, txIds) => + _.foreach { case (acc, txIds) => if (stateSettings.txContractTxIds || acc.bytes.arr(0) == Address.AddressVersion) { val startIdxShift = sp().accountTransactionsLengths.get(acc.bytes).getOrElse(0) txIds.reverse.foldLeft(startIdxShift) { case (shift, txId) => sp().accountTransactionIds.put(accountIndexKey(acc, shift), txId) @@ -53,17 +53,20 @@ class StateWriterImpl(p: StateStorage, synchronizationToken: ReentrantReadWriteL } sp().accountTransactionsLengths.put(acc.bytes, startIdxShift + txIds.length) } + } } if(stateSettings.txTypeAccountTxIds) { measureSizeLog("txTypeAccountTxIds")(blockDiff.txsDiff.txTypeAccountTxIds) { _.foreach { case ((txType, acc), txIds) => - val startIdxShift = sp().txTypeAccTxLengths.get(txTypeAccKey(txType, acc)).getOrElse(0) - txIds.reverse.foldLeft(startIdxShift) { case (shift, txId) => - sp().txTypeAccountTxIds.put(txTypeAccIndexKey(txType, acc, shift), txId) - shift + 1 + if (stateSettings.txContractTxIds || acc.bytes.arr(0) == Address.AddressVersion) { + val startIdxShift = sp().txTypeAccTxLengths.get(txTypeAccKey(txType, acc)).getOrElse(0) + txIds.reverse.foldLeft(startIdxShift) { case (shift, txId) => + sp().txTypeAccountTxIds.put(txTypeAccIndexKey(txType, acc, shift), txId) + shift + 1 + } + sp().txTypeAccTxLengths.put(txTypeAccKey(txType, acc), startIdxShift + txIds.length) } - sp().txTypeAccTxLengths.put(txTypeAccKey(txType, acc), startIdxShift + txIds.length) } } } diff --git a/src/main/scala/vsys/blockchain/state/diffs/CommonValidation.scala b/src/main/scala/vsys/blockchain/state/diffs/CommonValidation.scala index 01b206a42..e1930f845 100644 --- a/src/main/scala/vsys/blockchain/state/diffs/CommonValidation.scala +++ b/src/main/scala/vsys/blockchain/state/diffs/CommonValidation.scala @@ -39,15 +39,26 @@ object CommonValidation { else Right(tx) } + private def isTokenContracts(c: Contract): Boolean = + c == ContractPermitted.contract || c == ContractPermitted.contractWithoutSplit + + private def isDepositWithdrawContracts(c: Contract): Boolean = + c == ContractLock.contract || c == ContractPaymentChannel.contract || c == ContractNonFungible.contract + + private def isExchangeContracts(c: Contract): Boolean = + c == ContractAtomicSwap.contract || c == ContractVEscrow.contract || c == ContractVOption.contract || c == ContractVStableSwap.contract || c == ContractVSwap.contract || + c == ContractTokenV2.contractTokenBlackList || c == ContractTokenV2.contractTokenWhiteList || c == ContractNonFungibleV2.contractNFTBlacklist || c == ContractNonFungibleV2.contractNFTWhitelist + private def disallowInvalidContractTxs[T <: Transaction](settings: FunctionalitySettings, h: Int, tx: T, c: Contract): Either[ValidationError, T] = { if (h <= settings.allowContractTransactionAfterHeight) Left(GenericError(s"must not appear before height=${settings.allowContractTransactionAfterHeight}")) - else if (h <= settings.allowDepositWithdrawContractAfterHeight && - (c == ContractLock.contract || - c == ContractNonFungible.contract || - c == ContractPaymentChannel.contract)) + else if (h <= settings.allowDepositWithdrawContractAfterHeight && !isTokenContracts(c)) Left(GenericError(s"deposit withdraw contracts must not appear before height=${settings.allowDepositWithdrawContractAfterHeight}")) - else Right(tx) + else if (h <= settings.allowExchangeContractAfterHeight && !isTokenContracts(c) && !isDepositWithdrawContracts(c)) + Left(GenericError(s"exchange contracts must not appear before height=${settings.allowExchangeContractAfterHeight}")) + else if (isTokenContracts(c) || isDepositWithdrawContracts(c) || isExchangeContracts(c)) + Right(tx) + else Left(GenericError(s"unsupported contracts")) } def disallowBeforeActivationHeight[T <: Transaction](settings: FunctionalitySettings, h: Int, tx: T): Either[ValidationError, T] = diff --git a/src/main/scala/vsys/blockchain/state/diffs/ExecuteContractFunctionTransactionDiff.scala b/src/main/scala/vsys/blockchain/state/diffs/ExecuteContractFunctionTransactionDiff.scala index ee298bd84..3f966e3d6 100644 --- a/src/main/scala/vsys/blockchain/state/diffs/ExecuteContractFunctionTransactionDiff.scala +++ b/src/main/scala/vsys/blockchain/state/diffs/ExecuteContractFunctionTransactionDiff.scala @@ -30,7 +30,7 @@ object ExecuteContractFunctionTransactionDiff { contractNumDB = diff.contractNumDB, contractStateDB = diff.contractStateDB, contractTokens = diff.contractTokens, - relatedAddress = diff.relatedAddress, + relatedAddress = diff.relatedAddress ++ Map(exContext.contractId -> true), chargedFee = tx.transactionFee )) .left.flatMap( e => diff --git a/src/main/scala/vsys/blockchain/state/diffs/RegisterContractTransactionDiff.scala b/src/main/scala/vsys/blockchain/state/diffs/RegisterContractTransactionDiff.scala index 8e7671b2f..d298d3ee7 100644 --- a/src/main/scala/vsys/blockchain/state/diffs/RegisterContractTransactionDiff.scala +++ b/src/main/scala/vsys/blockchain/state/diffs/RegisterContractTransactionDiff.scala @@ -34,7 +34,7 @@ object RegisterContractTransactionDiff { contractTokens = diff.contractTokens, tokenDB = diff.tokenDB, tokenAccountBalance = diff.tokenAccountBalance, - relatedAddress = diff.relatedAddress, + relatedAddress = diff.relatedAddress ++ Map(exContext.contractId -> true), chargedFee = tx.transactionFee )) .left.flatMap( e => diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/AssertOpcDiff.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/AssertOpcDiff.scala index 9c20b55d1..9b95ca7a6 100644 --- a/src/main/scala/vsys/blockchain/state/opcdiffs/AssertOpcDiff.scala +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/AssertOpcDiff.scala @@ -18,43 +18,51 @@ object AssertOpcDiff extends OpcDiffer { else if (v.data sameElements Array(1.toByte)) Right(OpcDiff.empty) else - Left(GenericError(s"Invalid Assert (Boolean True): Value False")) + Left(GenericError(s"Invalid Assert (Boolean True): Value ${v.json} is False")) } def gtEq0(v: DataEntry): Either[ValidationError, OpcDiff] = { - if (v.dataType == DataType.Amount && Longs.fromByteArray(v.data) >= 0) + if (v.dataType != DataType.Amount) + Left(ContractDataTypeMismatch) + else if (Longs.fromByteArray(v.data) >= 0) Right(OpcDiff.empty) else - Left(GenericError(s"Invalid Assert (gteq0): Value ${Longs.fromByteArray(v.data)} is negative")) + Left(GenericError(s"Invalid Assert (gteq0): Value ${v.json} is negative")) } def ltEq(v1: DataEntry, v2: DataEntry): Either[ValidationError, OpcDiff] = { - if (v1.dataType == DataType.Amount && v2.dataType == DataType.Amount - && Longs.fromByteArray(v1.data) <= Longs.fromByteArray(v2.data)) + if (v1.dataType != DataType.Amount || v2.dataType != DataType.Amount) + Left(ContractDataTypeMismatch) + else if (Longs.fromByteArray(v1.data) <= Longs.fromByteArray(v2.data)) Right(OpcDiff.empty) else - Left(GenericError(s"Invalid Assert (lteq0): Value ${Longs.fromByteArray(v2.data)} is larger than $v1")) + Left(GenericError(s"Invalid Assert (lteq0): Value ${v1.json} " + + s"is larger than ${v2.json}")) } def ltInt64(m: DataEntry): Either[ValidationError, OpcDiff] = { - if (m.dataType == DataType.Amount && Longs.fromByteArray(m.data) <= Long.MaxValue) + if (m.dataType != DataType.Amount) + Left(ContractDataTypeMismatch) + else if (Longs.fromByteArray(m.data) <= Long.MaxValue) Right(OpcDiff.empty) else - Left(GenericError(s"Invalid Assert (ltint64): Value ${Longs.fromByteArray(m.data)} is invalid")) + Left(GenericError(s"Invalid Assert (ltint64): Value ${m.json} is invalid")) } def gt0(v: DataEntry): Either[ValidationError, OpcDiff] = { - if (v.dataType == DataType.Amount && Longs.fromByteArray(v.data) > 0) + if (v.dataType != DataType.Amount) + Left(ContractDataTypeMismatch) + else if (Longs.fromByteArray(v.data) > 0) Right(OpcDiff.empty) else - Left(GenericError(s"Invalid Assert (gt0): Value $v is non-positive")) + Left(GenericError(s"Invalid Assert (gt0): Value ${v.json} is non-positive")) } def equal(add1: DataEntry, add2: DataEntry): Either[ValidationError, OpcDiff] = { if (add1.bytes sameElements add2.bytes) Right(OpcDiff.empty) else - Left(GenericError(s"Invalid Assert (eq): DataEntry ${add1.data} is not equal to ${add2.data}")) + Left(GenericError(s"Invalid Assert (eq): DataEntry ${add1.json} is not equal to ${add2.json}")) } def isCallerOrigin(context: ExecutionContext)(address: DataEntry): Either[ValidationError, OpcDiff] = { diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/BasicOpcDiff.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/BasicOpcDiff.scala index 72d5f16a6..eb5e283f9 100644 --- a/src/main/scala/vsys/blockchain/state/opcdiffs/BasicOpcDiff.scala +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/BasicOpcDiff.scala @@ -1,209 +1,171 @@ package vsys.blockchain.state.opcdiffs -import com.google.common.primitives.{Ints, Longs} import vsys.blockchain.transaction.ValidationError import vsys.blockchain.transaction.ValidationError._ import vsys.blockchain.contract.{DataEntry, DataType, ExecutionContext} +import vsys.blockchain.contract.DataType._ +import vsys.blockchain.contract.DataEntry.ConvertHelper._ +import vsys.blockchain.state.opcdiffs.OpcDiffer._ -import scala.util.{Left, Right, Try} +import scala.language.implicitConversions +import scala.util.{Left, Try} object BasicOpcDiff extends OpcDiffer { - def add(context: ExecutionContext)(x: DataEntry, y: DataEntry, dataStack: Seq[DataEntry], - pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - if (x.dataType == y.dataType) { - val supportList = List(DataType.Amount, DataType.Timestamp, DataType.Int32) - supportList.find(a => a == x.dataType) match { - case Some(_) => { - if (x.dataType == DataType.Int32) { - val xValue = Ints.fromByteArray(x.data) - val yValue = Ints.fromByteArray(y.data) - if (Try(Math.addExact(xValue, yValue)).isFailure) Left(ValidationError.OverflowError) - else { - for { - zDataEntry <- DataEntry.create(Ints.toByteArray(xValue + yValue), x.dataType) - } yield dataStack.patch(pointer, Seq(zDataEntry), 1) - } - } else { - val xValue = Longs.fromByteArray(x.data) - val yValue = Longs.fromByteArray(y.data) - if (Try(Math.addExact(xValue, yValue)).isFailure) Left(ValidationError.OverflowError) - else { - for { - zDataEntry <- DataEntry.create(Longs.toByteArray(xValue + yValue), x.dataType) - } yield dataStack.patch(pointer, Seq(zDataEntry), 1) - } - } - } - case None => Left(ContractUnsupportedOPC) - } - } else Left(ContractDataTypeMismatch) + case class NumBiOperator(int$1: (Int, Int) => Int, long$1: (Long, Long) => Long, bigInt$1: (BigInt, BigInt) => BigInt) { + val int: (Int, Int) => Try[Int] = (x, y) => Try(int$1(x, y)) + val long: (Long, Long) => Try[Long] = (x, y) => Try(long$1(x, y)) + val bigInt: (BigInt, BigInt) => Try[BigInt] = (x, y) => Try(bigInt$1(x, y)) } + val add = NumBiOperator(Math.addExact, Math.addExact, _ + _) + val minus = NumBiOperator(Math.subtractExact, Math.subtractExact, _ - _) + val multiply = NumBiOperator(Math.multiplyExact, Math.multiplyExact, _ * _) + val divide = NumBiOperator(Math.floorDiv, Math.floorDiv, _ / _) + val minimum = NumBiOperator(Math.min, Math.min, (x, y) => x.min(y)) + val maximum = NumBiOperator(Math.max, Math.max, (x, y) => x.max(y)) - def minus(context: ExecutionContext)(x: DataEntry, y: DataEntry, dataStack: Seq[DataEntry], - pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - if (x.dataType == y.dataType) { - val supportList = List(DataType.Amount, DataType.Timestamp, DataType.Int32) - supportList.find(a => a == x.dataType) match { - case Some(_) => { - if (x.dataType == DataType.Int32) { - val xValue = Ints.fromByteArray(x.data) - val yValue = Ints.fromByteArray(y.data) - if (Try(Math.subtractExact(xValue, yValue)).isFailure) Left(ValidationError.OverflowError) - else { - for { - zDataEntry <- DataEntry.create(Ints.toByteArray(xValue - yValue), x.dataType) - } yield dataStack.patch(pointer, Seq(zDataEntry), 1) - } - } else { - val xValue = Longs.fromByteArray(x.data) - val yValue = Longs.fromByteArray(y.data) - if (Try(Math.subtractExact(xValue, yValue)).isFailure) Left(ValidationError.OverflowError) - else { - for { - zDataEntry <- DataEntry.create(Longs.toByteArray(xValue - yValue), x.dataType) - } yield dataStack.patch(pointer, Seq(zDataEntry), 1) - } - } - } - case None => Left(ContractUnsupportedOPC) - } - } else Left(ContractDataTypeMismatch) + // babylonian method + def sqrtBigInt(y: BigInt): BigInt = { + if (y > 3) Stream.iterate((y, (y >> 1) + 1)){ case (z, x) => (x, (y / x + x) >> 1) }.dropWhile{ case(z, x) => x < z }.head._1 + else if (y > 0) 1 else 0 } - def multiply(context: ExecutionContext)(x: DataEntry, y: DataEntry, dataStack: Seq[DataEntry], - pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - if (x.dataType == y.dataType) { - val supportList = List(DataType.Amount, DataType.Timestamp, DataType.Int32) - supportList.find(a => a == x.dataType) match { - case Some(_) => { - if (x.dataType == DataType.Int32) { - val xValue = Ints.fromByteArray(x.data) - val yValue = Ints.fromByteArray(y.data) - if (Try(Math.multiplyExact(xValue, yValue)).isFailure) Left(ValidationError.OverflowError) - else { - for { - zDataEntry <- DataEntry.create(Ints.toByteArray(xValue * yValue), x.dataType) - } yield dataStack.patch(pointer, Seq(zDataEntry), 1) - } - } else { - val xValue = Longs.fromByteArray(x.data) - val yValue = Longs.fromByteArray(y.data) - if (Try(Math.multiplyExact(xValue, yValue)).isFailure) Left(ValidationError.OverflowError) - else { - for { - zDataEntry <- DataEntry.create(Longs.toByteArray(xValue * yValue), x.dataType) - } yield dataStack.patch(pointer, Seq(zDataEntry), 1) - } - } - } - case None => Left(ContractUnsupportedOPC) - } - } else Left(ContractDataTypeMismatch) - } + // not converting byte to boolean + case class BoolByteBiOperator(val op: (Byte, Byte) => Byte) - def divide(context: ExecutionContext)(x: DataEntry, y: DataEntry, dataStack: Seq[DataEntry], - pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - if (x.dataType == y.dataType) { - val supportList = List(DataType.Amount, DataType.Timestamp, DataType.Int32) - supportList.find(a => a == x.dataType) match { - case Some(_) => { - if (x.dataType == DataType.Int32) { - val xValue = Ints.fromByteArray(x.data) - val yValue = Ints.fromByteArray(y.data) - if (yValue == 0) Left(ValidationError.OverflowError) - else { - for { - zDataEntry <- DataEntry.create(Ints.toByteArray(xValue / yValue), x.dataType) - } yield dataStack.patch(pointer, Seq(zDataEntry), 1) - } - } else { - val xValue = Longs.fromByteArray(x.data) - val yValue = Longs.fromByteArray(y.data) - if (yValue == 0) Left(ValidationError.OverflowError) - else { - for { - zDataEntry <- DataEntry.create(Longs.toByteArray(xValue / yValue), x.dataType) - } yield dataStack.patch(pointer, Seq(zDataEntry), 1) - } - } - } - case None => Left(ContractUnsupportedOPC) - } + val bAnd = BoolByteBiOperator(_ & _) + val bOr = BoolByteBiOperator(_ | _) + val bXor = BoolByteBiOperator(_ ^ _) + + val bNot: Byte => Byte = x => ~x & 1 + + private implicit def try2Either(t: Try[Either[ValidationError, DataEntry]]): Either[ValidationError, DataEntry] = + t.recover({ case _ => Left(OverflowError) }).get + + def numBiOperation(x: DataEntry, y: DataEntry, operator: NumBiOperator): Either[ValidationError, DataEntry] = + if (x.dataType == y.dataType) x.dataType match { + case Int32 => operator.int(x, y).map(formatRes[Int](_, Int32)) + case Amount => operator.long(x, y).map(formatRes[Long](_, Amount)) + case Timestamp => operator.long(x, y).map(formatRes[Long](_, Timestamp)) + case BigInteger => operator.bigInt(x, y).map(formatRes[BigInt](_, BigInteger)) + case _ => Left(ContractUnsupportedOPC) } else Left(ContractDataTypeMismatch) - } - def minimum(context: ExecutionContext)(x: DataEntry, y: DataEntry, dataStack: Seq[DataEntry], - pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - if (x.dataType == y.dataType) { - val supportList = List(DataType.Amount, DataType.Timestamp, DataType.Int32) - supportList.find(a => a == x.dataType) match { - case Some(_) => { - val xValue = if (x.dataType == DataType.Int32) Ints.fromByteArray(x.data) else Longs.fromByteArray(x.data) - val yValue = if (y.dataType == DataType.Int32) Ints.fromByteArray(y.data) else Longs.fromByteArray(y.data) - if (xValue > yValue) Right(dataStack.patch(pointer, Seq(y), 1)) - else Right(dataStack.patch(pointer, Seq(x), 1)) - } - case None => Left(ContractUnsupportedOPC) + def sqrt(x: DataEntry): Either[ValidationError, DataEntry] = + x.dataType match { + case BigInteger => { + val value = BigInteger.deserializer(x.data) + if (value < 0) Left(ValidationError.OverflowError) + else formatRes[BigInt](sqrtBigInt(value), BigInteger) } - } else Left(ContractDataTypeMismatch) - } + case _ => Left(ContractUnsupportedOPC) + } + + def boolBiOperation(x: DataEntry, y: DataEntry, operator: BoolByteBiOperator): Either[ValidationError, DataEntry] = + if (x.dataType == y.dataType && x.dataType == Boolean) formatResB[Boolean](Array(operator.op(x, y)), Boolean) + else Left(ContractInvalidOPCData) - def maximum(context: ExecutionContext)(x: DataEntry, y: DataEntry, dataStack: Seq[DataEntry], - pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - if (x.dataType == y.dataType) { - val supportList = List(DataType.Amount, DataType.Timestamp, DataType.Int32) - supportList.find(a => a == x.dataType) match { - case Some(_) => { - val xValue = if (x.dataType == DataType.Int32) Ints.fromByteArray(x.data) else Longs.fromByteArray(x.data) - val yValue = if (y.dataType == DataType.Int32) Ints.fromByteArray(y.data) else Longs.fromByteArray(y.data) - if (xValue > yValue) Right(dataStack.patch(pointer, Seq(x), 1)) - else Right(dataStack.patch(pointer, Seq(y), 1)) + def not(x: DataEntry): Either[ValidationError, DataEntry] = + x.dataType match { + case Boolean => formatResB[Boolean](Array(bNot(x)), Boolean) + case _ => Left(ContractUnsupportedOPC) + } + + def convertion(x: DataEntry, t: DataEntry): Either[ValidationError, DataEntry] = + t.dataType match { + case DataTypeObj =>(x.dataType match { + case Int32 | Amount | Timestamp => Right(x.data) + case BigInteger => Right(x.data.drop(2)) + case _ => Left(ContractUnsupportedOPC) + }) flatMap { bytes => { + val to: DataTypeVal[_] = DataTypeObj.deserializer(t.data) + to match { + case Int32 => formatResB[Int] (formatSignedByteArrayWithLength(bytes, 4), Int32) + case Amount => formatResB[Long] (formatPositiveByteArrayWithLength(bytes, 8), Amount) + case Timestamp => formatResB[Long] (formatPositiveByteArrayWithLength(bytes, 8), Timestamp) + case BigInteger => formatResB[BigInt](bigintBytes(bytes), BigInteger) + case _ => Left(ContractUnsupportedOPC) + } } - case None => Left(ContractUnsupportedOPC) } - } else Left(ContractDataTypeMismatch) - } + case _ => Left(ContractInvalidOPCData) + } - def concat(context: ExecutionContext)(x: DataEntry, y: DataEntry, dataStack: Seq[DataEntry], - pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - for { - res <- DataEntry.create(x.data ++ y.data, DataType.ShortBytes) - } yield dataStack.patch(pointer, Seq(res), 1) - } + private def formatPositiveByteArrayWithLength(in: Array[Byte], length: Int): Array[Byte] = + if (in.length > length) { + in.take(in.length - length).dropWhile(i => i == 0) ++ in.takeRight(length) + } else if (in(0) >= 0) { + new Array[Byte](length - in.length) ++ in + } else { + new Array[Byte](length + 1) // make overflow error when in < 0 + } - def constantGet(context: ExecutionContext)(constant: Array[Byte], dataStack: Seq[DataEntry], - pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - DataEntry.fromBytes(constant) match { - case Right(v) => Right(dataStack.patch(pointer, Seq(v), 1)) - case Left(e) => Left(e) + private def formatSignedByteArrayWithLength(in: Array[Byte], length: Int): Array[Byte] = + if (in(0) >= 0) { + formatPositiveByteArrayWithLength(in, length) + } else if (in.length > length) { + val right = in.takeRight(length) + if (right(0) >= 0) in else { + in.take(in.length - length).dropWhile(i => i == -1) ++ right + } + } else { + Array.fill[Byte](length - in.length)(-1) ++ in } - } + + def concat(x: DataEntry, y: DataEntry): Either[ValidationError, DataEntry] = + DataEntry.create(x.data ++ y.data, DataType.ShortBytes) + + def constantGet(constant: Array[Byte]): Either[ValidationError, DataEntry] = + DataEntry.fromBytes(constant) object BasicType extends Enumeration { - sealed case class basicTypeVal( + sealed case class BasicTypeVal( basicType: Int, len: Int, - differ: (ExecutionContext, Array[Byte], Seq[DataEntry]) => Either[ValidationError, Seq[DataEntry]]) + indexes: Seq[Int], + op: (Array[Byte], Seq[DataEntry]) => Either[ValidationError, DataEntry]) extends Val(basicType) { def *(n: Int): Int = n * basicType } - val Add = basicTypeVal(1, 4, (c, b, d) => add(c)(d(b(1)), d(b(2)), d, b(3))) - val Minus = basicTypeVal(2, 4, (c, b, d) => minus(c)(d(b(1)), d(b(2)), d, b(3))) - val Multiply = basicTypeVal(3, 4, (c, b, d) => multiply(c)(d(b(1)), d(b(2)), d, b(3))) - val Divide = basicTypeVal(4, 4, (c, b, d) => divide(c)(d(b(1)), d(b(2)), d, b(3))) - val Minimum = basicTypeVal(5, 4, (c, b, d) => minimum(c)(d(b(1)), d(b(2)), d, b(3))) - val Maximum = basicTypeVal(6, 4, (c, b, d) => maximum(c)(d(b(1)), d(b(2)), d, b(3))) - val Concat = basicTypeVal(7, 4, (c, b, d) => concat(c)(d(b(1)), d(b(2)), d, b(3))) - val ConstantGet = basicTypeVal(8, 2, (c, b, d) => constantGet(c)(b.slice(1, b.length-1), d, b(b.length-1))) + val Add = BasicTypeVal(1, 4, Seq(1, 2), (b, d) => numBiOperation(d(b(1)), d(b(2)), add)) + val Minus = BasicTypeVal(2, 4, Seq(1, 2), (b, d) => numBiOperation(d(b(1)), d(b(2)), minus)) + val Multiply = BasicTypeVal(3, 4, Seq(1, 2), (b, d) => numBiOperation(d(b(1)), d(b(2)), multiply)) + val Divide = BasicTypeVal(4, 4, Seq(1, 2), (b, d) => numBiOperation(d(b(1)), d(b(2)), divide)) + val Minimum = BasicTypeVal(5, 4, Seq(1, 2), (b, d) => numBiOperation(d(b(1)), d(b(2)), minimum)) + val Maximum = BasicTypeVal(6, 4, Seq(1, 2), (b, d) => numBiOperation(d(b(1)), d(b(2)), maximum)) + val Concat = BasicTypeVal(7, 4, Seq(1, 2), (b, d) => concat(d(b(1)), d(b(2)))) + val ConstantGet = BasicTypeVal(8, 2, Seq(), (b, d) => constantGet(b.slice(1, b.length-1))) + val SqrtBigInt = BasicTypeVal(9, 3, Seq(1), (b, d) => sqrt(d(b(1)))) + val Convert = BasicTypeVal(10, 4, Seq(1, 2), (b, d) => convertion(d(b(1)), d(b(2)))) + val And = BasicTypeVal(11, 4, Seq(1, 2), (b, d) => boolBiOperation(d(b(1)), d(b(2)), bAnd)) + val Or = BasicTypeVal(12, 4, Seq(1, 2), (b, d) => boolBiOperation(d(b(1)), d(b(2)), bOr)) + val Xor = BasicTypeVal(13, 4, Seq(1, 2), (b, d) => boolBiOperation(d(b(1)), d(b(2)), bXor)) + val Not = BasicTypeVal(14, 3, Seq(1), (b, d) => not(d(b(1)))) } + def bigintBytes (bytes: Array[Byte]): Array[Byte] = { + val sig = bytes(0) >> 7 + val x = bytes.dropRight(1).dropWhile(i => i == sig) ++ bytes.takeRight(1) + x.headOption match { + case Some(a: Byte) if (a >> 7) != sig => Array[Byte](sig.toByte) ++ x + case _ => x + } + } + + private def formatRes[T] (res: T, dt: DataTypeVal[T]): Either[ValidationError, DataEntry] = + formatResB[T](dt.serializer(res), dt) + + private def formatResB[T] (res: Array[Byte], dt: DataTypeVal[T]): Either[ValidationError, DataEntry] = + DataEntry.create(res, dt).left.map(_ => ValidationError.OverflowError) + + def differ(bytes: Array[Byte], data: Seq[DataEntry], t: BasicType.BasicTypeVal): Either[ValidationError, Seq[DataEntry]] = updateStack(data, bytes.last, t.op(bytes, data)) + override def parseBytesDt(context: ExecutionContext)(bytes: Array[Byte], data: Seq[DataEntry]): Either[ValidationError, Seq[DataEntry]] = bytes.headOption.flatMap(f => Try(BasicType(f)).toOption) match { - case Some(t: BasicType.basicTypeVal) if checkBytesLength(bytes, t) => t.differ(context, bytes, data) + case Some(t: BasicType.BasicTypeVal) if checkBytesLength(bytes, t) && checkIndexes(bytes, data, t.indexes) => differ(bytes, data, t) case _ => Left(ContractInvalidOPCData) } - private def checkBytesLength(bytes: Array[Byte], t: BasicType.basicTypeVal): Boolean = { - (t.basicType == 8 && bytes.length > t.len) || (bytes.length == t.len) + private def checkBytesLength(bytes: Array[Byte], t: BasicType.BasicTypeVal): Boolean = { + (t == BasicType.ConstantGet && bytes.length > t.len) || (bytes.length == t.len) } } diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/CDBVOpcDiff.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/CDBVOpcDiff.scala index 94e62857c..893d0c6dd 100644 --- a/src/main/scala/vsys/blockchain/state/opcdiffs/CDBVOpcDiff.scala +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/CDBVOpcDiff.scala @@ -36,45 +36,87 @@ object CDBVOpcDiff extends OpcDiffer { } yield diff } - def mapValueAdd(context: ExecutionContext)(stateMap: Array[Byte], - keyValue: DataEntry, dataValue: DataEntry): Either[ValidationError, OpcDiff] = { - val combinedKey = ByteStr(context.contractId.bytes.arr ++ Array(stateMap(0)) ++ keyValue.bytes) - if (!checkStateMap(stateMap, keyValue.dataType, dataValue.dataType)) - Left(ContractInvalidStateMap) - if (dataValue.dataType == DataType.Amount){ - val cntBalance = context.state.contractNumInfo(combinedKey) - val addAmount = Longs.fromByteArray(dataValue.data) + private def numDBAddChange(context: ExecutionContext)(value: DataEntry, dbKey: ByteStr): Either[ValidationError, Long] = { + if (value.dataType == DataType.Amount) { + val cntBalance = context.state.contractNumInfo(dbKey) + val addAmount = Longs.fromByteArray(value.data) if (addAmount < 0){ Left(InvalidDataEntry) - } - if (Try(Math.addExact(cntBalance, addAmount)).isFailure) + } else if (Try(Math.addExact(cntBalance, addAmount)).isFailure) Left(ValidationError.OverflowError) else - Right(OpcDiff(contractNumDB = Map(combinedKey -> addAmount))) + Right(addAmount) } else Left(ContractDataTypeMismatch) } - def mapValueMinus(context: ExecutionContext)(stateMap: Array[Byte], - keyValue: DataEntry, dataValue: DataEntry): Either[ValidationError, OpcDiff] = { - val combinedKey = ByteStr(context.contractId.bytes.arr ++ Array(stateMap(0)) ++ keyValue.bytes) - if (!checkStateMap(stateMap, keyValue.dataType, dataValue.dataType)) - Left(ContractInvalidStateMap) - if (dataValue.dataType == DataType.Amount){ - val cntBalance = context.state.contractNumInfo(combinedKey) - val minusAmount = Longs.fromByteArray(dataValue.data) + private def numDBMinusChange(context: ExecutionContext)(value: DataEntry, dbKey: ByteStr): Either[ValidationError, Long] = { + if (value.dataType == DataType.Amount) { + val cntBalance = context.state.contractNumInfo(dbKey) + val minusAmount = Longs.fromByteArray(value.data) if (minusAmount < 0){ Left(InvalidDataEntry) - } - if (cntBalance >= minusAmount) - Right(OpcDiff(contractNumDB = Map(combinedKey -> -minusAmount))) + } else if (cntBalance >= minusAmount) + Right(-minusAmount) else Left(ContractMapValueInsufficient) } else Left(ContractDataTypeMismatch) } + def mapValueAdd(context: ExecutionContext)(stateMap: Array[Byte], + keyValue: DataEntry, dataValue: DataEntry): Either[ValidationError, OpcDiff] = { + for { + _ <- Either.cond(checkStateMap(stateMap, keyValue.dataType, dataValue.dataType), (), ContractInvalidStateMap) + combinedKey = ByteStr(context.contractId.bytes.arr ++ Array(stateMap(0)) ++ keyValue.bytes) + updateAmount <- numDBAddChange(context)(dataValue, combinedKey) + diff = OpcDiff(contractNumDB = Map(combinedKey -> updateAmount)) + } yield diff + } + + def mapValueMinus(context: ExecutionContext)(stateMap: Array[Byte], + keyValue: DataEntry, dataValue: DataEntry): Either[ValidationError, OpcDiff] = { + for { + _ <- Either.cond(checkStateMap(stateMap, keyValue.dataType, dataValue.dataType), (), ContractInvalidStateMap) + combinedKey = ByteStr(context.contractId.bytes.arr ++ Array(stateMap(0)) ++ keyValue.bytes) + updateAmount <- numDBMinusChange(context)(dataValue, combinedKey) + diff = OpcDiff(contractNumDB = Map(combinedKey -> updateAmount)) + } yield diff + } + + def valueAdd(context: ExecutionContext)(stateVar: Array[Byte], + value: DataEntry): Either[ValidationError, OpcDiff] = { + for { + _ <- Either.cond(checkStateVar(stateVar, value.dataType), (), ContractInvalidStateVariable) + addrOpt <- if (value.dataType == DataType.Address) Address.fromBytes(value.data).map(Some(_)) + else Right(None) + combinedKey = ByteStr(context.contractId.bytes.arr ++ Array(stateVar(0))) + updateAmount <- numDBAddChange(context)(value, combinedKey) + contractNumDB = Map(combinedKey -> updateAmount) + diff = addrOpt match { + case Some(addr) => OpcDiff(relatedAddress = Map(addr -> true), contractNumDB = contractNumDB) + case None => OpcDiff(contractNumDB = contractNumDB) + } + } yield diff + } + + def valueMinus(context: ExecutionContext)(stateVar: Array[Byte], + value: DataEntry): Either[ValidationError, OpcDiff] = { + for { + _ <- Either.cond(checkStateVar(stateVar, value.dataType), (), ContractInvalidStateVariable) + addrOpt <- if (value.dataType == DataType.Address) Address.fromBytes(value.data).map(Some(_)) + else Right(None) + combinedKey = ByteStr(context.contractId.bytes.arr ++ Array(stateVar(0))) + updateAmount <- numDBMinusChange(context)(value, combinedKey) + contractNumDB = Map(combinedKey -> updateAmount) + diff = addrOpt match { + case Some(addr) => OpcDiff(relatedAddress = Map(addr -> true), contractNumDB = contractNumDB) + case None => OpcDiff(contractNumDB = contractNumDB) + } + } yield diff + } + object CDBVType extends Enumeration(1) { - val SetCDBV, mapSetCDBV, mapValueAddCDBV, mapValueMinusCDBV = Value + val SetCDBV, mapSetCDBV, mapValueAddCDBV, mapValueMinusCDBV, stateValueAddCDBV, stateValueMinusCDBV = Value } private def checkCDBVBytes(bytes: Array[Byte], dLength: Int, stateVarOrMapSize: Int): Boolean = @@ -90,6 +132,10 @@ object CDBVOpcDiff extends OpcDiffer { mapValueAdd(context)(context.stateMap(bytes(1)), data(bytes(2)), data(bytes(3))) case (Some(CDBVType.mapValueMinusCDBV), 4) if checkCDBVBytes(bytes, data.length, context.stateMap.length) => mapValueMinus(context)(context.stateMap(bytes(1)), data(bytes(2)), data(bytes(3))) + case (Some(CDBVType.stateValueAddCDBV), 3) if checkCDBVBytes(bytes, data.length, context.stateVar.length) => + valueAdd(context)(context.stateVar(bytes(1)), data(bytes(2))) + case (Some(CDBVType.stateValueMinusCDBV), 3) if checkCDBVBytes(bytes, data.length, context.stateVar.length) => + valueMinus(context)(context.stateVar(bytes(1)), data(bytes(2))) case _ => Left(ContractInvalidOPCData) } } diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/CDBVROpcDiff.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/CDBVROpcDiff.scala index 3d92b1627..495932c87 100644 --- a/src/main/scala/vsys/blockchain/state/opcdiffs/CDBVROpcDiff.scala +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/CDBVROpcDiff.scala @@ -11,55 +11,72 @@ import scala.util.{Left, Right, Try} object CDBVROpcDiff extends OpcDiffer { + private def dbGet(context: ExecutionContext)(dbKey: ByteStr, dataTypeByte: Byte): Either[ValidationError, DataEntry] = { + context.state.contractInfo(dbKey) match { + case Some(v) => Right(v) + case _ => Left(GenericError("Can not find the data.")) + } + } + + private def dbGetOrDefault(context: ExecutionContext)(dbKey: ByteStr, dataTypeByte: Byte): Either[ValidationError, DataEntry] = { + context.state.contractInfo(dbKey) match { + case Some(v) => Right(v) + case _ if DataType(dataTypeByte) == DataType.Timestamp => + Right(DataEntry(Longs.toByteArray(0L), DataType.Timestamp)) + case _ if DataType(dataTypeByte) == DataType.Amount => + Right(DataEntry(Longs.toByteArray(context.state.contractNumInfo(dbKey)), DataType.Amount)) + case _ if DataType(dataTypeByte) == DataType.Boolean => + Right(DataEntry(Array(0.toByte), DataType.Boolean)) + case _ => Left(GenericError("Can not find the data.")) + } + } + def get(context: ExecutionContext)(stateVar: Array[Byte], dataStack: Seq[DataEntry], pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - if (!checkStateVar(stateVar, DataType(stateVar(1)))) { - Left(ContractInvalidStateVariable) - } else if (pointer > dataStack.length || pointer < 0) { - Left(ContractLocalVariableIndexOutOfRange) - } else { - context.state.contractInfo(ByteStr(context.contractId.bytes.arr ++ Array(stateVar(0)))) match { - case Some(v) => Right(dataStack.patch(pointer, Seq(v), 1)) - case _ => Left(ContractStateVariableNotDefined) - } - } + for { + _ <- Either.cond(checkStateVar(stateVar), (), ContractInvalidStateVariable) + _ <- Either.cond(pointer <= dataStack.length && pointer >= 0, (), ContractLocalVariableIndexOutOfRange) + combinedKey = ByteStr(context.contractId.bytes.arr ++ Array(stateVar(0))) + updatedDataEntry <- dbGet(context)(combinedKey, stateVar(1)).left.map(_ => ContractStateVariableNotDefined) + updatedSeq = dataStack.patch(pointer, Seq(updatedDataEntry), 1) + } yield updatedSeq } def mapGet(context: ExecutionContext)(stateMap: Array[Byte], keyValue: DataEntry, dataStack: Seq[DataEntry], pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - if (!checkStateMap(stateMap, keyValue.dataType, DataType(stateMap(2)))) { - Left(ContractInvalidStateMap) - } else if (pointer > dataStack.length || pointer < 0) { - Left(ContractLocalVariableIndexOutOfRange) - } else { - val combinedKey = context.contractId.bytes.arr ++ Array(stateMap(0)) ++ keyValue.bytes - context.state.contractInfo(ByteStr(combinedKey)) match { - case Some(v) => Right(dataStack.patch(pointer, Seq(v), 1)) - case _ => Left(ContractStateMapNotDefined) - } - } + for { + _ <- Either.cond(checkStateMap(stateMap, keyValue.dataType), (), ContractInvalidStateMap) + _ <- Either.cond(pointer <= dataStack.length && pointer >= 0, (), ContractLocalVariableIndexOutOfRange) + combinedKey = ByteStr(context.contractId.bytes.arr ++ Array(stateMap(0)) ++ keyValue.bytes) + updatedDataEntry <- dbGet(context)(combinedKey, stateMap(2)).left.map(_ => ContractStateMapNotDefined) + updatedSeq = dataStack.patch(pointer, Seq(updatedDataEntry), 1) + } yield updatedSeq } def mapGetOrDefault(context: ExecutionContext)(stateMap: Array[Byte], keyValue: DataEntry, dataStack: Seq[DataEntry], pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - if (!checkStateMap(stateMap, keyValue.dataType, DataType(stateMap(2)))) { - Left(ContractInvalidStateMap) - } else if (pointer > dataStack.length || pointer < 0) { - Left(ContractLocalVariableIndexOutOfRange) - } else { - val combinedKey = context.contractId.bytes.arr ++ Array(stateMap(0)) ++ keyValue.bytes - context.state.contractInfo(ByteStr(combinedKey)) match { - case Some(v) => Right(dataStack.patch(pointer, Seq(v), 1)) - case _ if (DataType(stateMap(2)) == DataType.Timestamp) => Right(dataStack.patch(pointer, Seq(DataEntry(Longs.toByteArray(0L), DataType.Timestamp)), 1)) - case _ if (DataType(stateMap(2)) == DataType.Amount) => Right(dataStack.patch(pointer, - Seq(DataEntry(Longs.toByteArray(context.state.contractNumInfo(ByteStr(combinedKey))), DataType.Amount)), 1)) - case _ => Left(ContractStateMapNotDefined) - } - } + for { + _ <- Either.cond(checkStateMap(stateMap, keyValue.dataType), (), ContractInvalidStateMap) + _ <- Either.cond(pointer <= dataStack.length && pointer >= 0, (), ContractLocalVariableIndexOutOfRange) + combinedKey = ByteStr(context.contractId.bytes.arr ++ Array(stateMap(0)) ++ keyValue.bytes) + updatedDataEntry <- dbGetOrDefault(context)(combinedKey, stateMap(2)).left.map(_ => ContractStateMapNotDefined) + updatedSeq = dataStack.patch(pointer, Seq(updatedDataEntry), 1) + } yield updatedSeq + } + + def getOrDefault(context: ExecutionContext)(stateVar: Array[Byte], dataStack: Seq[DataEntry], + pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { + for { + _ <- Either.cond(checkStateVar(stateVar), (), ContractInvalidStateVariable) + _ <- Either.cond(pointer <= dataStack.length && pointer >= 0, (), ContractLocalVariableIndexOutOfRange) + combinedKey = ByteStr(context.contractId.bytes.arr ++ Array(stateVar(0))) + updatedDataEntry <- dbGetOrDefault(context)(combinedKey, stateVar(1)).left.map(_ => ContractStateVariableNotDefined) + updatedSeq = dataStack.patch(pointer, Seq(updatedDataEntry), 1) + } yield updatedSeq } object CDBVRType extends Enumeration(1) { - val GetCDBVR, MapGetOrDefaultCDBVR, MapGetCDVVR = Value + val GetCDBVR, MapGetOrDefaultCDBVR, MapGetCDVVR, StateVarGetOrDefaultCDBVR = Value } private def checkCDBVRIndex(bytes: Array[Byte], id: Int, stateVarOrMapSize: Int): Boolean = @@ -73,6 +90,8 @@ object CDBVROpcDiff extends OpcDiffer { bytes(2) <= data.length => mapGetOrDefault(context)(context.stateMap(bytes(1)), data(bytes(2)), data, bytes(3)) case (Some(CDBVRType.MapGetCDVVR), 4) if checkCDBVRIndex(bytes, 1, context.stateMap.length) && bytes(2) >=0 && bytes(2) <= data.length => mapGet(context)(context.stateMap(bytes(1)), data(bytes(2)), data, bytes(3)) + case (Some(CDBVRType.StateVarGetOrDefaultCDBVR), 3) if checkCDBVRIndex(bytes, 1, context.stateVar.length) => + getOrDefault(context)(context.stateVar(bytes(1)), data, bytes(2)) case _ => Left(ContractInvalidOPCData) } } diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/CompareOpcDiff.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/CompareOpcDiff.scala index fa1d10f45..540ce5c2f 100644 --- a/src/main/scala/vsys/blockchain/state/opcdiffs/CompareOpcDiff.scala +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/CompareOpcDiff.scala @@ -1,40 +1,62 @@ package vsys.blockchain.state.opcdiffs -import com.google.common.primitives.Longs import vsys.blockchain.transaction.ValidationError import vsys.blockchain.transaction.ValidationError._ import vsys.blockchain.contract.{DataEntry, DataType, ExecutionContext} +import vsys.blockchain.contract.DataType._ +import vsys.blockchain.contract.DataEntry.ConvertHelper._ +import vsys.blockchain.state.opcdiffs.OpcDiffer._ +import scala.language.implicitConversions import scala.util.{Left, Right, Try} object CompareOpcDiff extends OpcDiffer { - def geq(context: ExecutionContext)(x: DataEntry, y: DataEntry, dataStack: Seq[DataEntry], - pointer: Byte): Either[ValidationError, Seq[DataEntry]] = { - if (x.dataType == y.dataType) { - val supportList = List(DataType.Amount, DataType.Timestamp) - supportList.find(a => a == x.dataType) match { - case Some(_) => { - val xValue = Longs.fromByteArray(x.data) - val yValue = Longs.fromByteArray(y.data) - if (xValue >= yValue) { - Right(dataStack.patch(pointer, Seq(DataEntry(Array(1.toByte), DataType.Boolean)), 1)) - } else { - Right(dataStack.patch(pointer, Seq(DataEntry(Array(0.toByte), DataType.Boolean)), 1)) - } - } - case None => Left(ContractUnsupportedOPC) - } + case class NumComparator(int: (Int, Int) => Boolean, long: (Long, Long) => Boolean, bigInt: (BigInt, BigInt) => Boolean) + + val ge = NumComparator(_ >= _, _ >= _, _ >= _) + val gt = NumComparator(_ > _, _ > _, _ > _) + val le = NumComparator(_ <= _, _ <= _, _ <= _) + val lt = NumComparator(_ < _, _ < _, _ < _) + val _eq = NumComparator(_ == _, _ == _, _ == _) + val _ne = NumComparator(_ != _, _ != _, _ != _) + + private implicit def booleanToDataEntry(b: Boolean): DataEntry = DataEntry(Array((if(b) 1 else 0).toByte), DataType.Boolean) + + def numBiComparation(x: DataEntry, y: DataEntry, comparator: NumComparator): Either[ValidationError, DataEntry] = + if (x.dataType == y.dataType) x.dataType match { + case Int32 => Right(comparator.int(x, y)) + case Amount => Right(comparator.long(x, y)) + case Timestamp => Right(comparator.long(x, y)) + case BigInteger => Right(comparator.bigInt(x, y)) + case _ => Left(ContractUnsupportedOPC) } else Left(ContractDataTypeMismatch) - } object CompareType extends Enumeration { - val Geq = Value(1) + class CompareTypeVal(compareType: Int) extends Val(compareType) { def *(n: Int): Int = n * compareType } + sealed case class NumCompareTypeVal(compareType: Int, op: NumComparator) extends CompareTypeVal(compareType) + sealed case class BytesCompareTypeVal(compareType: Int, op: (DataEntry, DataEntry) => Boolean) extends CompareTypeVal(compareType) + val Ge = NumCompareTypeVal(1, ge) + val Gt = NumCompareTypeVal(2, gt) + val Le = NumCompareTypeVal(3, le) + val Lt = NumCompareTypeVal(4, lt) + val Eq = NumCompareTypeVal(5, _eq) + val Ne = NumCompareTypeVal(6, _ne) + val Beq = BytesCompareTypeVal(7, _ == _) + val Bne = BytesCompareTypeVal(8, _ != _) } + def differ(bytes: Array[Byte], data: Seq[DataEntry], t: CompareType.NumCompareTypeVal) = + updateStack(data, bytes.last, numBiComparation(data(bytes(1)), data(bytes(2)), t.op)) + + def differ(bytes: Array[Byte], data: Seq[DataEntry], t: CompareType.BytesCompareTypeVal) = + updateStack(data, bytes.last, Right(t.op(data(bytes(1)), data(bytes(2))))) + + private val ensured = (b: Array[Byte], d: Seq[DataEntry]) => b.length == 4 && checkIndexes(b, d, Seq(1, 2)) override def parseBytesDt(context: ExecutionContext)(bytes: Array[Byte], data: Seq[DataEntry]): Either[ValidationError, Seq[DataEntry]] = bytes.headOption.flatMap(f => Try(CompareType(f)).toOption) match { - case Some(CompareType.Geq) if bytes.length == 4 => geq(context)(data(bytes(1)), data(bytes(2)), data, bytes(3)) + case Some(t: CompareType.NumCompareTypeVal) if ensured(bytes, data) => differ(bytes, data, t) + case Some(t: CompareType.BytesCompareTypeVal) if ensured(bytes, data) => differ(bytes, data, t) case _ => Left(ContractInvalidOPCData) } } diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/IfOpcDiff.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/IfOpcDiff.scala new file mode 100644 index 000000000..61108d439 --- /dev/null +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/IfOpcDiff.scala @@ -0,0 +1,67 @@ +package vsys.blockchain.state.opcdiffs + +import cats.implicits._ +import vsys.blockchain.contract.{DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.reader.CompositeStateReader +import vsys.blockchain.transaction.ValidationError +import vsys.blockchain.transaction.ValidationError._ +import vsys.utils.serialization.Deser + +import scala.util.{Left, Right, Try, Failure, Success} + +object IfOpcDiff extends OpcDiffer { + + def executeOpcBlock(context: ExecutionContext, opcBlock: DataEntry, dataStack: Seq[DataEntry]): Either[ValidationError, (OpcDiff, Seq[DataEntry])] = { + opcBlock.dataType match { + case DataType.OpcBlock => { + val opcBytes = DataType.OpcBlock.deserializer(opcBlock.data) + if(opcBytes.length == 0) Right((OpcDiff.empty, dataStack)) + else Try(Deser.parseArrays(opcBytes)) match { + case Success(opclines) => { + opclines.foldLeft(Right((OpcDiff.empty, dataStack)): Either[ValidationError, (OpcDiff, Seq[DataEntry])]) { + case (acc, opc) => acc.flatMap { case (oldDiff, oldData) => { + val oldState = new CompositeStateReader(context.state, oldDiff.asBlockDiff(context.height, context.transaction)) + OpcDiffer(context.copy(state = oldState))(opc, oldData) map { case (opcDiff, newData) => (oldDiff.combine(opcDiff), newData) } + }} + } + } + case Failure(_) => Left(ContractInvalidOPCData) + } + } + case _ => Left(ContractDataTypeMismatch) + } + } + + def runCondition(cond: DataEntry): Either[ValidationError, Boolean] = { + cond.dataType match { + case DataType.Boolean => Right(cond.data sameElements Array(1.toByte)) + case _ => Left(ContractDataTypeMismatch) + } + } + + object IfType extends Enumeration(1) { + val If, IfElse = Value + } + + override def parseBytesDf(context: ExecutionContext)(bytes: Array[Byte], data: Seq[DataEntry]): Either[ValidationError, OpcDiff] = + throw new UnsupportedOperationException(s"Could not be reached method") + + override def parseBytesDt(context: ExecutionContext)(bytes: Array[Byte], data: Seq[DataEntry]): Either[ValidationError, Seq[DataEntry]] = + throw new UnsupportedOperationException(s"Could not be reached method") + + override def parseBytes(context: ExecutionContext)(bytes: Array[Byte], data: Seq[DataEntry]): Either[ValidationError, (OpcDiff, Seq[DataEntry])] = + (bytes.headOption.flatMap(f => Try(IfType(f)).toOption), bytes.length) match { + case (Some(IfType.If), 3) if checkData(bytes, data.length, 2, false) => { + runCondition(data(bytes(1))).flatMap(cond => + if(cond) executeOpcBlock(context, data(bytes(2)), data) + else Right((OpcDiff.empty, data)) + ) + } + case (Some(IfType.IfElse), 4) if checkData(bytes, data.length, 3, false) => { + runCondition(data(bytes(1))).flatMap(cond => + if(cond) executeOpcBlock(context, data(bytes(2)), data) + else executeOpcBlock(context, data(bytes(3)), data) + ) + } + } +} diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/LoadOpcDiff.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/LoadOpcDiff.scala index 74d9ebd2e..7c83a8257 100644 --- a/src/main/scala/vsys/blockchain/state/opcdiffs/LoadOpcDiff.scala +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/LoadOpcDiff.scala @@ -60,7 +60,7 @@ object LoadOpcDiff extends OpcDiffer { if (pointer > dataStack.length || pointer < 0) { Left(ContractLocalVariableIndexOutOfRange) } else { - Right(dataStack.patch(pointer, Seq(DataEntry(Longs.toByteArray(context.height), DataType.Int32)), 1)) + Right(dataStack.patch(pointer, Seq(DataEntry(Ints.toByteArray(context.height), DataType.Int32)), 1)) } } diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/OpcDiff.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/OpcDiff.scala index 139dbb542..31a2b659a 100644 --- a/src/main/scala/vsys/blockchain/state/opcdiffs/OpcDiff.scala +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/OpcDiff.scala @@ -4,7 +4,7 @@ import cats.Monoid import cats.implicits._ import vsys.blockchain.state.{BlockDiff, ByteStr, Diff, Portfolio} import vsys.blockchain.transaction.Transaction -import vsys.account.{Account, Address} +import vsys.account.Account case class OpcDiff(contractDB: Map[ByteStr, Array[Byte]] = Map.empty, contractNumDB: Map[ByteStr, Long] = Map.empty, @@ -12,7 +12,7 @@ case class OpcDiff(contractDB: Map[ByteStr, Array[Byte]] = Map.empty, contractTokens: Map[ByteStr, Int] = Map.empty, tokenDB: Map[ByteStr, Array[Byte]] = Map.empty, tokenAccountBalance: Map[ByteStr, Long] = Map.empty, - relatedAddress: Map[Address, Boolean] = Map.empty, + relatedAddress: Map[Account, Boolean] = Map.empty, portfolios: Map[Account, Portfolio] = Map.empty ) { diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/OpcDiffer.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/OpcDiffer.scala index 477e33d66..1447952c8 100644 --- a/src/main/scala/vsys/blockchain/state/opcdiffs/OpcDiffer.scala +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/OpcDiffer.scala @@ -2,7 +2,7 @@ package vsys.blockchain.state.opcdiffs import com.google.common.primitives.Ints import vsys.blockchain.transaction.ValidationError -import vsys.blockchain.transaction.ValidationError.ContractUnsupportedOPC +import vsys.blockchain.transaction.ValidationError._ import vsys.blockchain.contract.{DataEntry, DataType, ExecutionContext} import scala.util.Try @@ -48,6 +48,7 @@ object OpcDiffer { val ReturnOpc = OpcTypeVal(9, ReturnOpcDiff) val CompareOpc = OpcTypeVal(10, CompareOpcDiff) val BasicOpc = OpcTypeVal(11, BasicOpcDiff) + val IfOpc = OpcTypeVal(12, IfOpcDiff) def fromByte(implicit b: Byte): Option[OpcTypeVal] = Try(OpcType(b).asInstanceOf[OpcTypeVal]).toOption @@ -56,4 +57,12 @@ object OpcDiffer { def apply(context: ExecutionContext)(opc: Array[Byte], data: Seq[DataEntry]): Either[ValidationError, (OpcDiff, Seq[DataEntry])] = opc.headOption.flatMap(OpcType.fromByte(_)).toRight(ContractUnsupportedOPC).flatMap(_.opcDiffer.parseBytes(context)(opc.tail, data)) + // res is call-by-name + def updateStack(dataStack: Seq[DataEntry], pointer: Byte, res: => Either[ValidationError, DataEntry]): Either[ValidationError, Seq[DataEntry]] = + if (pointer > dataStack.length || pointer < 0) Left(ContractLocalVariableIndexOutOfRange) + else res.map(r => dataStack.patch(pointer, Seq(r), 1)) + + // indexes.max < bytes.length should be ensured before calling this + def checkIndexes(bytes: Array[Byte], dataStack: Seq[DataEntry], indexes: Seq[Int]): Boolean = + indexes.map(bytes(_)).filterNot(idx => idx < dataStack.length && idx >= 0).isEmpty } diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/SystemTransferDiff.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/SystemTransferDiff.scala index e7361a007..59a323d5a 100644 --- a/src/main/scala/vsys/blockchain/state/opcdiffs/SystemTransferDiff.scala +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/SystemTransferDiff.scala @@ -54,7 +54,7 @@ object SystemTransferDiff extends OpcDiffer { _ <- Either.cond(Try(Math.addExact(transferAmount, recipientBalance)).isSuccess, (), OverflowError) _ <- Either.cond(transferAmount >= 0, (), ContractInvalidAmount) senderCallDiff <- getTriggerCallOpcDiff(context, OpcDiff.empty, sender, recipient, amount, sysTokenId, CallType.Trigger, 2) - senderRelatedAddress = if (sender.dataType == DataType.Address) Map(Address.fromBytes(sender.data).explicitGet() -> true) else Map[Address, Boolean]() + senderRelatedAddress = if (sender.dataType == DataType.Address) Map(Account.fromBytes(sender.data,0).explicitGet()._1 -> true) else Map[Account, Boolean]() senderPortDiff: Map[Account, Portfolio] = Map( senderAddr._1 -> Portfolio( balance = -transferAmount, @@ -63,7 +63,7 @@ object SystemTransferDiff extends OpcDiffer { senderDiff = OpcDiff(relatedAddress = senderRelatedAddress, portfolios = senderPortDiff) senderTotalDiff = OpcDiff.opcDiffMonoid.combine(senderCallDiff, senderDiff) recipientCallDiff <- getTriggerCallOpcDiff(context, senderTotalDiff, sender, recipient, amount, sysTokenId, CallType.Trigger, 1) - recipientRelatedAddress = if (recipient.dataType == DataType.Address) Map(Address.fromBytes(recipient.data).explicitGet() -> true) else Map[Address, Boolean]() + recipientRelatedAddress = if (recipient.dataType == DataType.Address) Map(Account.fromBytes(recipient.data,0).explicitGet()._1 -> true) else Map[Account, Boolean]() recipientPortDiff: Map[Account, Portfolio] = Map( recipientAddr._1 -> Portfolio( balance = transferAmount, diff --git a/src/main/scala/vsys/blockchain/state/opcdiffs/TDBAOpcDiff.scala b/src/main/scala/vsys/blockchain/state/opcdiffs/TDBAOpcDiff.scala index 80131c2e3..7c9db7a78 100644 --- a/src/main/scala/vsys/blockchain/state/opcdiffs/TDBAOpcDiff.scala +++ b/src/main/scala/vsys/blockchain/state/opcdiffs/TDBAOpcDiff.scala @@ -3,7 +3,7 @@ package vsys.blockchain.state.opcdiffs import cats.Monoid import com.google.common.primitives.{Bytes, Ints, Longs} import vsys.blockchain.state._ -import vsys.account.{Address, ContractAccount} +import vsys.account.{Account, Address, ContractAccount} import vsys.blockchain.transaction.ValidationError import vsys.blockchain.transaction.ValidationError._ import vsys.account.ContractAccount.tokenIdFromBytes @@ -131,12 +131,12 @@ object TDBAOpcDiff extends OpcDiffer { // relatedContract needed for { senderCallDiff <- getTriggerCallOpcDiff(context, OpcDiff.empty, sender, recipient, amount, tokenIdDataEntry, CallType.Trigger, 2) - senderRelatedAddress = if (sender.dataType == DataType.Address) Map(Address.fromBytes(sender.data).explicitGet() -> true) else Map[Address, Boolean]() + senderRelatedAddress = Map[Account, Boolean](Account.fromBytes(sender.data,0).explicitGet()._1 -> true) senderDiff = OpcDiff(relatedAddress = senderRelatedAddress, tokenAccountBalance = Map(senderBalanceKey -> -transferAmount)) senderTotalDiff = OpcDiff.opcDiffMonoid.combine(senderCallDiff, senderDiff) recipientCallDiff <- getTriggerCallOpcDiff(context, senderTotalDiff, sender, recipient, amount, tokenIdDataEntry, CallType.Trigger, 1) - recipientRelatedAddress = if (recipient.dataType == DataType.Address) Map(Address.fromBytes(recipient.data).explicitGet() -> true) else Map[Address, Boolean]() + recipientRelatedAddress = Map[Account, Boolean](Account.fromBytes(recipient.data,0).explicitGet()._1 -> true) recipientDiff = OpcDiff(relatedAddress = recipientRelatedAddress, tokenAccountBalance = Map(recipientBalanceKey -> transferAmount)) returnDiff = Monoid.combineAll(Seq(senderTotalDiff, recipientCallDiff, recipientDiff)) diff --git a/src/main/scala/vsys/blockchain/transaction/MintingTransaction.scala b/src/main/scala/vsys/blockchain/transaction/MintingTransaction.scala index a34a316e7..95c0bc62a 100644 --- a/src/main/scala/vsys/blockchain/transaction/MintingTransaction.scala +++ b/src/main/scala/vsys/blockchain/transaction/MintingTransaction.scala @@ -9,13 +9,17 @@ import vsys.account.Address import vsys.utils.crypto.hash.FastCryptographicHash import vsys.blockchain.transaction.TransactionParser._ import vsys.blockchain.consensus.SPoSCalc._ - +import io.swagger.annotations._ import scala.util.{Failure, Success, Try} -case class MintingTransaction private(recipient: Address, +case class MintingTransaction private(@ApiModelProperty(required = true) + recipient: Address, + @ApiModelProperty(required = true) amount: Long, + @ApiModelProperty(required = true) timestamp: Long, - currentBlockHeight: Int) extends NonFeeTransaction with AmountInvolved { + @ApiModelProperty(required = true) + currentBlockHeight: Int) extends NonFeeTransaction { val transactionType = TransactionType.MintingTransaction @@ -39,7 +43,7 @@ case class MintingTransaction private(recipient: Address, ) override lazy val bytes: Array[Byte] = Bytes.concat(toSign) - + } object MintingTransaction extends TransactionParser { diff --git a/src/main/scala/vsys/network/BasicMessagesRepo.scala b/src/main/scala/vsys/network/BasicMessagesRepo.scala index 002895752..7fddb0fbb 100755 --- a/src/main/scala/vsys/network/BasicMessagesRepo.scala +++ b/src/main/scala/vsys/network/BasicMessagesRepo.scala @@ -202,7 +202,7 @@ object TransactionMessageSpec extends MessageSpec[Transaction] { override val messageName: String = "Transaction message" - override val maxLength = 4096 + override val maxLength = 8192 override def deserializeData(bytes: Array[Byte]): Try[Transaction] = TransactionParser.parseBytes(bytes) diff --git a/src/main/scala/vsys/network/PeerDatabaseImpl.scala b/src/main/scala/vsys/network/PeerDatabaseImpl.scala index c29191a91..e8ef19902 100755 --- a/src/main/scala/vsys/network/PeerDatabaseImpl.scala +++ b/src/main/scala/vsys/network/PeerDatabaseImpl.scala @@ -75,7 +75,7 @@ class PeerDatabaseImpl(settings: NetworkSettings) extends PeerDatabase with Auto removeObsoleteRecords(suspension, settings.suspensionResidenceTime.toMillis).keySet().asScala.toSet override def detailedBlacklist: Map[InetAddress, (Long, String)] = - removeObsoleteRecords(suspension, settings.blackListResidenceTime.toMillis).asScala.map{ + removeObsoleteRecords(blacklist, settings.blackListResidenceTime.toMillis).asScala.map{ case ((h, t)) => h -> ((t, Option(reasons.get(h)).getOrElse(""))) }.toMap diff --git a/src/main/scala/vsys/network/PeerInfo.scala b/src/main/scala/vsys/network/PeerInfo.scala index c2d85221f..ba1c7946f 100644 --- a/src/main/scala/vsys/network/PeerInfo.scala +++ b/src/main/scala/vsys/network/PeerInfo.scala @@ -1,6 +1,12 @@ package vsys.network import java.net.InetSocketAddress +import io.swagger.annotations._ + +case class PeerNetworkConnection(@ApiModelProperty(required = true, example = "127.0.0.1") + host: String, + @ApiModelProperty(required = true, example = "0") + port: Int) case class PeerInfo( remoteAddress: InetSocketAddress, diff --git a/src/main/scala/vsys/settings/BlockchainSettings.scala b/src/main/scala/vsys/settings/BlockchainSettings.scala index da0acc23d..620432cc6 100644 --- a/src/main/scala/vsys/settings/BlockchainSettings.scala +++ b/src/main/scala/vsys/settings/BlockchainSettings.scala @@ -8,7 +8,8 @@ import net.ceedubs.ficus.readers.EnumerationReader._ case class FunctionalitySettings(numOfSlots: Int, mintingSpeed: Int, allowContractTransactionAfterHeight: Int, - allowDepositWithdrawContractAfterHeight: Int) + allowDepositWithdrawContractAfterHeight: Int, + allowExchangeContractAfterHeight: Int) object FunctionalitySettings { val MAINNET = FunctionalitySettings( @@ -17,7 +18,8 @@ object FunctionalitySettings { //TODO //set the value allowContractTransactionAfterHeight = 6100000, - allowDepositWithdrawContractAfterHeight = 13140520) + allowDepositWithdrawContractAfterHeight = 13140520, + allowExchangeContractAfterHeight = 25416184) val TESTNET = FunctionalitySettings( numOfSlots = 60, @@ -25,12 +27,14 @@ object FunctionalitySettings { //TODO //set the value allowContractTransactionAfterHeight = 4236000, - allowDepositWithdrawContractAfterHeight = 12550000) + allowDepositWithdrawContractAfterHeight = 12550000, + allowExchangeContractAfterHeight = 18030000) val configPath = "vsys.blockchain.custom.functionality" } -case class StateSettings(txTypeAccountTxIds: Boolean) +case class StateSettings(txTypeAccountTxIds: Boolean, + txContractTxIds: Boolean) object StateSettings { val configPath = "vsys.blockchain.state" diff --git a/src/test/scala/tools/ContractTranslator.scala b/src/test/scala/tools/ContractTranslator.scala index a917483be..9d3db340f 100644 --- a/src/test/scala/tools/ContractTranslator.scala +++ b/src/test/scala/tools/ContractTranslator.scala @@ -16,12 +16,14 @@ import vsys.blockchain.state.opcdiffs.TDBAROpcDiff.TDBARType import vsys.blockchain.state.opcdiffs.TDBOpcDiff.TDBType import vsys.blockchain.state.opcdiffs.TDBROpcDiff.TDBRType import vsys.blockchain.state.opcdiffs.SystemTransferDiff.TransferType +import vsys.blockchain.state.opcdiffs.IfOpcDiff.IfType import vsys.utils.serialization.Deser import scala.util.{Failure, Success, Try} object ContractTranslator extends App { - val bytes = ContractPaymentChannel.contract.bytes.arr + val bytes = ContractVStableSwap.contract.bytes.arr + val showHex = false println(Base58.encode(bytes)) print("Contract Bytes Length:") @@ -62,7 +64,8 @@ object ContractTranslator extends App { val textual = Deser.parseArrays(bytes.slice(last, bytes.length)) val textualStr = textualFromBytes(textual) - val dataTypeList = Seq("PublicKey", "Address", "Amount", "Int32", "ShortText", "ContractAccount", "Account", "TokenId", "Timestamp", "Boolean", "ShortBytes") + val dataTypeList = Seq("DataTypeObj", "PublicKey", "Address", "Amount", "Int32", "ShortText", "ContractAccount", + "Account", "TokenId", "Timestamp", "Boolean", "ShortBytes", "Balance", "OpcBlock", "BigInt") val triggerTypeList = Seq("onInit trigger", "onDeposit trigger", "onWithdraw trigger") @@ -95,7 +98,7 @@ object ContractTranslator extends App { // print input variables val inputVarList = ftString(idx).slice(retNum + 1, ftString(idx).length) List.range(0, listParaTypes.size).foreach { i => - print(dataTypeList(listParaTypes(i) - 1) + " " + inputVarList(i)) + print(dataTypeList(listParaTypes(i)) + " " + inputVarList(i)) if (i < listParaTypes.size - 1) print(", ") } print(")") @@ -103,35 +106,39 @@ object ContractTranslator extends App { //print return variables if (listReturnTypes.length == 1) { print(" return ") - println(dataTypeList(listReturnTypes(0) - 1) + " " + ftString(idx)(0)) - } else if (listReturnTypes.length > 1){ + println(dataTypeList(listReturnTypes(0)) + " " + ftString(idx)(0)) + } else if (listReturnTypes.length > 1) { print(" return (") List.range(0, retNum).foreach { i => - print(dataTypeList(listReturnTypes(i) - 1) + " " + ftString(idx)(i)) + print(dataTypeList(listReturnTypes(i)) + " " + ftString(idx)(i)) if (i < listParaTypes.size - 1) print(", ") } println(")") } else println() // print function or trigger head opc - print("| ") - print(convertBytesToHex(Shorts.toByteArray(funcIdx))) - print("| ") - print(convertBytesToHex(Array(funcType))) - print("| ") - print(convertBytesToHex(listParaTypes)) - print("| ") - print(convertBytesToHex(listReturnTypes)) - println("|") + if (showHex) { + print("| ") + print(convertBytesToHex(Shorts.toByteArray(funcIdx))) + print("| ") + print(convertBytesToHex(Array(funcType))) + print("| ") + print(convertBytesToHex(listParaTypes)) + print("| ") + print(convertBytesToHex(listReturnTypes)) + println("|") + } // print opc lines listOpcLines.foreach { case l => print(" ") val x = opcToName(l, inputVarList) println(x) - print(" | " + convertBytesToHex(l.slice(0, 2))) - print("| ") - println(convertBytesToHex(l.slice(2, l.length)) + "|") + if (showHex) { + print(" | " + convertBytesToHex(l.slice(0, 2))) + print("| ") + println(convertBytesToHex(l.slice(2, l.length)) + "|") + } } println() } @@ -168,7 +175,7 @@ object ContractTranslator extends App { } def printTextual(t: Try[(Seq[Seq[String]], Seq[Seq[String]], Seq[String], Seq[Seq[String]])]): Unit = { - if (t.isFailure) println("Invalid Texture") + if (t.isFailure) println("Invalid Textural") else { val r = t.get val (trig, desc, stav, stam) = r @@ -178,30 +185,49 @@ object ContractTranslator extends App { printSeqSeqString(desc) println("State Variables:") List.range(0, stav.size).foreach { i => - println("%02d".format(i) + " | " + stav(i) + ": " + dataTypeList(stateVar(i)(1) - 1)) + println("%02d".format(i) + " | " + stav(i) + ": " + dataTypeList(stateVar(i)(1))) } println("State Maps:") List.range(0, stam.size).foreach { i => - println("%02d".format(i) + " | " + stam(i).head + " | " + stam(i)(1) + " -> " + stam(i)(2) + " | Map[" + dataTypeList(stateMap(i)(1) - 1) + ", " + dataTypeList(stateMap(i)(2) - 1) + "]") + println("%02d".format(i) + " | " + stam(i).head + " | " + stam(i)(1) + " -> " + stam(i)(2) + " | Map[" + dataTypeList(stateMap(i)(1)) + ", " + dataTypeList(stateMap(i)(2)) + "]") } } } - def strDataEntry(s: Array[Byte]): String = { + def strDataEntry(s: Array[Byte], nameList: Seq[String]): String = { var res = "DataEntry(" if (s(0) == DataType.Int32.id.toByte) { res = res + Ints.fromByteArray(s.tail).toString } else if (s(0) == DataType.Amount.id.toByte || s(0) == DataType.Timestamp.id.toByte) { res = res + Longs.fromByteArray(s.tail).toString + } else if (s(0) == DataType.BigInteger.id.toByte) { + res = res + BigInt(s.drop(3)).toString() } else if (s(0) == DataType.Boolean.id.toByte) { if (s(1) == 1.toByte) { res = res + "true" } else if (s(1) == 0.toByte) { res = res + "false" } + } else if (s(0) == DataType.OpcBlock.id.toByte) { + res = res + "Seq(\n" + val ifOpcLines = Deser.parseArrays(s.drop(3)) + ifOpcLines.foreach { case l => + res = res + " " + val x = opcToName(l, nameList) + res = res + x + "\n" + if (showHex) { + res = res + " | " + convertBytesToHex(l.slice(0, 2)) + "| " + res = res + convertBytesToHex(l.slice(2, l.length)) + "|\n" + } + } + res = res + "\n" + res = res + " )" + } else if (s(0) == DataType.DataTypeObj.id.toByte) { + res = res + "DataType." + res = res + dataTypeList(s(1).toInt) } res = res + ", " - res = res + dataTypeList(s(0).toInt - 1) + res = res + dataTypeList(s(0).toInt) res = res + ")" res } @@ -297,8 +323,11 @@ object ContractTranslator extends App { y match { case opcType: Byte if opcType == CDBVType.SetCDBV.id => "operation.db.setVariable(" + "db." + stateNameList(data(2)) + ", " + nameList(data(3)) + ")" case opcType: Byte if opcType == CDBVType.mapValueAddCDBV.id => "operation.db.mapValueAdd(" + "db." + stateMapList(data(2)) + ", " + nameList(data(3)) + ", " + nameList(data(4)) + ")" - case opcType: Byte if opcType == CDBVType.mapValueMinusCDBV.id => "operation.db.mapValueMinus(" + "db." + stateMapList(data(2)) + ", " + nameList(data(3)) + ", " + nameList(data(4)) + ")" - case opcType: Byte if opcType == CDBVType.mapSetCDBV.id => "operation.db.mapSet(" + "db." + stateMapList(data(2)) + ", " + nameList(data(3)) + ", " + nameList(data(4)) + ")" + case opcType: Byte if opcType == CDBVType.mapValueMinusCDBV.id => "operation.db.mapValueMinus(" + "db." + stateMapList(data(2)) + ", " + nameList(data(3)) + ", " + nameList(data(4)) + ")" + case opcType: Byte if opcType == CDBVType.mapSetCDBV.id => "operation.db.mapSet(" + "db." + stateMapList(data(2)) + ", " + nameList(data(3)) + ", " + nameList(data(4)) + ")" + case opcType: Byte if opcType == CDBVType.stateValueAddCDBV.id => "operation.db.stateValueAdd(" + "db." + stateNameList(data(2)) + ", " + nameList(data(3)) + ")" + case opcType: Byte if opcType == CDBVType.stateValueMinusCDBV.id => "operation.db.stateValueMinus(" + "db." + stateNameList(data(2)) + ", " + nameList(data(3)) + ")" + case _ => "--- invalid opc code ---" } @@ -307,6 +336,7 @@ object ContractTranslator extends App { case opcType: Byte if opcType == CDBVRType.GetCDBVR.id => nameList(data(3)) + " = operation.db.getVariable(db." + stateNameList(data(2)) + ")" case opcType: Byte if opcType == CDBVRType.MapGetOrDefaultCDBVR.id => nameList(data(4)) + " = operation.db.mapGetOrDefault(db." + stateMapList(data(2)) + ", " + nameList(data(3)) + ")" case opcType: Byte if opcType == CDBVRType.MapGetCDVVR.id => nameList(data(4)) + " = operation.db.mapGet(db." + stateMapList(data(2)) + ", " + nameList(data(3)) + ")" + case opcType: Byte if opcType == CDBVRType.StateVarGetOrDefaultCDBVR.id => nameList(data(3)) + " = operation.db.stateGetOrDefault(db." + stateNameList(data(2)) + ")" case _ => "--- invalid opc code ---" } @@ -326,8 +356,8 @@ object ContractTranslator extends App { case opcType: Byte if opcType == OpcType.TDBAOpc.id => y match { - case opcType: Byte if opcType == TDBAType.DepositTDBA.id => "operation.token.deposit(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" - case opcType: Byte if opcType == TDBAType.WithdrawTDBA.id => "operation.token.withdraw(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" + case opcType: Byte if opcType == TDBAType.DepositTDBA.id => "operation.token.issue(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" + case opcType: Byte if opcType == TDBAType.WithdrawTDBA.id => "operation.token.brun(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" case opcType: Byte if opcType == TDBAType.TransferTDBA.id => "operation.token.transfer(" + nameList(data(2)) + ", " + nameList(data(3)) + ", " + nameList(data(4)) + ")" case _ => "--- invalid opc code ---" } @@ -342,7 +372,10 @@ object ContractTranslator extends App { case opcType: Byte if opcType == OpcType.CompareOpc.id => y match { - case opcType: Byte if opcType == CompareType.Geq.id => nameList(data(4)) + " = operation.compare.greater(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" + case opcType: Byte if opcType == CompareType.Ge.id => nameList(data(4)) + " = operation.compare.greaterEqual(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" + case opcType: Byte if opcType == CompareType.Gt.id => nameList(data(4)) + " = operation.compare.greater(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" + case opcType: Byte if opcType == CompareType.Beq.id => nameList(data(4)) + " = operation.compare.bytesEqual(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" + case opcType: Byte if opcType == CompareType.Le.id => nameList(data(4)) + " = operation.compare.lessEqual(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" case _ => "--- invalid opc code ---" } @@ -355,9 +388,16 @@ object ContractTranslator extends App { case opcType: Byte if opcType == BasicType.Minimum.id => nameList(data(4)) + " = operation.basic.minimum(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" case opcType: Byte if opcType == BasicType.Maximum.id => nameList(data(4)) + " = operation.basic.maximum(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" case opcType: Byte if opcType == BasicType.Concat.id => nameList(data(4)) + " = operation.basic.concat(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" - case opcType: Byte if opcType == BasicType.ConstantGet.id => nameList(data.last) + " = operation.basic.getConstant(" + strDataEntry(data.slice(2, data.length - 1)) + ")" + case opcType: Byte if opcType == BasicType.Convert.id => nameList(data(4)) + " = operation.basic.convert(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" + case opcType: Byte if opcType == BasicType.ConstantGet.id => nameList(data.last) + " = operation.basic.getConstant(" + strDataEntry(data.slice(2, data.length - 1), nameList) + ")" + case opcType: Byte if opcType == BasicType.SqrtBigInt.id => nameList(data(3)) + " = operation.basic.sqrt(" + nameList(data(2)) + ")" + case opcType: Byte if opcType == BasicType.And.id => nameList(data(4)) + " = operation.basic.and(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" case _ => "--- invalid opc code ---" } + case opcType: Byte if opcType == OpcType.IfOpc.id => + y match { + case opcType: Byte if opcType == IfType.If.id => "operation.if(" + nameList(data(2)) + ", " + nameList(data(3)) + ")" + } case _ => "--- invalid opc code ---" diff --git a/src/test/scala/vsys/api/http/TransactionsRouteSpec.scala b/src/test/scala/vsys/api/http/TransactionsRouteSpec.scala index ae5f4e36b..6813ccddf 100644 --- a/src/test/scala/vsys/api/http/TransactionsRouteSpec.scala +++ b/src/test/scala/vsys/api/http/TransactionsRouteSpec.scala @@ -34,8 +34,14 @@ class TransactionsRouteSpec extends RouteSpec("/transactions") routePath("/address/{address}/limit/{limit}") - { "handles invalid address" in { - forAll(bytes32gen, choose(1, MaxTransactionsPerRequest)) { case (bytes, limit) => - Get(routePath(s"/address/${Base58.encode(bytes)}/limit/$limit")) ~> route should produce(InvalidAddress) + forAll(fakeAddressGen, choose(1, MaxTransactionsPerRequest)) { case (bytes, limit) => + Get(routePath(s"/address/$bytes/limit/$limit")) ~> route should produce(InvalidAddress) + } + } + + "handles invalid contract address" in { + forAll(fakeAccountGen, choose(1, MaxTransactionsPerRequest)) { case (bytes, limit) => + Get(routePath(s"/address/$bytes/limit/$limit")) ~> route should produce(InvalidContractAddress) } } diff --git a/src/test/scala/vsys/blockchain/contract/ContractGenHelper.scala b/src/test/scala/vsys/blockchain/contract/ContractGenHelper.scala index 33bd4e193..913de29eb 100644 --- a/src/test/scala/vsys/blockchain/contract/ContractGenHelper.scala +++ b/src/test/scala/vsys/blockchain/contract/ContractGenHelper.scala @@ -42,7 +42,7 @@ object ContractGenHelper extends TransactionGen { val returnValue = Array(9.toByte, 1.toByte) - val compareGreater = Array(10.toByte, 1.toByte) + val compareGreaterEqual = Array(10.toByte, 1.toByte) val ENOUGH_AMT: Long = Long.MaxValue / 3 @@ -73,8 +73,8 @@ object ContractGenHelper extends TransactionGen { Bytes.concat(retType, paraType) } - def dataListGen(seqDataByte: Seq[Array[Byte]], seqDataType: Seq[DataType.Value]): Gen[Seq[DataEntry]] = - seqDataByte.zip(seqDataType).map { case (e1: Array[Byte], e2: DataType.Value) => DataEntry.create(e1, e2).explicitGet()} + def dataListGen(seqDataByte: Seq[Array[Byte]], seqDataType: Seq[DataType.DataTypeVal[_]]): Gen[Seq[DataEntry]] = + seqDataByte.zip(seqDataType).map { case (e1: Array[Byte], e2: DataType.DataTypeVal[_]) => DataEntry.create(e1, e2).explicitGet()} def basicContractTestGen(): Gen[(PrivateKeyAccount, Long, Long)] = for { master <- accountGen diff --git a/src/test/scala/vsys/blockchain/contract/DataEntrySpecification.scala b/src/test/scala/vsys/blockchain/contract/DataEntrySpecification.scala index 3091847e2..206b9343f 100644 --- a/src/test/scala/vsys/blockchain/contract/DataEntrySpecification.scala +++ b/src/test/scala/vsys/blockchain/contract/DataEntrySpecification.scala @@ -1,12 +1,13 @@ package vsys.blockchain.contract -import com.google.common.primitives.Longs +import com.google.common.primitives.{Ints, Longs} import vsys.blockchain.transaction.TransactionGen import org.scalacheck.Gen import org.scalatest.{Matchers, PropSpec} import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} import vsys.account.PublicKeyAccount import vsys.blockchain.transaction.ValidationError.InvalidDataEntry +import vsys.utils.serialization.Deser class DataEntrySpecification extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers with TransactionGen { @@ -19,9 +20,41 @@ class DataEntrySpecification extends PropSpec with PropertyChecks with Generator } yield DataEntry(data, DataType.Address) val validAmountEntryGen: Gen[DataEntry] = for { - data <- positiveLongGen.map(Longs.toByteArray(_)) + data <- positiveLongGen.map(Longs.toByteArray) } yield DataEntry(data, DataType.Amount) + val validInt32EntryGen: Gen[DataEntry] = for { + data <- positiveIntGen.map(Ints.toByteArray) + } yield DataEntry(data, DataType.Int32) + + val validShortTextEntryGen: Gen[DataEntry] = for { + data <- attachmentGen.map(Deser.serializeArray) + } yield DataEntry(data, DataType.ShortText) + + val validContractAccountEntryGen: Gen[DataEntry] = for { + data <- contractAccountGen.map(_.bytes.arr) + } yield DataEntry(data, DataType.ContractAccount) + + val validTimestampEntryGen: Gen[DataEntry] = for { + data <- timestampGen.map(Longs.toByteArray) + } yield DataEntry(data, DataType.Timestamp) + + val validBooleanEntryGen: Gen[DataEntry] = for { + data <- byteArrayGen(1).map(_.deep == Array(1.toByte).deep).map(if(_) 1 else 0).map(_.toByte).map(Array(_)) + } yield DataEntry(data, DataType.Boolean) + + val validShortBytesEntryGen: Gen[DataEntry] = for { + data <- attachmentGen.map(Deser.serializeArray) + } yield DataEntry(data, DataType.ShortBytes) + + val validOpcBlockEntryGen: Gen[DataEntry] = for { + data <- attachmentGen.map(Deser.serializeArray) + } yield DataEntry(data, DataType.OpcBlock) + + val validBigIntegerEntryGen: Gen[DataEntry] = for { + data <- bigIntGen.map(Deser.serializeArray) + } yield DataEntry(data, DataType.BigInteger) + property("convert entry to bytes and convert back") { forAll(validPublicKeyEntryGen) { pk: DataEntry => val recovered = DataEntry.fromBytes(pk.bytes).right.get @@ -40,19 +73,95 @@ class DataEntrySpecification extends PropSpec with PropertyChecks with Generator assertEys(recovered, pk) } + + forAll(validInt32EntryGen) { pk: DataEntry => + val recovered = DataEntry.fromBytes(pk.bytes).right.get + + assertEys(recovered, pk) + } + + forAll(validShortTextEntryGen) { pk: DataEntry => + val recovered = DataEntry.fromBytes(pk.bytes).right.get + + assertEys(recovered, pk) + } + + forAll(validContractAccountEntryGen) { pk: DataEntry => + val recovered = DataEntry.fromBytes(pk.bytes).right.get + + assertEys(recovered, pk) + } + + forAll(validTimestampEntryGen) { pk: DataEntry => + val recovered = DataEntry.fromBytes(pk.bytes).right.get + + assertEys(recovered, pk) + } + + forAll(validBooleanEntryGen) { pk: DataEntry => + val recovered = DataEntry.fromBytes(pk.bytes).right.get + + assertEys(recovered, pk) + } + + forAll(validShortBytesEntryGen) { pk: DataEntry => + val recovered = DataEntry.fromBytes(pk.bytes).right.get + + assertEys(recovered, pk) + } + + forAll(validOpcBlockEntryGen) { pk: DataEntry => + val recovered = DataEntry.fromBytes(pk.bytes).right.get + + assertEys(recovered, pk) + } + + forAll(validBigIntegerEntryGen) { pk: DataEntry => + val recovered = DataEntry.fromBytes(pk.bytes).right.get + + assertEys(recovered, pk) + } + } - property("report invalid data type") { + property("report invalid and valid data type") { val byteArray1 = Array[Byte](0, 118, 97, 108, 117, 101, 49) + val byteArray1_1 = Array[Byte](0, 1) + val byteArray1_2 = Array[Byte](0, 20) val byteArray2 = Array[Byte](4, 118, 97, 108, 117, 101, 49) val byteArray3 = Array.fill[Byte](1 + 32)(1) val byteArray4 = Array.fill[Byte](1 + 26)(2) val byteArray5 = Array.fill[Byte](1 + 8)(3) + val byteArray6 = Array.fill[Byte](1 + 4)(4) + val byteArray7 = Array[Byte](5, 0, 0) + val byteArray8 = Array.fill[Byte](1 + 26)(6) + val byteArray9 = Array.fill[Byte](1 + 26)(7) + val byteArray10 = Array.fill[Byte](1 + 8)(9) + val byteArray11 = Array[Byte](10, 1) + val byteArray12 = Array[Byte](11, 0, 0) + val byteArray13 = Array[Byte](12, 118, 97, 108, 117, 101, 49) + val byteArray14 = Array[Byte](13, 0, 0) + val byteArray15 = Array[Byte](14, 0, 0) + val byteArray15_1 = Array[Byte](14, 0, 1, 100) DataEntry.fromBytes(byteArray1) should be (Left(InvalidDataEntry)) + DataEntry.fromBytes(byteArray1_1).map(_.dataType) should be (Right(DataType.DataTypeObj)) + DataEntry.fromBytes(byteArray1_2) should be (Left(InvalidDataEntry)) DataEntry.fromBytes(byteArray2) should be (Left(InvalidDataEntry)) DataEntry.fromBytes(byteArray3).map(_.dataType) should be (Right(DataType.PublicKey)) DataEntry.fromBytes(byteArray4).map(_.dataType) should be (Left(InvalidDataEntry)) DataEntry.fromBytes(byteArray5).map(_.dataType) should be (Right(DataType.Amount)) + DataEntry.fromBytes(byteArray6).map(_.dataType) should be (Right(DataType.Int32)) + DataEntry.fromBytes(byteArray7).map(_.dataType) should be (Right(DataType.ShortText)) + DataEntry.fromBytes(byteArray8).map(_.dataType) should be (Left(InvalidDataEntry)) + DataEntry.fromBytes(byteArray9).map(_.dataType) should be (Left(InvalidDataEntry)) + DataEntry.fromBytes(byteArray10).map(_.dataType) should be (Right(DataType.Timestamp)) + DataEntry.fromBytes(byteArray11).map(_.dataType) should be (Right(DataType.Boolean)) + DataEntry.fromBytes(byteArray12).map(_.dataType) should be (Right(DataType.ShortBytes)) + DataEntry.fromBytes(byteArray13).map(_.dataType) should be (Left(InvalidDataEntry)) + DataEntry.fromBytes(byteArray14).map(_.dataType) should be (Right(DataType.OpcBlock)) + DataEntry.fromBytes(byteArray15).map(_.dataType) should be (Left(InvalidDataEntry)) + DataEntry.fromBytes(byteArray15_1).map(_.dataType) should be (Right(DataType.BigInteger)) + } private def assertEys(first: DataEntry, second: DataEntry): Unit = { diff --git a/src/test/scala/vsys/blockchain/contract/DataTypeSpecification.scala b/src/test/scala/vsys/blockchain/contract/DataTypeSpecification.scala index 38d5f8424..d19485616 100644 --- a/src/test/scala/vsys/blockchain/contract/DataTypeSpecification.scala +++ b/src/test/scala/vsys/blockchain/contract/DataTypeSpecification.scala @@ -6,6 +6,7 @@ import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} class DataTypeSpecification extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { property("convert byte to DataType") { + DataType.fromByte(0) should be (Some(DataType.DataTypeObj)) DataType.fromByte(1) should be (Some(DataType.PublicKey)) DataType.fromByte(2) should be (Some(DataType.Address)) DataType.fromByte(3) should be (Some(DataType.Amount)) @@ -17,11 +18,14 @@ class DataTypeSpecification extends PropSpec with PropertyChecks with GeneratorD DataType.fromByte(9) should be (Some(DataType.Timestamp)) DataType.fromByte(10) should be (Some(DataType.Boolean)) DataType.fromByte(11) should be (Some(DataType.ShortBytes)) - DataType.fromByte(0) should be (None) - DataType.fromByte(12) should be (None) + DataType.fromByte(12) should be (Some(DataType.Balance)) + DataType.fromByte(13) should be (Some(DataType.OpcBlock)) + DataType.fromByte(14) should be (Some(DataType.BigInteger)) + DataType.fromByte(15) should be (None) } property("convert DataType to byte") { + DataType.DataTypeObj.id should be (0) DataType.PublicKey.id should be (1) DataType.Address.id should be (2) DataType.Amount.id should be (3) @@ -33,5 +37,8 @@ class DataTypeSpecification extends PropSpec with PropertyChecks with GeneratorD DataType.Timestamp.id should be (9) DataType.Boolean.id should be (10) DataType.ShortBytes.id should be (11) + DataType.Balance.id should be (12) + DataType.OpcBlock.id should be (13) + DataType.BigInteger.id should be (14) } } diff --git a/src/test/scala/vsys/blockchain/contract/channel/PaymentChannelContractGen.scala b/src/test/scala/vsys/blockchain/contract/channel/PaymentChannelContractGen.scala index e4e6dcbbe..3db0b24cd 100644 --- a/src/test/scala/vsys/blockchain/contract/channel/PaymentChannelContractGen.scala +++ b/src/test/scala/vsys/blockchain/contract/channel/PaymentChannelContractGen.scala @@ -39,7 +39,7 @@ trait PaymentChannelContractGen extends SystemContractGen description: String, fee: Long, ts: Long): Gen[RegisterContractTransaction] = RegisterContractTransaction.create(signer, contract, dataStack, description, fee, feeScale, ts).explicitGet() - def createChannelGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def createChannelGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { val id: Short = createIndex.toShort for { @@ -47,7 +47,7 @@ trait PaymentChannelContractGen extends SystemContractGen } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() } - def updateExpiredTimeChannelGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def updateExpiredTimeChannelGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { val id: Short = updateExpiredTimeIndex.toShort for { @@ -55,7 +55,7 @@ trait PaymentChannelContractGen extends SystemContractGen } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() } - def chargeChannelGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def chargeChannelGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { val id: Short = chargeIndex.toShort for { @@ -79,7 +79,7 @@ trait PaymentChannelContractGen extends SystemContractGen } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() } - def executePaymentChannelGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def executePaymentChannelGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { val id: Short = executePaymentIndex.toShort for { diff --git a/src/test/scala/vsys/blockchain/contract/lock/LockFunction.scala b/src/test/scala/vsys/blockchain/contract/lock/LockFunction.scala index ceef71bbd..716f26258 100644 --- a/src/test/scala/vsys/blockchain/contract/lock/LockFunction.scala +++ b/src/test/scala/vsys/blockchain/contract/lock/LockFunction.scala @@ -16,7 +16,7 @@ trait LockFunction { val lockFunctionOpcs: Seq[Array[Byte]] = Seq( loadCaller ++ Array(1.toByte), cdbvrMapGetOrDefault ++ Array(lockTimeMap.index, 1.toByte, 2.toByte), - compareGreater ++ Array(0.toByte, 2.toByte, 3.toByte), + compareGreaterEqual ++ Array(0.toByte, 2.toByte, 3.toByte), assertTrue ++ Array(3.toByte), cdbvMapSet ++ Array(lockTimeMap.index, 1.toByte, 0.toByte) ) @@ -24,7 +24,7 @@ trait LockFunction { val lockWrongFunctionOpcs: Seq[Array[Byte]] = Seq( Array(2.toByte, 0.toByte) ++ Array(1.toByte), cdbvrMapGetOrDefault ++ Array(lockTimeMap.index, 1.toByte, 2.toByte), - compareGreater ++ Array(0.toByte, 2.toByte, 3.toByte), + compareGreaterEqual ++ Array(0.toByte, 2.toByte, 3.toByte), assertTrue ++ Array(3.toByte), cdbvMapSet ++ Array(lockTimeMap.index, 1.toByte, 0.toByte) ) diff --git a/src/test/scala/vsys/blockchain/contract/swap/AtomicSwapContractGen.scala b/src/test/scala/vsys/blockchain/contract/swap/AtomicSwapContractGen.scala new file mode 100644 index 000000000..dc5f87ac4 --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/swap/AtomicSwapContractGen.scala @@ -0,0 +1,147 @@ +package vsys.blockchain.contract.swap + +import com.google.common.primitives.{Ints, Longs} +import org.scalacheck.Gen +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.account.{ContractAccount, PrivateKeyAccount} +import vsys.blockchain.contract.Contract +import vsys.blockchain.contract.ContractGenHelper._ +import vsys.blockchain.contract.token.{SystemContractGen, TokenContractGen} +import vsys.blockchain.contract._ +import vsys.blockchain.state._ +import vsys.blockchain.transaction.GenesisTransaction +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +trait AtomicSwapContractGen extends SystemContractGen + with TokenContractGen { + val lockIndex: Short = 0 + val solvePuzzleIndex: Short = 1 + val expireWithdrawIndex: Short = 2 + + def atomicSwapContractGen: Gen[Contract] = + ContractAtomicSwap.contract + + def initAtomicSwapContractDataStackGen(tokenId: Array[Byte]): Gen[Seq[DataEntry]] = for { + tokenId <- Gen.const(DataEntry(tokenId, DataType.TokenId)) + } yield Seq(tokenId) + + def genesisAtomicSwapGen(rep: PrivateKeyAccount, ts: Long): Gen[GenesisTransaction] = + GenesisTransaction.create(rep, ENOUGH_AMT, -1, ts).explicitGet() + + def registerAtomicSwapGen(signer: PrivateKeyAccount, contract: Contract, dataStack: Seq[DataEntry], + description: String, fee: Long, ts: Long): Gen[RegisterContractTransaction] = + RegisterContractTransaction.create(signer, contract, dataStack, description, fee, feeScale, ts).explicitGet() + + def lockAtomicSwapContractDataStackGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = lockIndex + for { + data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() + } + + def solvePuzzleAtomicSwapContractDataStackGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = solvePuzzleIndex + for { + data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() + } + + def expireWithdrawAtomicSwapContractDataStackGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = expireWithdrawIndex + for { + data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() + } + + val atomicSwapContract: Gen[Contract] = atomicSwapContractGen + + def createAndDepositVSYSAtomicSwapContractGen(depositValue: Long): Gen[(GenesisTransaction, + GenesisTransaction, PrivateKeyAccount, PrivateKeyAccount, RegisterContractTransaction, ExecuteContractFunctionTransaction, + Long, Long, String, Array[Byte])] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + genesis <- genesisAtomicSwapGen(master, ts) + user <- accountGen + genesis2 <- genesisAtomicSwapGen(user, ts) + contract <- atomicSwapContract + description <- validDescStringGen + sysTokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + dataStack <- initAtomicSwapContractDataStackGen(sysTokenId.arr) + // Register an atomic swap contract that swaps VSYS + regContract <- registerAtomicSwapGen(master, contract, dataStack, description, fee, ts + 1) + contractId = regContract.contractId + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + depositData = Seq(master.toAddress.bytes.arr, contractId.bytes.arr, Longs.toByteArray(depositValue)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + depositVSYS <- depositVSYSGen(master, depositData, depositType, attach, fee, ts + 2) + } yield (genesis, genesis2, master, user, regContract, depositVSYS, ts, fee, description, attach) + + val tokenContract: Gen[Contract] = tokenContractGen(false) + + def createAndDepositTokenAtomicSwapContractGen(totalSupply: Long, unity: Long, issueAmount: Long, depositValue: Long): Gen[(GenesisTransaction, + GenesisTransaction, PrivateKeyAccount, PrivateKeyAccount, RegisterContractTransaction, ExecuteContractFunctionTransaction, + RegisterContractTransaction, ExecuteContractFunctionTransaction, + Long, Long, String, Array[Byte])] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + genesis <- genesisAtomicSwapGen(master, ts) + user <- accountGen + genesis2 <- genesisAtomicSwapGen(user, ts) + aContract <- atomicSwapContract + tContract <- tokenContract + description <- validDescStringGen + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + initTokenDataStack: Seq[DataEntry] <- initTokenDataStackGen(totalSupply, unity, "init") + regTokenContract <- registerTokenGen(master, tContract, initTokenDataStack, description, fee + 10000000000L, ts) + tokenContractId = regTokenContract.contractId + issueToken <- issueTokenGen(master, tokenContractId, issueAmount, attach, fee, ts + 4) + tokenId = tokenIdFromBytes(tokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + dataStack <- initAtomicSwapContractDataStackGen(tokenId.arr) + // Register an atomic swap contract that swaps Tokens + regContract <- registerAtomicSwapGen(master, aContract, dataStack, description, fee, ts) + contractId = regContract.contractId + depositData = Seq(master.toAddress.bytes.arr, contractId.bytes.arr, Longs.toByteArray(depositValue)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + depositToken <- depositTokenGen(master, tokenContractId, false, depositData, depositType, attach, fee, ts + 1) + } yield (genesis, genesis2, master, user, regTokenContract, issueToken, regContract, depositToken, ts, fee, description, attach) + + def createTwoTokenAndDepositSwapGen(totalSupply: Long, unity: Long, issueAmount: Long, depositValue: Long): Gen[(GenesisTransaction, + GenesisTransaction, PrivateKeyAccount, PrivateKeyAccount, RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, Long, Long, String, Array[Byte])] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + genesis <- genesisAtomicSwapGen(master, ts) + user <- accountGen + genesis2 <- genesisAtomicSwapGen(user, ts) + tContract <- tokenContract + aContract <- atomicSwapContract + description <- validDescStringGen + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + // Register and deposit first token + initTokenDataStack: Seq[DataEntry] <- initTokenDataStackGen(totalSupply, unity, "init") + regTokenContract <- registerTokenGen(master, tContract, initTokenDataStack, description, fee + 10000000000L, ts) + tokenContractId = regTokenContract.contractId + issueToken <- issueTokenGen(master, tokenContractId, issueAmount, attach, fee, ts + 1) + tokenId = tokenIdFromBytes(tokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + swapDataStack <- initAtomicSwapContractDataStackGen(tokenId.arr) + regSwapContract <- registerAtomicSwapGen(master, aContract, swapDataStack, description, fee, ts + 2) + swapContractId = regSwapContract.contractId + depositData = Seq(master.toAddress.bytes.arr, swapContractId.bytes.arr, Longs.toByteArray(depositValue)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + depositToken <- depositTokenGen(master, tokenContractId, false, depositData, depositType, attach, fee, ts + 3) + // Register and deposit second token + initToken2DataStack: Seq[DataEntry] <- initTokenDataStackGen(totalSupply, unity, "init") + regToken2Contract <- registerTokenGen(user, tContract, initToken2DataStack, description, fee + 10000000000L, ts + 4) + token2ContractId = regToken2Contract.contractId + issueToken2 <- issueTokenGen(user, token2ContractId, issueAmount, attach, fee, ts + 5) + token2Id = tokenIdFromBytes(token2ContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + swap2DataStack <- initAtomicSwapContractDataStackGen(token2Id.arr) + regSwap2Contract <- registerAtomicSwapGen(user, aContract, swap2DataStack, description, fee, ts + 6) + swap2ContractId = regSwap2Contract.contractId + deposit2Data = Seq(user.toAddress.bytes.arr, swap2ContractId.bytes.arr, Longs.toByteArray(depositValue)) + deposit2Type = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + deposit2Token <- depositTokenGen(user, token2ContractId, false, deposit2Data, deposit2Type, attach, fee, ts + 7) + } yield (genesis, genesis2, master, user, regTokenContract, regToken2Contract, regSwapContract, regSwap2Contract, issueToken, issueToken2, + depositToken, deposit2Token, ts, fee, description, attach) +} diff --git a/src/test/scala/vsys/blockchain/contract/token/NonFungibleContractGen.scala b/src/test/scala/vsys/blockchain/contract/token/NonFungibleContractGen.scala index 66959e554..a9aa46cf5 100644 --- a/src/test/scala/vsys/blockchain/contract/token/NonFungibleContractGen.scala +++ b/src/test/scala/vsys/blockchain/contract/token/NonFungibleContractGen.scala @@ -60,7 +60,7 @@ trait NonFungibleContractGen { } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() } - def transferNonFungibleGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def transferNonFungibleGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { val id: Short = transferIndex for { @@ -68,7 +68,7 @@ trait NonFungibleContractGen { } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() } - def depositNonFungibleGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def depositNonFungibleGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { val id: Short = depositIndex for { @@ -76,7 +76,7 @@ trait NonFungibleContractGen { } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() } - def withdrawNonFungibleGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def withdrawNonFungibleGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { val id: Short = withdrawIndex for { diff --git a/src/test/scala/vsys/blockchain/contract/token/NonFungibleContractV2Gen.scala b/src/test/scala/vsys/blockchain/contract/token/NonFungibleContractV2Gen.scala new file mode 100644 index 000000000..4bd88f5a0 --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/token/NonFungibleContractV2Gen.scala @@ -0,0 +1,96 @@ +package vsys.blockchain.contract.token + +import com.google.common.primitives.Ints +import org.scalacheck.Gen +import vsys.account.{Address, ContractAccount, PrivateKeyAccount} +import vsys.blockchain.contract.ContractGenHelper.{ENOUGH_AMT, feeScale} +import vsys.blockchain.contract.{Contract, ContractGenHelper, ContractNonFungibleV2, DataEntry, DataType} +import vsys.blockchain.state._ +import vsys.blockchain.transaction.GenesisTransaction +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +trait NonFungibleContractV2Gen { + val supersedeIndex: Short = 0 + val issueIndex: Short = 1 + val updateListIndex: Short = 2 + val sendIndex: Short = 3 + val transferIndex: Short = 4 + val depositIndex: Short = 5 + val withdrawIndex: Short = 6 + + def initTokenDataStackGen(): Gen[Seq[DataEntry]] = Seq() + + def issueDataStackGen(desc: String): Gen[Seq[DataEntry]] = for { + shortText <- Gen.const(DataEntry.create(desc.getBytes(), DataType.ShortText).right.get) + } yield Seq(shortText) + + def addressDataStackGen(address: Address): Gen[Seq[DataEntry]] = for { + add <- Gen.const(DataEntry(address.bytes.arr, DataType.Address)) + } yield Seq(add) + + def sendDataStackGen(recipient: Address, tokenIndex: Int): Gen[Seq[DataEntry]] = for { + reci <- Gen.const(DataEntry(recipient.bytes.arr, DataType.Address)) + toIdx <- Gen.const(DataEntry(Ints.toByteArray(tokenIndex), DataType.Int32)) + } yield Seq(reci, toIdx) + + def nonFungibleContractWhiteGen(): Gen[Contract] = ContractNonFungibleV2.contractNFTWhitelist + + def nonFungibleContractBlackGen(): Gen[Contract] = ContractNonFungibleV2.contractNFTBlacklist + + def registerNonFungibleV2Gen(signer: PrivateKeyAccount, contract: Contract, dataStack: Seq[DataEntry], + description: String, fee: Long, ts: Long): Gen[RegisterContractTransaction] = + RegisterContractTransaction.create(signer, contract, dataStack, description, fee, feeScale, ts).explicitGet() + + def genesisNonFungibleV2Gen(rep: PrivateKeyAccount, ts: Long): Gen[GenesisTransaction] = + GenesisTransaction.create(rep, ENOUGH_AMT, -1, ts).explicitGet() + + def supersedeNonFungibleV2Gen(signer: PrivateKeyAccount, contractId: ContractAccount, newAdd: Address, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = for { + data: Seq[DataEntry] <- addressDataStackGen(newAdd) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, supersedeIndex, data, attachment, fee, feeScale, ts).explicitGet() + + def issueNonFungibleV2Gen(signer: PrivateKeyAccount, contractId: ContractAccount, desc: String, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = for { + data: Seq[DataEntry] <- issueDataStackGen(desc) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, issueIndex, data, attachment, fee, feeScale, ts).explicitGet() + + def updateListNonFungibleV2Gen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = updateListIndex + for { + data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() + } + + def sendNonFungibleV2Gen(sender: PrivateKeyAccount, contractId: ContractAccount, rep: Address, toIdx: Int, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = sendIndex + for { + data: Seq[DataEntry] <- sendDataStackGen(rep, toIdx) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() + } + + def transferNonFungibleV2Gen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = transferIndex + for { + data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() + } + + def depositNonFungibleV2Gen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = depositIndex + for { + data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() + } + + def withdrawNonFungibleV2Gen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = withdrawIndex + for { + data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/contract/token/SystemContractGen.scala b/src/test/scala/vsys/blockchain/contract/token/SystemContractGen.scala index 9f70d4bdf..029925834 100644 --- a/src/test/scala/vsys/blockchain/contract/token/SystemContractGen.scala +++ b/src/test/scala/vsys/blockchain/contract/token/SystemContractGen.scala @@ -27,21 +27,21 @@ trait SystemContractGen { } yield ExecuteContractFunctionTransaction.create(sender, ContractAccount.systemContractId, sysSend, data, attachment, fee, feeScale, ts).explicitGet() } - def transferVSYSGen(signer: PrivateKeyAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def transferVSYSGen(signer: PrivateKeyAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { for { data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) } yield ExecuteContractFunctionTransaction.create(signer, ContractAccount.systemContractId, sysTransfer, data, attachment, fee, feeScale, ts).explicitGet() } - def depositVSYSGen(signer: PrivateKeyAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def depositVSYSGen(signer: PrivateKeyAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { for { data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) } yield ExecuteContractFunctionTransaction.create(signer, ContractAccount.systemContractId, sysDeposit, data, attachment, fee, feeScale, ts).explicitGet() } - def withdrawVSYSGen(signer: PrivateKeyAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def withdrawVSYSGen(signer: PrivateKeyAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { for { data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) diff --git a/src/test/scala/vsys/blockchain/contract/token/TokenContractGen.scala b/src/test/scala/vsys/blockchain/contract/token/TokenContractGen.scala index 178c7349e..e371c5f8f 100644 --- a/src/test/scala/vsys/blockchain/contract/token/TokenContractGen.scala +++ b/src/test/scala/vsys/blockchain/contract/token/TokenContractGen.scala @@ -88,7 +88,7 @@ trait TokenContractGen { } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() } - def transferTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, split: Boolean, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def transferTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, split: Boolean, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { val id: Short = if (split) transferIndex else (transferIndex - 1).toShort for { @@ -96,7 +96,7 @@ trait TokenContractGen { } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() } - def depositTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, split: Boolean, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def depositTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, split: Boolean, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { val id: Short = if (split) depositIndex else (depositIndex - 1).toShort for { @@ -104,7 +104,7 @@ trait TokenContractGen { } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, data, attachment, fee, feeScale, ts).explicitGet() } - def withdrawTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, split: Boolean, data: Seq[Array[Byte]], dataType: Seq[DataType.Value], + def withdrawTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, split: Boolean, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { val id: Short = if (split) withdrawIndex else (withdrawIndex - 1).toShort for { diff --git a/src/test/scala/vsys/blockchain/contract/token/TokenContractV2Gen.scala b/src/test/scala/vsys/blockchain/contract/token/TokenContractV2Gen.scala new file mode 100644 index 000000000..30e43c5c3 --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/token/TokenContractV2Gen.scala @@ -0,0 +1,97 @@ +package vsys.blockchain.contract.token + +import org.scalacheck.Gen +import vsys.account.{Address, ContractAccount, PrivateKeyAccount} +import vsys.blockchain.contract.ContractGenHelper._ +import vsys.blockchain.contract.{Contract, ContractGenHelper, ContractTokenV2, DataEntry, DataType} +import vsys.blockchain.state._ +import vsys.blockchain.transaction.contract.ExecuteContractFunctionTransaction + +trait TokenContractV2Gen extends TokenContractGen { + + val updateListIndex: Short = 3 + + def supersedeDataStackGen(address1: Address, address2: Address): Gen[Seq[DataEntry]] = for { + add1 <- Gen.const(DataEntry(address1.bytes.arr, DataType.Address)) + add2 <- Gen.const(DataEntry(address2.bytes.arr, DataType.Address)) + } yield Seq(add1, add2) + + def updateListDataStackGen(user: Address, value: Boolean): Gen[Seq[DataEntry]] = for { + u <- Gen.const(DataEntry(user.bytes.arr, DataType.Address)) + b: Byte = if (value) 1.toByte else 0.toByte + v <- Gen.const(DataEntry(Array(b), DataType.Boolean)) + } yield Seq(u, v) + + def updateListDataStackGen(user: ContractAccount, value: Boolean): Gen[Seq[DataEntry]] = for { + u <- Gen.const(DataEntry(user.bytes.arr, DataType.ContractAccount)) + b: Byte = if (value) 1.toByte else 0.toByte + v <- Gen.const(DataEntry(Array(b), DataType.Boolean)) + } yield Seq(u, v) + + def tokenContractV2Gen(white: Boolean): Gen[Contract] = + if (white) ContractTokenV2.contractTokenWhiteList + else ContractTokenV2.contractTokenBlackList + + def supersedeTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, add1: Address, add2: Address, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = for { + data: Seq[DataEntry] <- supersedeDataStackGen(add1, add2) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, supersedeIndex, data, attachment, fee, feeScale, ts).explicitGet() + + def updateListTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, user: Address, value: Boolean, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = for { + data: Seq[DataEntry] <- updateListDataStackGen(user, value) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, updateListIndex, data, attachment, fee, feeScale, ts).explicitGet() + + def updateListTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, user: ContractAccount, value: Boolean, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = for { + data: Seq[DataEntry] <- updateListDataStackGen(user, value) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, updateListIndex, data, attachment, fee, feeScale, ts).explicitGet() + + def sendTokenGen(sender: PrivateKeyAccount, contractId: ContractAccount, rep: Address, amount: Long, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = for { + data: Seq[DataEntry] <- sendDataStackGen(rep, amount) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, sendIndex, data, attachment, fee, feeScale, ts).explicitGet() + + def transferTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + for { + data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, transferIndex, data, attachment, fee, feeScale, ts).explicitGet() + } + + def depositTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + for { + data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, depositIndex, data, attachment, fee, feeScale, ts).explicitGet() + } + + def withdrawTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, data: Seq[Array[Byte]], dataType: Seq[DataType.DataTypeVal[_]], + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + for { + data: Seq[DataEntry] <- ContractGenHelper.dataListGen(data, dataType) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, withdrawIndex, data, attachment, fee, feeScale, ts).explicitGet() + } + + def totalSupplyTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + ExecuteContractFunctionTransaction.create(signer, contractId, totalSupplyIndex, Nil, attachment, fee, feeScale, ts).explicitGet() + } + + def maxSupplyTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + ExecuteContractFunctionTransaction.create(signer, contractId, maxSupplyIndex, Nil, attachment, fee, feeScale, ts).explicitGet() + } + + def balanceOfTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, add: Address, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + for { + data: Seq[DataEntry] <- addressDataStackGen(add) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, balanceOfIndex, data, attachment, fee, feeScale, ts).explicitGet() + } + + def getIssuerTokenGen(signer: PrivateKeyAccount, contractId: ContractAccount, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = { + ExecuteContractFunctionTransaction.create(signer, contractId, getIssuerIndex, Nil, attachment, fee, feeScale, ts).explicitGet() + } +} diff --git a/src/test/scala/vsys/blockchain/contract/vescrow/VEscrowContractGen.scala b/src/test/scala/vsys/blockchain/contract/vescrow/VEscrowContractGen.scala new file mode 100644 index 000000000..60feb7323 --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/vescrow/VEscrowContractGen.scala @@ -0,0 +1,316 @@ +package vsys.blockchain.contract.vescrow + +import com.google.common.primitives.Longs +import org.scalacheck.Gen +import vsys.account.{Address, ContractAccount, PrivateKeyAccount} +import vsys.blockchain.contract.ContractGenHelper.{ENOUGH_AMT, feeScale} +import vsys.blockchain.contract.{Contract, ContractVEscrow, DataEntry, DataType} +import vsys.blockchain.state._ +import vsys.blockchain.transaction.GenesisTransaction +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +trait VEscrowContractGen { + + val vEscrowsupersedeIndex: Short = 0 + val createIndex: Short = 1 + val recipientDepositIndex: Short = 2 + val judgeDepositIndex: Short = 3 + val payerCancelIndex: Short = 4 + val recipientCancelIndex: Short = 5 + val judgeCancelIndex: Short = 6 + val submitWorkIndex: Short = 7 + val approveWorkIndex: Short = 8 + val applyToJudgeIndex: Short = 9 + val judgeIndex: Short = 10 + val submitPenaltyIndex: Short = 11 + val payerRefundIndex: Short = 12 + val recipientRefundIndex: Short = 13 + val collectIndex: Short = 14 + + def vEscrowContractGen(): Gen[Contract] = ContractVEscrow.contract + + def genesisVEscrowGen(rep: PrivateKeyAccount, + ts: Long): Gen[GenesisTransaction] = + GenesisTransaction.create(rep, ENOUGH_AMT, -1, ts).explicitGet() + + def registerVEscrowGen(signer: PrivateKeyAccount, + contract: Contract, + dataStack: Seq[DataEntry], + description: String, + fee: Long, + ts: Long): Gen[RegisterContractTransaction] = + RegisterContractTransaction.create(signer, contract, dataStack, description, fee, feeScale, ts).explicitGet() + + def vEscrowAddressDataStackGen(address: Address): Gen[Seq[DataEntry]] = for { + addr <- Gen.const(DataEntry.create(address.bytes.arr, DataType.Address).right.get) + } yield Seq(addr) + + def createDataStackGen(recipient: Address, + amount: Long, + repDeposit: Long, + judgeDeposit: Long, + fee: Long, + refund: Long, + expirationTime: Long): Gen[Seq[DataEntry]] = for { + recipient <- Gen.const(DataEntry.create(recipient.bytes.arr, DataType.Address).right.get) + amount <- Gen.const(DataEntry.create(Longs.toByteArray(amount), DataType.Amount).right.get) + repDeposit <- Gen.const(DataEntry.create(Longs.toByteArray(repDeposit), DataType.Amount).right.get) + judgeDeposit <- Gen.const(DataEntry.create(Longs.toByteArray(judgeDeposit), DataType.Amount).right.get) + fee <- Gen.const(DataEntry.create(Longs.toByteArray(fee), DataType.Amount).right.get) + refund <- Gen.const(DataEntry.create(Longs.toByteArray(refund), DataType.Amount).right.get) + expirationTime <- Gen.const(DataEntry.create(Longs.toByteArray(expirationTime), DataType.Timestamp).right.get) + } yield Seq(recipient, amount, repDeposit, judgeDeposit, fee, refund, expirationTime) + + def escrowDepositDataStackGen(orderId: Array[Byte]): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + } yield Seq(orderId) + + def escrowCancelDataStackGen(orderId: Array[Byte]): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + } yield Seq(orderId) + + def submitWorkDataStackGen(orderId: Array[Byte]): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + } yield Seq(orderId) + + def approveWorkDataStackGen(orderId: Array[Byte]): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + } yield Seq(orderId) + + def applyToJudgeDataStackGen(orderId: Array[Byte]): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + } yield Seq(orderId) + + def judgeDataStackGen(orderId: Array[Byte], + payerAmount: Long, + recipientAmount: Long): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + payerAmount <- Gen.const(DataEntry.create(Longs.toByteArray(payerAmount), DataType.Amount).right.get) + recipientAmount <- Gen.const(DataEntry.create(Longs.toByteArray(recipientAmount), DataType.Amount).right.get) + } yield Seq(orderId, payerAmount, recipientAmount) + + def submitPenaltyDataStackGen(orderId: Array[Byte]): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + } yield Seq(orderId) + + def payerRefundDataStackGen(orderId: Array[Byte]): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + } yield Seq(orderId) + + def recipientRefundDataStackGen(orderId: Array[Byte]): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + } yield Seq(orderId) + + def collectDataStackGen(orderId: Array[Byte]): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + } yield Seq(orderId) + + def initVEscrowDataStackGen(tokenId: Array[Byte], duration: Long, judgeDuration: Long): Gen[Seq[DataEntry]] = for { + tokenId <- Gen.const(DataEntry.create(tokenId, DataType.TokenId).right.get) + duration <- Gen.const(DataEntry.create(Longs.toByteArray(duration), DataType.Timestamp).right.get) + judgeDuration <- Gen.const(DataEntry.create(Longs.toByteArray(judgeDuration), DataType.Timestamp).right.get) + } yield Seq(tokenId, duration, judgeDuration) + + def supersedeVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + newJudge: Address, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = vEscrowsupersedeIndex + for { + data: Seq[DataEntry] <- vEscrowAddressDataStackGen(newJudge) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def createVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + recipient: Address, + amount: Long, + repDeposit: Long, + judgeDeposit: Long, + judgeFee: Long, + refund: Long, + expirationTime: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = createIndex + for { + data: Seq[DataEntry] <- createDataStackGen(recipient, amount, repDeposit, judgeDeposit, judgeFee, refund, expirationTime) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def recipientDepositVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = recipientDepositIndex + for { + data: Seq[DataEntry] <- escrowDepositDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def judgeDepositVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = judgeDepositIndex + for { + data: Seq[DataEntry] <- escrowDepositDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def payerCancelVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = payerCancelIndex + for { + data: Seq[DataEntry] <- escrowCancelDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def recipientCancelVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = recipientCancelIndex + for { + data: Seq[DataEntry] <- escrowCancelDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def judgeCancelVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = judgeCancelIndex + for { + data: Seq[DataEntry] <- escrowCancelDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def submitWorkVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = submitWorkIndex + for { + data: Seq[DataEntry] <- submitWorkDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def approveWorkVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = approveWorkIndex + for { + data: Seq[DataEntry] <- approveWorkDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def applyToJudgeVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = applyToJudgeIndex + for { + data: Seq[DataEntry] <- applyToJudgeDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def judgeVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + payerAmount: Long, + recipientAmount: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = judgeIndex + for { + data: Seq[DataEntry] <- judgeDataStackGen(orderId, payerAmount, recipientAmount) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def submitPenaltyVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = submitPenaltyIndex + for { + data: Seq[DataEntry] <- submitPenaltyDataStackGen(orderId: Array[Byte]) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def payerRefundVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = payerRefundIndex + for { + data: Seq[DataEntry] <- payerRefundDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def recipientRefundVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = recipientRefundIndex + for { + data: Seq[DataEntry] <- recipientRefundDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def collectVEscrowGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = collectIndex + for { + data: Seq[DataEntry] <- collectDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/contract/vescrow/VEscrowFunctionHelperGen.scala b/src/test/scala/vsys/blockchain/contract/vescrow/VEscrowFunctionHelperGen.scala new file mode 100644 index 000000000..fad1531ef --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/vescrow/VEscrowFunctionHelperGen.scala @@ -0,0 +1,42 @@ +package vsys.blockchain.contract.vescrow + +import com.google.common.primitives.Bytes +import vsys.blockchain.contract.{DataEntry, DataType} +import vsys.blockchain.state.ByteStr + +trait VEscrowFunctionHelperGen extends VEscrowContractGen { + + def getEscrowContractStateVarKeys(vEscrowContractId: Array[Byte]): Seq[ByteStr] = { + val makerKey = ByteStr(Bytes.concat(vEscrowContractId, Array(0.toByte))) + val judgeKey = ByteStr(Bytes.concat(vEscrowContractId, Array(1.toByte))) + val tokenIdKey = ByteStr(Bytes.concat(vEscrowContractId, Array(2.toByte))) + val durationKey = ByteStr(Bytes.concat(vEscrowContractId, Array(3.toByte))) + val judgeDurationKey = ByteStr(Bytes.concat(vEscrowContractId, Array(4.toByte))) + + Seq(makerKey, judgeKey, tokenIdKey, durationKey, judgeDurationKey) + } + + def getEscrowContractStateMapKeys(vEscrowContractId: Array[Byte], orderId: Array[Byte]): Seq[ByteStr] = { + val orderPayerKey = ByteStr(Bytes.concat(vEscrowContractId, Array(1.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderRecipientKey = ByteStr(Bytes.concat(vEscrowContractId, Array(2.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderAmountKey = ByteStr(Bytes.concat(vEscrowContractId, Array(3.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderRecipientDepositKey = ByteStr(Bytes.concat(vEscrowContractId, Array(4.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderJudgeDepositKey = ByteStr(Bytes.concat(vEscrowContractId, Array(5.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderFeeKey = ByteStr(Bytes.concat(vEscrowContractId, Array(6.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderRecipientAmountKey = ByteStr(Bytes.concat(vEscrowContractId, Array(7.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderRefundKey = ByteStr(Bytes.concat(vEscrowContractId, Array(8.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderRecipientRefundKey = ByteStr(Bytes.concat(vEscrowContractId, Array(9.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderExpirationTimeKey = ByteStr(Bytes.concat(vEscrowContractId, Array(10.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderStatusKey = ByteStr(Bytes.concat(vEscrowContractId, Array(11.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderRepDepositStatusKey = ByteStr(Bytes.concat(vEscrowContractId, Array(12.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderJudgeDepositStatusKey = ByteStr(Bytes.concat(vEscrowContractId, Array(13.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderSubmitStatusKey = ByteStr(Bytes.concat(vEscrowContractId, Array(14.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderJudgeStatusKey = ByteStr(Bytes.concat(vEscrowContractId, Array(15.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderRepLockedAmountKey = ByteStr(Bytes.concat(vEscrowContractId, Array(16.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderJudgeLockedAmountKey = ByteStr(Bytes.concat(vEscrowContractId, Array(17.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + + Seq(orderPayerKey, orderRecipientKey, orderAmountKey, orderRecipientDepositKey, orderJudgeDepositKey, orderFeeKey, + orderRecipientAmountKey, orderRefundKey, orderRecipientRefundKey, orderExpirationTimeKey, orderStatusKey, orderRepDepositStatusKey, + orderJudgeDepositStatusKey, orderSubmitStatusKey, orderJudgeStatusKey, orderRepLockedAmountKey, orderJudgeLockedAmountKey) + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/contract/voption/VOptionContractGen.scala b/src/test/scala/vsys/blockchain/contract/voption/VOptionContractGen.scala new file mode 100644 index 000000000..cb0c99dfd --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/voption/VOptionContractGen.scala @@ -0,0 +1,148 @@ +package vsys.blockchain.contract.voption + +import com.google.common.primitives.Longs +import org.scalacheck.Gen +import vsys.account.{Address, ContractAccount, PrivateKeyAccount} +import vsys.blockchain.contract.{Contract, ContractVOption, DataEntry, DataType} +import vsys.blockchain.contract.ContractGenHelper._ +import vsys.blockchain.state._ +import vsys.blockchain.transaction.GenesisTransaction +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +trait VOptionContractGen { + + val supersedeIndex: Short = 0 + val activateIndex: Short = 1 + val mintIndex: Short = 2 + val unlockIndex: Short = 3 + val executeIndex: Short = 4 + val collectIndex: Short = 5 + + def vOptionContractGen(): Gen[Contract] = ContractVOption.contract + + def genesisVOptionGen(rep: PrivateKeyAccount, + ts: Long): Gen[GenesisTransaction] = + GenesisTransaction.create(rep, ENOUGH_AMT, -1, ts).explicitGet() + + def registerVOptionGen(signer: PrivateKeyAccount, + contract: Contract, + dataStack: Seq[DataEntry], + description: String, + fee: Long, + ts: Long): Gen[RegisterContractTransaction] = + RegisterContractTransaction.create(signer, contract, dataStack, description, fee, feeScale, ts).explicitGet() + + def addressDataStackGen(address: Address): Gen[Seq[DataEntry]] = for { + addr <- Gen.const(DataEntry(address.bytes.arr, DataType.Address)) + } yield Seq(addr) + + def activateDataStackGen(maxIssueNum: Long, + price: Long, + priceUnit: Long): Gen[Seq[DataEntry]] = for { + maxIssueNum <- Gen.const(DataEntry(Longs.toByteArray(maxIssueNum), DataType.Amount)) + price <- Gen.const(DataEntry(Longs.toByteArray(price), DataType.Amount)) + priceUnit <- Gen.const(DataEntry(Longs.toByteArray(priceUnit), DataType.Amount)) + } yield Seq(maxIssueNum, price, priceUnit) + + def commonOptionDataStackGen(amount: Long): Gen[Seq[DataEntry]] = for { + amount <- Gen.const(DataEntry(Longs.toByteArray(amount), DataType.Amount)) + } yield Seq(amount) + + def initVOptionDataStackGen(baseTokenId: Array[Byte], + targetTokenId: Array[Byte], + optionTokenId: Array[Byte], + proofTokenId: Array[Byte], + executeTime: Long, + executeDeadLine: Long): Gen[Seq[DataEntry]] = { + for { + baseTokenId <- Gen.const(DataEntry.create(baseTokenId, DataType.TokenId).right.get) + targetTokenId <- Gen.const(DataEntry.create(targetTokenId, DataType.TokenId).right.get) + optionTokenId <- Gen.const(DataEntry.create(optionTokenId, DataType.TokenId).right.get) + proofTokenId <- Gen.const(DataEntry.create(proofTokenId, DataType.TokenId).right.get) + executeTime <- Gen.const(DataEntry.create(Longs.toByteArray(executeTime), DataType.Timestamp).right.get) + executeDeadLine <- Gen.const(DataEntry.create(Longs.toByteArray(executeDeadLine), DataType.Timestamp).right.get) + } yield Seq(baseTokenId, targetTokenId, optionTokenId, proofTokenId, executeTime, executeDeadLine) + } + + + def supersedeVOptionGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + newAddr: Address, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = supersedeIndex + for { + data: Seq[DataEntry] <- addressDataStackGen(newAddr) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def activateVOptionGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + maxIssueNum: Long, + price: Long, + priceUnit: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = activateIndex + for { + data <- activateDataStackGen(maxIssueNum, price, priceUnit) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def mintVOptionGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + amount: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = mintIndex + for { + data <- commonOptionDataStackGen(amount) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def unlockVOptionGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + amount: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = unlockIndex + for { + data <- commonOptionDataStackGen(amount) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def executeVOptionGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + amount: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = executeIndex + for { + data <- commonOptionDataStackGen(amount) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def collectVOptionGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + amount: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = collectIndex + for { + data <- commonOptionDataStackGen(amount) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/contract/voption/VOptionFunction.scala b/src/test/scala/vsys/blockchain/contract/voption/VOptionFunction.scala new file mode 100644 index 000000000..b54511b3f --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/voption/VOptionFunction.scala @@ -0,0 +1,46 @@ +package vsys.blockchain.contract.voption + +import vsys.blockchain.contract.ContractGen.{basicConstantGet, cdbvSet, getFunctionBytes, loadSigner} +import vsys.blockchain.contract.ContractVOption.{baseTokenIdStateVar, executeDeadlineStateVar, executeTimeStateVar, makerStateVar, optionStatusStateVar, optionTokenIdStateVar, proofTokenIdStateVar, targetTokenIdStateVar} +import vsys.blockchain.contract.ContractVSwap._ +import vsys.blockchain.contract.{DataEntry, DataType} + +trait VOptionFunction { + + val initId: Short = 0 + + val initDataType: Array[Byte] = Array(DataType.TokenId.id.toByte, DataType.TokenId.id.toByte,DataType.TokenId.id.toByte, DataType.TokenId.id.toByte, DataType.Timestamp.id.toByte, DataType.Timestamp.id.toByte) + val initWrongDataType: Array[Byte] = Array(DataType.Amount.id.toByte, DataType.TokenId.id.toByte,DataType.TokenId.id.toByte, DataType.TokenId.id.toByte, DataType.Timestamp.id.toByte, DataType.Timestamp.id.toByte) + + val initTriggerOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(6.toByte), + cdbvSet ++ Array(makerStateVar.index, 6.toByte), + cdbvSet ++ Array(baseTokenIdStateVar.index, 0.toByte), + cdbvSet ++ Array(targetTokenIdStateVar.index, 1.toByte), + cdbvSet ++ Array(optionTokenIdStateVar.index, 2.toByte), + cdbvSet ++ Array(proofTokenIdStateVar.index, 3.toByte), + cdbvSet ++ Array(executeTimeStateVar.index, 4.toByte), + cdbvSet ++ Array(executeDeadlineStateVar.index, 5.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + cdbvSet ++ Array(optionStatusStateVar.index, 5.toByte) + ) + + val initWrongTriggerOpcs: Seq[Array[Byte]] = Seq( + Array(5.toByte, 3.toByte) ++ loadSigner ++ Array(6.toByte), + cdbvSet ++ Array(makerStateVar.index, 6.toByte), + cdbvSet ++ Array(baseTokenIdStateVar.index, 0.toByte), + cdbvSet ++ Array(targetTokenIdStateVar.index, 1.toByte), + cdbvSet ++ Array(optionTokenIdStateVar.index, 2.toByte), + cdbvSet ++ Array(proofTokenIdStateVar.index, 3.toByte), + cdbvSet ++ Array(executeTimeStateVar.index, 4.toByte), + cdbvSet ++ Array(executeDeadlineStateVar.index, 5.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + cdbvSet ++ Array(optionStatusStateVar.index, 5.toByte) + ) + + val nonReturnType: Array[Byte] = Array[Byte]() + val onInitTriggerType: Byte = 0 + + lazy val wrongDataTrigger: Seq[Array[Byte]] = Seq(getFunctionBytes(initId, onInitTriggerType, nonReturnType, initWrongDataType, initTriggerOpcs)) + lazy val wrongOpcTrigger: Seq[Array[Byte]] = Seq(getFunctionBytes(initId, onInitTriggerType, nonReturnType, initDataType, initWrongTriggerOpcs)) +} diff --git a/src/test/scala/vsys/blockchain/contract/voption/VOptionFunctionHelperGen.scala b/src/test/scala/vsys/blockchain/contract/voption/VOptionFunctionHelperGen.scala new file mode 100644 index 000000000..9590a6888 --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/voption/VOptionFunctionHelperGen.scala @@ -0,0 +1,193 @@ +package vsys.blockchain.contract.voption + +import com.google.common.primitives.{Bytes, Ints, Longs} +import org.scalacheck.Gen +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.account.{Address, ContractAccount, PrivateKeyAccount, PublicKeyAccount} +import vsys.blockchain.contract.{DataEntry, DataType} +import vsys.blockchain.contract.token.TokenContractGen +import vsys.blockchain.contract.ContractGenHelper._ +import vsys.blockchain.state.ByteStr +import vsys.blockchain.transaction.GenesisTransaction +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +trait VOptionFunctionHelperGen extends VOptionContractGen with TokenContractGen { + + override val supersedeIndex: Short = 0 + + override def addressDataStackGen(address: Address): Gen[Seq[DataEntry]] = for { + addr <- Gen.const(DataEntry(address.bytes.arr, DataType.Address)) + } yield Seq(addr) + + def registerToken(user: PrivateKeyAccount, + totalSupply: Long, + unity: Long, + desc: String, + fee: Long, + timestamp: Long): Gen[RegisterContractTransaction] = for { + initTokenDataStack: Seq[DataEntry] <- initTokenDataStackGen(totalSupply, unity, desc) + description <- validDescStringGen + tokenContract <- tokenContractGen(false) + regTokenContract <- registerTokenGen(user, tokenContract, initTokenDataStack, description, fee, timestamp) + } yield regTokenContract + + def issueToken(user: PrivateKeyAccount, + contractId: ContractAccount, + issueAmount: Long, + fee: Long, + timestamp: Long): Gen[ExecuteContractFunctionTransaction] = for { + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issueToken <- issueTokenGen(user, contractId, issueAmount, attach, fee, timestamp) + } yield issueToken + + def depositToken(user: PrivateKeyAccount, + contractId: ContractAccount, + sender: Array[Byte], + contract: Array[Byte], + amount: Long, + fee: Long, + timestamp: Long + ): Gen[ExecuteContractFunctionTransaction] = for { + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + depositTokenData = Seq(sender, contract, Longs.toByteArray(amount)) + depositTokenDataType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + depositToken <- depositTokenGen(user, contractId, false, depositTokenData, depositTokenDataType, attach, fee, timestamp) + } yield depositToken + + def withdrawToken(user: PrivateKeyAccount, + contractId: ContractAccount, + contract: Array[Byte], + sender: Array[Byte], + amount: Long, + fee: Long, + timestamp: Long): Gen[ExecuteContractFunctionTransaction] = for { + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + withdrawTokenData = Seq(contract, sender, Longs.toByteArray(amount)) + withdrawTokenDataType = Seq(DataType.ContractAccount, DataType.Address, DataType.Amount) + withdrawToken <- withdrawTokenGen(user, contractId, false, withdrawTokenData, withdrawTokenDataType, attach, fee, timestamp) + } yield withdrawToken + + def createBaseTargetOptionProofTokenAndInitVOption(baseTotalSupply: Long, + baseUnity: Long, + baseIssueAmount: Long, + targetTotalSupply: Long, + targetUnity: Long, + targetIssueAmount: Long, + optionTotalSupply: Long, + optionUnity: Long, + proofTotalSupply: Long, + proofUnity: Long, + baseTokenDepositAmount: Long, + targetTokenDepositAmount: Long, + optionTokenDepositAmount: Long, + proofTokenDepositAmount: Long): Gen[(GenesisTransaction, GenesisTransaction, + PrivateKeyAccount, PrivateKeyAccount, RegisterContractTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, Long, Long, Array[Byte])] = for { + (master, ts, fee) <- basicContractTestGen() + + genesis <- genesisVOptionGen(master, ts) + user <- accountGen + genesis2 <- genesisVOptionGen(user, ts) + vOptionContract <- vOptionContractGen() + + // register base token + regBaseTokenContract <- registerToken(master, baseTotalSupply, baseUnity, "init", fee + 10000000000L, ts) + baseTokenContractId = regBaseTokenContract.contractId + baseTokenId = tokenIdFromBytes(baseTokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // register target token + regTargetTokenContract <- registerToken(master, targetTotalSupply, targetUnity, "init", fee + 10000000000L, ts + 1) + targetTokenContractId = regTargetTokenContract.contractId + targetTokenId = tokenIdFromBytes(targetTokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // register option token + regOptionTokenContract <- registerToken(master, optionTotalSupply, optionUnity, "init", fee + 10000000000L, ts + 2) + optionTokenContractId = regOptionTokenContract.contractId + optionTokenId = tokenIdFromBytes(optionTokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // register proof token + regProofTokenContract <- registerToken(master, proofTotalSupply, proofUnity, "init", fee + 10000000000L, ts + 3) + proofTokenContractId = regProofTokenContract.contractId + proofTokenId = tokenIdFromBytes(proofTokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + // register VOption contract + description <- validDescStringGen + initVOptionDataStack: Seq[DataEntry] <- initVOptionDataStackGen(baseTokenId.arr, targetTokenId.arr, optionTokenId.arr, proofTokenId.arr, ts + 100, ts + 200) + regVOptionContract <- registerVOptionGen(master, vOptionContract, initVOptionDataStack, description, fee + 10000000000L, ts + 4) + vOptionContractId = regVOptionContract.contractId + + // issue base token + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issueBaseToken <- issueToken(master, baseTokenContractId, baseIssueAmount, fee, ts + 5) + // issue target token + issueTargetToken <- issueToken(master, targetTokenContractId, targetIssueAmount, fee, ts + 6) + // issue option token, always issue the entire supply of option tokens + issueOptionToken <- issueToken(master, optionTokenContractId, optionTotalSupply, fee, ts + 7) + // issue proof token, always issue the entire supply of proof tokens + issueProofToken <- issueToken(master, proofTokenContractId, proofTotalSupply, fee, ts + 8) + + // deposit all issued tokens into voption contract, always deposit the entire supply of option and proof tokens + depositBaseToken <- depositToken(master, baseTokenContractId, master.toAddress.bytes.arr, vOptionContractId.bytes.arr, baseTokenDepositAmount, fee + 10000000000L, ts + 9) + depositTargetToken <- depositToken(master, targetTokenContractId, master.toAddress.bytes.arr, vOptionContractId.bytes.arr, targetTokenDepositAmount, fee + 10000000000L, ts + 10) + depositOptionToken <- depositToken(master, optionTokenContractId, master.toAddress.bytes.arr, vOptionContractId.bytes.arr, optionTokenDepositAmount, fee + 10000000000L, ts + 11) + depositProofToken <- depositToken(master, proofTokenContractId, master.toAddress.bytes.arr, vOptionContractId.bytes.arr, proofTokenDepositAmount, fee + 10000000000L, ts + 12) + } yield (genesis, genesis2, master, user, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken , depositProofToken, fee, ts, attach) + + def getOptionContractTokenBalanceKeys(baseTokenContractId: Array[Byte], + targetTokenContractId: Array[Byte], + optionTokenContractId: Array[Byte], + proofTokenContractId: Array[Byte], + vOptionContractId: Array[Byte]): (ByteStr, ByteStr, ByteStr, ByteStr) = { + val baseTokenId = tokenIdFromBytes(baseTokenContractId, Ints.toByteArray(0)).explicitGet() + val targetTokenId = tokenIdFromBytes(targetTokenContractId, Ints.toByteArray(0)).explicitGet() + val optionTokenId = tokenIdFromBytes(optionTokenContractId, Ints.toByteArray(0)).explicitGet() + val proofTokenId = tokenIdFromBytes(proofTokenContractId, Ints.toByteArray(0)).explicitGet() + + val contractBaseTokenBalanceKey = ByteStr(Bytes.concat(baseTokenId.arr, vOptionContractId)) + val contractTargetTokenBalanceKey = ByteStr(Bytes.concat(targetTokenId.arr, vOptionContractId)) + val contractOptionTokenBalanceKey = ByteStr(Bytes.concat(optionTokenId.arr, vOptionContractId)) + val contractProofTokenBalanceKey = ByteStr(Bytes.concat(proofTokenId.arr, vOptionContractId)) + + (contractBaseTokenBalanceKey, contractTargetTokenBalanceKey, contractOptionTokenBalanceKey, contractProofTokenBalanceKey) + } + + def getOptionUserTokenBalanceKeys(baseTokenContractId: Array[Byte], + targetTokenContractId: Array[Byte], + optionTokenContractId: Array[Byte], + proofTokenContractId: Array[Byte], + user: PublicKeyAccount): (ByteStr, ByteStr, ByteStr, ByteStr) = { + val baseTokenId = tokenIdFromBytes(baseTokenContractId, Ints.toByteArray(0)).explicitGet() + val targetTokenId = tokenIdFromBytes(targetTokenContractId, Ints.toByteArray(0)).explicitGet() + val optionTokenId = tokenIdFromBytes(optionTokenContractId, Ints.toByteArray(0)).explicitGet() + val proofTokenId = tokenIdFromBytes(proofTokenContractId, Ints.toByteArray(0)).explicitGet() + + val userBaseTokenBalanceKey = ByteStr(Bytes.concat(baseTokenId.arr, user.toAddress.bytes.arr)) + val userTargetTokenBalanceKey = ByteStr(Bytes.concat(targetTokenId.arr, user.toAddress.bytes.arr)) + val userOptionTokenBalanceKey = ByteStr(Bytes.concat(optionTokenId.arr, user.toAddress.bytes.arr)) + val userProofTokenBalanceKey = ByteStr(Bytes.concat(proofTokenId.arr, user.toAddress.bytes.arr)) + + (userBaseTokenBalanceKey, userTargetTokenBalanceKey, userOptionTokenBalanceKey, userProofTokenBalanceKey) + } + + def getOptionContractStateVarKeys(vOptionContractId: Array[Byte]): (ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr) = { + val optionStatusKey = ByteStr(Bytes.concat(vOptionContractId, Array(7.toByte))) + val maxIssueNumKey = ByteStr(Bytes.concat(vOptionContractId, Array(8.toByte))) + val reservedOptionKey = ByteStr(Bytes.concat(vOptionContractId, Array(9.toByte))) + val reservedProofKey = ByteStr(Bytes.concat(vOptionContractId, Array(10.toByte))) + val priceKey = ByteStr(Bytes.concat(vOptionContractId, Array(11.toByte))) + val priceUnitKey = ByteStr(Bytes.concat(vOptionContractId, Array(12.toByte))) + val tokenLockedKey = ByteStr(Bytes.concat(vOptionContractId, Array(13.toByte))) + val tokenCollectedKey = ByteStr(Bytes.concat(vOptionContractId, Array(14.toByte))) + + (optionStatusKey, maxIssueNumKey, reservedOptionKey, reservedProofKey, priceKey, priceUnitKey, tokenLockedKey, tokenCollectedKey) + } + + def getOptionContractStateMapKeys(vOptionContractId: Array[Byte], user: PublicKeyAccount): (ByteStr, ByteStr, ByteStr, ByteStr) = { + val stateMapBaseTokenBalanceKey = ByteStr(Bytes.concat(vOptionContractId, Array(0.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + val stateMapTargetTokenBalanceKey = ByteStr(Bytes.concat(vOptionContractId, Array(1.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + val stateMapOptionTokenBalanceKey = ByteStr(Bytes.concat(vOptionContractId, Array(2.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + val stateMapProofTokenBalanceKey = ByteStr(Bytes.concat(vOptionContractId, Array(3.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + + (stateMapBaseTokenBalanceKey, stateMapTargetTokenBalanceKey, stateMapOptionTokenBalanceKey, stateMapProofTokenBalanceKey) + } +} diff --git a/src/test/scala/vsys/blockchain/contract/vstableswap/VStableSwapContractGen.scala b/src/test/scala/vsys/blockchain/contract/vstableswap/VStableSwapContractGen.scala new file mode 100644 index 000000000..a716d76e0 --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/vstableswap/VStableSwapContractGen.scala @@ -0,0 +1,263 @@ +package vsys.blockchain.contract.vstableswap + +import com.google.common.primitives.Longs +import org.scalacheck.Gen +import vsys.account.{Address, ContractAccount, PrivateKeyAccount} +import vsys.blockchain.contract._ +import vsys.blockchain.contract.ContractGenHelper._ +import vsys.blockchain.state._ +import vsys.blockchain.transaction.GenesisTransaction +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +trait VStableSwapContractGen { + + val supersedeIndex: Short = 0 + val setOrderIndex: Short = 1 + val updateIndex: Short = 2 + val orderDepositIndex: Short = 3 + val orderWithdrawIndex: Short = 4 + val closeIndex: Short = 5 + val swapBaseToTargetIndex: Short = 6 + val swapTargetToBaseIndex: Short = 7 + + def vStableSwapContractGen(): Gen[Contract] = ContractVStableSwap.contract + + def genesisVStableSwapGen(rep: PrivateKeyAccount, + ts: Long): Gen[GenesisTransaction] = + GenesisTransaction.create(rep, ENOUGH_AMT, -1, ts).explicitGet() + + def registerVStableSwapGen(signer: PrivateKeyAccount, + contract: Contract, + dataStack: Seq[DataEntry], + description: String, + fee: Long, + ts: Long): Gen[RegisterContractTransaction] = + RegisterContractTransaction.create(signer, contract, dataStack, description, fee, feeScale, ts).explicitGet() + + def addressDataStackGen(address: Address): Gen[Seq[DataEntry]] = for { + addr <- Gen.const(DataEntry(address.bytes.arr, DataType.Address)) + } yield Seq(addr) + + def setOrderDataStackGen(feeBase: Long, + feeTarget: Long, + minBase: Long, + maxBase: Long, + minTarget: Long, + maxTarget: Long, + priceBase: Long, + priceTarget: Long, + baseDeposit: Long, + targetDeposit: Long): Gen[Seq[DataEntry]] = for { + feeBase <- Gen.const(DataEntry(Longs.toByteArray(feeBase), DataType.Amount)) + feeTarget <- Gen.const(DataEntry(Longs.toByteArray(feeTarget), DataType.Amount)) + minBase <- Gen.const(DataEntry(Longs.toByteArray(minBase), DataType.Amount)) + maxBase <- Gen.const(DataEntry(Longs.toByteArray(maxBase), DataType.Amount)) + minTarget <- Gen.const(DataEntry(Longs.toByteArray(minTarget), DataType.Amount)) + maxTarget <- Gen.const(DataEntry(Longs.toByteArray(maxTarget), DataType.Amount)) + priceBase <- Gen.const(DataEntry(Longs.toByteArray(priceBase), DataType.Amount)) + priceTarget <- Gen.const(DataEntry(Longs.toByteArray(priceTarget), DataType.Amount)) + baseDeposit <- Gen.const(DataEntry(Longs.toByteArray(baseDeposit), DataType.Amount)) + targetDeposit <- Gen.const(DataEntry(Longs.toByteArray(targetDeposit), DataType.Amount)) + } yield Seq(feeBase, feeTarget, minBase, maxBase, minTarget, maxTarget, priceBase, priceTarget, baseDeposit, targetDeposit) + + def updateDataStackGen(orderId: Array[Byte], + feeBase: Long, + feeTarget: Long, + minBase: Long, + maxBase: Long, + minTarget: Long, + maxTarget: Long, + priceBase: Long, + priceTarget: Long): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + feeBase <- Gen.const(DataEntry(Longs.toByteArray(feeBase), DataType.Amount)) + feeTarget <- Gen.const(DataEntry(Longs.toByteArray(feeTarget), DataType.Amount)) + minBase <- Gen.const(DataEntry(Longs.toByteArray(minBase), DataType.Amount)) + maxBase <- Gen.const(DataEntry(Longs.toByteArray(maxBase), DataType.Amount)) + minTarget <- Gen.const(DataEntry(Longs.toByteArray(minTarget), DataType.Amount)) + maxTarget <- Gen.const(DataEntry(Longs.toByteArray(maxTarget), DataType.Amount)) + priceBase <- Gen.const(DataEntry(Longs.toByteArray(priceBase), DataType.Amount)) + priceTarget <- Gen.const(DataEntry(Longs.toByteArray(priceTarget), DataType.Amount)) + } yield Seq(orderId, feeBase, feeTarget, minBase, maxBase, minTarget, maxTarget, priceBase, priceTarget) + + def orderDepositDataStackGen(orderId: Array[Byte], + baseDeposit: Long, + targetDeposit: Long): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + baseDeposit <- Gen.const(DataEntry(Longs.toByteArray(baseDeposit), DataType.Amount)) + targetDeposit <- Gen.const(DataEntry(Longs.toByteArray(targetDeposit), DataType.Amount)) + } yield Seq(orderId, baseDeposit, targetDeposit) + + def orderWithdrawDataStackGen(orderId: Array[Byte], + baseWithdraw: Long, + targetWithdraw: Long): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + baseWithdraw <- Gen.const(DataEntry(Longs.toByteArray(baseWithdraw), DataType.Amount)) + targetWithdraw <- Gen.const(DataEntry(Longs.toByteArray(targetWithdraw), DataType.Amount)) + } yield Seq(orderId, baseWithdraw, targetWithdraw) + + def closeDataStackGen(orderId: Array[Byte]): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + } yield Seq(orderId) + + def swapBaseToTargetDataStackGen(orderId: Array[Byte], + amount: Long, + fee: Long, + price: Long, + deadLine: Long): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + amount <- Gen.const(DataEntry(Longs.toByteArray(amount), DataType.Amount)) + fee <- Gen.const(DataEntry(Longs.toByteArray(fee), DataType.Amount)) + price <- Gen.const(DataEntry(Longs.toByteArray(price), DataType.Amount)) + deadLine <- Gen.const(DataEntry(Longs.toByteArray(deadLine), DataType.Timestamp)) + } yield Seq(orderId, amount, fee, price, deadLine) + + def swapTargetToBaseDataStackGen(orderId: Array[Byte], + amount: Long, + fee: Long, + price: Long, + deadLine: Long): Gen[Seq[DataEntry]] = for { + orderId <- Gen.const(DataEntry.create(orderId, DataType.ShortBytes).right.get) + amount <- Gen.const(DataEntry(Longs.toByteArray(amount), DataType.Amount)) + fee <- Gen.const(DataEntry(Longs.toByteArray(fee), DataType.Amount)) + price <- Gen.const(DataEntry(Longs.toByteArray(price), DataType.Amount)) + deadLine <- Gen.const(DataEntry(Longs.toByteArray(deadLine), DataType.Timestamp)) + } yield Seq(orderId, amount, fee, price, deadLine) + + def initVStableSwapDataStackGen(baseTokenId: Array[Byte], + targetTokenId: Array[Byte], + maxOrderPerUser: Long, + unitPriceBase: Long, + unitPriceTarget: Long): Gen[Seq[DataEntry]] = for { + baseTokenId <- Gen.const(DataEntry(baseTokenId, DataType.TokenId)) + targetTokenId <- Gen.const(DataEntry(targetTokenId, DataType.TokenId)) + maxOrderPerUser <- Gen.const(DataEntry(Longs.toByteArray(maxOrderPerUser), DataType.Amount)) + unitPriceBase <- Gen.const(DataEntry(Longs.toByteArray(unitPriceBase), DataType.Amount)) + unitPriceTarget <- Gen.const(DataEntry(Longs.toByteArray(unitPriceTarget), DataType.Amount)) + } yield Seq(baseTokenId, targetTokenId, maxOrderPerUser, unitPriceBase, unitPriceTarget) + + def supersedeVStableSwapGen(signer: PrivateKeyAccount, contractId: ContractAccount, newAdd: Address, + attachment: Array[Byte], fee: Long, ts: Long): Gen[ExecuteContractFunctionTransaction] = for { + data: Seq[DataEntry] <- addressDataStackGen(newAdd) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, supersedeIndex, data, attachment, fee, feeScale, ts).explicitGet() + + def setOrderVStableSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + feeBase: Long, + feeTarget: Long, + minBase: Long, + maxBase: Long, + minTarget: Long, + maxTarget: Long, + priceBase: Long, + priceTarget: Long, + baseDeposit: Long, + targetDeposit: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = setOrderIndex + for { + data: Seq[DataEntry] <- setOrderDataStackGen(feeBase, feeTarget, minBase, maxBase, minTarget, maxTarget, priceBase, priceTarget, baseDeposit, targetDeposit) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def updateVStableSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + feeBase: Long, + feeTarget: Long, + minBase: Long, + maxBase: Long, + minTarget: Long, + maxTarget: Long, + priceBase: Long, + priceTarget: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = updateIndex + for { + data: Seq[DataEntry] <- updateDataStackGen(orderId, feeBase, feeTarget, minBase, maxBase, minTarget, maxTarget, priceBase, priceTarget) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def orderDepositVStableSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + baseDeposit: Long, + targetDeposit: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = orderDepositIndex + for { + data: Seq[DataEntry] <- orderDepositDataStackGen(orderId, baseDeposit, targetDeposit) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def orderWithdrawVStableSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + baseWithdraw: Long, + targetWithdraw: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = orderWithdrawIndex + for { + data: Seq[DataEntry] <- orderWithdrawDataStackGen(orderId, baseWithdraw, targetWithdraw) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def closeVStableSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = closeIndex + for { + data: Seq[DataEntry] <- closeDataStackGen(orderId) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def swapBaseToTargetVStableSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + amount: Long, + swapFee: Long, + price: Long, + deadLine: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = swapBaseToTargetIndex + for { + data: Seq[DataEntry] <- swapBaseToTargetDataStackGen(orderId, amount, swapFee, price, deadLine) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def swapTargetToBaseVStableSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + orderId: Array[Byte], + amount: Long, + swapFee: Long, + price: Long, + deadLine: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = swapTargetToBaseIndex + for { + data: Seq[DataEntry] <- swapTargetToBaseDataStackGen(orderId, amount, swapFee, price, deadLine) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/contract/vstableswap/VStableSwapFunction.scala b/src/test/scala/vsys/blockchain/contract/vstableswap/VStableSwapFunction.scala new file mode 100644 index 000000000..f2e2ad085 --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/vstableswap/VStableSwapFunction.scala @@ -0,0 +1,41 @@ +package vsys.blockchain.contract.vstableswap + +import vsys.blockchain.contract.ContractGenHelper._ +import vsys.blockchain.contract.DataType +import vsys.blockchain.contract.ContractVStableSwap._ + +trait VStableSwapFunction { + + val initId: Short = 0 + + val initDataType: Array[Byte] = Array(DataType.TokenId.id.toByte, DataType.TokenId.id.toByte, DataType.Amount.id.toByte, DataType.Amount.id.toByte, DataType.Amount.id.toByte) + + val initWrongDataType: Array[Byte] = Array(DataType.TokenId.id.toByte, DataType.Amount.id.toByte, DataType.Amount.id.toByte, DataType.Amount.id.toByte, DataType.Amount.id.toByte) + + val initTriggerOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(5.toByte), + cdbvSet ++ Array(makerStateVar.index, 5.toByte), + cdbvSet ++ Array(baseTokenIdStateVar.index, 0.toByte), + cdbvSet ++ Array(targetTokenIdStateVar.index, 1.toByte), + cdbvSet ++ Array(maxOrderPerUserStateVar.index, 2.toByte), + cdbvSet ++ Array(unitPriceBaseStateVar.index, 3.toByte), + cdbvSet ++ Array(unitPriceTargetStateVar.index, 4.toByte) + ) + + val initWrongTriggerOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(5.toByte), + cdbvSet ++ Array(makerStateVar.index, 5.toByte), + cdbvSet ++ Array(baseTokenIdStateVar.index, 0.toByte), + cdbvSet ++ Array(targetTokenIdStateVar.index, 1.toByte), + cdbvSet ++ Array(maxOrderPerUserStateVar.index, 2.toByte), + cdbvSet ++ Array(unitPriceBaseStateVar.index, 3.toByte), + Array(5.toByte, 3.toByte) ++ Array(unitPriceTargetStateVar.index, 4.toByte) + ) + + val nonReturnType: Array[Byte] = Array[Byte]() + val onInitTriggerType: Byte = 0 + + lazy val wrongDataTrigger: Seq[Array[Byte]] = Seq(getFunctionBytes(initId, onInitTriggerType, nonReturnType, initWrongDataType, initTriggerOpcs)) + lazy val wrongOpcTrigger: Seq[Array[Byte]] = Seq(getFunctionBytes(initId, onInitTriggerType, nonReturnType, initDataType, initWrongTriggerOpcs)) + +} diff --git a/src/test/scala/vsys/blockchain/contract/vstableswap/VStableSwapFunctionHelperGen.scala b/src/test/scala/vsys/blockchain/contract/vstableswap/VStableSwapFunctionHelperGen.scala new file mode 100644 index 000000000..f71e467e9 --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/vstableswap/VStableSwapFunctionHelperGen.scala @@ -0,0 +1,161 @@ +package vsys.blockchain.contract.vstableswap + +import com.google.common.primitives.{Bytes, Ints, Longs} +import org.scalacheck.Gen +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.account.{Address, ContractAccount, PrivateKeyAccount, PublicKeyAccount} +import vsys.blockchain.contract.{DataEntry, DataType} +import vsys.blockchain.contract.token.TokenContractGen +import vsys.blockchain.contract.ContractGenHelper._ +import vsys.blockchain.state.ByteStr +import vsys.blockchain.transaction.GenesisTransaction +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +trait VStableSwapFunctionHelperGen extends VStableSwapContractGen with TokenContractGen { + + override val supersedeIndex: Short = 0 + + override def addressDataStackGen(address: Address): Gen[Seq[DataEntry]] = for { + addr <- Gen.const(DataEntry(address.bytes.arr, DataType.Address)) + } yield Seq(addr) + + def registerToken(user: PrivateKeyAccount, + totalSupply: Long, + unity: Long, + desc: String, + fee: Long, + timestamp: Long): Gen[RegisterContractTransaction] = for { + initTokenDataStack: Seq[DataEntry] <- initTokenDataStackGen(totalSupply, unity, desc) + description <- validDescStringGen + tokenContract <- tokenContractGen(false) + regTokenContract <- registerTokenGen(user, tokenContract, initTokenDataStack, description, fee, timestamp) + } yield regTokenContract + + def issueToken(user: PrivateKeyAccount, + contractId: ContractAccount, + issueAmount: Long, + fee: Long, + timestamp: Long): Gen[ExecuteContractFunctionTransaction] = for { + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issueToken <- issueTokenGen(user, contractId, issueAmount, attach, fee, timestamp) + } yield issueToken + + def depositToken(user: PrivateKeyAccount, + contractId: ContractAccount, + sender: Array[Byte], + contract: Array[Byte], + amount: Long, + fee: Long, + timestamp: Long + ): Gen[ExecuteContractFunctionTransaction] = for { + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + depositTokenData = Seq(sender, contract, Longs.toByteArray(amount)) + depositTokenDataType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + depositToken <- depositTokenGen(user, contractId, false, depositTokenData, depositTokenDataType, attach, fee, timestamp) + } yield depositToken + + def withdrawToken(user: PrivateKeyAccount, + contractId: ContractAccount, + contract: Array[Byte], + sender: Array[Byte], + amount: Long, + fee: Long, + timestamp: Long): Gen[ExecuteContractFunctionTransaction] = for { + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + withdrawTokenData = Seq(contract, sender, Longs.toByteArray(amount)) + withdrawTokenDataType = Seq(DataType.ContractAccount, DataType.Address, DataType.Amount) + withdrawToken <- withdrawTokenGen(user, contractId, false, withdrawTokenData, withdrawTokenDataType, attach, fee, timestamp) + } yield withdrawToken + + def createBaseTokenTargetTokenAndInitVStableSwap(totalSupplyBase: Long, + unityBase: Long, + issueAmountBase: Long, + totalSupplyTarget: Long, + unityTarget: Long, + issueAmountTarget: Long, + maxOrderPerUser: Long, + unitPriceBase: Long, + unitPriceTarget: Long): Gen[(GenesisTransaction, GenesisTransaction, + PrivateKeyAccount, PrivateKeyAccount, RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, Long, Long, Array[Byte])] = for { + (master, ts, fee) <- basicContractTestGen() + genesis <- genesisVStableSwapGen(master, ts) + user <- accountGen + genesis2 <- genesisVStableSwapGen(user, ts) + vStableSwapContract <- vStableSwapContractGen() + // Register base token + regTokenBase <- registerToken(master, totalSupplyBase, unityBase, "init", fee, ts) + tokenBaseContractId = regTokenBase.contractId + tokenBaseId = tokenIdFromBytes(tokenBaseContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // Register target token + regTokenTarget <- registerToken(master, totalSupplyTarget, unityTarget, "init", fee, ts + 1) + tokenTargetContractId = regTokenTarget.contractId + tokenTargetId = tokenIdFromBytes(tokenTargetContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // Register V Stable Swap Contract + description <- validDescStringGen + initVStableSwapDataStack <- initVStableSwapDataStackGen(tokenBaseId.arr, tokenTargetId.arr, maxOrderPerUser, unitPriceBase, unitPriceTarget) + regVStableSwapContract <- registerVStableSwapGen(master, vStableSwapContract, initVStableSwapDataStack, description, fee, ts + 2) + // Issue base token + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issueTokenBase <- issueToken(master, tokenBaseContractId, issueAmountBase, fee, ts + 3) + // Issue target token + issueTokenTarget <- issueToken(master, tokenTargetContractId, issueAmountTarget, fee, ts + 4) + // Deposit base and target token into V Stable Swap + depositBase <- depositToken(master, tokenBaseContractId, master.toAddress.bytes.arr, regVStableSwapContract.contractId.bytes.arr, issueAmountBase, fee, ts + 5) + depositTarget <- depositToken(master, tokenTargetContractId, master.toAddress.bytes.arr, regVStableSwapContract.contractId.bytes.arr, issueAmountTarget, fee, ts + 6) + } yield (genesis, genesis2, master, user, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attach) + + def getContractTokenBalanceKeys(tokenBaseContractId: Array[Byte], tokenTargetContractId: Array[Byte], vStableSwapContractId: Array[Byte]): (ByteStr, ByteStr) = { + val tokenBaseId = tokenIdFromBytes(tokenBaseContractId, Ints.toByteArray(0)).explicitGet() + val tokenTargetId = tokenIdFromBytes(tokenTargetContractId, Ints.toByteArray(0)).explicitGet() + + val contractTokenBaseBalanceKey = ByteStr(Bytes.concat(tokenBaseId.arr, vStableSwapContractId)) + val contractTokenTargetBalanceKey = ByteStr(Bytes.concat(tokenTargetId.arr, vStableSwapContractId)) + + (contractTokenBaseBalanceKey, contractTokenTargetBalanceKey) + } + + def getUserTokenBalanceKeys(tokenBaseContractId: Array[Byte], tokenTargetContractId: Array[Byte], user: PublicKeyAccount): (ByteStr, ByteStr) = { + val tokenBaseId = tokenIdFromBytes(tokenBaseContractId, Ints.toByteArray(0)).explicitGet() + val tokenTargetId = tokenIdFromBytes(tokenTargetContractId, Ints.toByteArray(0)).explicitGet() + + val userTokenBaseBalanceKey = ByteStr(Bytes.concat(tokenBaseId.arr, user.toAddress.bytes.arr)) + val userTokenTargetBalanceKey = ByteStr(Bytes.concat(tokenTargetId.arr, user.toAddress.bytes.arr)) + + (userTokenBaseBalanceKey, userTokenTargetBalanceKey) + } + + def getStableSwapContractStateVarKeys(vStableSwapContractId: Array[Byte]): (ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr) = { + val makerKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(0.toByte))) + val baseTokenIdKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(1.toByte))) + val targetTokenIdKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(2.toByte))) + val maxOrderPerUserKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(3.toByte))) + val unitPriceBaseKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(4.toByte))) + val unitPriceTargetKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(5.toByte))) + + (makerKey, baseTokenIdKey, targetTokenIdKey, maxOrderPerUserKey, unitPriceBaseKey, unitPriceTargetKey) + } + + def getStableSwapContractStateMapKeys(vStableSwapContractId: Array[Byte], orderId: Array[Byte], user: PublicKeyAccount): (ByteStr, ByteStr, ByteStr, + ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr) = { + val baseTokenBalanceKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(0.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + val targetTokenBalanceKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(1.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + val userOrdersKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(2.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + val orderOwnerKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(3.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val feeBaseKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(4.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val feeTargetKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(5.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val minBaseKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(6.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val maxBaseKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(7.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val minTargetKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(8.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val maxTargetKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(9.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val priceBaseKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(10.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val priceTargetKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(11.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val baseTokenLockedKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(12.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val targetTokenLockedKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(13.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + val orderStatusKey = ByteStr(Bytes.concat(vStableSwapContractId, Array(14.toByte), DataEntry.create(orderId, DataType.ShortBytes).right.get.bytes)) + + (baseTokenBalanceKey, targetTokenBalanceKey, userOrdersKey, orderOwnerKey, feeBaseKey, feeTargetKey, minBaseKey, maxBaseKey, + minTargetKey, maxTargetKey, priceBaseKey, priceTargetKey, baseTokenLockedKey, targetTokenLockedKey, orderStatusKey) + } +} diff --git a/src/test/scala/vsys/blockchain/contract/vswap/VSwapContractGen.scala b/src/test/scala/vsys/blockchain/contract/vswap/VSwapContractGen.scala new file mode 100644 index 000000000..638c4105a --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/vswap/VSwapContractGen.scala @@ -0,0 +1,231 @@ +package vsys.blockchain.contract.vswap + +import com.google.common.primitives.Longs +import org.scalacheck.Gen +import vsys.account.{Address, ContractAccount, PrivateKeyAccount} +import vsys.blockchain.contract.{Contract, ContractVSwap, DataEntry, DataType} +import vsys.blockchain.contract.ContractGenHelper._ +import vsys.blockchain.state._ +import vsys.blockchain.transaction.GenesisTransaction +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + + +trait VSwapContractGen { + + val supersedeIndex: Short = 0 + val setSwapIndex: Short = 1 + val addLiquidityIndex: Short = 2 + val removeLiquidityIndex: Short = 3 + val swapTokenForExactBaseTokenIndex: Short = 4 + val swapExactTokenForBaseTokenIndex: Short = 5 + val swapTokenForExactTargetTokenIndex: Short = 6 + val swapExactTokenForTargetTokenIndex: Short = 7 + + def vSwapContractGen(): Gen[Contract] = ContractVSwap.contract + + def genesisVSwapGen(rep: PrivateKeyAccount, + ts: Long): Gen[GenesisTransaction] = + GenesisTransaction.create(rep, ENOUGH_AMT, -1, ts).explicitGet() + + def registerVSwapGen(signer: PrivateKeyAccount, + contract: Contract, + dataStack: Seq[DataEntry], + description: String, + fee: Long, + ts: Long): Gen[RegisterContractTransaction] = + RegisterContractTransaction.create(signer, contract, dataStack, description, fee, feeScale, ts).explicitGet() + + def addressDataStackGen(address: Address): Gen[Seq[DataEntry]] = for { + addr <- Gen.const(DataEntry(address.bytes.arr, DataType.Address)) + } yield Seq(addr) + + def setSwapDataStackGen(amountADesired: Long, + amountBDesired: Long): Gen[Seq[DataEntry]] = for { + amountADesired <- Gen.const(DataEntry(Longs.toByteArray(amountADesired), DataType.Amount)) + amountBDesired <- Gen.const(DataEntry(Longs.toByteArray(amountBDesired), DataType.Amount)) + } yield Seq(amountADesired, amountBDesired) + + def addLiquidityDataStackGen(amountADesired: Long, + amountBDesired: Long, + amountAMin: Long, + amountBMin: Long, + deadline: Long): Gen[Seq[DataEntry]] = for { + amountADesired <- Gen.const(DataEntry(Longs.toByteArray(amountADesired), DataType.Amount)) + amountBDesired <- Gen.const(DataEntry(Longs.toByteArray(amountBDesired), DataType.Amount)) + amountAMin <- Gen.const(DataEntry(Longs.toByteArray(amountAMin), DataType.Amount)) + amountBMin <- Gen.const(DataEntry(Longs.toByteArray(amountBMin), DataType.Amount)) + deadline <- Gen.const(DataEntry(Longs.toByteArray(deadline), DataType.Timestamp)) + } yield Seq(amountADesired, amountBDesired, amountAMin, amountBMin, deadline) + + def removeLiquidityDataStackGen(liquidity: Long, + amountAMin: Long, + amountBMin: Long, + deadline: Long): Gen[Seq[DataEntry]] = for { + liquidity <- Gen.const(DataEntry(Longs.toByteArray(liquidity), DataType.Amount)) + amountAMin <- Gen.const(DataEntry(Longs.toByteArray(amountAMin), DataType.Amount)) + amountBMin <- Gen.const(DataEntry(Longs.toByteArray(amountBMin), DataType.Amount)) + deadline <- Gen.const(DataEntry(Longs.toByteArray(deadline), DataType.Timestamp)) + } yield Seq(liquidity, amountAMin, amountBMin, deadline) + + def swapTokenForExactBaseTokenDataStackGen(amountOut: Long, + amountInMax: Long, + deadline: Long): Gen[Seq[DataEntry]] = for { + amountOut <- Gen.const(DataEntry(Longs.toByteArray(amountOut), DataType.Amount)) + amountInMax <- Gen.const(DataEntry(Longs.toByteArray(amountInMax), DataType.Amount)) + deadline <- Gen.const(DataEntry(Longs.toByteArray(deadline), DataType.Timestamp)) + } yield Seq(amountOut, amountInMax, deadline) + + def swapExactTokenForBaseTokenDataStackGen(amountOutMin: Long, + amountIn: Long, + deadline: Long): Gen[Seq[DataEntry]] = for { + amountOutMin <- Gen.const(DataEntry(Longs.toByteArray(amountOutMin), DataType.Amount)) + amountIn <- Gen.const(DataEntry(Longs.toByteArray(amountIn), DataType.Amount)) + deadline <- Gen.const(DataEntry(Longs.toByteArray(deadline), DataType.Timestamp)) + } yield Seq(amountOutMin, amountIn, deadline) + + def swapTokenForExactTargetTokenDataStackGen(amountOut: Long, + amountInMax: Long, + deadline: Long): Gen[Seq[DataEntry]] = for { + amountOut <- Gen.const(DataEntry(Longs.toByteArray(amountOut), DataType.Amount)) + amountInMax <- Gen.const(DataEntry(Longs.toByteArray(amountInMax), DataType.Amount)) + deadline <- Gen.const(DataEntry(Longs.toByteArray(deadline), DataType.Timestamp)) + } yield Seq(amountOut, amountInMax, deadline) + + def swapExactTokenForTargetTokenDataStackGen(amountOutMin: Long, + amountIn: Long, + deadline: Long): Gen[Seq[DataEntry]] = for { + amountOutMin <- Gen.const(DataEntry(Longs.toByteArray(amountOutMin), DataType.Amount)) + amountIn <- Gen.const(DataEntry(Longs.toByteArray(amountIn), DataType.Amount)) + deadline <- Gen.const(DataEntry(Longs.toByteArray(deadline), DataType.Timestamp)) + } yield Seq(amountOutMin, amountIn, deadline) + + def initVSwapDataStackGen(tokenAId: Array[Byte], + tokenBId: Array[Byte], + liquidityTokenId: Array[Byte], + minimumLiquidity: Long): Gen[Seq[DataEntry]] = for { + tokenAId <- Gen.const(DataEntry.create(tokenAId, DataType.TokenId).right.get) + tokenBId <- Gen.const(DataEntry.create(tokenBId, DataType.TokenId).right.get) + liquidityTokenId <- Gen.const(DataEntry.create(liquidityTokenId, DataType.TokenId).right.get) + minimumLiquidity <- Gen.const(DataEntry.create(Longs.toByteArray(minimumLiquidity), DataType.Amount).right.get) + } yield Seq(tokenAId, tokenBId, liquidityTokenId, minimumLiquidity) + + def supersedeVSwapGen(signer: PrivateKeyAccount, + contractId: ContractAccount, + newAddr: Address, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = supersedeIndex + for { + data: Seq[DataEntry] <- addressDataStackGen(newAddr) + } yield ExecuteContractFunctionTransaction.create(signer, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def setSwapVSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + amountADesired: Long, + amountBDesired: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = setSwapIndex + for { + data: Seq[DataEntry] <- setSwapDataStackGen(amountADesired, amountBDesired) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def addLiquidityVSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + amountADesired: Long, + amountBDesired: Long, + amountAMin: Long, + amountBMin: Long, + deadline: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = addLiquidityIndex + for { + data: Seq[DataEntry] <- addLiquidityDataStackGen(amountADesired, amountBDesired, amountAMin, amountBMin, deadline) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def removeLiquidityVSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + liquidity: Long, + amountAMin: Long, + amountBMin: Long, + deadline: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = removeLiquidityIndex + for { + data: Seq[DataEntry] <- removeLiquidityDataStackGen(liquidity, amountAMin, amountBMin, deadline) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def swapTokenForExactBaseTokenVSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + amountOut: Long, + amountInMax: Long, + deadline: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = swapTokenForExactBaseTokenIndex + for { + data: Seq[DataEntry] <- swapTokenForExactBaseTokenDataStackGen(amountOut, amountInMax, deadline) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def swapExactTokenForBaseTokenVSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + amountOutMin: Long, + amountIn: Long, + deadline: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = swapExactTokenForBaseTokenIndex + for { + data: Seq[DataEntry] <- swapExactTokenForBaseTokenDataStackGen(amountOutMin, amountIn, deadline) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def swapTokenForExactTargetTokenVSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + amountOut: Long, + amountInMax: Long, + deadline: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = swapTokenForExactTargetTokenIndex + for { + data: Seq[DataEntry] <- swapTokenForExactTargetTokenDataStackGen(amountOut, amountInMax, deadline) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } + + def swapExactTokenForTargetTokenVSwapGen(sender: PrivateKeyAccount, + contractId: ContractAccount, + amountOutMin: Long, + amountIn: Long, + deadline: Long, + attachment: Array[Byte], + fee: Long, + ts: Long): Gen[ExecuteContractFunctionTransaction] = { + val id: Short = swapExactTokenForTargetTokenIndex + for { + data: Seq[DataEntry] <- swapExactTokenForTargetTokenDataStackGen(amountOutMin, amountIn, deadline) + } yield ExecuteContractFunctionTransaction.create(sender, contractId, id, + data, attachment, fee, feeScale, ts).explicitGet() + } +} diff --git a/src/test/scala/vsys/blockchain/contract/vswap/VSwapFunction.scala b/src/test/scala/vsys/blockchain/contract/vswap/VSwapFunction.scala new file mode 100644 index 000000000..17478bd27 --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/vswap/VSwapFunction.scala @@ -0,0 +1,41 @@ +package vsys.blockchain.contract.vswap + +import vsys.blockchain.contract.ContractGen.{basicConstantGet, cdbvSet, getFunctionBytes, loadSigner} +import vsys.blockchain.contract.ContractVSwap._ +import vsys.blockchain.contract.{DataEntry, DataType} + +trait VSwapFunction { + + val initId: Short = 0 + + val initDataType: Array[Byte] = Array(DataType.TokenId.id.toByte, DataType.TokenId.id.toByte, DataType.TokenId.id.toByte, DataType.Amount.id.toByte) + val initWrongDataType: Array[Byte] = Array(DataType.Amount.id.toByte, DataType.TokenId.id.toByte, DataType.TokenId.id.toByte, DataType.Amount.id.toByte) + + val initTriggerOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(4.toByte), + cdbvSet ++ Array(makerStateVar.index, 4.toByte), + cdbvSet ++ Array(tokenAIdStateVar.index, 0.toByte), + cdbvSet ++ Array(tokenBIdStateVar.index, 1.toByte), + cdbvSet ++ Array(tokenLiquidityIdStateVar.index, 2.toByte), + cdbvSet ++ Array(minimumLiquidityStateVar.index, 3.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + cdbvSet ++ Array(swapStatusStateVar.index, 5.toByte) + ) + + val initWrongTriggerOpcs: Seq[Array[Byte]] = Seq( + loadSigner ++ Array(4.toByte), + Array(5.toByte, 3.toByte) ++ Array(makerStateVar.index, 4.toByte), + cdbvSet ++ Array(tokenAIdStateVar.index, 0.toByte), + cdbvSet ++ Array(tokenBIdStateVar.index, 1.toByte), + cdbvSet ++ Array(tokenLiquidityIdStateVar.index, 2.toByte), + cdbvSet ++ Array(minimumLiquidityStateVar.index, 3.toByte), + basicConstantGet ++ DataEntry(Array(0.toByte), DataType.Boolean).bytes ++ Array(5.toByte), + cdbvSet ++ Array(swapStatusStateVar.index, 5.toByte) + ) + + val nonReturnType: Array[Byte] = Array[Byte]() + val onInitTriggerType: Byte = 0 + + lazy val wrongDataTrigger: Seq[Array[Byte]] = Seq(getFunctionBytes(initId, onInitTriggerType, nonReturnType, initWrongDataType, initTriggerOpcs)) + lazy val wrongOpcTrigger: Seq[Array[Byte]] = Seq(getFunctionBytes(initId, onInitTriggerType, nonReturnType, initDataType, initWrongTriggerOpcs)) +} diff --git a/src/test/scala/vsys/blockchain/contract/vswap/VSwapFunctionHelperGen.scala b/src/test/scala/vsys/blockchain/contract/vswap/VSwapFunctionHelperGen.scala new file mode 100644 index 000000000..9940002d6 --- /dev/null +++ b/src/test/scala/vsys/blockchain/contract/vswap/VSwapFunctionHelperGen.scala @@ -0,0 +1,163 @@ +package vsys.blockchain.contract.vswap + +import com.google.common.primitives.{Bytes, Ints, Longs} +import org.scalacheck.Gen +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.account.{Address, ContractAccount, PrivateKeyAccount, PublicKeyAccount} +import vsys.blockchain.contract.{DataEntry, DataType} +import vsys.blockchain.contract.token.TokenContractGen +import vsys.blockchain.contract.ContractGenHelper._ +import vsys.blockchain.state.ByteStr +import vsys.blockchain.transaction.GenesisTransaction +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +trait VSwapFunctionHelperGen extends VSwapContractGen with TokenContractGen { + + override val supersedeIndex: Short = 0 + + override def addressDataStackGen(address: Address): Gen[Seq[DataEntry]] = for { + addr <- Gen.const(DataEntry(address.bytes.arr, DataType.Address)) + } yield Seq(addr) + + def registerToken(user: PrivateKeyAccount, + totalSupply: Long, + unity: Long, + desc: String, + fee: Long, + timestamp: Long): Gen[RegisterContractTransaction] = for { + initTokenDataStack: Seq[DataEntry] <- initTokenDataStackGen(totalSupply, unity, desc) + description <- validDescStringGen + tokenContract <- tokenContractGen(false) + regTokenContract <- registerTokenGen(user, tokenContract, initTokenDataStack, description, fee, timestamp) + } yield regTokenContract + + def issueToken(user: PrivateKeyAccount, + contractId: ContractAccount, + issueAmount: Long, + fee: Long, + timestamp: Long): Gen[ExecuteContractFunctionTransaction] = for { + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issueToken <- issueTokenGen(user, contractId, issueAmount, attach, fee, timestamp) + } yield issueToken + + def depositToken(user: PrivateKeyAccount, + contractId: ContractAccount, + sender: Array[Byte], + contract: Array[Byte], + amount: Long, + fee: Long, + timestamp: Long + ): Gen[ExecuteContractFunctionTransaction] = for { + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + depositTokenData = Seq(sender, contract, Longs.toByteArray(amount)) + depositTokenDataType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + depositToken <- depositTokenGen(user, contractId, false, depositTokenData, depositTokenDataType, attach, fee, timestamp) + } yield depositToken + + def withdrawToken(user: PrivateKeyAccount, + contractId: ContractAccount, + contract: Array[Byte], + sender: Array[Byte], + amount: Long, + fee: Long, + timestamp: Long): Gen[ExecuteContractFunctionTransaction] = for { + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + withdrawTokenData = Seq(contract, sender, Longs.toByteArray(amount)) + withdrawTokenDataType = Seq(DataType.ContractAccount, DataType.Address, DataType.Amount) + withdrawToken <- withdrawTokenGen(user, contractId, false, withdrawTokenData, withdrawTokenDataType, attach, fee, timestamp) + } yield withdrawToken + + def createABLiquidityTokenAndInitVSwap(totalSupplyA: Long, + unityA: Long, + issueAmountA: Long, + totalSupplyB: Long, + unityB: Long, + issueAmountB: Long, + liquidityTotalSupply: Long, + liquidityUnity: Long, + minimumLiquidity: Long, + tokenADepositAmount: Long, + tokenBDepositAmount: Long): Gen[(GenesisTransaction, GenesisTransaction, + PrivateKeyAccount, PrivateKeyAccount, RegisterContractTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, Long, Long, Array[Byte])] = for { + (master, ts, fee) <- basicContractTestGen() + genesis <- genesisVSwapGen(master, ts) + user <- accountGen + genesis2 <- genesisVSwapGen(user, ts) + vSwapContract <- vSwapContractGen() + // register token A + regTokenAContract <- registerToken(master, totalSupplyA, unityA, "init", fee + 10000000000L, ts) + tokenAContractId = regTokenAContract.contractId + tokenAId = tokenIdFromBytes(tokenAContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // register token B + regTokenBContract <- registerToken(master, totalSupplyB, unityB, "init", fee + 10000000000L, ts + 1) + tokenBContractId = regTokenBContract.contractId + tokenBId = tokenIdFromBytes(tokenBContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // register liquidity token + regLiquidityTokenContract <- registerToken(master, liquidityTotalSupply, liquidityUnity, "init", fee + 10000000000L, ts + 2) + liquidityTokenContractId = regLiquidityTokenContract.contractId + liquidityTokenId = tokenIdFromBytes(liquidityTokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // register VSwap contract + description <- validDescStringGen + initVSwapDataStack: Seq[DataEntry] <- initVSwapDataStackGen(tokenAId.arr, tokenBId.arr, liquidityTokenId.arr, minimumLiquidity) + regVSwapContract <- registerVSwapGen(master, vSwapContract, initVSwapDataStack, description, fee + 10000000000L, ts + 3) + vSwapContractId = regVSwapContract.contractId + // issue token A + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issueTokenA <- issueToken(master, tokenAContractId, issueAmountA, fee, ts + 4) + // issue token B + issueTokenB <- issueToken(master, tokenBContractId, issueAmountB, fee, ts + 5) + // issue liquidity token, always issue the entire supply of liquidity tokens + issueLiquidityToken <- issueToken(master, liquidityTokenContractId, liquidityTotalSupply, fee, ts + 6) + // deposit all issued tokens into swap contract, always deposit the entire supply of liquidity tokens + depositTokenA <- depositToken(master, tokenAContractId, master.toAddress.bytes.arr, vSwapContractId.bytes.arr, tokenADepositAmount, fee + 10000000000L, ts + 7) + depositTokenB <- depositToken(master, tokenBContractId, master.toAddress.bytes.arr, vSwapContractId.bytes.arr, tokenBDepositAmount, fee + 10000000000L, ts + 8) + depositLiquidity <- depositToken(master, liquidityTokenContractId, master.toAddress.bytes.arr, vSwapContractId.bytes.arr, liquidityTotalSupply, fee + 10000000000L, ts + 9) + } yield (genesis, genesis2, master, user, regTokenAContract, regTokenBContract, regLiquidityTokenContract, regVSwapContract, + issueTokenA, issueTokenB, issueLiquidityToken, depositTokenA, depositTokenB, depositLiquidity, fee, ts, attach) + + def getContractTokenBalanceKeys(tokenAContractId: Array[Byte], tokenBContractId: Array[Byte], liquidityContractId: Array[Byte], vSwapContractId: Array[Byte]): (ByteStr, ByteStr, ByteStr) = { + val tokenAId = tokenIdFromBytes(tokenAContractId, Ints.toByteArray(0)).explicitGet() + val tokenBId = tokenIdFromBytes(tokenBContractId, Ints.toByteArray(0)).explicitGet() + val liquidityTokenId = tokenIdFromBytes(liquidityContractId, Ints.toByteArray(0)).explicitGet() + + val contractTokenABalanceKey = ByteStr(Bytes.concat(tokenAId.arr, vSwapContractId)) + val contractTokenBBalanceKey = ByteStr(Bytes.concat(tokenBId.arr, vSwapContractId)) + val contractLiquidityBalanceKey = ByteStr(Bytes.concat(liquidityTokenId.arr, vSwapContractId)) + + (contractTokenABalanceKey, contractTokenBBalanceKey, contractLiquidityBalanceKey) + } + + def getUserTokenBalanceKeys(tokenAContractId: Array[Byte], tokenBContractId: Array[Byte], liquidityContractId: Array[Byte], user: PublicKeyAccount): (ByteStr, ByteStr, ByteStr) = { + val tokenAId = tokenIdFromBytes(tokenAContractId, Ints.toByteArray(0)).explicitGet() + val tokenBId = tokenIdFromBytes(tokenBContractId, Ints.toByteArray(0)).explicitGet() + val liquidityTokenId = tokenIdFromBytes(liquidityContractId, Ints.toByteArray(0)).explicitGet() + + val masterTokenABalanceKey = ByteStr(Bytes.concat(tokenAId.arr, user.toAddress.bytes.arr)) + val masterTokenBBalanceKey = ByteStr(Bytes.concat(tokenBId.arr, user.toAddress.bytes.arr)) + val masterLiquidityBalanceKey = ByteStr(Bytes.concat(liquidityTokenId.arr, user.toAddress.bytes.arr)) + + (masterTokenABalanceKey, masterTokenBBalanceKey, masterLiquidityBalanceKey) + } + + def getSwapContractStateVarKeys(vSwapContractId: Array[Byte]): (ByteStr, ByteStr, ByteStr, ByteStr, ByteStr, ByteStr) = { + val swapStatusKey = ByteStr(Bytes.concat(vSwapContractId, Array(4.toByte))) + val minimumLiquidityKey = ByteStr(Bytes.concat(vSwapContractId, Array(5.toByte))) + val tokenAReservedKey = ByteStr(Bytes.concat(vSwapContractId, Array(6.toByte))) + val tokenBReservedKey = ByteStr(Bytes.concat(vSwapContractId, Array(7.toByte))) + val totalSupplyKey = ByteStr(Bytes.concat(vSwapContractId, Array(8.toByte))) + val liquidityTokenLeft = ByteStr(Bytes.concat(vSwapContractId, Array(9.toByte))) + + (swapStatusKey, minimumLiquidityKey, tokenAReservedKey, tokenBReservedKey, totalSupplyKey, liquidityTokenLeft) + } + + def getSwapContractStateMapKeys(vSwapContractId: Array[Byte], user: PublicKeyAccount): (ByteStr, ByteStr, ByteStr) = { + val stateMapTokenABalanceKey = ByteStr(Bytes.concat(vSwapContractId, Array(0.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + val stateMapTokenBBalanceKey = ByteStr(Bytes.concat(vSwapContractId, Array(1.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + val stateMapLiquidityTokenBalanceKey = ByteStr(Bytes.concat(vSwapContractId, Array(2.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + + (stateMapTokenABalanceKey, stateMapTokenBBalanceKey, stateMapLiquidityTokenBalanceKey) + } +} diff --git a/src/test/scala/vsys/blockchain/state/contract/swap/AtomicSwapContractDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/swap/AtomicSwapContractDiffTest.scala new file mode 100644 index 000000000..0f6d7ed8c --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/swap/AtomicSwapContractDiffTest.scala @@ -0,0 +1,214 @@ +package vsys.blockchain.state.contract.swap + +import cats.Monoid +import com.google.common.primitives.{Bytes, Longs} +import org.scalacheck.{Gen, Shrink} +import org.scalatest.{Matchers, PropSpec} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import scorex.crypto.hash.Sha256 +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.token.{SystemContractGen, TokenContractGen} +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import vsys.blockchain.contract._ +import vsys.blockchain.contract.swap.AtomicSwapContractGen +import vsys.blockchain.state.{ByteStr, Portfolio} +import vsys.blockchain.state.diffs.assertDiffAndStateCorrectBlockTime +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +class AtomicSwapContractDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with TokenContractGen + with SystemContractGen + with AtomicSwapContractGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val preconditionsSwapContractAndSolvePuzzle: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, Long, Long)] = for { + (genesis, genesis2, master, user, regContract, depositVSYS, ts, fee, description, attach) <- createAndDepositVSYSAtomicSwapContractGen(1000L) + lockData = Seq(Longs.toByteArray(50L), user.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 100)) + lockType = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock <- lockAtomicSwapContractDataStackGen(master, regContract.contractId, lockData, lockType, attach, fee, ts) + solvePuzzleData = Seq(lock.id.arr, Longs.toByteArray(123L)) + solvePuzzleType = Seq(DataType.ShortBytes, DataType.ShortBytes) + solvePuzzle <- solvePuzzleAtomicSwapContractDataStackGen(user, regContract.contractId, solvePuzzleData, solvePuzzleType, attach, fee, ts) + } yield (genesis, genesis2, regContract, depositVSYS, lock, solvePuzzle, ts, fee) + + property("solve puzzle correctly allocates VSYS") { + forAll(preconditionsSwapContractAndSolvePuzzle) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, reg: RegisterContractTransaction, + deposit: ExecuteContractFunctionTransaction, lock: ExecuteContractFunctionTransaction, solve: ExecuteContractFunctionTransaction, ts: Long, fee: Long) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(deposit.timestamp, Seq(reg, deposit))), + TestBlock.create(lock.timestamp + 1, Seq(lock, solve))) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + totalPortfolioDiff.balance shouldBe -2 * fee + totalPortfolioDiff.effectiveBalance shouldBe -2 * fee + +// val master = reg.proofs.firstCurveProof.explicitGet().publicKey +// val user = solve.proofs.firstCurveProof.explicitGet().publicKey + val masterBytes = genesis.recipient.bytes.arr + val userBytes = genesis2.recipient.bytes.arr + val contractId = reg.contractId.bytes + + // StateVar Keys +// val makerKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte))) +// val contractTokenIdKey = ByteStr(Bytes.concat(contractId.arr, Array(1.toByte))) + + // StateMap Keys + val masterBalanceInContractKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(masterBytes, DataType.Address).bytes)) + val userBalanceInContractKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(userBytes, DataType.Address).bytes)) + + newState.contractNumInfo(masterBalanceInContractKey) shouldBe 1000L - 50L // deposited - locked + newState.contractNumInfo(userBalanceInContractKey) shouldBe 50L // solving puzzle allocates the locked funds + + } + } + } + + val preconditionsSwapContractAndSolvePuzzleTokens: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, Long, Long)] = for { + (genesis, genesis2, master, user, regTokenContract, issueToken, regContract, depositToken, ts, fee, description, attach) <- createAndDepositTokenAtomicSwapContractGen(1000000L, 100L, 1000L, 1000L) + lockData = Seq(Longs.toByteArray(50L), user.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 100)) + lockType = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock <- lockAtomicSwapContractDataStackGen(master, regContract.contractId, lockData, lockType, attach, fee, ts) + solvePuzzleData = Seq(lock.id.arr, Longs.toByteArray(123L)) + solvePuzzleType = Seq(DataType.ShortBytes, DataType.ShortBytes) + solvePuzzle <- solvePuzzleAtomicSwapContractDataStackGen(user, regContract.contractId, solvePuzzleData, solvePuzzleType, attach, fee, ts) + } yield (genesis, genesis2, regTokenContract, issueToken, regContract, depositToken, lock, solvePuzzle, ts, fee) + + property("solve puzzle correctly allocates tokens") { + forAll(preconditionsSwapContractAndSolvePuzzleTokens) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, + regToken: RegisterContractTransaction, issue: ExecuteContractFunctionTransaction, reg: RegisterContractTransaction, + deposit: ExecuteContractFunctionTransaction, lock: ExecuteContractFunctionTransaction, solve: ExecuteContractFunctionTransaction, ts: Long, fee: Long) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(deposit.timestamp, Seq(regToken, issue, reg, deposit))), + TestBlock.create(lock.timestamp + 1, Seq(lock, solve))) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + totalPortfolioDiff.balance shouldBe -2 * fee + totalPortfolioDiff.effectiveBalance shouldBe -2 * fee + +// val master = reg.proofs.firstCurveProof.explicitGet().publicKey +// val user = solve.proofs.firstCurveProof.explicitGet().publicKey + val masterBytes = genesis.recipient.bytes.arr + val userBytes = genesis2.recipient.bytes.arr + val contractId = reg.contractId.bytes + + // StateVar Keys +// val makerKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte))) +// val contractTokenIdKey = ByteStr(Bytes.concat(contractId.arr, Array(1.toByte))) + + // StateMap Keys + val masterBalanceInContractKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(masterBytes, DataType.Address).bytes)) + val userBalanceInContractKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(userBytes, DataType.Address).bytes)) + + newState.contractNumInfo(masterBalanceInContractKey) shouldBe 1000L - 50L // deposited - locked + newState.contractNumInfo(userBalanceInContractKey) shouldBe 50L // solving puzzle allocates the locked funds + + } + } + } + + val preconditionsSwapContractAndSwapTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, Long, Long)] = for { + (genesis, genesis2, master, user, regTokenContract, regToken2Contract, regSwapContract, regSwap2Contract, issueToken, issueToken2, + depositToken, deposit2Token, ts, fee, description, attach) <- createTwoTokenAndDepositSwapGen(100000000L, 100L, 100000L, 10000L) + // Lock first swap + lockData = Seq(Longs.toByteArray(50L), user.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 100)) + lockType = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock <- lockAtomicSwapContractDataStackGen(master, regSwapContract.contractId, lockData, lockType, attach, fee, ts) + solvePuzzleData = Seq(lock.id.arr, Longs.toByteArray(123L)) + solvePuzzleType = Seq(DataType.ShortBytes, DataType.ShortBytes) + solvePuzzle <- solvePuzzleAtomicSwapContractDataStackGen(user, regSwapContract.contractId, solvePuzzleData, solvePuzzleType, attach, fee, ts) + // Lock second swap + lock2Data = Seq(Longs.toByteArray(50L), master.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 50)) + lock2Type = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock2 <- lockAtomicSwapContractDataStackGen(user, regSwap2Contract.contractId, lock2Data, lock2Type, attach, fee, ts) + solvePuzzle2Data = Seq(lock2.id.arr, Longs.toByteArray(123L)) + solvePuzzle2Type = Seq(DataType.ShortBytes, DataType.ShortBytes) + solvePuzzle2 <- solvePuzzleAtomicSwapContractDataStackGen(master, regSwap2Contract.contractId, solvePuzzle2Data, solvePuzzle2Type, attach, fee, ts) + } yield (genesis, genesis2, regTokenContract, regToken2Contract, regSwapContract, regSwap2Contract, issueToken, issueToken2, + depositToken, deposit2Token, lock, lock2, solvePuzzle, solvePuzzle2, ts, fee) + + property("swap handled correctly") { + forAll(preconditionsSwapContractAndSwapTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regToken: RegisterContractTransaction, + regToken2: RegisterContractTransaction, regSwap: RegisterContractTransaction, regSwap2: RegisterContractTransaction, issue: ExecuteContractFunctionTransaction, + issue2: ExecuteContractFunctionTransaction, deposit: ExecuteContractFunctionTransaction, deposit2: ExecuteContractFunctionTransaction, lock: ExecuteContractFunctionTransaction, + lock2: ExecuteContractFunctionTransaction, solve: ExecuteContractFunctionTransaction, solve2: ExecuteContractFunctionTransaction, ts: Long, fee: Long) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(deposit.timestamp, Seq(regToken, + regToken2, regSwap, regSwap2, issue, issue2, deposit, deposit2))), + TestBlock.create(lock.timestamp + 1, Seq(lock, lock2, solve, solve2))) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + totalPortfolioDiff.balance shouldBe -4 * fee + totalPortfolioDiff.effectiveBalance shouldBe -4 * fee + +// val master = reg.proofs.firstCurveProof.explicitGet().publicKey +// val user = solve.proofs.firstCurveProof.explicitGet().publicKey + val masterBytes = genesis.recipient.bytes.arr + val userBytes = genesis2.recipient.bytes.arr + val contractId = regSwap.contractId.bytes + val contract2Id = regSwap2.contractId.bytes + + // Contract 1 + + // StateMap Keys + val masterBalanceInContractKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(masterBytes, DataType.Address).bytes)) + val userBalanceInContractKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(userBytes, DataType.Address).bytes)) + + newState.contractNumInfo(masterBalanceInContractKey) shouldBe 10000L - 50L // deposited - locked + newState.contractNumInfo(userBalanceInContractKey) shouldBe 50L // solving puzzle allocates the locked funds + + // Contract 2 + val masterBalanceInContract2Key = ByteStr(Bytes.concat(contract2Id.arr, Array(0.toByte), DataEntry(masterBytes, DataType.Address).bytes)) + val userBalanceInContract2Key = ByteStr(Bytes.concat(contract2Id.arr, Array(0.toByte), DataEntry(userBytes, DataType.Address).bytes)) + + newState.contractNumInfo(userBalanceInContract2Key) shouldBe 10000L - 50L // deposited - locked + newState.contractNumInfo(masterBalanceInContract2Key) shouldBe 50L // solving puzzle allocates the locked funds + + } + } + } + + val preconditionsAndWithdrawExpiredSwapVSYS: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, Long, Long)] = for { + (genesis, genesis2, master, user, regContract, depositVSYS, ts, fee, description, attach) <- createAndDepositVSYSAtomicSwapContractGen(1000L) + lockData = Seq(Longs.toByteArray(50L), user.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 100)) + lockType = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock <- lockAtomicSwapContractDataStackGen(master, regContract.contractId, lockData, lockType, attach, fee, ts) + withdrawData = Seq(lock.id.arr) + withdrawType = Seq(DataType.ShortBytes) + withdraw <- expireWithdrawAtomicSwapContractDataStackGen(master, regContract.contractId, withdrawData, withdrawType, attach, fee, ts) + withdrawVSYSData = Seq(regContract.contractId.bytes.arr, master.toAddress.bytes.arr, Longs.toByteArray(1000L)) + withdrawVSYSType = Seq(DataType.ContractAccount, DataType.Address, DataType.Amount) + withdrawVSYS <- withdrawVSYSGen(master, withdrawVSYSData, withdrawVSYSType, attach, fee, ts) + } yield (genesis, genesis2, regContract, depositVSYS, lock, withdraw, withdrawVSYS, ts, fee) + + property("expired swap allows withdrawal") { + forAll(preconditionsAndWithdrawExpiredSwapVSYS) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, reg: RegisterContractTransaction, + deposit: ExecuteContractFunctionTransaction, lock: ExecuteContractFunctionTransaction, withdraw: ExecuteContractFunctionTransaction, + withdrawVSYS: ExecuteContractFunctionTransaction, ts: Long, fee: Long) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(deposit.timestamp, Seq(reg, deposit))), + TestBlock.create(lock.timestamp + 110, Seq(lock, withdraw, withdrawVSYS))) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + totalPortfolioDiff.balance shouldBe -3 * fee + totalPortfolioDiff.effectiveBalance shouldBe -3 * fee + + val masterBytes = genesis.recipient.bytes.arr + val contractId = reg.contractId.bytes + + // StateMap Keys + val masterBalanceInContractKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(masterBytes, DataType.Address).bytes)) + + newState.contractNumInfo(masterBalanceInContractKey) shouldBe 0L + } + } + } +} diff --git a/src/test/scala/vsys/blockchain/state/contract/swap/AtomicSwapContractInvalidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/swap/AtomicSwapContractInvalidDiffTest.scala new file mode 100644 index 000000000..5739b1c99 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/swap/AtomicSwapContractInvalidDiffTest.scala @@ -0,0 +1,166 @@ +package vsys.blockchain.state.contract.swap + +//import cats.Monoid +import com.google.common.primitives.Longs +import org.scalacheck.{Gen, Shrink} +import org.scalatest.{Matchers, PropSpec} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import scorex.crypto.hash.Sha256 +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.token.{SystemContractGen, TokenContractGen} +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import vsys.blockchain.contract._ +import vsys.blockchain.contract.swap.AtomicSwapContractGen +import vsys.blockchain.state.diffs.assertDiffAndStateCorrectBlockTime +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +class AtomicSwapContractInvalidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with TokenContractGen + with SystemContractGen + with AtomicSwapContractGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val preconditionsAndAtomicSwapInsufficientDepositedAmountInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, Long, Long)] = for { + (genesis, genesis2, master, user, regContract, depositVSYS, ts, fee, description, attach) <- createAndDepositVSYSAtomicSwapContractGen(1000L) + lockData = Seq(Longs.toByteArray(1001L), user.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 100)) + lockType = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock <- lockAtomicSwapContractDataStackGen(master, regContract.contractId, lockData, lockType, attach, fee, ts) + } yield (genesis, genesis2, regContract, depositVSYS, lock, ts, fee) + + property("unable to lock more than deposited") { + forAll(preconditionsAndAtomicSwapInsufficientDepositedAmountInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, reg: RegisterContractTransaction, + deposit: ExecuteContractFunctionTransaction, lock: ExecuteContractFunctionTransaction, ts: Long, fee: Long) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(deposit.timestamp, Seq(reg, deposit))), + TestBlock.createWithTxStatus(lock.timestamp + 1, Seq(lock), TransactionStatus.ContractMapValueInsufficient)) { (blockDiff, newState) => + blockDiff.txsDiff.contractDB.isEmpty shouldBe true + blockDiff.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiff.txsDiff.portfolios.isEmpty shouldBe false + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + } + } + + val preconditionsAndAtomicSwapExpiredWithdrawInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, Long, Long)] = for { + (genesis, genesis2, master, user, regContract, depositVSYS, ts, fee, description, attach) <- createAndDepositVSYSAtomicSwapContractGen(1000L) + lockData = Seq(Longs.toByteArray(1000L), user.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 100)) + lockType = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock <- lockAtomicSwapContractDataStackGen(master, regContract.contractId, lockData, lockType, attach, fee, ts) + withdrawData = Seq(lock.id.arr) + withdrawType = Seq(DataType.ShortBytes) + withdraw <- expireWithdrawAtomicSwapContractDataStackGen(master, regContract.contractId, withdrawData, withdrawType, attach, fee, ts) + } yield (genesis, genesis2, regContract, depositVSYS, lock, withdraw, ts, fee) + + property("unable to withdraw before lock expires") { + forAll(preconditionsAndAtomicSwapExpiredWithdrawInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, reg: RegisterContractTransaction, + deposit: ExecuteContractFunctionTransaction, lock: ExecuteContractFunctionTransaction, withdraw: ExecuteContractFunctionTransaction, ts: Long, fee: Long) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(deposit.timestamp, Seq(reg, deposit, lock))), + TestBlock.createWithTxStatus(lock.timestamp + 99, Seq(withdraw), TransactionStatus.Failed)) { (blockDiff, newState) => + blockDiff.txsDiff.contractDB.isEmpty shouldBe true + blockDiff.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiff.txsDiff.portfolios.isEmpty shouldBe false + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndAtomicSwapLockWrongAddressInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, Long, Long)] = for { + (genesis, genesis2, master, user, regContract, depositVSYS, ts, fee, description, attach) <- createAndDepositVSYSAtomicSwapContractGen(1000L) + lockData = Seq(Longs.toByteArray(1000L), user.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 100)) + lockType = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock <- lockAtomicSwapContractDataStackGen(user, regContract.contractId, lockData, lockType, attach, fee, ts + 3) + } yield (genesis, genesis2, regContract, depositVSYS, lock, ts, fee) + + property("insufficient balance cannot lock contract") { + forAll(preconditionsAndAtomicSwapLockWrongAddressInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, reg: RegisterContractTransaction, + deposit: ExecuteContractFunctionTransaction, lock: ExecuteContractFunctionTransaction, ts: Long, fee: Long) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(deposit.timestamp, Seq(reg, deposit))), + TestBlock.createWithTxStatus(lock.timestamp + 1, Seq(lock), TransactionStatus.ContractMapValueInsufficient)) { (blockDiff, newState) => + blockDiff.txsDiff.contractDB.isEmpty shouldBe true + blockDiff.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiff.txsDiff.portfolios.isEmpty shouldBe false + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + } + } + + val preconditionsAndAtomicSwapSolvePuzzleWrongAddressInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, Long, Long)] = for { + (genesis, genesis2, master, user, regContract, depositVSYS, ts, fee, description, attach) <- createAndDepositVSYSAtomicSwapContractGen(1000L) + lockData = Seq(Longs.toByteArray(1000L), master.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 100)) + lockType = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock <- lockAtomicSwapContractDataStackGen(master, regContract.contractId, lockData, lockType, attach, fee, ts) + solvePuzzleData = Seq(lock.id.arr, Longs.toByteArray(123L)) + solvePuzzleType = Seq(DataType.ShortBytes, DataType.ShortBytes) + solvePuzzle <- solvePuzzleAtomicSwapContractDataStackGen(user, regContract.contractId, solvePuzzleData, solvePuzzleType, attach, fee, ts) + } yield (genesis, genesis2, regContract, depositVSYS, lock, solvePuzzle, ts, fee) + + property("wrong address cannot solve puzzle") { + forAll(preconditionsAndAtomicSwapSolvePuzzleWrongAddressInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, reg: RegisterContractTransaction, + deposit: ExecuteContractFunctionTransaction, lock: ExecuteContractFunctionTransaction, solvePuzzle: ExecuteContractFunctionTransaction, ts: Long, fee: Long) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(deposit.timestamp, Seq(reg, deposit, lock))), + TestBlock.createWithTxStatus(lock.timestamp + 1, Seq(solvePuzzle), TransactionStatus.ContractInvalidCaller)) { (blockDiff, newState) => + blockDiff.txsDiff.contractDB.isEmpty shouldBe true + blockDiff.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiff.txsDiff.portfolios.isEmpty shouldBe false + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + } + } + + val preconditionsAndAtomicSwapSolvePuzzleExpiredInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, Long, Long)] = for { + (genesis, genesis2, master, user, regContract, depositVSYS, ts, fee, description, attach) <- createAndDepositVSYSAtomicSwapContractGen(1000L) + lockData = Seq(Longs.toByteArray(1000L), master.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 100)) + lockType = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock <- lockAtomicSwapContractDataStackGen(master, regContract.contractId, lockData, lockType, attach, fee, ts) + solvePuzzleData = Seq(lock.id.arr, Longs.toByteArray(123L)) + solvePuzzleType = Seq(DataType.ShortBytes, DataType.ShortBytes) + solvePuzzle <- solvePuzzleAtomicSwapContractDataStackGen(user, regContract.contractId, solvePuzzleData, solvePuzzleType, attach, fee, ts) + } yield (genesis, genesis2, regContract, depositVSYS, lock, solvePuzzle, ts, fee) + + property("puzzle cannot be solved after the lock expires") { + forAll(preconditionsAndAtomicSwapSolvePuzzleExpiredInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, reg: RegisterContractTransaction, + deposit: ExecuteContractFunctionTransaction, lock: ExecuteContractFunctionTransaction, solvePuzzle: ExecuteContractFunctionTransaction, ts: Long, fee: Long) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(deposit.timestamp, Seq(reg, deposit, lock))), + TestBlock.createWithTxStatus(lock.timestamp + 101, Seq(solvePuzzle), TransactionStatus.ContractInvalidCaller)) { (blockDiff, newState) => + blockDiff.txsDiff.contractDB.isEmpty shouldBe true + blockDiff.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiff.txsDiff.portfolios.isEmpty shouldBe false + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + } + } + + val preconditionsAndAtomicSwapSolvePuzzleInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, Long, Long)] = for { + (genesis, genesis2, master, user, regContract, depositVSYS, ts, fee, description, attach) <- createAndDepositVSYSAtomicSwapContractGen(1000L) + lockData = Seq(Longs.toByteArray(1000L), user.toAddress.bytes.arr, Sha256(Longs.toByteArray(123L)), Longs.toByteArray(ts + 100)) + lockType = Seq(DataType.Amount, DataType.Address, DataType.ShortBytes, DataType.Timestamp) + lock <- lockAtomicSwapContractDataStackGen(master, regContract.contractId, lockData, lockType, attach, fee, ts) + solvePuzzleData = Seq(lock.id.arr, Longs.toByteArray(124L)) + solvePuzzleType = Seq(DataType.ShortBytes, DataType.ShortBytes) + solvePuzzle <- solvePuzzleAtomicSwapContractDataStackGen(user, regContract.contractId, solvePuzzleData, solvePuzzleType, attach, fee, ts) + } yield (genesis, genesis2, regContract, depositVSYS, lock, solvePuzzle, ts, fee) + + property("puzzle cannot be solved by incorrect key") { + forAll(preconditionsAndAtomicSwapSolvePuzzleInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, reg: RegisterContractTransaction, + deposit: ExecuteContractFunctionTransaction, lock: ExecuteContractFunctionTransaction, solvePuzzle: ExecuteContractFunctionTransaction, ts: Long, fee: Long) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(deposit.timestamp, Seq(reg, deposit, lock))), + TestBlock.createWithTxStatus(lock.timestamp + 1, Seq(solvePuzzle), TransactionStatus.ContractInvalidHash)) { (blockDiff, newState) => + blockDiff.txsDiff.contractDB.isEmpty shouldBe true + blockDiff.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiff.txsDiff.portfolios.isEmpty shouldBe false + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidHash + } + } + } + +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/state/contract/token/ExecuteNonFungibleContractV2InvalidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/token/ExecuteNonFungibleContractV2InvalidDiffTest.scala new file mode 100644 index 000000000..466581b59 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/token/ExecuteNonFungibleContractV2InvalidDiffTest.scala @@ -0,0 +1,668 @@ +package vsys.blockchain.state.contract.token + +import com.google.common.primitives.Ints +import org.scalacheck.{Gen, Shrink} +import org.scalatest.{Matchers, PropSpec} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.lock.LockContractGen +import vsys.blockchain.contract.{Contract, ContractGenHelper, DataEntry, DataType} +import vsys.blockchain.contract.token.NonFungibleContractV2Gen +import vsys.blockchain.state.diffs.assertDiffAndStateCorrectBlockTime +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +class ExecuteNonFungibleContractV2InvalidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with LockContractGen + with NonFungibleContractV2Gen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val nonFungibleWhiteContract: Gen[Contract] = nonFungibleContractWhiteGen() + val nonFungibleBlackContract: Gen[Contract] = nonFungibleContractBlackGen() + + val preconditionsNonFungibleContractV2UpdateListInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContract <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractId = regContract.contractId + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractId, issueData, attach, fee, ts+1) + + updateListData = Seq(user.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(user.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(user.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(user, contractId, updateListData, updateListType, attach, fee, ts) + } yield (genesis, genesis2, regContract, issue, updateList1, updateList2, updateList3) + + property("Execute update list in non fungible contract V2") { + forAll(preconditionsNonFungibleContractV2UpdateListInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, reg: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, updateList3: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(reg.timestamp, Seq(reg, issue))), + TestBlock.createWithTxStatus(updateList1.timestamp, Seq(updateList1), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(reg.timestamp, Seq(reg, issue))), + TestBlock.createWithTxStatus(updateList2.timestamp, Seq(updateList2), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + // update list not with a NFT contract owner + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(reg.timestamp, Seq(reg, issue))), + TestBlock.createWithTxStatus(updateList3.timestamp, Seq(updateList3), TransactionStatus.ContractInvalidCaller)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + } + } + + val preconditionsNonFungibleWhiteContractV2SendInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractWhite <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractWhiteId = regContractWhite.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractWhiteId, issueData, attach, fee, ts+1) + + updateListData = Seq(user.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(user.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + send <- sendNonFungibleV2Gen(master, contractWhiteId, user, 0, attach, fee, ts) + + + } yield (genesis, genesis2, regContractWhite, issue, updateList1, updateList2, updateList3, updateList4, send) + + property("Execute send in non fungible white contract") { + forAll(preconditionsNonFungibleWhiteContractV2SendInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractWhite: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, + updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, send: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractWhite, issue, updateList1, updateList2))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + + // only receiver on white list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList1.timestamp, Seq(regContractWhite, issue, updateList1))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // only sender on white list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractWhite, issue, updateList2))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // update receiver not on white list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList3.timestamp, Seq(regContractWhite, issue, updateList1, updateList2, updateList3))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // update sender not on white list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList4.timestamp, Seq(regContractWhite, issue, updateList1, updateList2, updateList4))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsNonFungibleBlackContractV2SendInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractBlack <- nonFungibleBlackContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractBlack <- registerNonFungibleV2Gen(master, contractBlack, dataStack, description, fee + 10000000000L, ts) + contractBlackId = regContractBlack.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractBlackId, issueData, attach, fee, ts+1) + + updateListData = Seq(user.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(user.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + send <- sendNonFungibleV2Gen(master, contractBlackId, user, 0, attach, fee, ts) + + + } yield (genesis, genesis2, regContractBlack, issue, updateList1, updateList2, updateList3, updateList4, send) + + property("Execute send in non fungible black contract") { + forAll(preconditionsNonFungibleBlackContractV2SendInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractBlack: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, + updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, send: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractBlack, issue, updateList1, updateList2))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + + // only receiver on black list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList1.timestamp, Seq(regContractBlack, issue, updateList3))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // only sender on black list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractBlack, issue, updateList4))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // update receiver not on black list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList3.timestamp, Seq(regContractBlack, issue, updateList3, updateList4, updateList1))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // update sender not on black list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList4.timestamp, Seq(regContractBlack, issue, updateList3, updateList4, updateList2))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsNonFungibleWhiteContractV2TransferInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractWhite <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractWhiteId = regContractWhite.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractWhiteId, issueData, attach, fee, ts+1) + + updateListData = Seq(user.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(user.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + transferData = Seq(master.toAddress.bytes.arr, user.toAddress.bytes.arr, Ints.toByteArray(0)) + transferType = Seq(DataType.Address, DataType.Address, DataType.Int32) + transfer <- transferNonFungibleV2Gen(master, contractWhiteId, transferData, transferType, attach, fee, ts) + + + } yield (genesis, genesis2, regContractWhite, issue, updateList1, updateList2, updateList3, updateList4, transfer) + + property("Execute transfer in non fungible white contract") { + forAll(preconditionsNonFungibleWhiteContractV2TransferInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractWhite: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, + updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, transfer: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractWhite, issue, updateList1, updateList2))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + + // only receiver on white list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList1.timestamp, Seq(regContractWhite, issue, updateList1))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // only sender on white list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractWhite, issue, updateList2))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // update receiver not on white list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList3.timestamp, Seq(regContractWhite, issue, updateList1, updateList2, updateList3))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // update sender not on white list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList4.timestamp, Seq(regContractWhite, issue, updateList1, updateList2, updateList4))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsNonFungibleBlackContractV2TransferInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractBlack <- nonFungibleBlackContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractBlack <- registerNonFungibleV2Gen(master, contractBlack, dataStack, description, fee + 10000000000L, ts) + contractBlackId = regContractBlack.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractBlackId, issueData, attach, fee, ts+1) + + updateListData = Seq(user.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(user.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + transferData = Seq(master.toAddress.bytes.arr, user.toAddress.bytes.arr, Ints.toByteArray(0)) + transferType = Seq(DataType.Address, DataType.Address, DataType.Int32) + transfer <- transferNonFungibleV2Gen(master, contractBlackId, transferData, transferType, attach, fee, ts) + + + } yield (genesis, genesis2, regContractBlack, issue, updateList1, updateList2, updateList3, updateList4, transfer) + + property("Execute transfer in non fungible black contract") { + forAll(preconditionsNonFungibleBlackContractV2TransferInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractBlack: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, + updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, transfer: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractBlack, issue, updateList1, updateList2))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + + // only receiver on black list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList1.timestamp, Seq(regContractBlack, issue, updateList3))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // only sender on black list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractBlack, issue, updateList4))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // update receiver not on black list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList3.timestamp, Seq(regContractBlack, issue, updateList3, updateList4, updateList1))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // update sender not on black list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList4.timestamp, Seq(regContractBlack, issue, updateList3, updateList4, updateList2))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsNonFungibleBlackContractV2DepositInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractBlack <- nonFungibleBlackContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractBlack <- registerNonFungibleV2Gen(master, contractBlack, dataStack, description, fee + 10000000000L, ts) + contractBlackId = regContractBlack.contractId + tokenId = tokenIdFromBytes(contractBlackId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + contractLock <- lockContractGen + dataStack: Seq[DataEntry] <- initLockContractDataStackGen(tokenId.arr) + description <- validDescStringGen + regContractLock <- registerLockGen(master, contractLock, dataStack, description, fee + 10000000000L, ts) + contractLockId = regContractLock.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractBlackId, issueData, attach, fee, ts+1) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + depositData = Seq(master.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + deposit <- depositNonFungibleV2Gen(master, contractBlackId, depositData, depositType, attach, fee, ts) + + depositData = Seq(user.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + depositInvalid <- depositNonFungibleV2Gen(master, contractBlackId, depositData, depositType, attach, fee, ts) + + depositData = Seq(master.toAddress.bytes.arr, contractBlackId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + depositInvalid2 <- depositNonFungibleV2Gen(master, contractBlackId, depositData, depositType, attach, fee, ts) + + + } yield (genesis, genesis2, regContractBlack, regContractLock, issue, updateList1, deposit, depositInvalid, depositInvalid2) + + property("Execute deposit in non fungible black contract") { + forAll(preconditionsNonFungibleBlackContractV2DepositInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractBlack: RegisterContractTransaction, regContractLock: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, deposit: ExecuteContractFunctionTransaction, depositInvalid: ExecuteContractFunctionTransaction, depositInvalid2: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractBlack, regContractLock, issue))), + TestBlock.createWithTxStatus(deposit.timestamp, Seq(deposit), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + + // master depositor on black list + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList1.timestamp, Seq(regContractBlack, regContractLock, issue, updateList1))), + TestBlock.createWithTxStatus(deposit.timestamp, Seq(deposit), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // user depositor try to deposit + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList1.timestamp, Seq(regContractBlack, regContractLock, issue, updateList1))), + TestBlock.createWithTxStatus(depositInvalid.timestamp, Seq(depositInvalid), TransactionStatus.ContractInvalidCaller)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + + // deposit into a wrong contract + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList1.timestamp, Seq(regContractBlack, regContractLock, issue))), + TestBlock.createWithTxStatus(depositInvalid2.timestamp, Seq(depositInvalid2), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsNonFungibleWhiteContractV2DepositInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractWhite <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractWhiteId = regContractWhite.contractId + tokenId = tokenIdFromBytes(contractWhiteId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + contractLock <- lockContractGen + dataStack: Seq[DataEntry] <- initLockContractDataStackGen(tokenId.arr) + description <- validDescStringGen + regContractLock <- registerLockGen(master, contractLock, dataStack, description, fee + 10000000000L, ts) + contractLockId = regContractLock.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractWhiteId, issueData, attach, fee, ts+1) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(contractLockId.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.ContractAccount, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(contractLockId.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.ContractAccount, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + depositData = Seq(master.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + deposit <- depositNonFungibleV2Gen(master, contractWhiteId, depositData, depositType, attach, fee, ts) + + depositData = Seq(user.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + depositInvalid <- depositNonFungibleV2Gen(master, contractWhiteId, depositData, depositType, attach, fee, ts) + + depositData = Seq(master.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + depositInvalid2 <- depositNonFungibleV2Gen(master, contractWhiteId, depositData, depositType, attach, fee, ts) + + } yield (genesis, genesis2, regContractWhite, regContractLock, issue, updateList1, updateList2, updateList3, updateList4, deposit, depositInvalid, depositInvalid2) + + property("Execute deposit in non fungible white contract") { + forAll(preconditionsNonFungibleWhiteContractV2DepositInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractWhite: RegisterContractTransaction, regContractLock: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, deposit: ExecuteContractFunctionTransaction, depositInvalid: ExecuteContractFunctionTransaction, depositInvalid2: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractWhite, regContractLock, issue, updateList1, updateList2))), + TestBlock.createWithTxStatus(deposit.timestamp, Seq(deposit), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + // only update master depositor to whitelist + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractWhite, regContractLock, issue, updateList1))), + TestBlock.createWithTxStatus(deposit.timestamp, Seq(deposit), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // only update contract to whitelist + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractWhite, regContractLock, issue, updateList2))), + TestBlock.createWithTxStatus(deposit.timestamp, Seq(deposit), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // deposit with a wrong caller + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractWhite, regContractLock, issue, updateList1, updateList2))), + TestBlock.createWithTxStatus(depositInvalid.timestamp, Seq(depositInvalid), TransactionStatus.ContractInvalidCaller)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + } + } + val preconditionsNonFungibleBlackContractV2WithdrawInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractBlack <- nonFungibleBlackContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractBlack <- registerNonFungibleV2Gen(master, contractBlack, dataStack, description, fee + 10000000000L, ts) + contractBlackId = regContractBlack.contractId + tokenId = tokenIdFromBytes(contractBlackId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + contractLock <- lockContractGen + dataStack: Seq[DataEntry] <- initLockContractDataStackGen(tokenId.arr) + description <- validDescStringGen + regContractLock <- registerLockGen(master, contractLock, dataStack, description, fee + 10000000000L, ts) + contractLockId = regContractLock.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractBlackId, issueData, attach, fee, ts+1) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(contractLockId.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.ContractAccount, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + depositData = Seq(master.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + deposit <- depositNonFungibleV2Gen(master, contractBlackId, depositData, depositType, attach, fee, ts) + + withdrawData = Seq(contractLockId.bytes.arr, master.toAddress.bytes.arr, Ints.toByteArray(0)) + withdrawType = Seq(DataType.ContractAccount, DataType.Address, DataType.Int32) + withdraw <- withdrawNonFungibleV2Gen(master, contractBlackId, withdrawData, withdrawType, attach, fee, ts) + + withdrawData = Seq(contractLockId.bytes.arr, user.toAddress.bytes.arr, Ints.toByteArray(0)) + withdrawType = Seq(DataType.ContractAccount, DataType.Address, DataType.Int32) + withdrawInvalid <- withdrawNonFungibleV2Gen(master, contractBlackId, withdrawData, withdrawType, attach, fee, ts) + + } yield (genesis, genesis2, regContractBlack, regContractLock, issue, updateList1, updateList2, deposit, withdraw, withdrawInvalid) + + property("Execute withdraw in non fungible black contract") { + forAll(preconditionsNonFungibleBlackContractV2WithdrawInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractBlack: RegisterContractTransaction, regContractLock: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, deposit: ExecuteContractFunctionTransaction, withdraw: ExecuteContractFunctionTransaction, withdrawInvalid: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractBlack, regContractLock, issue, deposit))), + TestBlock.createWithTxStatus(withdraw.timestamp, Seq(withdraw), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + // update master withdrawer into blacklist before withdraw + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractBlack, regContractLock, issue, deposit, updateList1))), + TestBlock.createWithTxStatus(withdraw.timestamp, Seq(withdraw), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // update contract into blacklist before withdraw + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractBlack, regContractLock, issue, deposit, updateList2))), + TestBlock.createWithTxStatus(withdraw.timestamp, Seq(withdraw), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // withdraw with a wrong caller + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractBlack, regContractLock, issue, deposit))), + TestBlock.createWithTxStatus(withdrawInvalid.timestamp, Seq(withdrawInvalid), TransactionStatus.ContractInvalidCaller)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + + } + } + + val preconditionsNonFungibleWhiteContractV2WithdrawInvalidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractWhite <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractWhiteId = regContractWhite.contractId + tokenId = tokenIdFromBytes(contractWhiteId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + contractLock <- lockContractGen + dataStack: Seq[DataEntry] <- initLockContractDataStackGen(tokenId.arr) + description <- validDescStringGen + regContractLock <- registerLockGen(master, contractLock, dataStack, description, fee + 10000000000L, ts) + contractLockId = regContractLock.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractWhiteId, issueData, attach, fee, ts+1) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(contractLockId.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.ContractAccount, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(contractLockId.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.ContractAccount, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + depositData = Seq(master.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + deposit <- depositNonFungibleV2Gen(master, contractWhiteId, depositData, depositType, attach, fee, ts) + + withdrawData = Seq(contractLockId.bytes.arr, master.toAddress.bytes.arr, Ints.toByteArray(0)) + withdrawType = Seq(DataType.ContractAccount, DataType.Address, DataType.Int32) + withdraw <- withdrawNonFungibleV2Gen(master, contractWhiteId, withdrawData, withdrawType, attach, fee, ts) + + withdrawData = Seq(contractLockId.bytes.arr, user.toAddress.bytes.arr, Ints.toByteArray(0)) + withdrawType = Seq(DataType.ContractAccount, DataType.Address, DataType.Int32) + withdrawInvalid <- withdrawNonFungibleV2Gen(master, contractWhiteId, withdrawData, withdrawType, attach, fee, ts) + + } yield (genesis, genesis2, regContractWhite, regContractLock, issue, updateList1, updateList2, updateList3, updateList4, deposit, withdraw, withdrawInvalid) + + property("Execute withdraw in non fungible white contract") { + forAll(preconditionsNonFungibleWhiteContractV2WithdrawInvalidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractWhite: RegisterContractTransaction, regContractLock: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, deposit: ExecuteContractFunctionTransaction, withdraw: ExecuteContractFunctionTransaction, withdrawInvalid: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractWhite, regContractLock, issue, updateList1, updateList2, deposit))), + TestBlock.createWithTxStatus(withdraw.timestamp, Seq(withdraw), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + // only update withdrawer to whitelist before withdraw + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractWhite, regContractLock, issue, updateList1, updateList2, deposit, updateList3))), + TestBlock.createWithTxStatus(withdraw.timestamp, Seq(withdraw), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // only update contract to whitelist before withdraw + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractWhite, regContractLock, issue, updateList1, updateList2, deposit, updateList4))), + TestBlock.createWithTxStatus(withdraw.timestamp, Seq(withdraw), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // withdraw with a wrong caller + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractWhite, regContractLock, issue, updateList1, updateList2, deposit))), + TestBlock.createWithTxStatus(withdrawInvalid.timestamp, Seq(withdrawInvalid), TransactionStatus.ContractInvalidCaller)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + } + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/state/contract/token/ExecuteNonFungibleContractV2ValidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/token/ExecuteNonFungibleContractV2ValidDiffTest.scala new file mode 100644 index 000000000..b551a1a68 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/token/ExecuteNonFungibleContractV2ValidDiffTest.scala @@ -0,0 +1,613 @@ +package vsys.blockchain.state.contract.token + +import vsys.blockchain.state._ +import com.google.common.primitives.{Bytes, Ints} +import org.scalacheck.{Gen, Shrink} +import org.scalatest.{Matchers, PropSpec} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.{Contract, ContractGenHelper, DataEntry, DataType} +import vsys.blockchain.contract.lock.LockContractGen +import vsys.blockchain.contract.token.NonFungibleContractV2Gen +import vsys.blockchain.state.ByteStr +import vsys.blockchain.state.diffs.assertDiffAndStateCorrectBlockTime +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +class ExecuteNonFungibleContractV2ValidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with LockContractGen + with NonFungibleContractV2Gen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val nonFungibleWhiteContract: Gen[Contract] = nonFungibleContractWhiteGen() + val nonFungibleBlackContract: Gen[Contract] = nonFungibleContractBlackGen() + + val preconditionsNonFungibleContractV2IssueValidTest: Gen[(GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContract <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractId = regContract.contractId + genesis <- genesisNonFungibleV2Gen(master, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractId, issueData, attach, fee, ts+1) + } yield (genesis, regContract, issue) + + property("Execute issue with non fungible contract V2") { + forAll(preconditionsNonFungibleContractV2IssueValidTest) { case (genesis: GenesisTransaction, reg: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(reg.timestamp, Seq(reg))), + TestBlock.createWithTxStatus(issue.timestamp, Seq(issue), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val contractId = reg.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val maker = reg.proofs.firstCurveProof.explicitGet().publicKey + val issuer = issue.proofs.firstCurveProof.explicitGet().publicKey + + //Statevar keys + val issuerBalanceKey = ByteStr(Bytes.concat(tokenId.arr, issuer.toAddress.bytes.arr)) + val makerBalanceKey = ByteStr(Bytes.concat(tokenId.arr, maker.toAddress.bytes.arr)) + + val issuerKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte))) + val makerKey = ByteStr(Bytes.concat(contractId.arr, Array(1.toByte))) + + //Statemap keys + newState.contractInfo(issuerKey) shouldEqual Some(DataEntry(issuer.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(makerKey) shouldEqual Some(DataEntry(maker.toAddress.bytes.arr, DataType.Address)) + newState.tokenAccountBalance(issuerBalanceKey) shouldBe 1L + newState.tokenAccountBalance(makerBalanceKey) shouldBe 1L + } + } + } + + val preconditionsNonFungibleContractV2UpdateListValidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContract <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractId = regContract.contractId + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractId, issueData, attach, fee, ts+1) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractId, updateListData, updateListType, attach, fee, ts) + + } yield (genesis, genesis2, regContract, issue, updateList1, updateList2) + + property("Execute update list in non fungible contract V2") { + forAll(preconditionsNonFungibleContractV2UpdateListValidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, reg: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(reg.timestamp, Seq(reg, issue))), + TestBlock.createWithTxStatus(updateList1.timestamp, Seq(updateList1), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val contractId = reg.contractId.bytes + val updateLister = updateList1.proofs.firstCurveProof.explicitGet().publicKey + val updateListKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(updateLister.toAddress.bytes.arr, DataType.Address).bytes)) + + newState.contractInfo(updateListKey) shouldEqual Some(DataEntry(Array(0.toByte), DataType.Boolean)) + } + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(reg.timestamp, Seq(reg, issue, updateList2))), + TestBlock.createWithTxStatus(updateList1.timestamp, Seq(updateList1), TransactionStatus.Success)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + val preconditionsNonFungibleWhiteContractV2SendValidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractWhite <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractWhiteId = regContractWhite.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractWhiteId, issueData, attach, fee, ts+1) + + updateListData = Seq(user.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(user.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + send <- sendNonFungibleV2Gen(master, contractWhiteId, user, 0, attach, fee, ts) + + + } yield (genesis, genesis2, regContractWhite, issue, updateList1, updateList2, updateList3, updateList4, send) + + property("Execute send in non fungible white contract") { + forAll(preconditionsNonFungibleWhiteContractV2SendValidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractWhite: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, + updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, send: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractWhite, issue, updateList3, updateList4, updateList1, updateList2))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val contractId = regContractWhite.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val master = regContractWhite.proofs.firstCurveProof.explicitGet().publicKey + val user = genesis2.recipient + + val masterBalanceKey = ByteStr(Bytes.concat(tokenId.arr, master.toAddress.bytes.arr)) + val userBalanceKey = ByteStr(Bytes.concat(tokenId.arr, user.bytes.arr)) + + newState.tokenAccountBalance(masterBalanceKey) shouldBe 0L + newState.tokenAccountBalance(userBalanceKey) shouldBe 1L + } + } + } + + val preconditionsNonFungibleBlackContractV2SendValidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractBlack <- nonFungibleBlackContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractBlack <- registerNonFungibleV2Gen(master, contractBlack, dataStack, description, fee + 10000000000L, ts) + contractBlackId = regContractBlack.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractBlackId, issueData, attach, fee, ts+1) + + updateListData = Seq(user.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(user.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + send <- sendNonFungibleV2Gen(master, contractBlackId, user, 0, attach, fee, ts) + } yield (genesis, genesis2, regContractBlack, issue, updateList1, updateList2, updateList3, updateList4, send) + + property("Execute send in non fungible black contract") { + forAll(preconditionsNonFungibleBlackContractV2SendValidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractBlack: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, + updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, send: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractBlack, issue, updateList3, updateList4, updateList1, updateList2))), + TestBlock.createWithTxStatus(send.timestamp, Seq(send), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val contractId = regContractBlack.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val master = regContractBlack.proofs.firstCurveProof.explicitGet().publicKey + val user = genesis2.recipient + + val masterBalanceKey = ByteStr(Bytes.concat(tokenId.arr, master.toAddress.bytes.arr)) + val userBalanceKey = ByteStr(Bytes.concat(tokenId.arr, user.bytes.arr)) + + newState.tokenAccountBalance(masterBalanceKey) shouldBe 0L + newState.tokenAccountBalance(userBalanceKey) shouldBe 1L + } + } + } + + val preconditionsNonFungibleWhiteContractV2TransferValidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractWhite <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractWhiteId = regContractWhite.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractWhiteId, issueData, attach, fee, ts+1) + + updateListData = Seq(user.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(user.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + + transferData = Seq(master.toAddress.bytes.arr, user.toAddress.bytes.arr, Ints.toByteArray(0)) + transferType = Seq(DataType.Address, DataType.Address, DataType.Int32) + transfer <- transferNonFungibleV2Gen(master, contractWhiteId, transferData, transferType, attach, fee, ts) + + + } yield (genesis, genesis2, regContractWhite, issue, updateList1, updateList2, updateList3, updateList4, transfer) + + property("Execute transfer in non fungible white contract") { + forAll(preconditionsNonFungibleWhiteContractV2TransferValidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractWhite: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, + updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, transfer: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractWhite, issue, updateList3, updateList4, updateList1, updateList2))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val contractId = regContractWhite.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val master = regContractWhite.proofs.firstCurveProof.explicitGet().publicKey + val user = genesis2.recipient + + val masterBalanceKey = ByteStr(Bytes.concat(tokenId.arr, master.toAddress.bytes.arr)) + val userBalanceKey = ByteStr(Bytes.concat(tokenId.arr, user.bytes.arr)) + + newState.tokenAccountBalance(masterBalanceKey) shouldBe 0L + newState.tokenAccountBalance(userBalanceKey) shouldBe 1L + } + } + } + + val preconditionsNonFungibleBlackContractV2TransferValidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractBlack <- nonFungibleBlackContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractBlack <- registerNonFungibleV2Gen(master, contractBlack, dataStack, description, fee + 10000000000L, ts) + contractBlackId = regContractBlack.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractBlackId, issueData, attach, fee, ts+1) + + updateListData = Seq(user.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(user.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + transferData = Seq(master.toAddress.bytes.arr, user.toAddress.bytes.arr, Ints.toByteArray(0)) + transferType = Seq(DataType.Address, DataType.Address, DataType.Int32) + transfer <- transferNonFungibleV2Gen(master, contractBlackId, transferData, transferType, attach, fee, ts) + + + } yield (genesis, genesis2, regContractBlack, issue, updateList1, updateList2, updateList3, updateList4, transfer) + + property("Execute transfer in non fungible black contract") { + forAll(preconditionsNonFungibleWhiteContractV2TransferValidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractBlack: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, + updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, transfer: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(updateList2.timestamp, Seq(regContractBlack, issue, updateList1, updateList2))), + TestBlock.createWithTxStatus(transfer.timestamp, Seq(transfer), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val contractId = regContractBlack.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val master = regContractBlack.proofs.firstCurveProof.explicitGet().publicKey + val user = genesis2.recipient + + val masterBalanceKey = ByteStr(Bytes.concat(tokenId.arr, master.toAddress.bytes.arr)) + val userBalanceKey = ByteStr(Bytes.concat(tokenId.arr, user.bytes.arr)) + + newState.tokenAccountBalance(masterBalanceKey) shouldBe 0L + newState.tokenAccountBalance(userBalanceKey) shouldBe 1L + } + } + } + + val preconditionsNonFungibleBlackContractV2DepositValidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractBlack <- nonFungibleBlackContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractBlack <- registerNonFungibleV2Gen(master, contractBlack, dataStack, description, fee + 10000000000L, ts) + contractBlackId = regContractBlack.contractId + tokenId = tokenIdFromBytes(contractBlackId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + contractLock <- lockContractGen + dataStack: Seq[DataEntry] <- initLockContractDataStackGen(tokenId.arr) + description <- validDescStringGen + regContractLock <- registerLockGen(master, contractLock, dataStack, description, fee + 10000000000L, ts) + contractLockId = regContractLock.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractBlackId, issueData, attach, fee, ts+1) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + depositData = Seq(master.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + deposit <- depositNonFungibleV2Gen(master, contractBlackId, depositData, depositType, attach, fee, ts) + + } yield (genesis, genesis2, regContractBlack, regContractLock, issue, updateList1, deposit) + + property("Execute deposit in non fungible black contract") { + forAll(preconditionsNonFungibleBlackContractV2DepositValidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractBlack: RegisterContractTransaction, regContractLock: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, deposit: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractBlack, regContractLock, issue))), + TestBlock.createWithTxStatus(deposit.timestamp, Seq(deposit), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val contractId = regContractBlack.contractId.bytes + val lockContractId = regContractLock.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val master = regContractBlack.proofs.firstCurveProof.explicitGet().publicKey + + + val masterBalanceKey = ByteStr(Bytes.concat(tokenId.arr, master.toAddress.bytes.arr)) + val contractBalanceKey = ByteStr(Bytes.concat(tokenId.arr, lockContractId.arr)) + + newState.tokenAccountBalance(masterBalanceKey) shouldBe 0L + newState.tokenAccountBalance(contractBalanceKey) shouldBe 1L + } + } + } + + val preconditionsNonFungibleWhiteContractV2DepositValidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractWhite <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractWhiteId = regContractWhite.contractId + tokenId = tokenIdFromBytes(contractWhiteId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + contractLock <- lockContractGen + dataStack: Seq[DataEntry] <- initLockContractDataStackGen(tokenId.arr) + description <- validDescStringGen + regContractLock <- registerLockGen(master, contractLock, dataStack, description, fee + 10000000000L, ts) + contractLockId = regContractLock.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractWhiteId, issueData, attach, fee, ts+1) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(contractLockId.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.ContractAccount, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(contractLockId.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.ContractAccount, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + depositData = Seq(master.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + deposit <- depositNonFungibleV2Gen(master, contractWhiteId, depositData, depositType, attach, fee, ts) + + } yield (genesis, genesis2, regContractWhite, regContractLock, issue, updateList1, updateList2, updateList3, updateList4, deposit) + + property("Execute deposit in non fungible white contract") { + forAll(preconditionsNonFungibleWhiteContractV2DepositValidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractWhite: RegisterContractTransaction, regContractLock: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, deposit: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractWhite, regContractLock, issue, updateList1, updateList2))), + TestBlock.createWithTxStatus(deposit.timestamp, Seq(deposit), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val contractId = regContractWhite.contractId.bytes + val lockContractId = regContractLock.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val master = regContractWhite.proofs.firstCurveProof.explicitGet().publicKey + + val masterBalanceKey = ByteStr(Bytes.concat(tokenId.arr, master.toAddress.bytes.arr)) + val contractBalanceKey = ByteStr(Bytes.concat(tokenId.arr, lockContractId.arr)) + + newState.tokenAccountBalance(masterBalanceKey) shouldBe 0L + newState.tokenAccountBalance(contractBalanceKey) shouldBe 1L + } + } + } + + val preconditionsNonFungibleBlackContractV2WithdrawValidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractBlack <- nonFungibleBlackContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractBlack <- registerNonFungibleV2Gen(master, contractBlack, dataStack, description, fee + 10000000000L, ts) + contractBlackId = regContractBlack.contractId + tokenId = tokenIdFromBytes(contractBlackId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + contractLock <- lockContractGen + dataStack: Seq[DataEntry] <- initLockContractDataStackGen(tokenId.arr) + description <- validDescStringGen + regContractLock <- registerLockGen(master, contractLock, dataStack, description, fee + 10000000000L, ts) + contractLockId = regContractLock.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractBlackId, issueData, attach, fee, ts+1) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(contractLockId.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.ContractAccount, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractBlackId, updateListData, updateListType, attach, fee, ts) + + depositData = Seq(master.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + deposit <- depositNonFungibleV2Gen(master, contractBlackId, depositData, depositType, attach, fee, ts) + + withdrawData = Seq(contractLockId.bytes.arr, master.toAddress.bytes.arr, Ints.toByteArray(0)) + withdrawType = Seq(DataType.ContractAccount, DataType.Address, DataType.Int32) + withdraw <- withdrawNonFungibleV2Gen(master, contractBlackId, withdrawData, withdrawType, attach, fee, ts) + + } yield (genesis, genesis2, regContractBlack, regContractLock, issue, updateList1, updateList2, deposit, withdraw) + + property("Execute withdraw in non fungible black contract") { + forAll(preconditionsNonFungibleBlackContractV2WithdrawValidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractBlack: RegisterContractTransaction, regContractLock: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, deposit: ExecuteContractFunctionTransaction, withdraw: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractBlack, regContractLock, issue, deposit))), + TestBlock.createWithTxStatus(withdraw.timestamp, Seq(withdraw), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val contractId = regContractBlack.contractId.bytes + val lockContractId = regContractLock.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val master = regContractBlack.proofs.firstCurveProof.explicitGet().publicKey + + val masterBalanceKey = ByteStr(Bytes.concat(tokenId.arr, master.toAddress.bytes.arr)) + val contractBalanceKey = ByteStr(Bytes.concat(tokenId.arr, lockContractId.arr)) + + newState.tokenAccountBalance(masterBalanceKey) shouldBe 1L + newState.tokenAccountBalance(contractBalanceKey) shouldBe 0L + } + } + } + + val preconditionsNonFungibleWhiteContractV2WithdrawValidTest: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contractWhite <- nonFungibleWhiteContract + user <- accountGen + dataStack: Seq[DataEntry] <- initTokenDataStackGen() + description <- validDescStringGen + regContractWhite <- registerNonFungibleV2Gen(master, contractWhite, dataStack, description, fee + 10000000000L, ts) + contractWhiteId = regContractWhite.contractId + tokenId = tokenIdFromBytes(contractWhiteId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + contractLock <- lockContractGen + dataStack: Seq[DataEntry] <- initLockContractDataStackGen(tokenId.arr) + description <- validDescStringGen + regContractLock <- registerLockGen(master, contractLock, dataStack, description, fee + 10000000000L, ts) + contractLockId = regContractLock.contractId + + genesis <- genesisNonFungibleV2Gen(master, ts) + genesis2 <- genesisNonFungibleV2Gen(user, ts) + + issueData = "first token" + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issue <- issueNonFungibleV2Gen(master, contractWhiteId, issueData, attach, fee, ts+1) + + updateListData = Seq(master.toAddress.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList1 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(contractLockId.bytes.arr, Array(1.toByte)) + updateListType = Seq(DataType.ContractAccount, DataType.Boolean) + updateList2 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(master.toAddress.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.Address, DataType.Boolean) + updateList3 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + updateListData = Seq(contractLockId.bytes.arr, Array(0.toByte)) + updateListType = Seq(DataType.ContractAccount, DataType.Boolean) + updateList4 <- updateListNonFungibleV2Gen(master, contractWhiteId, updateListData, updateListType, attach, fee, ts) + + depositData = Seq(master.toAddress.bytes.arr, contractLockId.bytes.arr, Ints.toByteArray(0)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Int32) + deposit <- depositNonFungibleV2Gen(master, contractWhiteId, depositData, depositType, attach, fee, ts) + + withdrawData = Seq(contractLockId.bytes.arr, master.toAddress.bytes.arr, Ints.toByteArray(0)) + withdrawType = Seq(DataType.ContractAccount, DataType.Address, DataType.Int32) + withdraw <- withdrawNonFungibleV2Gen(master, contractWhiteId, withdrawData, withdrawType, attach, fee, ts) + + } yield (genesis, genesis2, regContractWhite, regContractLock, issue, updateList1, updateList2, updateList3, updateList4, deposit, withdraw) + + property("Execute withdraw in non fungible white contract") { + forAll(preconditionsNonFungibleWhiteContractV2WithdrawValidTest) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regContractWhite: RegisterContractTransaction, regContractLock: RegisterContractTransaction, + issue: ExecuteContractFunctionTransaction, updateList1: ExecuteContractFunctionTransaction, updateList2: ExecuteContractFunctionTransaction, updateList3: ExecuteContractFunctionTransaction, updateList4: ExecuteContractFunctionTransaction, deposit: ExecuteContractFunctionTransaction, withdraw: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(issue.timestamp, Seq(regContractWhite, regContractLock, issue, updateList1, updateList2, deposit))), + TestBlock.createWithTxStatus(withdraw.timestamp, Seq(withdraw), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val contractId = regContractWhite.contractId.bytes + val lockContractId = regContractLock.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val master = regContractWhite.proofs.firstCurveProof.explicitGet().publicKey + + val masterBalanceKey = ByteStr(Bytes.concat(tokenId.arr, master.toAddress.bytes.arr)) + val contractBalanceKey = ByteStr(Bytes.concat(tokenId.arr, lockContractId.arr)) + + newState.tokenAccountBalance(masterBalanceKey) shouldBe 1L + newState.tokenAccountBalance(contractBalanceKey) shouldBe 0L + } + } + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/state/contract/token/ExecuteTokenContractV2DiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/token/ExecuteTokenContractV2DiffTest.scala new file mode 100644 index 000000000..691efa93a --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/token/ExecuteTokenContractV2DiffTest.scala @@ -0,0 +1,543 @@ +package vsys.blockchain.state.contract.token + +import cats.Monoid +import com.google.common.primitives.{Bytes, Ints, Longs} +import org.scalacheck.{Gen, Shrink} +import org.scalatest.{Matchers, PropSpec} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.{Contract, ContractGenHelper, ContractTokenV2, DataEntry, DataType} +import vsys.blockchain.contract.token.{SystemContractGen, TokenContractV2Gen} +import vsys.blockchain.state._ +import vsys.blockchain.state.diffs.{assertDiffAndState, assertDiffEi} +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} +import vsys.utils.serialization.Deser + +class ExecuteTokenContractV2DiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with SystemContractGen + with TokenContractV2Gen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val tokenContractWhiteV2: Gen[Contract] = tokenContractV2Gen(true) + val tokenContractBlackV2: Gen[Contract] = tokenContractV2Gen(false) + + val preconditionsAndExecuteContractTestWhite: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, Long)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + user <- accountGen + contract <- tokenContractWhiteV2 + dataStack: Seq[DataEntry] <- initTokenDataStackGen(100000000L, 100L, "init") + description <- validDescStringGen + regContract <- registerTokenGen(master, contract, dataStack, description, fee + 10000000000L, ts) + contractId = regContract.contractId + genesis <- genesisTokenGen(master, ts) + genesis2 <- genesisTokenGen(user, ts) + feeEx: Long <- smallFeeGen + descEx <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + updateList <- updateListTokenGen(master, contractId, master.toAddress, true, descEx, feeEx, ts + 1) + updateList2 <- updateListTokenGen(master, contractId, user.toAddress, true, descEx, feeEx, ts + 2) + supersede <- supersedeTokenGen(master, contractId, user.toAddress, user.toAddress, descEx, feeEx, ts + 3) + issue <- issueTokenGen(user, contractId, 10000L, descEx, feeEx, ts + 4) + destroy <- destroyTokenGen(user, contractId, 100L, descEx, feeEx, ts + 5) + send <- sendTokenGen(user, contractId, master.toAddress, 500L, descEx, feeEx, ts + 6) + selfSend <- sendTokenGen(user, contractId, user.toAddress, 500L, descEx, feeEx, ts + 7) + } yield (genesis, genesis2, regContract, updateList, updateList2, supersede, issue, destroy, send, selfSend, send.transactionFee) + + property("execute token contract white v2 function transactions doesn't break invariant") { + forAll(preconditionsAndExecuteContractTestWhite) { case (genesis, genesis2, reg: RegisterContractTransaction, updateList, updateList2, supersede, + issue, destroy, send: ExecuteContractFunctionTransaction, selfSend, feeEx: Long) => + assertDiffAndState(Seq(TestBlock.create(Seq(genesis, genesis2)), TestBlock.create(Seq(reg, updateList, updateList2, supersede, issue, destroy))), TestBlock.create(Seq(send, selfSend))) { (blockDiff, newState) => + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + totalPortfolioDiff.balance shouldBe -(feeEx + feeEx) + totalPortfolioDiff.effectiveBalance shouldBe -(feeEx + feeEx) + val master = reg.proofs.firstCurveProof.explicitGet().publicKey + val user = send.proofs.firstCurveProof.explicitGet().publicKey + val contractId = reg.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val issuerKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte))) + val makerKey = ByteStr(Bytes.concat(contractId.arr, Array(1.toByte))) + val maxKey = ByteStr(Bytes.concat(tokenId.arr, Array(0.toByte))) + val totalKey = ByteStr(Bytes.concat(tokenId.arr, Array(1.toByte))) + val unityKey = ByteStr(Bytes.concat(tokenId.arr, Array(2.toByte))) + val descKey = ByteStr(Bytes.concat(tokenId.arr, Array(3.toByte))) + val descDE = Deser.serilizeString("init") + val masterBalanceKey = ByteStr(Bytes.concat(tokenId.arr, master.toAddress.bytes.arr)) + val userBalanceKey = ByteStr(Bytes.concat(tokenId.arr, user.toAddress.bytes.arr)) + val masterUpdateListKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(master.toAddress.bytes.arr, DataType.Address).bytes)) + val userUpdateListKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + + val (_, masterTxs) = newState.accountTransactionIds(master, 10, 0) + masterTxs.size shouldBe 6 // genesis, reg, update, update2, supersede, send + val (_, userTxs) = newState.accountTransactionIds(user, 10, 0) + userTxs.size shouldBe 6 // genesis2, supersede, issue, destory, send, selfSend + + // contract tokens + newState.contractTokens(contractId) shouldBe 1 + + // contract info + newState.contractContent(contractId) shouldEqual Some((2, reg.id, ContractTokenV2.contractTokenWhiteList)) + newState.contractInfo(issuerKey) shouldEqual Some(DataEntry(user.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(makerKey) shouldEqual Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(masterUpdateListKey) shouldEqual Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(userUpdateListKey) shouldEqual Some(DataEntry(Array(1.toByte), DataType.Boolean)) + + // token info + newState.tokenInfo(maxKey) shouldEqual Some(DataEntry(Longs.toByteArray(100000000L), DataType.Amount)) + newState.tokenInfo(unityKey) shouldEqual Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.tokenInfo(descKey) shouldEqual Some(DataEntry.create(descDE, DataType.ShortText).explicitGet()) + newState.tokenAccountBalance(totalKey) shouldBe 10000L - 100L //destroy 100 + + // token account balance + newState.tokenAccountBalance(masterBalanceKey) shouldBe 500L // user send 500 to master + newState.tokenAccountBalance(userBalanceKey) shouldBe 10000L - 100L - 500L // send out 500 destroy 100 + } + } + } + + val preconditionsAndExecuteContractTestBlack: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, Long)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + user <- accountGen + contract <- tokenContractBlackV2 + dataStack: Seq[DataEntry] <- initTokenDataStackGen(100000000L, 100L, "init") + description <- validDescStringGen + regContract <- registerTokenGen(master, contract, dataStack, description, fee + 10000000000L, ts) + contractId = regContract.contractId + genesis <- genesisTokenGen(master, ts) + genesis2 <- genesisTokenGen(user, ts) + feeEx: Long <- smallFeeGen + descEx <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + updateList <- updateListTokenGen(master, contractId, master.toAddress, false, descEx, feeEx, ts + 1) + updateList2 <- updateListTokenGen(master, contractId, user.toAddress, false, descEx, feeEx, ts + 2) + supersede <- supersedeTokenGen(master, contractId, user.toAddress, user.toAddress, descEx, feeEx, ts + 3) + issue <- issueTokenGen(user, contractId, 10000L, descEx, feeEx, ts + 4) + destroy <- destroyTokenGen(user, contractId, 100L, descEx, feeEx, ts + 5) + send <- sendTokenGen(user, contractId, master.toAddress, 500L, descEx, feeEx, ts + 6) + selfSend <- sendTokenGen(user, contractId, user.toAddress, 500L, descEx, feeEx, ts + 7) + } yield (genesis, genesis2, regContract, updateList, updateList2, supersede, issue, destroy, send, selfSend, send.transactionFee) + + property("execute token contract black v2 function transactions doesn't break invariant") { + forAll(preconditionsAndExecuteContractTestBlack) { case (genesis, genesis2, reg: RegisterContractTransaction, updateList, updateList2, supersede, + issue, destroy, send: ExecuteContractFunctionTransaction, selfSend, feeEx: Long) => + assertDiffAndState(Seq(TestBlock.create(Seq(genesis, genesis2)), TestBlock.create(Seq(reg, updateList, updateList2, supersede, issue, destroy))), TestBlock.create(Seq(send, selfSend))) { (blockDiff, newState) => + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + totalPortfolioDiff.balance shouldBe -(feeEx + feeEx) + totalPortfolioDiff.effectiveBalance shouldBe -(feeEx + feeEx) + val master = reg.proofs.firstCurveProof.explicitGet().publicKey + val user = send.proofs.firstCurveProof.explicitGet().publicKey + val contractId = reg.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val issuerKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte))) + val makerKey = ByteStr(Bytes.concat(contractId.arr, Array(1.toByte))) + val maxKey = ByteStr(Bytes.concat(tokenId.arr, Array(0.toByte))) + val totalKey = ByteStr(Bytes.concat(tokenId.arr, Array(1.toByte))) + val unityKey = ByteStr(Bytes.concat(tokenId.arr, Array(2.toByte))) + val descKey = ByteStr(Bytes.concat(tokenId.arr, Array(3.toByte))) + val descDE = Deser.serilizeString("init") + val masterBalanceKey = ByteStr(Bytes.concat(tokenId.arr, master.toAddress.bytes.arr)) + val userBalanceKey = ByteStr(Bytes.concat(tokenId.arr, user.toAddress.bytes.arr)) + val masterUpdateListKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(master.toAddress.bytes.arr, DataType.Address).bytes)) + val userUpdateListKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte), DataEntry(user.toAddress.bytes.arr, DataType.Address).bytes)) + + val (_, masterTxs) = newState.accountTransactionIds(master, 10, 0) + masterTxs.size shouldBe 6 // genesis, reg, update, update2, supersede, send + val (_, userTxs) = newState.accountTransactionIds(user, 10, 0) + userTxs.size shouldBe 6 // genesis2, supersede, issue, destory, send, selfSend + + // contract tokens + newState.contractTokens(contractId) shouldBe 1 + + // contract info + newState.contractContent(contractId) shouldEqual Some((2, reg.id, ContractTokenV2.contractTokenBlackList)) + newState.contractInfo(issuerKey) shouldEqual Some(DataEntry(user.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(makerKey) shouldEqual Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(masterUpdateListKey) shouldEqual Some(DataEntry(Array(0.toByte), DataType.Boolean)) + newState.contractInfo(userUpdateListKey) shouldEqual Some(DataEntry(Array(0.toByte), DataType.Boolean)) + + // token info + newState.tokenInfo(maxKey) shouldEqual Some(DataEntry(Longs.toByteArray(100000000L), DataType.Amount)) + newState.tokenInfo(unityKey) shouldEqual Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.tokenInfo(descKey) shouldEqual Some(DataEntry.create(descDE, DataType.ShortText).explicitGet()) + newState.tokenAccountBalance(totalKey) shouldBe 10000L - 100L //destroy 100 + + // token account balance + newState.tokenAccountBalance(masterBalanceKey) shouldBe 500L // user send 500 to master + newState.tokenAccountBalance(userBalanceKey) shouldBe 10000L - 100L - 500L // send out 500 destroy 100 + } + } + } + + val preconditionsAndExecuteContractTransactionWhite: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, Long)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + newIssuer <- accountGen + genesis <- genesisTokenGen(master, ts) + genesis1 <- genesisTokenGen(newIssuer, ts) + contract <- tokenContractWhiteV2 + dataStack: Seq[DataEntry] <- initTokenDataStackGen(100000000L, 100L, "init") + description <- validDescStringGen + regContract <- registerTokenGen(master, contract, dataStack, description, fee, ts) + contractId = regContract.contractId + description <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + executeContractSupersede <- supersedeTokenGen(master, contractId, newIssuer.toAddress, newIssuer.toAddress, description, fee, ts) + executeContractIssue <- issueTokenGen(master, contractId, 100000L, description, fee, ts) + executeContractIssue1 <- issueTokenGen(newIssuer, contractId, 100000L, description, fee, ts) + executeContractDestroy <- destroyTokenGen(master, contractId, 10000L, description, fee, ts) + executeContractUpdateList <- updateListTokenGen(master, contractId, master.toAddress,true, description, fee, ts) + recipient <- mintingAddressGen + executeContractUpdateList2 <- updateListTokenGen(master, contractId, recipient,true, description, fee, ts) + executeContractSend <- sendTokenGen(master, contractId, recipient, 100000L, description, fee, ts) + executeContractSelfSend <- sendTokenGen(master, contractId, master.toAddress, 100000L, description, fee, ts) + transferData = Seq(master.toAddress.bytes.arr, recipient.bytes.arr, Longs.toByteArray(1000L)) + transferType = Seq(DataType.Address, DataType.Address, DataType.Amount) + executeContractTransfer <- transferTokenGen(master, contractId, transferData, transferType, description, fee, ts) + executeContractTotalSupply <- totalSupplyTokenGen(master, contractId, description, fee, ts) + executeContractMaxSupply <- maxSupplyTokenGen(master, contractId, description, fee, ts) + executeContractBalanceOf <- balanceOfTokenGen(master, contractId, master.toAddress, description, fee, ts) + executeContractGetIssuer <- getIssuerTokenGen(master, contractId, description, fee, ts) + } yield (genesis, genesis1, regContract, executeContractSupersede, executeContractIssue, executeContractIssue1, + executeContractDestroy, executeContractUpdateList, executeContractUpdateList2, executeContractSend, + executeContractSelfSend, executeContractTransfer, executeContractTotalSupply, executeContractMaxSupply, + executeContractBalanceOf, executeContractGetIssuer, executeContractSelfSend.transactionFee) + + property("execute token contract white v2 transaction supersede function successfully") { + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, genesis1, regContract, executeContractSupersede, executeContractIssue, + executeContractIssue1, _, _, _, _, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, genesis1, regContract))), TestBlock.create(Seq(executeContractSupersede))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.contractDB.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + + assertDiffEi(Seq(TestBlock.create(Seq(genesis, genesis1, regContract, executeContractSupersede))), + TestBlock.createWithTxStatus(Seq(executeContractIssue), TransactionStatus.ContractInvalidCaller)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + + assertDiffEi(Seq(TestBlock.create(Seq(genesis, genesis1, regContract, executeContractSupersede))), TestBlock.create(Seq(executeContractIssue1))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract white v2 transaction issue function successfully"){ + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, _, regContract, _, + executeContractIssue, _, _, _, _, _, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract))), TestBlock.create(Seq(executeContractIssue))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract white v2 transaction destroy function successfully") { + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, _, regContract, _, executeContractIssue, _, + executeContractDestroy, _, _, _, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), TestBlock.create(Seq(executeContractDestroy))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract white v2 transaction updateList function successfully") { + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, _, regContract, _, _, _, _, + executeContractUpdateList, _, _, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract))), TestBlock.create(Seq(executeContractUpdateList))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.contractDB.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract white v2 transaction send function successfully") { + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, _, regContract, _, executeContractIssue, _, _, executeContractUpdateList, executeContractUpdateList2, + executeContractSend, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue, executeContractUpdateList, executeContractUpdateList2))), + TestBlock.create(Seq(executeContractSend))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract white v2 transaction send function self send successfully") { + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, _, regContract: RegisterContractTransaction, _, executeContractIssue, _, _, executeContractUpdateList, _, _, + executeContractSelfSend: ExecuteContractFunctionTransaction, _, _, _, _, _, feeSelfSend: Long) => + assertDiffAndState(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue, executeContractUpdateList))), + TestBlock.create(Seq(executeContractSelfSend))) { (blockDiff, newState) => + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + val sender = executeContractSelfSend.proofs.firstCurveProof.explicitGet().publicKey + totalPortfolioDiff.balance shouldBe -feeSelfSend + totalPortfolioDiff.effectiveBalance shouldBe -feeSelfSend + val contractId = regContract.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val senderBalanceKey = ByteStr(Bytes.concat(tokenId.arr, sender.toAddress.bytes.arr)) + val (_, senderTxs) = newState.accountTransactionIds(sender.toAddress, 10, 0) + senderTxs.size shouldBe 5 // genesis and payment, issue, updateList and send + newState.tokenAccountBalance(senderBalanceKey) shouldBe 100000L + } + } + } + + property("execute token contract white v2 transaction transfer function to address successfully") { + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, _, regContract, _, executeContractIssue, _, _, executeContractUpdateList, + executeContractUpdateList2, _, _, executeContractTransfer, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), TestBlock.create(Seq(regContract, executeContractIssue, executeContractUpdateList, executeContractUpdateList2))), + TestBlock.create(Seq(executeContractTransfer))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract white v2 transaction totalSupply function unsupported") { + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, _, regContract, _, executeContractIssue, + _, _, _, _, _, _, _, executeContractTotalSupply, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractTotalSupply), TransactionStatus.ContractUnsupportedOPC)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractUnsupportedOPC + } + } + } + + property("execute token contract white v2 transaction maxSupply function unsupported") { + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, _, regContract, _, executeContractIssue, + _, _, _, _, _, _, _, _, executeContractMaxSupply, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractMaxSupply), TransactionStatus.ContractUnsupportedOPC)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractUnsupportedOPC + } + } + } + + property("execute token contract white v2 transaction balanceOf function unsupported") { + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, _, regContract, _, executeContractIssue, + _, _, _, _, _, _, _, _, _, executeContractBalanceOf, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractBalanceOf), TransactionStatus.ContractUnsupportedOPC)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractUnsupportedOPC + } + } + } + + property("execute token contract white v2 transaction getIssuer function unsupported") { + forAll(preconditionsAndExecuteContractTransactionWhite) { case (genesis, _, regContract, _, executeContractIssue, + _, _, _, _, _, _, _, _, _, _, executeContractGetIssuer, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractGetIssuer), TransactionStatus.ContractUnsupportedOPC)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractUnsupportedOPC + } + } + } + + val preconditionsAndExecuteContractTransactionBlack: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, Long)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + newIssuer <- accountGen + genesis <- genesisTokenGen(master, ts) + genesis1 <- genesisTokenGen(newIssuer, ts) + contract <- tokenContractBlackV2 + dataStack: Seq[DataEntry] <- initTokenDataStackGen(100000000L, 100L, "init") + description <- validDescStringGen + regContract <- registerTokenGen(master, contract, dataStack, description, fee, ts) + contractId = regContract.contractId + description <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + executeContractSupersede <- supersedeTokenGen(master, contractId, newIssuer.toAddress, newIssuer.toAddress, description, fee, ts) + executeContractIssue <- issueTokenGen(master, contractId, 100000L, description, fee, ts) + executeContractIssue1 <- issueTokenGen(newIssuer, contractId, 100000L, description, fee, ts) + executeContractDestroy <- destroyTokenGen(master, contractId, 10000L, description, fee, ts) + executeContractUpdateList <- updateListTokenGen(master, contractId, master.toAddress,false, description, fee, ts) + recipient <- mintingAddressGen + executeContractUpdateList2 <- updateListTokenGen(master, contractId, recipient,false, description, fee, ts) + executeContractSend <- sendTokenGen(master, contractId, recipient, 100000L, description, fee, ts) + executeContractSelfSend <- sendTokenGen(master, contractId, master.toAddress, 100000L, description, fee, ts) + transferData = Seq(master.toAddress.bytes.arr, recipient.bytes.arr, Longs.toByteArray(1000L)) + transferType = Seq(DataType.Address, DataType.Address, DataType.Amount) + executeContractTransfer <- transferTokenGen(master, contractId, transferData, transferType, description, fee, ts) + executeContractTotalSupply <- totalSupplyTokenGen(master, contractId, description, fee, ts) + executeContractMaxSupply <- maxSupplyTokenGen(master, contractId, description, fee, ts) + executeContractBalanceOf <- balanceOfTokenGen(master, contractId, master.toAddress, description, fee, ts) + executeContractGetIssuer <- getIssuerTokenGen(master, contractId, description, fee, ts) + } yield (genesis, genesis1, regContract, executeContractSupersede, executeContractIssue, executeContractIssue1, + executeContractDestroy, executeContractUpdateList, executeContractUpdateList2, executeContractSend, + executeContractSelfSend, executeContractTransfer, executeContractTotalSupply, executeContractMaxSupply, + executeContractBalanceOf, executeContractGetIssuer, executeContractSelfSend.transactionFee) + + property("execute token contract black v2 transaction supersede function successfully") { + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, genesis1, regContract, executeContractSupersede, executeContractIssue, + executeContractIssue1, _, _, _, _, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, genesis1, regContract))), TestBlock.create(Seq(executeContractSupersede))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.contractDB.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + + assertDiffEi(Seq(TestBlock.create(Seq(genesis, genesis1, regContract, executeContractSupersede))), + TestBlock.createWithTxStatus(Seq(executeContractIssue), TransactionStatus.ContractInvalidCaller)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + + assertDiffEi(Seq(TestBlock.create(Seq(genesis, genesis1, regContract, executeContractSupersede))), TestBlock.create(Seq(executeContractIssue1))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract black v2 transaction issue function successfully"){ + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, _, regContract, _, + executeContractIssue, _, _, _, _, _, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract))), TestBlock.create(Seq(executeContractIssue))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract black v2 transaction destroy function successfully") { + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, _, regContract, _, executeContractIssue, _, + executeContractDestroy, _, _, _, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), TestBlock.create(Seq(executeContractDestroy))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract black v2 transaction updateList function successfully") { + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, _, regContract, _, _, _, _, + executeContractUpdateList, _, _, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract))), TestBlock.create(Seq(executeContractUpdateList))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.contractDB.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract black v2 transaction send function successfully") { + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, _, regContract, _, executeContractIssue, _, _, executeContractUpdateList, executeContractUpdateList2, + executeContractSend, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue, executeContractUpdateList, executeContractUpdateList2))), + TestBlock.create(Seq(executeContractSend))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract black v2 transaction send function self send successfully") { + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, _, regContract: RegisterContractTransaction, _, executeContractIssue, _, _, executeContractUpdateList, _, _, + executeContractSelfSend: ExecuteContractFunctionTransaction, _, _, _, _, _, feeSelfSend: Long) => + assertDiffAndState(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue, executeContractUpdateList))), + TestBlock.create(Seq(executeContractSelfSend))) { (blockDiff, newState) => + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + val sender = executeContractSelfSend.proofs.firstCurveProof.explicitGet().publicKey + totalPortfolioDiff.balance shouldBe -feeSelfSend + totalPortfolioDiff.effectiveBalance shouldBe -feeSelfSend + val contractId = regContract.contractId.bytes + val tokenId = tokenIdFromBytes(contractId.arr, Ints.toByteArray(0)).explicitGet() + val senderBalanceKey = ByteStr(Bytes.concat(tokenId.arr, sender.toAddress.bytes.arr)) + val (_, senderTxs) = newState.accountTransactionIds(sender.toAddress, 10, 0) + senderTxs.size shouldBe 5 // genesis and payment, issue, updateList and send + newState.tokenAccountBalance(senderBalanceKey) shouldBe 100000L + } + } + } + + property("execute token contract black v2 transaction transfer function to address successfully") { + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, _, regContract, _, executeContractIssue, _, _, executeContractUpdateList, + executeContractUpdateList2, _, _, executeContractTransfer, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), TestBlock.create(Seq(regContract, executeContractIssue, executeContractUpdateList, executeContractUpdateList2))), + TestBlock.create(Seq(executeContractTransfer))) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + } + + property("execute token contract black v2 transaction totalSupply function unsupported") { + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, _, regContract, _, executeContractIssue, + _, _, _, _, _, _, _, executeContractTotalSupply, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractTotalSupply), TransactionStatus.ContractUnsupportedOPC)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractUnsupportedOPC + } + } + } + + property("execute token contract black v2 transaction maxSupply function unsupported") { + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, _, regContract, _, executeContractIssue, + _, _, _, _, _, _, _, _, executeContractMaxSupply, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractMaxSupply), TransactionStatus.ContractUnsupportedOPC)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractUnsupportedOPC + } + } + } + + property("execute token contract black v2 transaction balanceOf function unsupported") { + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, _, regContract, _, executeContractIssue, + _, _, _, _, _, _, _, _, _, executeContractBalanceOf, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractBalanceOf), TransactionStatus.ContractUnsupportedOPC)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractUnsupportedOPC + } + } + } + + property("execute token contract black v2 transaction getIssuer function unsupported") { + forAll(preconditionsAndExecuteContractTransactionBlack) { case (genesis, _, regContract, _, executeContractIssue, + _, _, _, _, _, _, _, _, _, _, executeContractGetIssuer, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractGetIssuer), TransactionStatus.ContractUnsupportedOPC)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractUnsupportedOPC + } + } + } + +} diff --git a/src/test/scala/vsys/blockchain/state/contract/token/ExecuteTokenContractV2InvalidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/token/ExecuteTokenContractV2InvalidDiffTest.scala new file mode 100644 index 000000000..35efdd7c5 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/token/ExecuteTokenContractV2InvalidDiffTest.scala @@ -0,0 +1,595 @@ +package vsys.blockchain.state.contract.token + +import com.google.common.primitives.{Ints, Longs} +import org.scalacheck.{Gen, Shrink} +import org.scalatest.{Matchers, PropSpec} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.ContractGenHelper.feeScale +import vsys.blockchain.contract.lock.LockContractGen +import vsys.blockchain.contract.{Contract, ContractGenHelper, DataEntry, DataType} +import vsys.blockchain.contract.token.TokenContractV2Gen +import vsys.blockchain.state.EitherExt2 +import vsys.blockchain.state.diffs.assertDiffEi +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction, RegisterContractTransaction} + +class ExecuteTokenContractV2InvalidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with LockContractGen + with TokenContractV2Gen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val tokenContractWhiteV2: Gen[Contract] = tokenContractV2Gen(true) + val tokenContractBlackV2: Gen[Contract] = tokenContractV2Gen(false) + val lockContract: Gen[Contract] = lockContractGen() + + val executeTokenContractWhiteWithInvalidData: Gen[(GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contract <- tokenContractWhiteV2 + dataStack: Seq[DataEntry] <- initTokenDataStackGen(100000000L, 100L, "init") + description <- validDescStringGen + regContract <- registerTokenGen(master, contract, dataStack, description, fee + 10000000000L, ts) + contractId = regContract.contractId + genesis <- genesisTokenGen(master, ts) + rep <- mintingAddressGen + descEx <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + dataForIssueDestorySplit: Seq[DataEntry] <- amountDataStackGen(10000L) + dataForSend: Seq[DataEntry] <- sendDataStackGen(rep, 100L) + dataForSupersede: Seq[DataEntry] <- supersedeDataStackGen(rep, rep) + invalidIssue: ExecuteContractFunctionTransaction = ExecuteContractFunctionTransaction.create(master, + contractId, issueIndex, dataForSend, descEx, fee, feeScale, ts + 1000).explicitGet() + invalidUpdateList: ExecuteContractFunctionTransaction = ExecuteContractFunctionTransaction.create(master, + contractId, updateListIndex, dataForSupersede, descEx, fee, feeScale, ts + 1000).explicitGet() + invalidSend: ExecuteContractFunctionTransaction = ExecuteContractFunctionTransaction.create(master, + contractId, sendIndex, dataForSupersede, descEx, fee, feeScale, ts + 2000).explicitGet() + invalidSupersede: ExecuteContractFunctionTransaction = ExecuteContractFunctionTransaction.create(master, + contractId, supersedeIndex, dataForIssueDestorySplit, descEx, fee, feeScale, ts + 3000).explicitGet() + } yield (genesis, regContract, invalidIssue, invalidUpdateList, invalidSend, invalidSupersede) + + property("execute token contract white v2 transaction fail with invalid data") { + forAll(executeTokenContractWhiteWithInvalidData) { case (genesis, reg, invalid1, invalid2, invalid3, invalid4) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, reg))), TestBlock.createWithTxStatus(Seq(invalid1), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + + assertDiffEi(Seq(TestBlock.create(Seq(genesis, reg))), TestBlock.createWithTxStatus(Seq(invalid2), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.contractDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + + assertDiffEi(Seq(TestBlock.create(Seq(genesis, reg))), TestBlock.createWithTxStatus(Seq(invalid3), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + + assertDiffEi(Seq(TestBlock.create(Seq(genesis, reg))), TestBlock.createWithTxStatus(Seq(invalid4), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.contractDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + + } + } + + val executeTokenContractWhiteTransferDepositWithdraw: Gen[(GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contract <- tokenContractWhiteV2 + dataStack: Seq[DataEntry] <- initTokenDataStackGen(100000000L, 100L, "init") + description <- validDescStringGen + regContract <- registerTokenGen(master, contract, dataStack, description, fee + 10000000000L, ts) + contractId = regContract.contractId + genesis <- genesisTokenGen(master, ts) + recipient <- mintingAddressGen + description <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + updateList <- updateListTokenGen(master, contractId, master.toAddress,true, description, fee, ts) + updateList2 <- updateListTokenGen(master, contractId, recipient,true, description, fee, ts) + updateList3 <- updateListTokenGen(master, contractId, contractId,true, description, fee, ts) + executeContractIssue <- issueTokenGen(master, contractId, 100000L, description, fee, ts) + transferData = Seq(master.toAddress.bytes.arr, recipient.bytes.arr, Longs.toByteArray(1000L)) + transferType = Seq(DataType.Address, DataType.Address, DataType.Amount) + transferData2 = Seq(master.toAddress.bytes.arr, contractId.bytes.arr, Longs.toByteArray(1000L)) + transferType2 = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + executeContractTransfer <- transferTokenGen(master, contractId, transferData2, transferType2, description, fee, ts) + executeContractDeposit <- depositTokenGen(master, contractId, transferData2, transferType2, description, fee, ts) + withdrawData = Seq(contractId.bytes.arr, master.toAddress.bytes.arr, Longs.toByteArray(0L)) + withdrawType = Seq(DataType.ContractAccount, DataType.Address, DataType.Amount) + executeContractWithdraw <- withdrawTokenGen(master, contractId, withdrawData, withdrawType, description, fee, ts) + invalidDeposit <- depositTokenGen(master, contractId, transferData, transferType, description, fee, ts) + invalidWithdrawData = Seq(recipient.bytes.arr, master.toAddress.bytes.arr, Longs.toByteArray(1000L)) + invalidWithdrawType = Seq(DataType.Address, DataType.Address, DataType.Amount) + invalidWithdraw <- withdrawTokenGen(master, contractId, invalidWithdrawData, invalidWithdrawType, description, fee, ts) + } yield (genesis, regContract, executeContractIssue, executeContractTransfer, executeContractDeposit, + executeContractWithdraw, invalidDeposit, invalidWithdraw, updateList, updateList2, updateList3) + + // self deposit/withdraw/transfer (self contract) + // no deposit/withdraw trigger function + property("execute token contract white v2 transaction transfer function to unsupported contract fail"){ + forAll(executeTokenContractWhiteTransferDepositWithdraw) { case (genesis, regContract, executeContractIssue, + executeContractTransfer, _, _, _, _, updateList, _, updateList3) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), + TestBlock.create(Seq(regContract, executeContractIssue, updateList, updateList3))), + TestBlock.createWithTxStatus(Seq(executeContractTransfer), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + // no deposit/withdraw trigger function + property("execute token contract white v2 transaction deposit function to unsupported contract fail"){ + forAll(executeTokenContractWhiteTransferDepositWithdraw) { case (genesis, regContract, executeContractIssue, _, + executeContractDeposit, _, _, _, updateList, _, updateList3) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), + TestBlock.create(Seq(regContract, executeContractIssue, updateList, updateList3))), + TestBlock.createWithTxStatus(Seq(executeContractDeposit), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + // no deposit/withdraw trigger function + property("execute token contract white v2 transaction withdraw function from unsupported contract fail"){ + forAll(executeTokenContractWhiteTransferDepositWithdraw) { case (genesis, regContract, executeContractIssue, _, _, + executeContractWithdraw, _, _, updateList, _, updateList3) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), + TestBlock.create(Seq(regContract, executeContractIssue, updateList, updateList3))), + TestBlock.createWithTxStatus(Seq(executeContractWithdraw), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("execute token contract white v2 transaction deposit function invalid data type"){ + forAll(executeTokenContractWhiteTransferDepositWithdraw) { case (genesis, regContract, executeContractIssue, _, _, + _, invalidDeposit, _, updateList, _, updateList3) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), + TestBlock.create(Seq(regContract, executeContractIssue, updateList, updateList3))), + TestBlock.createWithTxStatus(Seq(invalidDeposit), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + } + } + + property("execute token contract white v2 transaction withdraw function invalid data type"){ + forAll(executeTokenContractWhiteTransferDepositWithdraw) { case (genesis, regContract, executeContractIssue, _, _, + _, _, invalidWithdraw, updateList, _, updateList3) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), + TestBlock.create(Seq(regContract, executeContractIssue, updateList, updateList3))), + TestBlock.createWithTxStatus(Seq(invalidWithdraw), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + } + } + + val preconditionsAndExecuteContractTransactionInvalidWhite: Gen[(GenesisTransaction, GenesisTransaction, + RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + newIssuer <- accountGen + genesis <- genesisTokenGen(master, ts) + genesis1 <- genesisTokenGen(newIssuer, ts) + contract <- tokenContractWhiteV2 + contract2 <- lockContract + dataStack: Seq[DataEntry] <- initTokenDataStackGen(100000000L, 100L, "init") + description <- validDescStringGen + regContract <- registerTokenGen(master, contract, dataStack, description, fee, ts) + blackTokenId = tokenIdFromBytes(regContract.contractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + dataStack2 <- initLockContractDataStackGen(blackTokenId.arr) + regContract2 <- registerTokenGen(master, contract2, dataStack2, description, fee, ts) + contractId = regContract.contractId + contractId2 = regContract2.contractId + description <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + invalidSupersede <- supersedeTokenGen(newIssuer, contractId, newIssuer.toAddress, newIssuer.toAddress, description, fee, ts) + executeContractIssue <- issueTokenGen(master, contractId, 100000L, description, fee, ts) + invalidIssue <- issueTokenGen(master, contractId, 100000001L, description, fee, ts) + invalidDestroy <- destroyTokenGen(master, contractId, 100001L, description, fee, ts) + invalidUpdateList <- updateListTokenGen(newIssuer, contractId, master.toAddress,true, description, fee, ts) + recipient <- mintingAddressGen + invalidSend <- sendTokenGen(master, contractId, recipient, 1000000L, description, fee, ts) + transferData = Seq(master.toAddress.bytes.arr, recipient.bytes.arr, Longs.toByteArray(1000L)) + transferType = Seq(DataType.Address, DataType.Address, DataType.Amount) + invalidTransfer <- transferTokenGen(master, contractId, transferData, transferType, description, fee, ts) + depositData = Seq(master.toAddress.bytes.arr, contractId2.bytes.arr, Longs.toByteArray(0L)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + invalidDeposit <- depositTokenGen(master, contractId, depositData, depositType, description, fee, ts) + withdrawData = Seq(contractId2.bytes.arr, master.toAddress.bytes.arr, Longs.toByteArray(0L)) + withdrawType = Seq(DataType.ContractAccount, DataType.Address, DataType.Amount) + invalidWithdraw <- withdrawTokenGen(master, contractId, withdrawData, withdrawType, description, fee, ts) + } yield (genesis, genesis1, regContract, invalidSupersede, executeContractIssue, invalidIssue, + invalidDestroy, invalidUpdateList, invalidSend, invalidTransfer, invalidDeposit, invalidWithdraw) + + property("execute contract white v2 transaction invalid supersede function") { + forAll(preconditionsAndExecuteContractTransactionInvalidWhite) { case (genesis, genesis1, regContract, + invalidSupersede, _, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, genesis1, regContract))), TestBlock.createWithTxStatus(Seq(invalidSupersede), TransactionStatus.ContractInvalidSigner)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.contractDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidSigner + } + } + } + + property("execute contract white v2 transaction invalid issue function"){ + forAll(preconditionsAndExecuteContractTransactionInvalidWhite) { case (genesis, _, regContract, _, _, + invalidIssue, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract))), + TestBlock.createWithTxStatus(Seq(invalidIssue), TransactionStatus.ContractTokenMaxExceeded)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractTokenMaxExceeded + } // total > max + } + } + + property("execute contract white v2 transaction invalid destroy function") { + forAll(preconditionsAndExecuteContractTransactionInvalidWhite) { case (genesis, _, regContract, _, + executeContractIssue, _, invalidDestroy, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(invalidDestroy), TransactionStatus.ContractTokenBalanceInsufficient)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractTokenBalanceInsufficient + } + } + } + + property("execute contract white v2 transaction updateList function invalid caller") { + forAll(preconditionsAndExecuteContractTransactionInvalidWhite) { case (genesis, genesis1, regContract, _, _, + _, _, invalidUpdateList, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, genesis1, regContract))), TestBlock.createWithTxStatus(Seq(invalidUpdateList), TransactionStatus.ContractInvalidCaller)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + } + } + + property("execute contract white v2 transaction send function failed with sender not in white list") { + forAll(preconditionsAndExecuteContractTransactionInvalidWhite) { case (genesis, _, regContract, _, + executeContractIssue, _, _, _, invalidSend, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(invalidSend), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("execute contract white v2 transaction transfer function failed with sender not in white list") { + forAll(preconditionsAndExecuteContractTransactionInvalidWhite) { case (genesis, _, regContract, _, + executeContractIssue, _, _, _, _, invalidTransfer, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(invalidTransfer), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("execute contract white v2 transaction deposit function failed with sender not in white list") { + forAll(preconditionsAndExecuteContractTransactionInvalidWhite) { case (genesis, _, regContract, _, + executeContractIssue, _, _, _, _, _, invalidDeposit, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(invalidDeposit), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("execute contract white v2 transaction withdraw function failed with sender not in white list") { + forAll(preconditionsAndExecuteContractTransactionInvalidWhite) { case (genesis, _, regContract, _, + executeContractIssue, _, _, _, _, _, _, invalidWithdraw) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(invalidWithdraw), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val executeTokenContractBlackWithInvalidData: Gen[(GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contract <- tokenContractBlackV2 + dataStack: Seq[DataEntry] <- initTokenDataStackGen(100000000L, 100L, "init") + description <- validDescStringGen + regContract <- registerTokenGen(master, contract, dataStack, description, fee + 10000000000L, ts) + contractId = regContract.contractId + genesis <- genesisTokenGen(master, ts) + rep <- mintingAddressGen + descEx <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + dataForIssueDestorySplit: Seq[DataEntry] <- amountDataStackGen(10000L) + dataForSend: Seq[DataEntry] <- sendDataStackGen(rep, 100L) + dataForSupersede: Seq[DataEntry] <- addressDataStackGen(rep) + invalidIssue: ExecuteContractFunctionTransaction = ExecuteContractFunctionTransaction.create(master, + contractId, issueIndex, dataForSend, descEx, fee, feeScale, ts + 1000).explicitGet() + invalidUpdateList: ExecuteContractFunctionTransaction = ExecuteContractFunctionTransaction.create(master, + contractId, updateListIndex, dataForSupersede, descEx, fee, feeScale, ts + 1000).explicitGet() + invalidSend: ExecuteContractFunctionTransaction = ExecuteContractFunctionTransaction.create(master, + contractId, sendIndex, dataForSupersede, descEx, fee, feeScale, ts + 2000).explicitGet() + invalidSupersede: ExecuteContractFunctionTransaction = ExecuteContractFunctionTransaction.create(master, + contractId, supersedeIndex, dataForIssueDestorySplit, descEx, fee, feeScale, ts + 3000).explicitGet() + } yield (genesis, regContract, invalidIssue, invalidUpdateList, invalidSend, invalidSupersede) + + property("execute token contract black v2 transaction fail with invalid data") { + forAll(executeTokenContractBlackWithInvalidData) { case (genesis, reg, invalid1, invalid2, invalid3, invalid4) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, reg))), TestBlock.createWithTxStatus(Seq(invalid1), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + + assertDiffEi(Seq(TestBlock.create(Seq(genesis, reg))), TestBlock.createWithTxStatus(Seq(invalid2), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.contractDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + + assertDiffEi(Seq(TestBlock.create(Seq(genesis, reg))), TestBlock.createWithTxStatus(Seq(invalid3), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + + assertDiffEi(Seq(TestBlock.create(Seq(genesis, reg))), TestBlock.createWithTxStatus(Seq(invalid4), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.contractDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + + } + } + + val executeTokenContractBlackTransferDepositWithdraw: Gen[(GenesisTransaction, RegisterContractTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contract <- tokenContractBlackV2 + dataStack: Seq[DataEntry] <- initTokenDataStackGen(100000000L, 100L, "init") + description <- validDescStringGen + regContract <- registerTokenGen(master, contract, dataStack, description, fee + 10000000000L, ts) + contractId = regContract.contractId + genesis <- genesisTokenGen(master, ts) + description <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + executeContractIssue <- issueTokenGen(master, contractId, 100000L, description, fee, ts) + recipient <- mintingAddressGen + transferData = Seq(master.toAddress.bytes.arr, recipient.bytes.arr, Longs.toByteArray(1000L)) + transferType = Seq(DataType.Address, DataType.Address, DataType.Amount) + transferData2 = Seq(master.toAddress.bytes.arr, contractId.bytes.arr, Longs.toByteArray(1000L)) + transferType2 = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + executeContractTransfer <- transferTokenGen(master, contractId, transferData2, transferType2, description, fee, ts) + executeContractDeposit <- depositTokenGen(master, contractId, transferData2, transferType2, description, fee, ts) + withdrawData = Seq(contractId.bytes.arr, master.toAddress.bytes.arr, Longs.toByteArray(0L)) + withdrawType = Seq(DataType.ContractAccount, DataType.Address, DataType.Amount) + executeContractWithdraw <- withdrawTokenGen(master, contractId, withdrawData, withdrawType, description, fee, ts) + invalidDeposit <- depositTokenGen(master, contractId, transferData, transferType, description, fee, ts) + invalidWithdrawData = Seq(recipient.bytes.arr, master.toAddress.bytes.arr, Longs.toByteArray(1000L)) + invalidWithdrawType = Seq(DataType.Address, DataType.Address, DataType.Amount) + invalidWithdraw <- withdrawTokenGen(master, contractId, invalidWithdrawData, invalidWithdrawType, description, fee, ts) + } yield (genesis, regContract, executeContractIssue, executeContractTransfer, executeContractDeposit, executeContractWithdraw, invalidDeposit, invalidWithdraw) + + // self deposit/withdraw/transfer (self contract) + // no deposit/withdraw trigger function + property("execute token contract black v2 transaction transfer function to unsupported contract fail"){ + forAll(executeTokenContractBlackTransferDepositWithdraw) { case (genesis, regContract, executeContractIssue, executeContractTransfer, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), TestBlock.create(Seq(regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractTransfer), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + // no deposit/withdraw trigger function + property("execute token contract black v2 transaction deposit function to unsupported contract fail"){ + forAll(executeTokenContractBlackTransferDepositWithdraw) { case (genesis, regContract, executeContractIssue, _, executeContractDeposit, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), TestBlock.create(Seq(regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractDeposit), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + // no deposit/withdraw trigger function + property("execute token contract black v2 transaction withdraw function from unsupported contract fail"){ + forAll(executeTokenContractBlackTransferDepositWithdraw) { case (genesis, regContract, executeContractIssue, _, _, executeContractWithdraw, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), TestBlock.create(Seq(regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(executeContractWithdraw), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("execute token contract black v2 transaction deposit function invalid data type"){ + forAll(executeTokenContractBlackTransferDepositWithdraw) { case (genesis, regContract, executeContractIssue, _, _, _, invalidDeposit, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), TestBlock.create(Seq(regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(invalidDeposit), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + } + } + + property("execute token contract black v2 transaction withdraw function invalid data type"){ + forAll(executeTokenContractBlackTransferDepositWithdraw) { case (genesis, regContract, executeContractIssue, _, _, _, _, invalidWithdraw) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis)), TestBlock.create(Seq(regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(invalidWithdraw), TransactionStatus.ContractDataTypeMismatch)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractDataTypeMismatch + } + } + } + + val preconditionsAndExecuteContractTransactionInvalidBlack: Gen[(GenesisTransaction, GenesisTransaction, + RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + newIssuer <- accountGen + genesis <- genesisTokenGen(master, ts) + genesis1 <- genesisTokenGen(newIssuer, ts) + contract <- tokenContractBlackV2 + contract2 <- lockContract + dataStack: Seq[DataEntry] <- initTokenDataStackGen(100000000L, 100L, "init") + description <- validDescStringGen + regContract <- registerTokenGen(master, contract, dataStack, description, fee, ts) + blackTokenId = tokenIdFromBytes(regContract.contractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + dataStack2 <- initLockContractDataStackGen(blackTokenId.arr) + regContract2 <- registerTokenGen(master, contract2, dataStack2, description, fee, ts) + contractId = regContract.contractId + contractId2 = regContract2.contractId + description <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + invalidSupersede <- supersedeTokenGen(newIssuer, contractId, newIssuer.toAddress, newIssuer.toAddress, description, fee, ts) + executeContractIssue <- issueTokenGen(master, contractId, 100000L, description, fee, ts) + invalidIssue <- issueTokenGen(master, contractId, 100000001L, description, fee, ts) + invalidDestroy <- destroyTokenGen(master, contractId, 100001L, description, fee, ts) + invalidUpdateList <- updateListTokenGen(newIssuer, contractId, master.toAddress,true, description, fee, ts) + recipient <- mintingAddressGen + updateList <- updateListTokenGen(master, contractId, master.toAddress,true, description, fee, ts) + invalidSend <- sendTokenGen(master, contractId, recipient, 1000L, description, fee, ts) + transferData = Seq(master.toAddress.bytes.arr, recipient.bytes.arr, Longs.toByteArray(1000L)) + transferType = Seq(DataType.Address, DataType.Address, DataType.Amount) + invalidTransfer <- transferTokenGen(master, contractId, transferData, transferType, description, fee, ts) + depositData = Seq(master.toAddress.bytes.arr, contractId2.bytes.arr, Longs.toByteArray(0L)) + depositType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + invalidDeposit <- depositTokenGen(master, contractId, depositData, depositType, description, fee, ts) + withdrawData = Seq(contractId2.bytes.arr, master.toAddress.bytes.arr, Longs.toByteArray(0L)) + withdrawType = Seq(DataType.ContractAccount, DataType.Address, DataType.Amount) + invalidWithdraw <- withdrawTokenGen(master, contractId, withdrawData, withdrawType, description, fee, ts) + } yield (genesis, genesis1, regContract, invalidSupersede, executeContractIssue, invalidIssue, invalidDestroy, + invalidUpdateList, invalidSend, updateList, invalidTransfer, invalidDeposit, invalidWithdraw) + + property("execute contract black v2 transaction invalid supersede function") { + forAll(preconditionsAndExecuteContractTransactionInvalidBlack) { case (genesis, genesis1, regContract, + invalidSupersede, _, _, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, genesis1, regContract))), TestBlock.createWithTxStatus(Seq(invalidSupersede), TransactionStatus.ContractInvalidSigner)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.contractDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidSigner + } + } + } + + property("execute contract black v2 transaction invalid issue function"){ + forAll(preconditionsAndExecuteContractTransactionInvalidBlack) { case (genesis, _, regContract, _, _, + invalidIssue, _, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract))), + TestBlock.createWithTxStatus(Seq(invalidIssue), TransactionStatus.ContractTokenMaxExceeded)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractTokenMaxExceeded + } // total > max + } + } + + property("execute contract black v2 transaction invalid destroy function") { + forAll(preconditionsAndExecuteContractTransactionInvalidBlack) { case (genesis, _, regContract, _, + executeContractIssue, _, invalidDestroy, _, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue))), + TestBlock.createWithTxStatus(Seq(invalidDestroy), TransactionStatus.ContractTokenBalanceInsufficient)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractTokenBalanceInsufficient + } + } + } + + property("execute contract black v2 transaction updateList function invalid caller") { + forAll(preconditionsAndExecuteContractTransactionInvalidBlack) { case (genesis, genesis1, regContract, _, _, _, + _, invalidUpdateList, _, _, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, genesis1, regContract))), + TestBlock.createWithTxStatus(Seq(invalidUpdateList), TransactionStatus.ContractInvalidCaller)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + } + } + + property("execute contract black v2 transaction send function failed with sender in black list") { + forAll(preconditionsAndExecuteContractTransactionInvalidBlack) { case (genesis, _, regContract, _, + executeContractIssue, _, _, _, invalidSend, updateList, _, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue, updateList))), + TestBlock.createWithTxStatus(Seq(invalidSend), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("execute contract black v2 transaction transfer function failed with sender in black list") { + forAll(preconditionsAndExecuteContractTransactionInvalidBlack) { case (genesis, _, regContract, _, + executeContractIssue, _, _, _, _, updateList, invalidTransfer, _, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue, updateList))), + TestBlock.createWithTxStatus(Seq(invalidTransfer), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("execute contract black v2 transaction deposit function failed with sender in black list") { + forAll(preconditionsAndExecuteContractTransactionInvalidBlack) { case (genesis, _, regContract, _, + executeContractIssue, _, _, _, _, updateList, _, invalidDeposit, _) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue, updateList))), + TestBlock.createWithTxStatus(Seq(invalidDeposit), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("execute contract black v2 transaction withdraw function failed with sender in black list") { + forAll(preconditionsAndExecuteContractTransactionInvalidBlack) { case (genesis, _, regContract, _, + executeContractIssue, _, _, _, _, updateList, _, _, invalidWithdraw) => + assertDiffEi(Seq(TestBlock.create(Seq(genesis, regContract, executeContractIssue, updateList))), + TestBlock.createWithTxStatus(Seq(invalidWithdraw), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + blockDiffEi.explicitGet().txsDiff.tokenAccountBalance.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + +} diff --git a/src/test/scala/vsys/blockchain/state/contract/vescrow/ExecuteVEscrowContractInvalidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vescrow/ExecuteVEscrowContractInvalidDiffTest.scala new file mode 100644 index 000000000..736fa784f --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vescrow/ExecuteVEscrowContractInvalidDiffTest.scala @@ -0,0 +1,485 @@ +package vsys.blockchain.state.contract.vescrow + +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract._ +import vsys.blockchain.contract.token.SystemContractGen +import vsys.blockchain.contract.vescrow.VEscrowContractGen +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.contract._ +import vsys.blockchain.state._ +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import com.google.common.primitives.{Ints, Longs} +import vsys.account.ContractAccount +import vsys.account.ContractAccount.tokenIdFromBytes + +class ExecuteVEscrowContractInvalidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with SystemContractGen + with VEscrowContractGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val preconditionsAndVEscrowInvalidDeposits: Gen[(GenesisTransaction, GenesisTransaction, GenesisTransaction, + RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (judge, ts, fee) <- ContractGenHelper.basicContractTestGen() + recipient <- accountGen + payer <- accountGen + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + genesis <- genesisVEscrowGen(judge, ts) + genesis2 <- genesisVEscrowGen(recipient, ts) + genesis3 <- genesisVEscrowGen(payer, ts) + vEscrow <- vEscrowContractGen() + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + initVEscrowDataStack <- initVEscrowDataStackGen(tokenId.arr, 1000000000L, 1000000000L) + regVEscrow <- registerVEscrowGen(judge, vEscrow, initVEscrowDataStack, "test", fee, ts) + + judgeDepositVSYSData = Seq(judge.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000000000000L)) + recipientDepositVSYSData = Seq(recipient.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000000000000L)) + payerDepositVSYSData = Seq(payer.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000000000000L)) + depositVSYSDataType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + judgeDepositVSYS <- depositVSYSGen(judge, judgeDepositVSYSData, depositVSYSDataType, attach, fee, ts + 1) + recipientDepositVSYS <- depositVSYSGen(recipient, recipientDepositVSYSData, depositVSYSDataType, attach, fee, ts + 2) + payerDepositVSYS <- depositVSYSGen(payer, payerDepositVSYSData, depositVSYSDataType, attach, fee, ts + 3) + + withdrawVSYSData = Seq(regVEscrow.contractId.bytes.arr, judge.toAddress.bytes.arr, Longs.toByteArray(1000000000001L)) + withdrawVSYSDataType = Seq(DataType.ContractAccount, DataType.Address, DataType.Amount) + judgeWithdrawVSYS <- withdrawVSYSGen(judge, withdrawVSYSData, withdrawVSYSDataType, attach, fee, ts + 4) + + createVEscrow <- createVEscrowGen(payer, regVEscrow.contractId, recipient.toAddress, 1000L, 1000L, 1000L, 10L, 10L, ts + 100L, attach, fee, ts + 5) + createVEscrowLargeDepositAmounts <- createVEscrowGen(payer, regVEscrow.contractId, recipient.toAddress, 1000L, 1000000000001L, 1000000000001L, 10L, 10L, ts + 100L, attach, fee, ts + 5) + invalidCreateVEscrowLargeCreateAmount <- createVEscrowGen(payer, regVEscrow.contractId, recipient.toAddress, 1000000000001L, 1000L, 1000L, 10L, 10L, ts + 100L, attach, fee, ts + 5) + + recipientDepositToOrder <- recipientDepositVEscrowGen(recipient, regVEscrow.contractId, createVEscrowLargeDepositAmounts.id.arr, attach, fee, ts + 6) + judgeDepositToOrder <- judgeDepositVEscrowGen(judge, regVEscrow.contractId, createVEscrowLargeDepositAmounts.id.arr, attach, fee, ts + 7) + + recipientSubmitWork <- submitWorkVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 8) + + payerSubmitPenalty <- submitPenaltyVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 9) + + approveWork <- approveWorkVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 10) + + applyToJudge <- applyToJudgeVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 11) + + recipientCollect <- collectVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 12) + + judgeOrder <- judgeVEscrowGen(judge, regVEscrow.contractId, createVEscrow.id.arr, 100, 100, attach, fee, ts + 13) + + payerRefundOrder <- payerRefundVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 14) + + recipientRefundOrder <- recipientRefundVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 15) + + } yield (genesis, genesis2, genesis3, regVEscrow, judgeDepositVSYS, recipientDepositVSYS, payerDepositVSYS, judgeWithdrawVSYS, createVEscrow, createVEscrowLargeDepositAmounts, invalidCreateVEscrowLargeCreateAmount, + recipientDepositToOrder, judgeDepositToOrder, recipientSubmitWork, payerSubmitPenalty, approveWork, applyToJudge, recipientCollect, judgeOrder, payerRefundOrder, recipientRefundOrder) + + property("v-escrow unable to withdraw more than deposited") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, _, _, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, _, _, judgeWithdrawVSYS: ExecuteContractFunctionTransaction, _, _, _, + _, _, _, _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS))), + TestBlock.createWithTxStatus(judgeWithdrawVSYS.timestamp, Seq(judgeWithdrawVSYS), TransactionStatus.ContractTokenBalanceInsufficient)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractTokenBalanceInsufficient + } + } + } + + property("v-escrow unable to create without sufficient amount") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, _, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, _, _, _, invalidCreateVEscrow: ExecuteContractFunctionTransaction, + _, _, _, _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS))), + TestBlock.createWithTxStatus(invalidCreateVEscrow.timestamp, Seq(invalidCreateVEscrow), TransactionStatus.ContractMapValueInsufficient)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + } + } + + property("v-escrow recipient unable to deposit without sufficient amount") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, _, createVEscrowLargeDepositAmounts: ExecuteContractFunctionTransaction, _, recipientDepositToOrder: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, createVEscrowLargeDepositAmounts))), + TestBlock.createWithTxStatus(recipientDepositToOrder.timestamp, Seq(recipientDepositToOrder), TransactionStatus.ContractMapValueInsufficient)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + } + } + + property("v-escrow judge unable to deposit without sufficient amount") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, _, createVEscrowLargeDepositAmounts: ExecuteContractFunctionTransaction, _, _, judgeDepositToOrder: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, createVEscrowLargeDepositAmounts))), + TestBlock.createWithTxStatus(judgeDepositToOrder.timestamp, Seq(judgeDepositToOrder), TransactionStatus.ContractMapValueInsufficient)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + } + } + + property("v-escrow recipient unable to submit before judge deposits") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, _, _, _, _, recipientSubmitWork: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, createVEscrow))), + TestBlock.createWithTxStatus(recipientSubmitWork.timestamp, Seq(recipientSubmitWork), TransactionStatus.Failed)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow payer unable to submit penalty before order deposits") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, _, _, _, _, _, payerSubmitPenalty: ExecuteContractFunctionTransaction, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, createVEscrow))), + TestBlock.createWithTxStatus(payerSubmitPenalty.timestamp, Seq(payerSubmitPenalty), TransactionStatus.Failed)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow payer unable to approve before order deposits") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, _, _, _, _, _, _, approveWork: ExecuteContractFunctionTransaction, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, createVEscrow))), + TestBlock.createWithTxStatus(approveWork.timestamp, Seq(approveWork), TransactionStatus.Failed)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow payer unable to apply to judge before order deposits") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _, applyToJudge: ExecuteContractFunctionTransaction, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, createVEscrow))), + TestBlock.createWithTxStatus(applyToJudge.timestamp, Seq(applyToJudge), TransactionStatus.Failed)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow recipient cannot collect before order deposits") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _, _, recipientCollect: ExecuteContractFunctionTransaction, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, createVEscrow))), + TestBlock.createWithTxStatus(recipientCollect.timestamp, Seq(recipientCollect), TransactionStatus.Failed)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow cannnot judge before order deposits") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _, _, _, judgeOrder: ExecuteContractFunctionTransaction, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, createVEscrow))), + TestBlock.createWithTxStatus(judgeOrder.timestamp, Seq(judgeOrder), TransactionStatus.Failed)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow payer cannot refund before order deposits") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _, _, _, _, payerRefundOrder: ExecuteContractFunctionTransaction, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, createVEscrow))), + TestBlock.createWithTxStatus(payerRefundOrder.timestamp, Seq(payerRefundOrder), TransactionStatus.Failed)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow recipient cannot refund before order deposits") { + forAll(preconditionsAndVEscrowInvalidDeposits) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _, _, _, _, _, recipientRefundOrder: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, createVEscrow))), + TestBlock.createWithTxStatus(recipientRefundOrder.timestamp, Seq(recipientRefundOrder), TransactionStatus.Failed)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVEscrowInvalidSubmit: Gen[(GenesisTransaction, GenesisTransaction, GenesisTransaction, + RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction)] = for { + (judge, ts, fee) <- ContractGenHelper.basicContractTestGen() + recipient <- accountGen + payer <- accountGen + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + genesis <- genesisVEscrowGen(judge, ts) + genesis2 <- genesisVEscrowGen(recipient, ts) + genesis3 <- genesisVEscrowGen(payer, ts) + vEscrow <- vEscrowContractGen() + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + initVEscrowDataStack <- initVEscrowDataStackGen(tokenId.arr, 1000000000L, 1000000000L) + regVEscrow <- registerVEscrowGen(judge, vEscrow, initVEscrowDataStack, "test", fee, ts) + + judgeDepositVSYSData = Seq(judge.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000000000000L)) + recipientDepositVSYSData = Seq(recipient.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000000000000L)) + payerDepositVSYSData = Seq(payer.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000000000000L)) + depositVSYSDataType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + judgeDepositVSYS <- depositVSYSGen(judge, judgeDepositVSYSData, depositVSYSDataType, attach, fee, ts + 1) + recipientDepositVSYS <- depositVSYSGen(recipient, recipientDepositVSYSData, depositVSYSDataType, attach, fee, ts + 2) + payerDepositVSYS <- depositVSYSGen(payer, payerDepositVSYSData, depositVSYSDataType, attach, fee, ts + 3) + + createVEscrow <- createVEscrowGen(payer, regVEscrow.contractId, recipient.toAddress, 1000L, 1000L, 1000L, 10L, 10L, ts + 100L, attach, fee, ts + 4) + + recipientDepositToOrder <- recipientDepositVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 5) + judgeDepositToOrder <- judgeDepositVEscrowGen(judge, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 6) + + approveWork <- approveWorkVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 7) + + applyToJudge <- applyToJudgeVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 8) + + recipientCollect <- collectVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 9) + + judgeOrder <- judgeVEscrowGen(judge, regVEscrow.contractId, createVEscrow.id.arr, 100, 100, attach, fee, ts + 10) + + payerRefundOrder <- payerRefundVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 11) + + recipientRefundOrder <- recipientRefundVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 12) + + } yield (genesis, genesis2, genesis3, regVEscrow, judgeDepositVSYS, recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, + approveWork, applyToJudge, recipientCollect, judgeOrder, payerRefundOrder, recipientRefundOrder) + + property("v-escrow judge cannot approve work before recipient submits work") { + forAll(preconditionsAndVEscrowInvalidSubmit) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + approveWork: ExecuteContractFunctionTransaction, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, recipientDepositVSYS, createVEscrow, + recipientDepositToOrder, judgeDepositToOrder))), + TestBlock.createWithTxStatus(approveWork.timestamp, Seq(approveWork), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow payer cannot apply to judge before recipient submits work") { + forAll(preconditionsAndVEscrowInvalidSubmit) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, applyToJudge: ExecuteContractFunctionTransaction, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, recipientDepositVSYS, createVEscrow, + recipientDepositToOrder, judgeDepositToOrder))), + TestBlock.createWithTxStatus(applyToJudge.timestamp, Seq(applyToJudge), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow recipient cannot collect before recipient submits work") { + forAll(preconditionsAndVEscrowInvalidSubmit) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, recipientCollect: ExecuteContractFunctionTransaction, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, recipientDepositVSYS, createVEscrow, + recipientDepositToOrder, judgeDepositToOrder))), + TestBlock.createWithTxStatus(recipientCollect.timestamp, Seq(recipientCollect), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow judge cannot judge order before recipient submits work") { + forAll(preconditionsAndVEscrowInvalidSubmit) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, judgeOrder: ExecuteContractFunctionTransaction, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, recipientDepositVSYS, createVEscrow, + recipientDepositToOrder, judgeDepositToOrder))), + TestBlock.createWithTxStatus(judgeOrder.timestamp, Seq(judgeOrder), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow payer cannot refund order before recipient submits work") { + forAll(preconditionsAndVEscrowInvalidSubmit) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, _, payerRefundOrder: ExecuteContractFunctionTransaction, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, recipientDepositVSYS, createVEscrow, + recipientDepositToOrder, judgeDepositToOrder))), + TestBlock.createWithTxStatus(payerRefundOrder.timestamp, Seq(payerRefundOrder), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow recipient cannot refund order before recipient submits work") { + forAll(preconditionsAndVEscrowInvalidSubmit) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, _, _, recipientRefundOrder: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS, recipientDepositVSYS, createVEscrow, + recipientDepositToOrder, judgeDepositToOrder))), + TestBlock.createWithTxStatus(recipientRefundOrder.timestamp, Seq(recipientRefundOrder), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVEscrowInvalidTimestamps: Gen[(GenesisTransaction, GenesisTransaction, GenesisTransaction, + RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (judge, ts, fee) <- ContractGenHelper.basicContractTestGen() + recipient <- accountGen + payer <- accountGen + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + genesis <- genesisVEscrowGen(judge, ts) + genesis2 <- genesisVEscrowGen(recipient, ts) + genesis3 <- genesisVEscrowGen(payer, ts) + vEscrow <- vEscrowContractGen() + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + initVEscrowDataStack <- initVEscrowDataStackGen(tokenId.arr, 1000000000L, 1000000000L) + regVEscrow <- registerVEscrowGen(judge, vEscrow, initVEscrowDataStack, "test", fee, ts) + + judgeDepositVSYSData = Seq(judge.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000000000000L)) + recipientDepositVSYSData = Seq(recipient.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000000000000L)) + payerDepositVSYSData = Seq(payer.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000000000000L)) + depositVSYSDataType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + judgeDepositVSYS <- depositVSYSGen(judge, judgeDepositVSYSData, depositVSYSDataType, attach, fee, ts + 1) + recipientDepositVSYS <- depositVSYSGen(recipient, recipientDepositVSYSData, depositVSYSDataType, attach, fee, ts + 2) + payerDepositVSYS <- depositVSYSGen(payer, payerDepositVSYSData, depositVSYSDataType, attach, fee, ts + 3) + + createVEscrow <- createVEscrowGen(payer, regVEscrow.contractId, recipient.toAddress, 1000L, 1000L, 1000L, 10L, 10L, ts + 100L, attach, fee, ts + 5) + + recipientDepositToOrder <- recipientDepositVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 6) + judgeDepositToOrder <- judgeDepositVEscrowGen(judge, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 7) + + recipientSubmitWork <- submitWorkVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 8) + + payerSubmitPenalty <- submitPenaltyVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 9) + + approveWork <- approveWorkVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 10) + + applyToJudge <- applyToJudgeVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 11) + + recipientCollect <- collectVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 12) + + judgeOrder <- judgeVEscrowGen(judge, regVEscrow.contractId, createVEscrow.id.arr, 100, 100, attach, fee, ts + 13) + + payerRefundOrder <- payerRefundVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 14) + + recipientRefundOrder <- recipientRefundVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 15) + + } yield (genesis, genesis2, genesis3, regVEscrow, judgeDepositVSYS, recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder, + judgeDepositToOrder, recipientSubmitWork, payerSubmitPenalty, approveWork, applyToJudge, recipientCollect, judgeOrder, payerRefundOrder, recipientRefundOrder) + + property("v-escrow payer cannot submit penalty before the order expires") { + forAll(preconditionsAndVEscrowInvalidTimestamps) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, + payerDepositVSYS: ExecuteContractFunctionTransaction, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, + judgeDepositToOrder: ExecuteContractFunctionTransaction, recipientSubmitWork: ExecuteContractFunctionTransaction, payerSubmitPenalty: ExecuteContractFunctionTransaction, + _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, + payerDepositVSYS, recipientDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, recipientSubmitWork))), + TestBlock.createWithTxStatus(payerSubmitPenalty.timestamp, Seq(payerSubmitPenalty), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow payer cannot approve the order after it expires") { + forAll(preconditionsAndVEscrowInvalidTimestamps) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, + payerDepositVSYS: ExecuteContractFunctionTransaction, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, + judgeDepositToOrder: ExecuteContractFunctionTransaction, recipientSubmitWork: ExecuteContractFunctionTransaction, _, + approveWork: ExecuteContractFunctionTransaction, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(recipientSubmitWork.timestamp, Seq(regVEscrow, judgeDepositVSYS, + payerDepositVSYS, recipientDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, recipientSubmitWork))), + TestBlock.createWithTxStatus(approveWork.timestamp + 1000000000L, Seq(approveWork), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow payer cannot apply to judge once order expires") { + forAll(preconditionsAndVEscrowInvalidTimestamps) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, + payerDepositVSYS: ExecuteContractFunctionTransaction, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, + judgeDepositToOrder: ExecuteContractFunctionTransaction, recipientSubmitWork: ExecuteContractFunctionTransaction, _, + _, applyToJudge: ExecuteContractFunctionTransaction, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(recipientSubmitWork.timestamp, Seq(regVEscrow, judgeDepositVSYS, + payerDepositVSYS, recipientDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, recipientSubmitWork))), + TestBlock.createWithTxStatus(applyToJudge.timestamp + 1000000000L, Seq(applyToJudge), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow recipient cannot collect before order expires") { + forAll(preconditionsAndVEscrowInvalidTimestamps) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, + payerDepositVSYS: ExecuteContractFunctionTransaction, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, + judgeDepositToOrder: ExecuteContractFunctionTransaction, recipientSubmitWork: ExecuteContractFunctionTransaction, _, + _, _, recipientCollect: ExecuteContractFunctionTransaction, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(recipientSubmitWork.timestamp, Seq(regVEscrow, judgeDepositVSYS, + payerDepositVSYS, recipientDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, recipientSubmitWork))), + TestBlock.createWithTxStatus(recipientCollect.timestamp, Seq(recipientCollect), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow judge cannot judge order after it expires") { + forAll(preconditionsAndVEscrowInvalidTimestamps) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, + payerDepositVSYS: ExecuteContractFunctionTransaction, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, + judgeDepositToOrder: ExecuteContractFunctionTransaction, recipientSubmitWork: ExecuteContractFunctionTransaction, _, + _, _, _, judgeOrder: ExecuteContractFunctionTransaction, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(recipientSubmitWork.timestamp, Seq(regVEscrow, judgeDepositVSYS, + payerDepositVSYS, recipientDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, recipientSubmitWork))), + TestBlock.createWithTxStatus(judgeOrder.timestamp, Seq(judgeOrder), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow payer cannot refund order before it expires") { + forAll(preconditionsAndVEscrowInvalidTimestamps) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, + payerDepositVSYS: ExecuteContractFunctionTransaction, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, + judgeDepositToOrder: ExecuteContractFunctionTransaction, recipientSubmitWork: ExecuteContractFunctionTransaction, _, + _, _, _, _, payerRefundOrder: ExecuteContractFunctionTransaction, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(recipientSubmitWork.timestamp, Seq(regVEscrow, judgeDepositVSYS, + payerDepositVSYS, recipientDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, recipientSubmitWork))), + TestBlock.createWithTxStatus(payerRefundOrder.timestamp, Seq(payerRefundOrder), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + property("v-escrow recipient cannot refund order before it expires") { + forAll(preconditionsAndVEscrowInvalidTimestamps) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, + regVEscrow: RegisterContractTransaction, judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, + payerDepositVSYS: ExecuteContractFunctionTransaction, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, + judgeDepositToOrder: ExecuteContractFunctionTransaction, recipientSubmitWork: ExecuteContractFunctionTransaction, _, + _, _, _, _, _, recipientRefundOrder: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(recipientSubmitWork.timestamp, Seq(regVEscrow, judgeDepositVSYS, + payerDepositVSYS, recipientDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, recipientSubmitWork))), + TestBlock.createWithTxStatus(recipientRefundOrder.timestamp, Seq(recipientRefundOrder), TransactionStatus.Failed)) { (blockDiff, _) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/state/contract/vescrow/ExecuteVEscrowContractValidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vescrow/ExecuteVEscrowContractValidDiffTest.scala new file mode 100644 index 000000000..5285ff0f4 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vescrow/ExecuteVEscrowContractValidDiffTest.scala @@ -0,0 +1,659 @@ +package vsys.blockchain.state.contract.vescrow + +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract._ +import vsys.blockchain.contract.token.SystemContractGen +import vsys.blockchain.contract.vescrow.{VEscrowContractGen, VEscrowFunctionHelperGen} +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.contract._ +import vsys.blockchain.state._ +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import com.google.common.primitives.{Bytes, Ints, Longs} +import vsys.account.ContractAccount +import vsys.account.ContractAccount.tokenIdFromBytes + +class ExecuteVEscrowContractValidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with SystemContractGen + with VEscrowContractGen + with VEscrowFunctionHelperGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val preconditionsAndExecuteContractEscrow: Gen[(GenesisTransaction, GenesisTransaction, GenesisTransaction, + RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (judge, ts, fee) <- ContractGenHelper.basicContractTestGen() + recipient <- accountGen + payer <- accountGen + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + genesis <- genesisVEscrowGen(judge, ts) + genesis2 <- genesisVEscrowGen(recipient, ts) + genesis3 <- genesisVEscrowGen(payer, ts) + vEscrow <- vEscrowContractGen() + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + initVEscrowDataStack <- initVEscrowDataStackGen(tokenId.arr, 1000000000L, 1000000000L) + regVEscrow <- registerVEscrowGen(judge, vEscrow, initVEscrowDataStack, "test", fee, ts) + judgeDepositVSYSData = Seq(judge.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000L)) + recipientDepositVSYSData = Seq(recipient.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000L)) + payerDepositVSYSData = Seq(payer.toAddress.bytes.arr, regVEscrow.contractId.bytes.arr, Longs.toByteArray(1000L)) + depositVSYSDataType = Seq(DataType.Address, DataType.ContractAccount, DataType.Amount) + judgeDepositVSYS <- depositVSYSGen(judge, judgeDepositVSYSData, depositVSYSDataType, attach, fee, ts + 1) + recipientDepositVSYS <- depositVSYSGen(recipient, recipientDepositVSYSData, depositVSYSDataType, attach, fee, ts + 2) + payerDepositVSYS <- depositVSYSGen(payer, payerDepositVSYSData, depositVSYSDataType, attach, fee, ts + 3) + withdrawVSYSData = Seq(regVEscrow.contractId.bytes.arr, judge.toAddress.bytes.arr, Longs.toByteArray(1000L)) + withdrawVSYSDataType = Seq(DataType.ContractAccount, DataType.Address, DataType.Amount) + judgeWithdrawVSYS <- withdrawVSYSGen(judge, withdrawVSYSData, withdrawVSYSDataType, attach, fee, ts + 4) + createVEscrow <- createVEscrowGen(payer, regVEscrow.contractId, recipient.toAddress, 1000L, 1000L, 1000L, 10L, 10L, ts + 100L, attach, fee, ts + 5) + recipientDepositToOrder <- recipientDepositVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 6) + judgeDepositToOrder <- judgeDepositVEscrowGen(judge, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 7) + payerCancelOrder <- payerCancelVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 8) + recipientCancelOrder <- recipientCancelVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 10) + judgeCancelOrder <- judgeCancelVEscrowGen(judge, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 11) + submitWork <- submitWorkVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 12) + approveWork <- approveWorkVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 13) + applyToJudge <- applyToJudgeVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach,fee, ts + 14) + judgeWork <- judgeVEscrowGen(judge, regVEscrow.contractId, createVEscrow.id.arr,0L, 1990L, attach, fee, ts + 15) + submitPenalty <- submitPenaltyVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 16) + payerRefund <- payerRefundVEscrowGen(payer, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 17) + recipientRefund <- recipientRefundVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 18) + recipientCollect <- collectVEscrowGen(recipient, regVEscrow.contractId, createVEscrow.id.arr, attach, fee, ts + 19) + } yield (genesis, genesis2, genesis3, regVEscrow, judgeDepositVSYS, recipientDepositVSYS, payerDepositVSYS, judgeWithdrawVSYS, + createVEscrow, recipientDepositToOrder, judgeDepositToOrder, payerCancelOrder, recipientCancelOrder, judgeCancelOrder, submitWork, approveWork, + applyToJudge, judgeWork, submitPenalty, payerRefund, recipientRefund, recipientCollect) + + property("v-escrow able to deposit and withdraw VSYS") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, _, _, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, _, _, judgeWithdrawVSYS: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _, + _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS))), + TestBlock.createWithTxStatus(judgeWithdrawVSYS.timestamp, Seq(judgeWithdrawVSYS), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateVarKeys = getEscrowContractStateVarKeys(regVEscrow.contractId.bytes.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 0L + + newState.contractInfo(contractStateVarKeys.head) shouldEqual Some(DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get) + newState.contractInfo(contractStateVarKeys(1)) shouldEqual Some(DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get) + newState.contractInfo(contractStateVarKeys(2)) shouldEqual Some(DataEntry.create(tokenId.arr, DataType.TokenId).right.get) + newState.contractInfo(contractStateVarKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000000000L), DataType.Timestamp).right.get) + newState.contractInfo(contractStateVarKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000000000L), DataType.Timestamp).right.get) + } + } + } + + property("v-escrow able to register and create") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, _, payerDepositVSYS: ExecuteContractFunctionTransaction, _, createVEscrow: ExecuteContractFunctionTransaction, _, _, _, _, _, _, + _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, payerDepositVSYS))), + TestBlock.createWithTxStatus(createVEscrow.timestamp, Seq(createVEscrow), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 1000L + newState.contractNumInfo(payerBalanceKey) shouldEqual 0L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(9)) shouldEqual Some(DataEntry.create(Longs.toByteArray(genesis.timestamp + 100), DataType.Timestamp).right.get) // orderExpirationTime + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(0L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(0L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow able to deposit to order") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, _, _, _, _, + _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow))), + TestBlock.createWithTxStatus(recipientDepositToOrder.timestamp, Seq(recipientDepositToOrder, judgeDepositToOrder), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 0L + newState.contractNumInfo(payerBalanceKey) shouldEqual 0L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 0L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(9)) shouldEqual Some(DataEntry.create(Longs.toByteArray(genesis.timestamp + 100), DataType.Timestamp).right.get) // orderExpirationTime + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow payer able to cancel order") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, _, judgeDepositToOrder: ExecuteContractFunctionTransaction, payerCancelOrder: ExecuteContractFunctionTransaction, _, _, _, _, + _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, judgeDepositToOrder))), + TestBlock.createWithTxStatus(payerCancelOrder.timestamp, Seq(payerCancelOrder), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 1000L + newState.contractNumInfo(payerBalanceKey) shouldEqual 1000L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 1000L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(9)) shouldEqual Some(DataEntry.create(Longs.toByteArray(genesis.timestamp + 100), DataType.Timestamp).right.get) // orderExpirationTime + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(0L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow recipient able to cancel order") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, _, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, recipientCancelOrder: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositVSYS.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, judgeDepositToOrder))), + TestBlock.createWithTxStatus(recipientCancelOrder.timestamp, Seq(recipientCancelOrder), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 1000L + newState.contractNumInfo(payerBalanceKey) shouldEqual 1000L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 1000L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(9)) shouldEqual Some(DataEntry.create(Longs.toByteArray(genesis.timestamp + 100), DataType.Timestamp).right.get) // orderExpirationTime + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(0L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow judge able to cancel order") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, _, + _, _, judgeCancelOrder: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(recipientDepositToOrder.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder))), + TestBlock.createWithTxStatus(judgeCancelOrder.timestamp, Seq(judgeCancelOrder), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 1000L + newState.contractNumInfo(payerBalanceKey) shouldEqual 1000L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 1000L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(9)) shouldEqual Some(DataEntry.create(Longs.toByteArray(genesis.timestamp + 100), DataType.Timestamp).right.get) // orderExpirationTime + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(0L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow recipient able to submit work") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, submitWork: ExecuteContractFunctionTransaction, _, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(judgeDepositToOrder.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder))), + TestBlock.createWithTxStatus(submitWork.timestamp, Seq(submitWork), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 0L + newState.contractNumInfo(payerBalanceKey) shouldEqual 0L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 0L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow payer able to approve work") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, submitWork: ExecuteContractFunctionTransaction, approveWork: ExecuteContractFunctionTransaction, _, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(submitWork.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, submitWork))), + TestBlock.createWithTxStatus(approveWork.timestamp, Seq(approveWork), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 1010L + newState.contractNumInfo(payerBalanceKey) shouldEqual 0L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 1990L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow payer able to apply to judge") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, submitWork: ExecuteContractFunctionTransaction, _, applyToJudge: ExecuteContractFunctionTransaction, _, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(submitWork.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, submitWork))), + TestBlock.createWithTxStatus(applyToJudge.timestamp, Seq(applyToJudge), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 0L + newState.contractNumInfo(payerBalanceKey) shouldEqual 0L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 0L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow judge able to judge work") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, submitWork: ExecuteContractFunctionTransaction, _, applyToJudge: ExecuteContractFunctionTransaction, judgeWork: ExecuteContractFunctionTransaction, _, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(recipientDepositToOrder.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, submitWork, applyToJudge))), + TestBlock.createWithTxStatus(judgeWork.timestamp, Seq(judgeWork), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 1010L + newState.contractNumInfo(payerBalanceKey) shouldEqual 0 + newState.contractNumInfo(recipientBalanceKey) shouldEqual 1990L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow payer able to submit penalty") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, _, _, _, _, submitPenalty: ExecuteContractFunctionTransaction, _, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(recipientDepositToOrder.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder))), + TestBlock.createWithTxStatus(submitPenalty.timestamp + 1000000000L, Seq(submitPenalty), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 1010L + newState.contractNumInfo(payerBalanceKey) shouldEqual 1990L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 0L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow payer able to refund") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, submitWork: ExecuteContractFunctionTransaction, _, applyToJudge: ExecuteContractFunctionTransaction, _, _, payerRefund: ExecuteContractFunctionTransaction, _, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(applyToJudge.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, submitWork, applyToJudge))), + TestBlock.createWithTxStatus(payerRefund.timestamp + 1000000000L, Seq(payerRefund), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 0L + newState.contractNumInfo(payerBalanceKey) shouldEqual 10L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 2990L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow recipient able to refund") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, submitWork: ExecuteContractFunctionTransaction, _, applyToJudge: ExecuteContractFunctionTransaction, _, _, _, + recipientRefund: ExecuteContractFunctionTransaction, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(applyToJudge.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, submitWork, applyToJudge))), + TestBlock.createWithTxStatus(recipientRefund.timestamp + 1000000000L, Seq(recipientRefund), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 0L + newState.contractNumInfo(payerBalanceKey) shouldEqual 10L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 2990L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } + + property("v-escrow recipient able to collect") { + forAll(preconditionsAndExecuteContractEscrow) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, genesis3: GenesisTransaction, regVEscrow: RegisterContractTransaction, + judgeDepositVSYS: ExecuteContractFunctionTransaction, recipientDepositVSYS: ExecuteContractFunctionTransaction, payerDepositVSYS: ExecuteContractFunctionTransaction, + _, createVEscrow: ExecuteContractFunctionTransaction, recipientDepositToOrder: ExecuteContractFunctionTransaction, judgeDepositToOrder: ExecuteContractFunctionTransaction, + _, _, _, submitWork: ExecuteContractFunctionTransaction, _, _, _, _, _, _, recipientCollect: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2, genesis3)), TestBlock.create(recipientDepositToOrder.timestamp, Seq(regVEscrow, judgeDepositVSYS, + recipientDepositVSYS, payerDepositVSYS, createVEscrow, recipientDepositToOrder, judgeDepositToOrder, submitWork))), + TestBlock.createWithTxStatus(recipientCollect.timestamp + 1000000000L, Seq(recipientCollect), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val judge = regVEscrow.proofs.firstCurveProof.explicitGet().publicKey + val payer = payerDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + val recipient = recipientDepositVSYS.proofs.firstCurveProof.explicitGet().publicKey + + val judgeBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(judge.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val payerBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(payer.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val recipientBalanceKey = ByteStr(Bytes.concat(regVEscrow.contractId.bytes.arr, Array(0.toByte), DataEntry.create(recipient.toAddress.bytes.arr, DataType.Address).right.get.bytes)) + val contractStateMapKeys = getEscrowContractStateMapKeys(regVEscrow.contractId.bytes.arr, createVEscrow.id.arr) + + newState.contractNumInfo(judgeBalanceKey) shouldEqual 1010L + newState.contractNumInfo(payerBalanceKey) shouldEqual 0L + newState.contractNumInfo(recipientBalanceKey) shouldEqual 1990L + + newState.contractInfo(contractStateMapKeys.head) shouldEqual Some(DataEntry.create(genesis3.recipient.bytes.arr, DataType.Address).right.get) // orderPayer + newState.contractInfo(contractStateMapKeys(1)) shouldEqual Some(DataEntry.create(genesis2.recipient.bytes.arr, DataType.Address).right.get) // orderRecipient + newState.contractInfo(contractStateMapKeys(2)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderAmount + newState.contractInfo(contractStateMapKeys(3)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRecipientDeposit + newState.contractInfo(contractStateMapKeys(4)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeDeposit + newState.contractInfo(contractStateMapKeys(5)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderFee + newState.contractInfo(contractStateMapKeys(6)) shouldEqual Some(DataEntry.create(Longs.toByteArray(990L), DataType.Amount).right.get) // orderRecipientAmount + newState.contractInfo(contractStateMapKeys(7)) shouldEqual Some(DataEntry.create(Longs.toByteArray(10L), DataType.Amount).right.get) // orderRefund + newState.contractInfo(contractStateMapKeys(8)) shouldEqual Some(DataEntry.create(Longs.toByteArray(2990L), DataType.Amount).right.get) // orderRecipientRefund + newState.contractInfo(contractStateMapKeys(10)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderStatus + newState.contractInfo(contractStateMapKeys(11)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderRepDepositStatus + newState.contractInfo(contractStateMapKeys(12)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderJudgeDepositStatus + newState.contractInfo(contractStateMapKeys(13)) shouldEqual Some(DataEntry.create(Array(1.toByte), DataType.Boolean).right.get) // orderSubmitStatus + newState.contractInfo(contractStateMapKeys(14)) shouldEqual Some(DataEntry.create(Array(0.toByte), DataType.Boolean).right.get) // orderJudgeStatus + newState.contractInfo(contractStateMapKeys(15)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderRepLockedAmount + newState.contractInfo(contractStateMapKeys(16)) shouldEqual Some(DataEntry.create(Longs.toByteArray(1000L), DataType.Amount).right.get) // orderJudgeLockedAmount + } + } + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/state/contract/vescrow/RegisterVEscrowContractDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vescrow/RegisterVEscrowContractDiffTest.scala new file mode 100644 index 000000000..a417f55b3 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vescrow/RegisterVEscrowContractDiffTest.scala @@ -0,0 +1,77 @@ +package vsys.blockchain.state.contract.vescrow + +import cats.Monoid +import com.google.common.primitives.{Bytes, Ints} +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract._ +import vsys.blockchain.contract.vescrow.VEscrowContractGen +import vsys.blockchain.state.diffs.assertDiffAndState +import vsys.blockchain.state.{ByteStr, Portfolio, _} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen} + +class RegisterVEscrowContractDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with VEscrowContractGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val languageCode: String = "vdds" + val languageVersion: Int = 2 + + val preconditionsAndBuildVEscrowContract: Gen[(Array[Byte], Array[Byte], Seq[Array[Byte]], + Seq[Array[Byte]], Seq[Array[Byte]], Seq[Array[Byte]], Seq[Array[Byte]])] = for { + langCode <- ContractGenHelper.languageCodeGen(languageCode) + langVer <- ContractGenHelper.languageVersionGen(languageVersion) + init <- Gen.const(ContractVEscrow.contract.trigger) + descriptor <- Gen.const(ContractVEscrow.contract.descriptor) + stateVar <- Gen.const(ContractVEscrow.contract.stateVar) + stateMap <- Gen.const(ContractVEscrow.contract.stateMap) + textual <- Gen.const(ContractVEscrow.contract.textual) + } yield (langCode, langVer, init, descriptor, stateVar, stateMap, textual) + + property("ensure v-escrow can be built as a valid contract") { + forAll(preconditionsAndBuildVEscrowContract) { case (langCode, langVer, init, descriptor, stateVar, stateMap, textual) => + Contract.buildContract(langCode, langVer, init, descriptor, stateVar, stateMap, textual) shouldBe an[Right[_,_]] + } + } + + val validContract: Gen[Contract] = vEscrowContractGen() + val preconditionsAndRegContractTest: Gen[(GenesisTransaction, RegisterContractTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + genesis <- genesisVEscrowGen(master, ts) + contract <- validContract + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + data: Seq[DataEntry] <- initVEscrowDataStackGen(tokenId.arr, 1, 1) + description <- validDescStringGen + create <- registerVEscrowGen(master, contract, data, description, fee, ts + 1) + } yield (genesis, create) + + property("register v-escrow contract transaction doesn't break invariant") { + forAll(preconditionsAndRegContractTest) { case (genesis, reg: RegisterContractTransaction) => + assertDiffAndState(Seq(TestBlock.create(Seq(genesis))), TestBlock.create(Seq(reg))) { (blockDiff, newState) => + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + totalPortfolioDiff.balance shouldBe -reg.transactionFee + totalPortfolioDiff.effectiveBalance shouldBe -reg.transactionFee + val master = reg.proofs.firstCurveProof.explicitGet().publicKey + val contractId = reg.contractId.bytes + val makerKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte))) + + val (_, masterTxs) = newState.accountTransactionIds(master, 2, 0) + masterTxs.size shouldBe 2 // genesis, reg + newState.contractTokens(contractId) shouldBe 0 + newState.contractContent(contractId) shouldEqual Some((2, reg.id, ContractVEscrow.contract)) + + newState.contractInfo(makerKey) shouldEqual Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + } + } + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/state/contract/voption/ExecuteVOptionInvalidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/voption/ExecuteVOptionInvalidDiffTest.scala new file mode 100644 index 000000000..858975245 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/voption/ExecuteVOptionInvalidDiffTest.scala @@ -0,0 +1,485 @@ +package vsys.blockchain.state.contract.voption + +import com.google.common.primitives.Ints +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.ContractGenHelper.basicContractTestGen +import vsys.blockchain.contract.DataEntry +import vsys.blockchain.contract.token.{SystemContractGen, TokenContractGen} +import vsys.blockchain.contract.voption.{VOptionContractGen, VOptionFunctionHelperGen} +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction => EC, RegisterContractTransaction => RC} +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import vsys.blockchain.state._ + +class ExecuteVOptionInvalidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with TokenContractGen + with SystemContractGen + with VOptionContractGen + with VOptionFunctionHelperGen { + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val preconditionsAndVOptionDepositAndWithdrawBaseTargetTokens: Gen[(GenesisTransaction, RC, RC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, _, _, _, regVOptionContract, + issueBaseToken, _, _, _, depositBaseToken, _, _, _, fee, ts, attach) <- createBaseTargetOptionProofTokenAndInitVOption(1000L, 1L, 1000L, 1000L, 1L, 1000L, + 100L, 1L, 100L, 1L, 100L, 100L, 1000L, 1000L) + withdrawBaseToken <- withdrawToken(master, regBaseTokenContract.contractId, regVOptionContract.contractId.bytes.arr, master.toAddress.bytes.arr, 100L, fee, ts + 13) + withdrawInvalidBaseToken <- withdrawToken(master, regBaseTokenContract.contractId, regVOptionContract.contractId.bytes.arr, master.toAddress.bytes.arr, 2000L, fee, ts + 13) + } yield (genesis, regBaseTokenContract, regVOptionContract, issueBaseToken, depositBaseToken, withdrawBaseToken, withdrawInvalidBaseToken) + + // withdraw base tokens + property("withdraw base tokens more than depositing in voption contract") { + forAll(preconditionsAndVOptionDepositAndWithdrawBaseTargetTokens) { case (genesis: GenesisTransaction, regBaseTokenContract: RC, regVOptionContract: RC, issueBaseToken: EC, + depositBaseToken: EC, withdrawBaseToken: EC, withdrawInvalidBaseToken: EC) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositBaseToken.timestamp, Seq(regBaseTokenContract, regVOptionContract, issueBaseToken, depositBaseToken))), + TestBlock.createWithTxStatus(withdrawBaseToken.timestamp, Seq(withdrawBaseToken), TransactionStatus.Success)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositBaseToken.timestamp, Seq(regBaseTokenContract, regVOptionContract, issueBaseToken, depositBaseToken))), + TestBlock.createWithTxStatus(withdrawInvalidBaseToken.timestamp, Seq(withdrawInvalidBaseToken), TransactionStatus.ContractTokenBalanceInsufficient)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractTokenBalanceInsufficient + } + } + } + + val preconditionsAndVOptionActivate: Gen[(GenesisTransaction, GenesisTransaction, RC, RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, genesis2, master, user, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- createBaseTargetOptionProofTokenAndInitVOption(1000, 1, 1000, 1000, 1, 1000, + 1000, 1,1000, 1, 1000, 1000, 1000, 1000) + activate <- activateVOptionGen(master, regVOptionContract.contractId, 1000, 1,1, attach, fee + 10000000000L, ts+13) + activateInvalid <- activateVOptionGen(master, regVOptionContract.contractId, 10000, 1,1, attach, fee + 10000000000L, ts+13) + activateInvalid2 <- activateVOptionGen(user, regVOptionContract.contractId, 1000, 1,1, attach, fee + 10000000000L, ts+13) + activateInvalid3 <- activateVOptionGen(master, regVOptionContract.contractId, 1000, 1,0, attach, fee + 10000000000L, ts+13) + activateInvalid4 <- activateVOptionGen(master, regVOptionContract.contractId, 1000, 0,1, attach, fee + 10000000000L, ts+13) + activateInvalid5 <- activateVOptionGen(master, regVOptionContract.contractId, 0, 1,1, attach, fee + 10000000000L, ts+13) + } yield (genesis, genesis2, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, activate, activateInvalid, activateInvalid2, activateInvalid3, activateInvalid4, activateInvalid5) + + property("unable to activate voption contract") { + forAll(preconditionsAndVOptionActivate) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regBaseTokenContract: RC, + regTargetTokenContract: RC, regOptionTokenContract: RC, regProofTokenContract: RC, + regVOptionContract: RC, issueBaseToken: EC, issueTargetToken: EC, + issueOptionToken: EC, issueProofToken: EC, depositBaseToken: EC, depositTargetToken: EC, depositOptionToken: EC, depositProofToken: EC, activate: EC, activateInvalid: EC, activateInvalid2: EC, activateInvalid3: EC, activateInvalid4: EC, activateInvalid5: EC) => + + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositProofToken.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken))), + TestBlock.createWithTxStatus(activate.timestamp, Seq(activate), TransactionStatus.Success)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // activate amount more than depositing in voption contract + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositProofToken.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken))), + TestBlock.createWithTxStatus(activateInvalid.timestamp, Seq(activateInvalid), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // activated by another user + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositProofToken.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken))), + TestBlock.createWithTxStatus(activateInvalid2.timestamp, Seq(activateInvalid2), TransactionStatus.ContractInvalidCaller)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractInvalidCaller + } + // activated with price unit zero + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositProofToken.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken))), + TestBlock.createWithTxStatus(activateInvalid3.timestamp, Seq(activateInvalid3), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // activated with price zero + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositProofToken.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken))), + TestBlock.createWithTxStatus(activateInvalid4.timestamp, Seq(activateInvalid4), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // activated with maxIssueNum zero + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositProofToken.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken))), + TestBlock.createWithTxStatus(activateInvalid5.timestamp, Seq(activateInvalid5), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + } + } + + val preconditionsAndVOptionMint: Gen[(GenesisTransaction, RC, RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- createBaseTargetOptionProofTokenAndInitVOption(1000, 1, 1000, 1000, 1, 1000, + 100, 1,100, 1, 1000, 1000, 100, 100) + + activate <- activateVOptionGen(master, regVOptionContract.contractId, 100, 1,1, attach, fee + 10000000000L, ts+13) + mint <- mintVOptionGen(master, regVOptionContract.contractId, 10, attach, fee + 10000000000L, ts+14) + mintInvalid <- mintVOptionGen(master, regVOptionContract.contractId, 1000, attach, fee + 10000000000L, ts+14) + mintInvalid2 <- mintVOptionGen(master, regVOptionContract.contractId, 10, attach, fee + 10000000000L, ts+101) + mintInvalid3 <- mintVOptionGen(master, regVOptionContract.contractId, 10, attach, fee + 10000000000L, ts+201) + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, activate, mint, mintInvalid, mintInvalid2, mintInvalid3) + + property("unable to mint voption") { + forAll(preconditionsAndVOptionMint) { case (genesis: GenesisTransaction, regBaseTokenContract: RC, + regTargetTokenContract: RC, regOptionTokenContract: RC, regProofTokenContract: RC, + regVOptionContract: RC, issueBaseToken: EC, issueTargetToken: EC, issueOptionToken: EC, + issueProofToken: EC, depositBaseToken: EC, depositTargetToken: EC, depositOptionToken: EC, depositProofToken: EC, + activate: EC, mint: EC, mintInvalid: EC, mintInvalid2: EC, mintInvalid3: EC) => + + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(activate.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate))), + TestBlock.createWithTxStatus(mint.timestamp, Seq(mint), TransactionStatus.Success)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + + // mint before activate + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositProofToken.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken))), + TestBlock.createWithTxStatus(mint.timestamp, Seq(mint), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // mint voption is greater than maxIssueNum + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(activate.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate))), + TestBlock.createWithTxStatus(mintInvalid.timestamp, Seq(mintInvalid), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // mint after executeTime + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(activate.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate))), + TestBlock.createWithTxStatus(mintInvalid2.timestamp, Seq(mintInvalid2), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // mint after executeDeadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(activate.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate))), + TestBlock.createWithTxStatus(mintInvalid3.timestamp, Seq(mintInvalid3), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVOptionUnlock: Gen[(GenesisTransaction, RC, RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- createBaseTargetOptionProofTokenAndInitVOption(1000, 1, 1000, 1000, 1, 1000, + 100, 1,100, 1, 1000, 1000, 100, 100) + + activate <- activateVOptionGen(master, regVOptionContract.contractId, 100, 1,1, attach, fee + 10000000000L, ts+13) + mint <- mintVOptionGen(master, regVOptionContract.contractId, 10, attach, fee + 10000000000L, ts+14) + unlock <- unlockVOptionGen(master, regVOptionContract.contractId, 10, attach, fee + 10000000000L, ts+14) + unlockInvalid <- unlockVOptionGen(master, regVOptionContract.contractId, 100, attach, fee + 10000000000L, ts+14) + unlockInvalid2 <- unlockVOptionGen(master, regVOptionContract.contractId, 10, attach, fee + 10000000000L, ts+201) + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, activate, mint, unlock, unlockInvalid, unlockInvalid2) + + property("unable to unlock") { + forAll(preconditionsAndVOptionUnlock) { case (genesis: GenesisTransaction, regBaseTokenContract: RC, + regTargetTokenContract: RC, regOptionTokenContract: RC, regProofTokenContract: RC, regVOptionContract: RC, issueBaseToken: EC, issueTargetToken: EC, + issueOptionToken: EC, issueProofToken: EC, depositBaseToken: EC, depositTargetToken: EC, depositOptionToken: EC, depositProofToken: EC, activate: EC, mint: EC, unlock: EC, + unlockInvalid: EC, unlockInvalid2: EC) => + + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint))), + TestBlock.createWithTxStatus(unlock.timestamp, Seq(unlock), TransactionStatus.Success)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + + // unlock before activate + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositProofToken.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken))), + TestBlock.createWithTxStatus(unlock.timestamp, Seq(unlock), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // unlock before mint + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(activate.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate))), + TestBlock.createWithTxStatus(unlock.timestamp, Seq(unlock), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + + // unlock voption greater than mint amount + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint))), + TestBlock.createWithTxStatus(unlockInvalid.timestamp, Seq(unlockInvalid), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // unlock voption after executedeadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint))), + TestBlock.createWithTxStatus(unlockInvalid2.timestamp, Seq(unlockInvalid2), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVOptionExecute: Gen[(GenesisTransaction, RC, RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- createBaseTargetOptionProofTokenAndInitVOption(1000L, 1L, 1000L, 1000L, 1L, 1000L, + 1000L, 1L,1000L, 1L, 1000L, 1000L, 1000, 1000) + + activate <- activateVOptionGen(master, regVOptionContract.contractId, 1000L, 10L,1L, attach, fee + 10000000000L, ts+13) + mint <- mintVOptionGen(master, regVOptionContract.contractId, 100L, attach, fee + 10000000000L, ts+14) + + execute <- executeVOptionGen(master, regVOptionContract.contractId, 10L, attach, fee + 10000000000L, ts+101) + executeInvalid <- executeVOptionGen(master, regVOptionContract.contractId, 1000L, attach, fee + 10000000000L, ts+101) + executeInvalid2 <- executeVOptionGen(master, regVOptionContract.contractId, 1000L, attach, fee + 10000000000L, ts+99) + executeInvalid3 <- executeVOptionGen(master, regVOptionContract.contractId, 1000L, attach, fee + 10000000000L, ts+201) + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, activate, mint, execute, executeInvalid, executeInvalid2, executeInvalid3) + + property("unable to execute voption") { + forAll(preconditionsAndVOptionExecute) { case (genesis: GenesisTransaction, regBaseTokenContract: RC, + regTargetTokenContract: RC, regOptionTokenContract: RC, regProofTokenContract: RC, + regVOptionContract: RC, issueBaseToken: EC, issueTargetToken: EC, issueOptionToken: EC, issueProofToken: EC, depositBaseToken: EC, depositTargetToken: EC, + depositOptionToken: EC, depositProofToken: EC, activate: EC, mint: EC, execute: EC, executeInvalid: EC, executeInvalid2: EC, executeInvalid3: EC) => + + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint))), + TestBlock.createWithTxStatus(execute.timestamp, Seq(execute), TransactionStatus.Success)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Success + } + + // execute voption before activate + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositProofToken.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken))), + TestBlock.createWithTxStatus(execute.timestamp, Seq(execute), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // execute before mint + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(activate.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate))), + TestBlock.createWithTxStatus(execute.timestamp, Seq(execute), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + + // execute voption more than target token balance + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint))), + TestBlock.createWithTxStatus(executeInvalid.timestamp, Seq(executeInvalid), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // execute voption before execute time + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint))), + TestBlock.createWithTxStatus(executeInvalid2.timestamp, Seq(executeInvalid2), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // execute voption after execute deadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint))), + TestBlock.createWithTxStatus(executeInvalid3.timestamp, Seq(executeInvalid3), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + val preconditionsAndVOptionCollect: Gen[(GenesisTransaction, RC, RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- createBaseTargetOptionProofTokenAndInitVOption(1000L, 1L, 1000L, 1000L, 1L, 1000L, + 1000L, 1L,1000L, 1L, 1000L, 1000L, 1000, 1000) + + activate <- activateVOptionGen(master, regVOptionContract.contractId, 1000L, 10L,1L, attach, fee + 10000000000L, ts+13) + mint <- mintVOptionGen(master, regVOptionContract.contractId, 100L, attach, fee + 10000000000L, ts+14) + unlock <- unlockVOptionGen(master, regVOptionContract.contractId, 100, attach, fee + 10000000000L, ts+15) + + collect <- collectVOptionGen(master, regVOptionContract.contractId, 100L, attach, fee + 10000000000L, ts+100000000) + collectInvalid <- collectVOptionGen(master, regVOptionContract.contractId, 1000L, attach, fee + 10000000000L, ts+100000000) + collectInvalid2 <- collectVOptionGen(master, regVOptionContract.contractId, 100L, attach, fee + 10000000000L, ts+99) + collectInvalid3 <- collectVOptionGen(master, regVOptionContract.contractId, 100L, attach, fee + 10000000000L, ts+199) + collectInvalid4 <- collectVOptionGen(master, regVOptionContract.contractId, 0L, attach, fee + 10000000000L, ts+100000000) + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, + activate, mint, unlock, collect, collectInvalid, collectInvalid2, collectInvalid3, collectInvalid4) + + property("unable to collect voption") { + forAll(preconditionsAndVOptionCollect) { case (genesis: GenesisTransaction, regBaseTokenContract: RC, + regTargetTokenContract: RC, regOptionTokenContract: RC, regProofTokenContract: RC, + regVOptionContract: RC, issueBaseToken: EC, issueTargetToken: EC, + issueOptionToken: EC, issueProofToken: EC, depositBaseToken: EC, depositTargetToken: EC, depositOptionToken: EC, depositProofToken: EC, + activate: EC, mint: EC, unlock: EC, collect: EC, collectInvalid: EC, collectInvalid2: EC, collectInvalid3: EC, collectInvalid4: EC) => + + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint)), + TestBlock.create(collect.timestamp, Seq())), + TestBlock.createWithTxStatus(collect.timestamp+1, Seq(collect), TransactionStatus.Success)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Success + } + // collect voption before activate + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositProofToken.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken)), + TestBlock.create(collect.timestamp, Seq())), + TestBlock.createWithTxStatus(collect.timestamp+1, Seq(collect), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // collect voption before mint + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(activate.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate)), + TestBlock.create(collect.timestamp, Seq())), + TestBlock.createWithTxStatus(collect.timestamp+1, Seq(collect), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // collect voption after unlock + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(unlock.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint, unlock)), + TestBlock.create(collect.timestamp, Seq())), + TestBlock.createWithTxStatus(collect.timestamp+1, Seq(collect), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // collect voption more than mint amount + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint)), + TestBlock.create(collectInvalid.timestamp, Seq())), + TestBlock.createWithTxStatus(collectInvalid.timestamp+1, Seq(collectInvalid), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // collect voption before execute time + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint)), + TestBlock.create(collectInvalid2.timestamp, Seq())), + TestBlock.createWithTxStatus(collectInvalid2.timestamp+1, Seq(collectInvalid2), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // collect voption after execute deadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint)), + TestBlock.create(collectInvalid3.timestamp, Seq())), + TestBlock.createWithTxStatus(collectInvalid3.timestamp+1, Seq(collectInvalid3), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + + // collect zero amount after collecting all amount + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(mint.timestamp, Seq(regBaseTokenContract, regTargetTokenContract, + regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, + depositOptionToken, depositProofToken, activate, mint)), + TestBlock.create(collect.timestamp, Seq()), + TestBlock.create(collect.timestamp, Seq(collect)), + TestBlock.create(collectInvalid4.timestamp, Seq())), + TestBlock.createWithTxStatus(collectInvalid4.timestamp+1, Seq(collectInvalid4), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVOptionDepositSameInputTokens: Gen[(GenesisTransaction, RC, RC, EC, EC)] = for { + (master, ts, fee) <- basicContractTestGen() + genesis <- genesisVOptionGen(master, ts) + user <- accountGen + genesis2 <- genesisVOptionGen(user, ts) + vOptionContract <- vOptionContractGen() + // register base token + regBaseTokenContract <- registerToken(master, 1000000000, 1000, "init", fee + 10000000000L, ts) + baseTokenContractId = regBaseTokenContract.contractId + baseTokenId = tokenIdFromBytes(baseTokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + // issue base token + attach <- genBoundedString(2, EC.MaxDescriptionSize) + issueBaseToken <- issueToken(master, baseTokenContractId, 1000000000, fee, ts + 5) + + // register VOption contract with same token Ids + description <- validDescStringGen + initVOptionDataStack: Seq[DataEntry] <- initVOptionDataStackGen(baseTokenId.arr, baseTokenId.arr, baseTokenId.arr, baseTokenId.arr, ts + 100, ts + 200) + regVOptionContract <- registerVOptionGen(master, vOptionContract, initVOptionDataStack, description, fee + 10000000000L, ts + 4) + vOptionContractId = regVOptionContract.contractId + + invalidDeposit <- depositToken(master, baseTokenContractId, master.toAddress.bytes.arr, vOptionContractId.bytes.arr, 1000000000, fee + 10000000000L, ts + 9) + } yield (genesis, regBaseTokenContract, regVOptionContract, issueBaseToken, invalidDeposit) + + property("unable to deposit tokens when 4 input token ids are the same") { + forAll(preconditionsAndVOptionDepositSameInputTokens) { case (genesis: GenesisTransaction, regBaseTokenContract: RC, regVOptionContract: RC, issueBaseToken: EC, + invalidDeposit: EC) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(issueBaseToken.timestamp, Seq(regBaseTokenContract, regVOptionContract, issueBaseToken))), + TestBlock.createWithTxStatus(invalidDeposit.timestamp, Seq(invalidDeposit), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } +} diff --git a/src/test/scala/vsys/blockchain/state/contract/voption/ExecuteVOptionValidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/voption/ExecuteVOptionValidDiffTest.scala new file mode 100644 index 000000000..e2ea6f7d0 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/voption/ExecuteVOptionValidDiffTest.scala @@ -0,0 +1,667 @@ + +package vsys.blockchain.state.contract.voption + +import com.google.common.primitives.{Ints, Longs} +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.ContractGenHelper.basicContractTestGen +import vsys.blockchain.contract.{DataEntry, DataType} +import vsys.blockchain.contract.token.SystemContractGen +import vsys.blockchain.contract.voption.{VOptionContractGen, VOptionFunctionHelperGen} +import vsys.blockchain.state._ +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.contract.{RegisterContractTransaction => RC, ExecuteContractFunctionTransaction => EC} +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} + +class ExecuteVOptionValidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with SystemContractGen + with VOptionContractGen + with VOptionFunctionHelperGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val preconditionsAndVOptionDepositBaseToken: Gen[(GenesisTransaction, RC, + RC, RC, RC, RC, EC, EC, EC, EC)] = for { + // Generates 4 register token contract transactions and a register contract tx for V Option + // Also generates 4 deposit functions, proof and option token deposits the entire supply, deposit amount for base and target tokens can be selected + (genesis, genesis2, master, user, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken , depositProofToken, fee, ts, attach) <- + createBaseTargetOptionProofTokenAndInitVOption( + 1000L, // baseTotalSupply + 1L, // baseUnity + 1000L, // baseIssueAmount + 1000L, // targetTotalSupply + 1L, // targetUnity + 1000L, // targetIssueAmount + 1000L, // optionTotalSupply + 1L, // optionUnity + 1000L, // proofTotalSupply + 1L, // proofUnity + 100L, // baseTokenDepositAmount + 500L, // targetTokenDepositAmount + 1000L, // optionTokenDepositAmount + 1000L) // targetTokenDepositAmount + + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, + issueTargetToken, depositBaseToken, depositTargetToken) + + property("vOption able to deposit") { + forAll(preconditionsAndVOptionDepositBaseToken) { case (genesis: GenesisTransaction, registerBase: RC, + registerTarget: RC, registerOption: RC, registerProof: RC, + registerVOption: RC, issueBase: EC, issueTarget: EC, + depositBase: EC, depositTarget: EC) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), + TestBlock.create(registerVOption.timestamp, Seq(registerBase, registerTarget, registerOption, registerProof, registerVOption, issueBase, issueTarget, + depositBase))), + TestBlock.createWithTxStatus(depositTarget.timestamp, Seq(depositTarget), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = registerVOption.proofs.firstCurveProof.explicitGet().publicKey + + val (contractBaseTokenBalanceKey, contractTargetTokenBalanceKey, _, _) = getOptionContractTokenBalanceKeys(registerBase.contractId.bytes.arr, + registerTarget.contractId.bytes.arr, registerOption.contractId.bytes.arr, + registerProof.contractId.bytes.arr, registerVOption.contractId.bytes.arr) + + val (masterBaseTokenBalanceKey, masterTargetTokenBalanceKey, _, _) = getOptionUserTokenBalanceKeys(registerBase.contractId.bytes.arr, + registerTarget.contractId.bytes.arr, registerOption.contractId.bytes.arr, + registerProof.contractId.bytes.arr, master) + + newState.tokenAccountBalance(masterBaseTokenBalanceKey) shouldBe 900L // issue 1000, deposit 100 into contract + newState.tokenAccountBalance(contractBaseTokenBalanceKey) shouldBe 100L // deposit 100 into contract + + newState.tokenAccountBalance(masterTargetTokenBalanceKey) shouldBe 500L // issue 1000, deposit 500 into contract + newState.tokenAccountBalance(contractTargetTokenBalanceKey) shouldBe 500L // deposit 500 into contract + } + } + } + + // test if we can deposit and withdraw all tokens to and from a v option contract + // all 4 tokens deposit and withdraw has been tested locally, the unit test will test for withdrawing base token only + + val preconditionsAndVoptionWithdrawBaseToken: Gen[(GenesisTransaction, RC, + RC, RC, RC, RC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, _, _, _, depositBaseToken, _, _ , _, fee, ts, _) <- + createBaseTargetOptionProofTokenAndInitVOption( + 1000L, // baseTotalSupply + 1L, // baseUnity + 1000L, // baseIssueAmount + 1000L, // targetTotalSupply + 1L, // targetUnity + 1000L, // targetIssueAmount + 1000L, // optionTotalSupply + 1L, // optionUnity + 1000L, // proofTotalSupply + 1L, // proofUnity + 1000L, // baseTokenDepositAmount + 1000L, // targetTokenDepositAmount + 1000L, // optionTokenDepositAmount + 1000L) // proofTokenDepositAmount + + withdrawBaseToken <- withdrawToken(master, regBaseTokenContract.contractId, regVOptionContract.contractId.bytes.arr, master.toAddress.bytes.arr, 100L, fee, ts + 13) + + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, depositBaseToken, + withdrawBaseToken) + + property("vOption able to withdraw") { + forAll(preconditionsAndVoptionWithdrawBaseToken) { case (genesis: GenesisTransaction, registerBase: RC, + registerTarget: RC, registerOption: RC, registerProof: RC, + registerVOption: RC, issueBase: EC, depositBase: EC, + withdrawBase: EC) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), + TestBlock.create(registerVOption.timestamp, Seq(registerBase, registerTarget, registerOption, registerProof, registerVOption, issueBase, depositBase))), + TestBlock.createWithTxStatus(withdrawBase.timestamp, Seq(withdrawBase), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = registerVOption.proofs.firstCurveProof.explicitGet().publicKey + + val (contractBaseTokenBalanceKey, _, _, _) = getOptionContractTokenBalanceKeys(registerBase.contractId.bytes.arr, + registerTarget.contractId.bytes.arr, registerOption.contractId.bytes.arr, + registerProof.contractId.bytes.arr, registerVOption.contractId.bytes.arr) + + val (masterBaseTokenBalanceKey, _, _, _) = getOptionUserTokenBalanceKeys(registerBase.contractId.bytes.arr, + registerTarget.contractId.bytes.arr, registerOption.contractId.bytes.arr, + registerProof.contractId.bytes.arr, master) + + newState.tokenAccountBalance(masterBaseTokenBalanceKey) shouldBe 100L // withdraw 100 + newState.tokenAccountBalance(contractBaseTokenBalanceKey) shouldBe 900L // deposit 1000, withdraw 100 + } + } + } + + // testing logic of supersede, address registering the v option contract and activating it should be different + // making the user address register and issue tokens reduces need for sending over tokens before activating + + val preconditionsAndVOptionSupersedeActivate: Gen[(GenesisTransaction, GenesisTransaction, RC, + RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + + (master, ts, fee) <- basicContractTestGen() + + genesis <- genesisVOptionGen(master, ts) + user <- accountGen + genesis2 <- genesisVOptionGen(user, ts) + vOptionContract <- vOptionContractGen() + + // register base token + regBaseTokenContract <- registerToken(user, 1000L, 1L, "init", fee + 10000000000L, ts) + baseTokenContractId = regBaseTokenContract.contractId + baseTokenId = tokenIdFromBytes(baseTokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // register target token + regTargetTokenContract <- registerToken(user, 1000L, 1L, "init", fee + 10000000000L, ts + 1) + targetTokenContractId = regTargetTokenContract.contractId + targetTokenId = tokenIdFromBytes(targetTokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // register option token + regOptionTokenContract <- registerToken(user, 1000L, 1L, "init", fee + 10000000000L, ts + 2) + optionTokenContractId = regOptionTokenContract.contractId + optionTokenId = tokenIdFromBytes(optionTokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + // register proof token + regProofTokenContract <- registerToken(user, 1000L, 1L, "init", fee + 10000000000L, ts + 3) + proofTokenContractId = regProofTokenContract.contractId + proofTokenId = tokenIdFromBytes(proofTokenContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + // register VSwap contract + description <- validDescStringGen + initVOptionDataStack: Seq[DataEntry] <- initVOptionDataStackGen(baseTokenId.arr, targetTokenId.arr, optionTokenId.arr, proofTokenId.arr, ts + 100, ts + 200) + regVOptionContract <- registerVOptionGen(master, vOptionContract, initVOptionDataStack, description, fee + 10000000000L, ts + 4) + vOptionContractId = regVOptionContract.contractId + + // issue base token + attach <- genBoundedString(2, EC.MaxDescriptionSize) + issueBaseToken <- issueToken(user, baseTokenContractId, 1000L, fee, ts + 5) + // issue target token + issueTargetToken <- issueToken(user, targetTokenContractId, 1000L, fee, ts + 6) + // issue option token, issue the entire supply of option tokens + issueOptionToken <- issueToken(user, optionTokenContractId, 1000L, fee, ts + 7) + // issue proof token, issue the entire supply of proof tokens + issueProofToken <- issueToken(user, proofTokenContractId, 1000L, fee, ts + 8) + + depositBaseToken <- depositToken(user, baseTokenContractId, user.toAddress.bytes.arr, vOptionContractId.bytes.arr, 1000L, fee + 10000000000L, ts + 9) + depositTargetToken <- depositToken(user, targetTokenContractId, user.toAddress.bytes.arr, vOptionContractId.bytes.arr, 1000L, fee + 10000000000L, ts + 10) + depositOptionToken <- depositToken(user, optionTokenContractId, user.toAddress.bytes.arr, vOptionContractId.bytes.arr, 1000L, fee + 10000000000L, ts + 11) + depositProofToken <- depositToken(user, proofTokenContractId, user.toAddress.bytes.arr, vOptionContractId.bytes.arr, 1000L, fee + 10000000000L, ts + 12) + + supersedeOption <- supersedeVOptionGen(master, regVOptionContract.contractId, user.toAddress, attach, fee, ts + 13) + activateOption <- activateVOptionGen(user, regVOptionContract.contractId, 1000L, 10L, 10L, attach, fee, ts + 14) + } yield (genesis, genesis2, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, + issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, supersedeOption, activateOption) + + property("vOption able to supersede and activate") { + forAll(preconditionsAndVOptionSupersedeActivate) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, registerBase: RC, registerTarget: RC, + registerOption: RC, registerProof: RC, registerVOption: RC, issueBase: EC, + issueTarget: EC, issueOption: EC, issueProof: EC, depositBase: EC, + depositTarget: EC, depositOption: EC, depositProof: EC, + supersede: EC, activate: EC) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), + TestBlock.create(registerVOption.timestamp, Seq(registerBase, registerTarget, registerOption, registerProof, registerVOption, issueBase, issueTarget, + issueOption, issueProof, depositBase, depositTarget, depositOption, depositProof))), + TestBlock.createWithTxStatus(activate.timestamp, Seq(supersede, activate), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val user = registerBase.proofs.firstCurveProof.explicitGet().publicKey + val vOptionContractId = registerVOption.contractId.bytes.arr + + val (optionStatusKey, maxIssueNumKey, reservedOptionKey, + reservedProofKey, priceKey, priceUnitKey, tokenLockedKey, _) = getOptionContractStateVarKeys(vOptionContractId) + + val (userStateMapBaseTokenBalanceKey, userStateMapTargetTokenBalanceKey, + userStateMapOptionTokenBalanceKey, userStateMapProofTokenBalanceKey) = getOptionContractStateMapKeys(vOptionContractId, user) + + newState.contractNumInfo(userStateMapBaseTokenBalanceKey) shouldBe 1000L + newState.contractNumInfo(userStateMapTargetTokenBalanceKey) shouldBe 1000L + newState.contractNumInfo(userStateMapOptionTokenBalanceKey) shouldBe 0L + newState.contractNumInfo(userStateMapProofTokenBalanceKey) shouldBe 0L + + newState.contractInfo(optionStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(maxIssueNumKey) shouldBe Some(DataEntry(Longs.toByteArray(1000L), DataType.Amount)) + newState.contractNumInfo(reservedOptionKey) shouldBe 1000L + newState.contractNumInfo(reservedProofKey) shouldBe 1000L + newState.contractInfo(priceKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractInfo(priceUnitKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractNumInfo(tokenLockedKey) shouldBe 0L + } + } + } + + val preconditionsAndVOptionMint: Gen[(GenesisTransaction, RC, + RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, + EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- + createBaseTargetOptionProofTokenAndInitVOption( + 1000L, // baseTotalSupply + 1L, // baseUnity + 1000L, // baseIssueAmount + 1000L, // targetTotalSupply + 1L, // targetUnity + 1000L, // targetIssueAmount + 1000L, // optionTotalSupply + 1L, // optionUnity + 1000L, // proofTotalSupply + 1L, // proofUnity + 1000L, // baseTokenDepositAmount + 1000L, // targetTokenDepositAmount + 1000L, // optionTokenDepositAmount + 1000L) // proofTokenDepositAmount + + activateOption <- activateVOptionGen(master, regVOptionContract.contractId, 1000L, 10L, 10L, attach, fee, ts + 13) + mintOption <- mintVOptionGen(master, regVOptionContract.contractId, 500L, attach, fee, ts + 14) + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, + issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, activateOption, mintOption) + + property("vOption able to mint") { + forAll(preconditionsAndVOptionMint) { case (genesis: GenesisTransaction, registerBase: RC, registerTarget: RC, + registerOption: RC, registerProof: RC, registerVOption: RC, issueBase: EC, + issueTarget: EC, issueOption: EC, issueProof: EC, depositBase: EC, + depositTarget: EC, depositOption: EC, depositProof: EC, activate: EC, + mint: EC) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), + TestBlock.create(registerVOption.timestamp, Seq(registerBase, registerTarget, registerOption, registerProof, registerVOption, issueBase, issueTarget, + issueOption, issueProof, depositBase, depositTarget, depositOption, depositProof, activate))), + TestBlock.createWithTxStatus(mint.timestamp, Seq(mint), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val user = registerBase.proofs.firstCurveProof.explicitGet().publicKey + val vOptionContractId = registerVOption.contractId.bytes.arr + + val (optionStatusKey, maxIssueNumKey, reservedOptionKey, + reservedProofKey, priceKey, priceUnitKey, tokenLockedKey, tokenCollectedKey) = getOptionContractStateVarKeys(vOptionContractId) + + val (userStateMapBaseTokenBalanceKey, userStateMapTargetTokenBalanceKey, + userStateMapOptionTokenBalanceKey, userStateMapProofTokenBalanceKey) = getOptionContractStateMapKeys(vOptionContractId, user) + + newState.contractNumInfo(userStateMapBaseTokenBalanceKey) shouldBe 1000L + newState.contractNumInfo(userStateMapTargetTokenBalanceKey) shouldBe 500L + newState.contractNumInfo(userStateMapOptionTokenBalanceKey) shouldBe 500L + newState.contractNumInfo(userStateMapProofTokenBalanceKey) shouldBe 500L + + newState.contractInfo(optionStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(maxIssueNumKey) shouldBe Some(DataEntry(Longs.toByteArray(1000L), DataType.Amount)) + newState.contractNumInfo(reservedOptionKey) shouldBe 500L // maxIssueNum 1000, mint 500 + newState.contractNumInfo(reservedProofKey) shouldBe 500L // maxIssueNum 1000, mint 500 + newState.contractInfo(priceKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractInfo(priceUnitKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractNumInfo(tokenLockedKey) shouldBe 500L // mint 500 + + } + } + } + + val preconditionsAndVOptionUnlock: Gen[(GenesisTransaction, RC, + RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- + createBaseTargetOptionProofTokenAndInitVOption( + 1000L, // baseTotalSupply + 1L, // baseUnity + 1000L, // baseIssueAmount + 1000L, // targetTotalSupply + 1L, // targetUnity + 1000L, // targetIssueAmount + 1000L, // optionTotalSupply + 1L, // optionUnity + 1000L, // proofTotalSupply + 1L, // proofUnity + 1000L, // baseTokenDepositAmount + 1000L, // targetTokenDepositAmount + 1000L, // optionTokenDepositAmount + 1000L) // proofTokenDepositAmount + + activateOption <- activateVOptionGen(master, regVOptionContract.contractId, 1000L, 10L, 10L, attach, fee, ts + 13) + mintOption <- mintVOptionGen(master, regVOptionContract.contractId, 500L, attach, fee, ts + 14) + unlockOption <- unlockVOptionGen(master, regVOptionContract.contractId, 10L, attach, fee, ts + 15) + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, + issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, activateOption, mintOption, unlockOption) + + property("vOption able to unlock") { + forAll(preconditionsAndVOptionUnlock) { case (genesis: GenesisTransaction, registerBase: RC, registerTarget: RC, + registerOption: RC, registerProof: RC, registerVOption: RC, issueBase: EC, + issueTarget: EC, issueOption: EC, issueProof: EC, depositBase: EC, + depositTarget: EC, depositOption: EC, depositProof: EC, activate: EC, + mint: EC, unlock: EC) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), + TestBlock.create(registerVOption.timestamp, Seq(registerBase, registerTarget, registerOption, registerProof, registerVOption, issueBase, issueTarget, + issueOption, issueProof, depositBase, depositTarget, depositOption, depositProof, activate, mint))), + TestBlock.createWithTxStatus(unlock.timestamp, Seq(unlock), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val user = registerBase.proofs.firstCurveProof.explicitGet().publicKey + val vOptionContractId = registerVOption.contractId.bytes.arr + + val (optionStatusKey, maxIssueNumKey, reservedOptionKey, + reservedProofKey, priceKey, priceUnitKey, tokenLockedKey, tokenCollectedKey) = getOptionContractStateVarKeys(vOptionContractId) + + val (userStateMapBaseTokenBalanceKey, userStateMapTargetTokenBalanceKey, + userStateMapOptionTokenBalanceKey, userStateMapProofTokenBalanceKey) = getOptionContractStateMapKeys(vOptionContractId, user) + + newState.contractNumInfo(userStateMapBaseTokenBalanceKey) shouldBe 1000L + newState.contractNumInfo(userStateMapTargetTokenBalanceKey) shouldBe 510L + newState.contractNumInfo(userStateMapOptionTokenBalanceKey) shouldBe 490L + newState.contractNumInfo(userStateMapProofTokenBalanceKey) shouldBe 490L + + newState.contractInfo(optionStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(maxIssueNumKey) shouldBe Some(DataEntry(Longs.toByteArray(1000L), DataType.Amount)) + newState.contractNumInfo(reservedOptionKey) shouldBe 510L // maxIssueNum 1000, mint 500, unlock 10 + newState.contractNumInfo(reservedProofKey) shouldBe 510L // maxIssueNum 1000, mint 500, unlock 10 + newState.contractInfo(priceKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractInfo(priceUnitKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractNumInfo(tokenLockedKey) shouldBe 490L // mint 500, unlock 10 + } + } + } + + val preconditionsAndVOptionExecute: Gen[(GenesisTransaction, RC, + RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- + createBaseTargetOptionProofTokenAndInitVOption( + 1000L, // baseTotalSupply + 1L, // baseUnity + 1000L, // baseIssueAmount + 1000L, // targetTotalSupply + 1L, // targetUnity + 1000L, // targetIssueAmount + 1000L, // optionTotalSupply + 1L, // optionUnity + 1000L, // proofTotalSupply + 1L, // proofUnity + 1000L, // baseTokenDepositAmount + 1000L, // targetTokenDepositAmount + 1000L, // optionTokenDepositAmount + 1000L) // proofTokenDepositAmount + + activateOption <- activateVOptionGen(master, regVOptionContract.contractId, 1000L, 10L, 1L, attach, fee, ts + 13) + mintOption <- mintVOptionGen(master, regVOptionContract.contractId, 100L, attach, fee, ts + 14) + executeOption <- executeVOptionGen(master, regVOptionContract.contractId, 10L, attach, fee, ts + 101) + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, + issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, activateOption, mintOption, executeOption) + + property("vOption able to execute") { + forAll(preconditionsAndVOptionExecute) { case (genesis: GenesisTransaction, registerBase: RC, registerTarget: RC, + registerOption: RC, registerProof: RC, registerVOption: RC, issueBase: EC, + issueTarget: EC, issueOption: EC, issueProof: EC, depositBase: EC, + depositTarget: EC, depositOption: EC, depositProof: EC, activate: EC, + mint: EC, execute: EC) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), + TestBlock.create(registerVOption.timestamp, Seq(registerBase, registerTarget, registerOption, registerProof, registerVOption, issueBase, issueTarget, + issueOption, issueProof, depositBase, depositTarget, depositOption, depositProof, activate, mint))), + TestBlock.createWithTxStatus(execute.timestamp, Seq(execute), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val user = registerBase.proofs.firstCurveProof.explicitGet().publicKey + val vOptionContractId = registerVOption.contractId.bytes.arr + + val (optionStatusKey, maxIssueNumKey, reservedOptionKey, + reservedProofKey, priceKey, priceUnitKey, tokenLockedKey, tokenCollectedKey) = getOptionContractStateVarKeys(vOptionContractId) + + val (userStateMapBaseTokenBalanceKey, userStateMapTargetTokenBalanceKey, + userStateMapOptionTokenBalanceKey, userStateMapProofTokenBalanceKey) = getOptionContractStateMapKeys(vOptionContractId, user) + + newState.contractNumInfo(userStateMapBaseTokenBalanceKey) shouldBe 899L + newState.contractNumInfo(userStateMapTargetTokenBalanceKey) shouldBe 910L + newState.contractNumInfo(userStateMapOptionTokenBalanceKey) shouldBe 90L + newState.contractNumInfo(userStateMapProofTokenBalanceKey) shouldBe 100L + + newState.contractInfo(optionStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(maxIssueNumKey) shouldBe Some(DataEntry(Longs.toByteArray(1000L), DataType.Amount)) + newState.contractNumInfo(reservedOptionKey) shouldBe 910L // maxIssueNum 1000, mint 100, execute 10 + newState.contractNumInfo(reservedProofKey) shouldBe 900L // maxIssueNum 1000, mint 100 + newState.contractInfo(priceKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractInfo(priceUnitKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractNumInfo(tokenLockedKey) shouldBe 90L // mint 100, execute 10 + } + } + } + + val preconditionsAndVOptionCollect: Gen[(GenesisTransaction, RC, + RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- + createBaseTargetOptionProofTokenAndInitVOption( + 1000L, // baseTotalSupply + 1L, // baseUnity + 1000L, // baseIssueAmount + 1000L, // targetTotalSupply + 1L, // targetUnity + 1000L, // targetIssueAmount + 1000L, // optionTotalSupply + 1L, // optionUnity + 1000L, // proofTotalSupply + 1L, // proofUnity + 1000L, // baseTokenDepositAmount + 1000L, // targetTokenDepositAmount + 1000L, // optionTokenDepositAmount + 1000L) // proofTokenDepositAmount + + activateOption <- activateVOptionGen(master, regVOptionContract.contractId, 1000L, 10L, 1L, attach, fee, ts + 13) + mintOption <- mintVOptionGen(master, regVOptionContract.contractId, 500L, attach, fee, ts + 14) + executeOption <- executeVOptionGen(master, regVOptionContract.contractId, 10L, attach, fee, ts + 101) + collectOption <- collectVOptionGen(master, regVOptionContract.contractId, 100L, attach, fee, ts + 202) + + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, + issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, activateOption, mintOption, executeOption, collectOption) + + property("vOption able to collect") { + forAll(preconditionsAndVOptionCollect) { case (genesis: GenesisTransaction, registerBase: RC, registerTarget: RC, + registerOption: RC, registerProof: RC, registerVOption: RC, issueBase: EC, + issueTarget: EC, issueOption: EC, issueProof: EC, depositBase: EC, + depositTarget: EC, depositOption: EC, depositProof: EC, activate: EC, + mint: EC, execute: EC, collect: EC) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), + TestBlock.create(registerVOption.timestamp, Seq(registerBase, registerTarget, registerOption, registerProof, registerVOption, issueBase, issueTarget, + issueOption, issueProof, depositBase, depositTarget, depositOption, depositProof, activate, mint)), TestBlock.create(execute.timestamp, Seq()), // empty block since transactions look at previous block timestamp + TestBlock.create(execute.timestamp + 1, Seq(execute))), + TestBlock.createWithTxStatus(collect.timestamp, Seq(collect), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val user = registerBase.proofs.firstCurveProof.explicitGet().publicKey + val vOptionContractId = registerVOption.contractId.bytes.arr + + val (optionStatusKey, maxIssueNumKey, reservedOptionKey, + reservedProofKey, priceKey, priceUnitKey, tokenLockedKey, tokenCollectedKey) = getOptionContractStateVarKeys(vOptionContractId) + + val (userStateMapBaseTokenBalanceKey, userStateMapTargetTokenBalanceKey, + userStateMapOptionTokenBalanceKey, userStateMapProofTokenBalanceKey) = getOptionContractStateMapKeys(vOptionContractId, user) + + newState.contractNumInfo(userStateMapBaseTokenBalanceKey) shouldBe 919L + newState.contractNumInfo(userStateMapTargetTokenBalanceKey) shouldBe 608L + newState.contractNumInfo(userStateMapOptionTokenBalanceKey) shouldBe 490L + newState.contractNumInfo(userStateMapProofTokenBalanceKey) shouldBe 400L + + newState.contractInfo(optionStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(maxIssueNumKey) shouldBe Some(DataEntry(Longs.toByteArray(1000L), DataType.Amount)) + newState.contractNumInfo(reservedOptionKey) shouldBe 510L // maxIssueNum 1000, mint 500, execute 10 + newState.contractNumInfo(reservedProofKey) shouldBe 600L // maxIssueNum 1000, mint 500, collect 100 + newState.contractInfo(priceKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractInfo(priceUnitKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + // the collect function gives the caller the fraction of the pool equal to the fraction of proof tokens they use to execute the function + // here there are 490 target tokens and 500 available proof tokens + newState.contractNumInfo(tokenLockedKey) shouldBe 392L // 490 - ((100/500) * 490) + } + } + } + + val preconditionsAndVOptionLargeAmountsExecute: Gen[(GenesisTransaction, RC, + RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- + createBaseTargetOptionProofTokenAndInitVOption( + Long.MaxValue, // baseTotalSupply + 1L, // baseUnity + Long.MaxValue, // baseIssueAmount + Long.MaxValue, // targetTotalSupply + 1L, // targetUnity + Long.MaxValue, // targetIssueAmount + Long.MaxValue, // optionTotalSupply + 1L, // optionUnity + Long.MaxValue, // proofTotalSupply + 1L, // proofUnity + Long.MaxValue, // baseTokenDepositAmount + Long.MaxValue, // targetTokenDepositAmount + Long.MaxValue, // optionTokenDepositAmount + Long.MaxValue) // proofTokenDepositAmount + + activateOption <- activateVOptionGen(master, regVOptionContract.contractId, Long.MaxValue, Long.MaxValue, Long.MaxValue, attach, fee, ts + 13) + mintOption <- mintVOptionGen(master, regVOptionContract.contractId, Long.MaxValue, attach, fee, ts + 14) + executeOption <- executeVOptionGen(master, regVOptionContract.contractId, Long.MaxValue - 1, attach, fee, ts + 101) + + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, + issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, activateOption, mintOption, executeOption) + + property("vOption able to execute very large numbers") { + forAll(preconditionsAndVOptionLargeAmountsExecute) { case (genesis: GenesisTransaction, registerBase: RC, registerTarget: RC, + registerOption: RC, registerProof: RC, registerVOption: RC, issueBase: EC, + issueTarget: EC, issueOption: EC, issueProof: EC, depositBase: EC, + depositTarget: EC, depositOption: EC, depositProof: EC, activate: EC, + mint: EC, execute: EC) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), + TestBlock.create(registerVOption.timestamp, Seq(registerBase, registerTarget, registerOption, registerProof, registerVOption, issueBase, issueTarget, + issueOption, issueProof, depositBase, depositTarget, depositOption, depositProof, activate, mint))), + TestBlock.createWithTxStatus(execute.timestamp, Seq(execute), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val user = registerBase.proofs.firstCurveProof.explicitGet().publicKey + val vOptionContractId = registerVOption.contractId.bytes.arr + + val (optionStatusKey, maxIssueNumKey, reservedOptionKey, + reservedProofKey, priceKey, priceUnitKey, tokenLockedKey, tokenCollectedKey) = getOptionContractStateVarKeys(vOptionContractId) + + val (userStateMapBaseTokenBalanceKey, userStateMapTargetTokenBalanceKey, + userStateMapOptionTokenBalanceKey, userStateMapProofTokenBalanceKey) = getOptionContractStateMapKeys(vOptionContractId, user) + + newState.contractNumInfo(userStateMapBaseTokenBalanceKey) shouldBe 0L + newState.contractNumInfo(userStateMapTargetTokenBalanceKey) shouldBe Long.MaxValue - 1 + newState.contractNumInfo(userStateMapOptionTokenBalanceKey) shouldBe 1L + newState.contractNumInfo(userStateMapProofTokenBalanceKey) shouldBe Long.MaxValue + + newState.contractInfo(optionStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(maxIssueNumKey) shouldBe Some(DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Amount)) + newState.contractNumInfo(reservedOptionKey) shouldBe Long.MaxValue - 1 + newState.contractNumInfo(reservedProofKey) shouldBe 0L + newState.contractInfo(priceKey) shouldBe Some(DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Amount)) + newState.contractInfo(priceUnitKey) shouldBe Some(DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Amount)) + newState.contractNumInfo(tokenLockedKey) shouldBe 1L + + val master = registerVOption.proofs.firstCurveProof.explicitGet().publicKey + + val (contractBaseTokenBalanceKey, contractTargetTokenBalanceKey, + contractOptionTokenBalanceKey, contractProofTokenBalanceKey) = getOptionContractTokenBalanceKeys(registerBase.contractId.bytes.arr, + registerTarget.contractId.bytes.arr, registerOption.contractId.bytes.arr, + registerProof.contractId.bytes.arr, registerVOption.contractId.bytes.arr) + + val (masterBaseTokenBalanceKey, masterTargetTokenBalanceKey, + masterOptionTokenBalanceKey, masterProofTokenBalanceKey) = getOptionUserTokenBalanceKeys(registerBase.contractId.bytes.arr, + registerTarget.contractId.bytes.arr, registerOption.contractId.bytes.arr, + registerProof.contractId.bytes.arr, master) + + // Ensure that the final token balance values are correct + newState.tokenAccountBalance(masterBaseTokenBalanceKey) shouldBe 0L + newState.tokenAccountBalance(contractBaseTokenBalanceKey) shouldBe Long.MaxValue + + newState.tokenAccountBalance(masterTargetTokenBalanceKey) shouldBe 0L + newState.tokenAccountBalance(contractTargetTokenBalanceKey) shouldBe Long.MaxValue + + newState.tokenAccountBalance(masterOptionTokenBalanceKey) shouldBe 0L + newState.tokenAccountBalance(contractOptionTokenBalanceKey) shouldBe Long.MaxValue + + newState.tokenAccountBalance(masterProofTokenBalanceKey) shouldBe 0L + newState.tokenAccountBalance(contractProofTokenBalanceKey) shouldBe Long.MaxValue + } + } + } + + val preconditionsAndVOptionLargeAmountsCollect: Gen[(GenesisTransaction, RC, + RC, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, + issueBaseToken, issueTargetToken, issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, fee, ts, attach) <- + createBaseTargetOptionProofTokenAndInitVOption( + Long.MaxValue, // baseTotalSupply + 1L, // baseUnity + Long.MaxValue, // baseIssueAmount + Long.MaxValue, // targetTotalSupply + 1L, // targetUnity + Long.MaxValue, // targetIssueAmount + Long.MaxValue, // optionTotalSupply + 1L, // optionUnity + Long.MaxValue, // proofTotalSupply + 1L, // proofUnity + Long.MaxValue, // baseTokenDepositAmount + Long.MaxValue, // targetTokenDepositAmount + Long.MaxValue, // optionTokenDepositAmount + Long.MaxValue) // proofTokenDepositAmount + + activateOption <- activateVOptionGen(master, regVOptionContract.contractId, Long.MaxValue, Long.MaxValue, 1L, attach, fee, ts + 13) + mintOption <- mintVOptionGen(master, regVOptionContract.contractId, Long.MaxValue, attach, fee, ts + 14) + collectOption <- collectVOptionGen(master, regVOptionContract.contractId, Long.MaxValue, attach, fee, ts + 202) + + } yield (genesis, regBaseTokenContract, regTargetTokenContract, regOptionTokenContract, regProofTokenContract, regVOptionContract, issueBaseToken, issueTargetToken, + issueOptionToken, issueProofToken, depositBaseToken, depositTargetToken, depositOptionToken, depositProofToken, activateOption, mintOption, collectOption) + + property("vOption able to collect very large numbers") { + forAll(preconditionsAndVOptionLargeAmountsCollect) { case (genesis: GenesisTransaction, registerBase: RC, registerTarget: RC, + registerOption: RC, registerProof: RC, registerVOption: RC, issueBase: EC, + issueTarget: EC, issueOption: EC, issueProof: EC, depositBase: EC, + depositTarget: EC, depositOption: EC, depositProof: EC, activate: EC, + mint: EC, collect: EC) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), + TestBlock.create(registerVOption.timestamp, Seq(registerBase, registerTarget, registerOption, registerProof, registerVOption, issueBase, issueTarget, + issueOption, issueProof, depositBase, depositTarget, depositOption, depositProof, activate, mint))), + TestBlock.createWithTxStatus(collect.timestamp, Seq(collect), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val user = registerBase.proofs.firstCurveProof.explicitGet().publicKey + val vOptionContractId = registerVOption.contractId.bytes.arr + + val (optionStatusKey, maxIssueNumKey, reservedOptionKey, + reservedProofKey, priceKey, priceUnitKey, tokenLockedKey, tokenCollectedKey) = getOptionContractStateVarKeys(vOptionContractId) + + val (userStateMapBaseTokenBalanceKey, userStateMapTargetTokenBalanceKey, + userStateMapOptionTokenBalanceKey, userStateMapProofTokenBalanceKey) = getOptionContractStateMapKeys(vOptionContractId, user) + + newState.contractNumInfo(userStateMapBaseTokenBalanceKey) shouldBe Long.MaxValue + newState.contractNumInfo(userStateMapTargetTokenBalanceKey) shouldBe Long.MaxValue + newState.contractNumInfo(userStateMapOptionTokenBalanceKey) shouldBe Long.MaxValue + newState.contractNumInfo(userStateMapProofTokenBalanceKey) shouldBe 0L + + newState.contractInfo(optionStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(maxIssueNumKey) shouldBe Some(DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Amount)) + newState.contractNumInfo(reservedOptionKey) shouldBe 0L + newState.contractNumInfo(reservedProofKey) shouldBe Long.MaxValue + newState.contractInfo(priceKey) shouldBe Some(DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Amount)) + newState.contractInfo(priceUnitKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractNumInfo(tokenLockedKey) shouldBe 0L + + val master = registerVOption.proofs.firstCurveProof.explicitGet().publicKey + + val (contractBaseTokenBalanceKey, contractTargetTokenBalanceKey, + contractOptionTokenBalanceKey, contractProofTokenBalanceKey) = getOptionContractTokenBalanceKeys(registerBase.contractId.bytes.arr, + registerTarget.contractId.bytes.arr, registerOption.contractId.bytes.arr, + registerProof.contractId.bytes.arr, registerVOption.contractId.bytes.arr) + + val (masterBaseTokenBalanceKey, masterTargetTokenBalanceKey, + masterOptionTokenBalanceKey, masterProofTokenBalanceKey) = getOptionUserTokenBalanceKeys(registerBase.contractId.bytes.arr, + registerTarget.contractId.bytes.arr, registerOption.contractId.bytes.arr, + registerProof.contractId.bytes.arr, master) + + // Ensure that the final token balance values are correct + newState.tokenAccountBalance(masterBaseTokenBalanceKey) shouldBe 0L + newState.tokenAccountBalance(contractBaseTokenBalanceKey) shouldBe Long.MaxValue + + newState.tokenAccountBalance(masterTargetTokenBalanceKey) shouldBe 0L + newState.tokenAccountBalance(contractTargetTokenBalanceKey) shouldBe Long.MaxValue + + newState.tokenAccountBalance(masterOptionTokenBalanceKey) shouldBe 0L + newState.tokenAccountBalance(contractOptionTokenBalanceKey) shouldBe Long.MaxValue + + newState.tokenAccountBalance(masterProofTokenBalanceKey) shouldBe 0L + newState.tokenAccountBalance(contractProofTokenBalanceKey) shouldBe Long.MaxValue + } + } + } +} diff --git a/src/test/scala/vsys/blockchain/state/contract/voption/RegisterVOptionContractDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/voption/RegisterVOptionContractDiffTest.scala new file mode 100644 index 000000000..6596e0240 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/voption/RegisterVOptionContractDiffTest.scala @@ -0,0 +1,78 @@ +package vsys.blockchain.state.contract.voption + +import cats.Monoid +import com.google.common.primitives.{Bytes, Ints} +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.{Contract, ContractGenHelper, ContractVOption, DataEntry, DataType} +import vsys.blockchain.contract.voption.VOptionContractGen +import vsys.blockchain.state.diffs.assertDiffAndState +import vsys.blockchain.state._ +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen} +import vsys.blockchain.transaction.contract.RegisterContractTransaction + +class RegisterVOptionContractDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with VOptionContractGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val languageCode: String = "vdds" + val languageVersion: Int = 2 + + val preconditionAndBuildVOptionContract: Gen[(Array[Byte], Array[Byte], Seq[Array[Byte]], + Seq[Array[Byte]], Seq[Array[Byte]], Seq[Array[Byte]], Seq[Array[Byte]])] = for { + langCode <- ContractGenHelper.languageCodeGen(languageCode) + langVer <- ContractGenHelper.languageVersionGen(languageVersion) + init <- Gen.const(ContractVOption.contract.trigger) + descriptor <- Gen.const(ContractVOption.contract.descriptor) + stateVar <- Gen.const(ContractVOption.contract.stateVar) + stateMap <- Gen.const(ContractVOption.contract.stateMap) + textual <- Gen.const(ContractVOption.contract.textual) + } yield (langCode, langVer, init, descriptor, stateVar, stateMap, textual) + + property("register v-option contract build doesn't break invariant"){ + forAll(preconditionAndBuildVOptionContract) { case (langCode, langVer, init, descriptor, stateVar, stateMap, textual) => + Contract.buildContract(langCode, langVer, init, descriptor, stateVar, stateMap, textual) shouldBe an[Right[_, _]] + } + } + + val validContract: Gen[Contract] = vOptionContractGen() + val preconditionsAndRegContractTest: Gen[(GenesisTransaction, RegisterContractTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + genesis <- genesisVOptionGen(master, ts) + contract <- validContract + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + data: Seq[DataEntry] <- initVOptionDataStackGen(tokenId.arr, tokenId.arr, tokenId.arr, tokenId.arr, ts + 10,ts + 1000) + description <- validDescStringGen + create <- registerVOptionGen(master, contract, data, description, fee, ts + 1) + } yield (genesis, create) + + // Simply registers the v option contract using the system contract token id as inputs + property("register v-option contract transaction doesn't break invariant") { + forAll(preconditionsAndRegContractTest) { case (genesis, reg: RegisterContractTransaction) => + assertDiffAndState(Seq(TestBlock.create(Seq(genesis))), TestBlock.create(Seq(reg))) { (blockDiff, newState) => + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + totalPortfolioDiff.balance shouldBe -reg.transactionFee + totalPortfolioDiff.effectiveBalance shouldBe -reg.transactionFee + val master = reg.proofs.firstCurveProof.explicitGet().publicKey + val contractId = reg.contractId.bytes + val makerKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte))) + + val (_, masterTxs) = newState.accountTransactionIds(master, 2, 0) + masterTxs.size shouldBe 2 // genesis, reg + newState.contractTokens(contractId) shouldBe 0 + newState.contractContent(contractId) shouldEqual Some((2, reg.id, ContractVOption.contract)) + + newState.contractInfo(makerKey) shouldEqual Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + } + } + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/state/contract/voption/VOptionContractOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/voption/VOptionContractOpcDiffTest.scala new file mode 100644 index 000000000..12b59db76 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/voption/VOptionContractOpcDiffTest.scala @@ -0,0 +1,75 @@ +package vsys.blockchain.state.contract.voption + +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.contract.{Contract, ContractGenHelper, DataEntry} +import vsys.blockchain.contract.ContractVOption._ +import vsys.utils.serialization.Deser +import com.google.common.primitives.Ints +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount +import vsys.blockchain.contract.voption.{VOptionContractGen, VOptionFunction} +import vsys.blockchain.state._ +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidOPCData} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.blockchain.transaction.TransactionGen + +class VOptionContractOpcDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with VOptionContractGen + with VOptionFunction { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val regWrongOpcFunContract: Gen[Contract] = + Gen.const(Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + wrongOpcTrigger, + Seq(supersedeFunc, activateFunc, mintFunc, unlockFunc, executeFunc, collectFunc), + stateVarSeq, stateMapSeq, Seq(triggerTextual, descriptorTextual, stateVarTextual, stateMapTextual)).explicitGet()) + + val regWrongDataContract: Gen[Contract] = + Gen.const(Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + wrongDataTrigger, + Seq(supersedeFunc, activateFunc, mintFunc, unlockFunc, executeFunc, collectFunc), + stateVarSeq, stateMapSeq, Seq(triggerTextual, descriptorTextual, stateVarTextual, stateMapTextual)).explicitGet()) + + val validContract: Gen[Contract] = vOptionContractGen() + val preconditionsAndRegContractWrongFun: Gen[(RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + validContract <- validContract + wrongOpcContract <- regWrongOpcFunContract + wrongDataContract <- regWrongDataContract + + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + data: Seq[DataEntry] <- initVOptionDataStackGen(tokenId.arr, tokenId.arr, tokenId.arr, tokenId.arr, ts+100, ts+200) + description <- validDescStringGen + validCreate <- registerVOptionGen(master, validContract, data, description, fee, ts + 1) + wrongOpcCreate <- registerVOptionGen(master, wrongOpcContract, data, description, fee + 10000000000L, ts+2) + wrongDataCreate <- registerVOptionGen(master, wrongDataContract, data, description, fee + 10000000000L, ts+3) + } yield (validCreate, wrongOpcCreate, wrongDataCreate) + + property("register contract transaction cannot pass due to wrong opcode"){ + forAll(preconditionsAndRegContractWrongFun) { case (validCreate, wrongOpcCreate, _) => + assertOpcFuncDifferEi(2, None, validCreate) { opcFunDiffEi => + opcFunDiffEi shouldBe an[Right[_, _]] + } + + assertOpcFuncDifferEi(2, None, wrongOpcCreate) { opcFunDiffEi => + opcFunDiffEi shouldBe Left(ContractInvalidOPCData) + } + } + } + + property("register contract transaction cannot pass due to wrong list of parameters"){ + forAll(preconditionsAndRegContractWrongFun) { case (_, _, wrongDataCreate) => + assertOpcFuncDifferEi(2, None, wrongDataCreate) { opcFunDiffEi => + opcFunDiffEi shouldBe Left(ContractDataTypeMismatch) + } + } + } +} diff --git a/src/test/scala/vsys/blockchain/state/contract/vstableswap/ExecuteVStableSwapInvalidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vstableswap/ExecuteVStableSwapInvalidDiffTest.scala new file mode 100644 index 000000000..a56c3b7c2 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vstableswap/ExecuteVStableSwapInvalidDiffTest.scala @@ -0,0 +1,330 @@ +package vsys.blockchain.state.contract.vstableswap + +import com.google.common.primitives.Ints +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.ContractGenHelper.basicContractTestGen +import vsys.blockchain.contract.token.SystemContractGen +import vsys.blockchain.contract.vstableswap.{VStableSwapContractGen, VStableSwapFunctionHelperGen} +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction => EC, RegisterContractTransaction => RC} +import vsys.blockchain.state._ + +class ExecuteVStableSwapInvalidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with SystemContractGen + with VStableSwapContractGen + with VStableSwapFunctionHelperGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val preconditionsAndVStableSwapDepositToken: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, _, depositBase, _, fee, ts, _) + <- createBaseTokenTargetTokenAndInitVStableSwap(1000, 1, 1000, 1000, + 1, 1000, 5, 1, 1) + withdrawBase <- withdrawToken(master, regTokenBase.contractId, regVStableSwapContract.contractId.bytes.arr, master.toAddress.bytes.arr, 100, fee, ts+7) + withdrawBaseInvalid <- withdrawToken(master, regTokenBase.contractId, regVStableSwapContract.contractId.bytes.arr, master.toAddress.bytes.arr, 10000, fee, ts+7) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, depositBase, withdrawBase, withdrawBaseInvalid) + + property("unable to withdraw tokens") { + forAll(preconditionsAndVStableSwapDepositToken) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, depositBase: EC, withdrawBase: EC, withdrawBaseInvalid: EC) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositBase.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, depositBase))), + TestBlock.createWithTxStatus(withdrawBase.timestamp, Seq(withdrawBase), TransactionStatus.Success)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // withdraw tokens more than depositing in vstable swap contract + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositBase.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, depositBase))), + TestBlock.createWithTxStatus(withdrawBaseInvalid.timestamp, Seq(withdrawBaseInvalid), TransactionStatus.ContractTokenBalanceInsufficient)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractTokenBalanceInsufficient + } + } + } + + val preconditionsAndVStableSwapSetOrder: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attachment) + <- createBaseTokenTargetTokenAndInitVStableSwap(1000, 1, 1000, 1000, + 1, 1000, 2, 1, 1) + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, 1000, 10, 1000, 10, 10, 100, 100, attachment, fee, ts+7) + setOrder2 <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, 1000, 10, 1000, 10, 10, 100, 100, attachment, fee, ts+8) + setOrder3 <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, 1000, 10, 1000, 10, 10, 100, 100, attachment, fee, ts+9) + setOrderInvalid <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, 1000, 10, 1000, 10, 10, 10000, 1000, attachment, fee, ts+7) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, setOrder2, setOrder3, setOrderInvalid) + + property("unable to set order") { + forAll(preconditionsAndVStableSwapSetOrder) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, setOrder2: EC, setOrder3: EC, setOrderInvalid: EC) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositTarget.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget))), + TestBlock.createWithTxStatus(setOrder.timestamp, Seq(setOrder), TransactionStatus.Success)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // set order and deposit into contract which is less than deposit into order + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositTarget.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget))), + TestBlock.createWithTxStatus(setOrderInvalid.timestamp, Seq(setOrderInvalid), TransactionStatus.ContractMapValueInsufficient)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // set order more than maxOrderPerUser + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder)), + TestBlock.create(setOrder2.timestamp, Seq(setOrder2))), + TestBlock.createWithTxStatus(setOrder3.timestamp, Seq(setOrder3), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVStableSwapUpdateOrder: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attachment) + <- createBaseTokenTargetTokenAndInitVStableSwap(1000, 1, 1000, 1000, + 1, 1000, 5, 1, 1) + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, 1000, 10, 1000, 10, 10, 1000, 1000, attachment, fee, ts+7) + closeOrder <- closeVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, attachment, fee, ts+8) + updateOrder <- updateVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 0, 0, 10, 1000, 10, 1000, 10, 10, attachment, fee, ts+9) + updateOrderInvalid <- updateVStableSwapGen(master, regVStableSwapContract.contractId, Array[Byte](10), 0, 0, 10, 1000, 10, 1000, 10, 10, attachment, fee, ts+9) + + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, closeOrder, updateOrder, updateOrderInvalid) + + property("unable to update order") { + forAll(preconditionsAndVStableSwapUpdateOrder) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, closeOrder: EC, updateOrder: EC, updateOrderInvalid: EC) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(updateOrder.timestamp, Seq(updateOrder), TransactionStatus.Success)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Success + } + // update order with wrong order id + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(updateOrderInvalid.timestamp, Seq(updateOrderInvalid), TransactionStatus.ContractStateMapNotDefined)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.ContractStateMapNotDefined + } + // update order after close order + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(closeOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder, closeOrder))), + TestBlock.createWithTxStatus(updateOrder.timestamp, Seq(updateOrder), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVStableSwapOrderDeposit: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attachment) + <- createBaseTokenTargetTokenAndInitVStableSwap(1000, 1, 1000, 1000, + 1, 1000, 5, 1, 1) + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, 1000, 10, 1000, 10, 10, 990, 990, attachment, fee, ts+7) + closeOrder <- closeVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, attachment, fee, ts+8) + orderDeposit <- orderDepositVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 10, 10, attachment, fee, ts+9) + orderDepositInvalid <- orderDepositVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 10000, 10, attachment, fee, ts+9) + orderDepositInvalid2 <- orderDepositVStableSwapGen(master, regVStableSwapContract.contractId, Array[Byte](10), 10, 10, attachment, fee, ts+9) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, closeOrder, orderDeposit, orderDepositInvalid, orderDepositInvalid2) + + property("unable to order deposit") { + forAll(preconditionsAndVStableSwapOrderDeposit) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, closeOrder: EC, orderDeposit: EC, orderDepositInvalid: EC, orderDepositInvalid2: EC) => + + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(orderDeposit.timestamp, Seq(orderDeposit), TransactionStatus.Success)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Success + } + // deposit into order is greater than deposit into contract + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(orderDepositInvalid.timestamp, Seq(orderDepositInvalid), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // order deposit with wrong order id + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(orderDepositInvalid2.timestamp, Seq(orderDepositInvalid2), TransactionStatus.ContractStateMapNotDefined)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.ContractStateMapNotDefined + } + // order deposit after order close + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(closeOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder, closeOrder))), + TestBlock.createWithTxStatus(orderDeposit.timestamp, Seq(orderDeposit), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVStableSwapOrderWithdraw: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attachment) + <- createBaseTokenTargetTokenAndInitVStableSwap(1000, 1, 1000, 1000, + 1, 1000, 5, 1, 1) + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, 1000, 10, 1000, 10, 10, 990, 990, attachment, fee, ts+7) + closeOrder <- closeVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, attachment, fee, ts+8) + orderWithdraw <- orderWithdrawVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 10, 10, attachment, fee, ts+9) + orderWithdrawInvalid <- orderWithdrawVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 10000, 10, attachment, fee, ts+9) + orderWithdrawInvalid2 <- orderWithdrawVStableSwapGen(master, regVStableSwapContract.contractId, Array[Byte](10), 10, 10, attachment, fee, ts+9) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, closeOrder, orderWithdraw, orderWithdrawInvalid, orderWithdrawInvalid2) + + property("unable to order withdraw") { + forAll(preconditionsAndVStableSwapOrderWithdraw) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, closeOrder: EC, orderWithdraw: EC, orderWithdrawInvalid: EC, orderWithdrawInvalid2: EC) => + + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(orderWithdraw.timestamp, Seq(orderWithdraw), TransactionStatus.Success)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Success + } + // withdraw from contract is greater than deposit into order + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(orderWithdrawInvalid.timestamp, Seq(orderWithdrawInvalid), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + + // order withdraw with wrong order id + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(orderWithdrawInvalid2.timestamp, Seq(orderWithdrawInvalid2), TransactionStatus.ContractStateMapNotDefined)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.ContractStateMapNotDefined + } + // order withdraw after order close + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(closeOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder, closeOrder))), + TestBlock.createWithTxStatus(orderWithdraw.timestamp, Seq(orderWithdraw), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVStableSwapOrderClose: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attachment) + <- createBaseTokenTargetTokenAndInitVStableSwap(1000, 1, 1000, 1000, + 1, 1000, 5, 1, 1) + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, 1000, 10, 1000, 10, 10, 1000, 1000, attachment, fee, ts+7) + closeOrder <- closeVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, attachment, fee, ts+8) + closeOrderInvalid <- closeVStableSwapGen(master, regVStableSwapContract.contractId, Array[Byte](10), attachment, fee, ts+8) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, closeOrder, closeOrderInvalid) + + property("unable to close order") { + forAll(preconditionsAndVStableSwapOrderClose) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, closeOrder: EC, closeOrderInvalid: EC) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(closeOrder.timestamp, Seq(closeOrder), TransactionStatus.Success)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // close order with wrong order id + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(closeOrderInvalid.timestamp, Seq(closeOrderInvalid), TransactionStatus.ContractStateMapNotDefined)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractStateMapNotDefined + } + } + } + val preconditionsAndVStableSwapBaseToTarget: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attachment) + <- createBaseTokenTargetTokenAndInitVStableSwap(Long.MaxValue, 1, Long.MaxValue, Long.MaxValue, + 1, Long.MaxValue, 5, 1, 1) + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, 1000, 10, 1000, 10, 1, 990, 100000, attachment, fee, ts+7) + setOrder1 <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, 1000, 10, 1000, 10, 1, 990, 10, attachment, fee, ts+7) + setOrder2 <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 10, Long.MaxValue, 10, 1000, 1, 1, 990, Long.MaxValue, attachment, fee, ts+7) + closeOrder <- closeVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, attachment, fee, ts+8) + swapBaseToTarget <- swapBaseToTargetVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 10, 0, 10, ts+100, attachment, fee, ts+9) + swapBaseToTargetInvalid <- swapBaseToTargetVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 10, 0, 20, ts+100, attachment, fee, ts+9) + swapBaseToTargetInvalid2 <- swapBaseToTargetVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 10, 10, 10, ts+100, attachment, fee, ts+9) + swapBaseToTargetInvalid3 <- swapBaseToTargetVStableSwapGen(master, regVStableSwapContract.contractId, setOrder1.id.arr, 10, 0, 10, ts+100, attachment, fee, ts+9) + swapBaseToTargetInvalid4 <- swapBaseToTargetVStableSwapGen(master, regVStableSwapContract.contractId, setOrder2.id.arr, Long.MaxValue, 0, 1, ts+100, attachment, fee, ts+9) + swapBaseToTargetInvalid5 <- swapBaseToTargetVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 10, 0, 10, ts-100, attachment, fee, ts+9) + swapBaseToTargetInvalid6 <- swapBaseToTargetVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 10000, 0, 10, ts+100, attachment, fee, ts+9) + swapBaseToTargetInvalid7 <- swapBaseToTargetVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 1, 0, 10, ts+100, attachment, fee, ts+9) + swapBaseToTargetInvalid8 <- swapBaseToTargetVStableSwapGen(master, regVStableSwapContract.contractId, Array[Byte](10), 10, 0, 10, ts+100, attachment, fee, ts+9) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, setOrder1, setOrder2, closeOrder, swapBaseToTarget, swapBaseToTargetInvalid, swapBaseToTargetInvalid2, + swapBaseToTargetInvalid3, swapBaseToTargetInvalid4, swapBaseToTargetInvalid5, swapBaseToTargetInvalid6, swapBaseToTargetInvalid7, swapBaseToTargetInvalid8) + + property("unable to swap base to target") { + forAll(preconditionsAndVStableSwapBaseToTarget) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, setOrder1: EC, setOrder2: EC, closeOrder: EC, swapBaseToTarget: EC, swapBaseToTargetInvalid: EC, swapBaseToTargetInvalid2: EC, + swapBaseToTargetInvalid3: EC, swapBaseToTargetInvalid4: EC, swapBaseToTargetInvalid5: EC, swapBaseToTargetInvalid6: EC, swapBaseToTargetInvalid7: EC, swapBaseToTargetInvalid8: EC) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(swapBaseToTarget.timestamp, Seq(swapBaseToTarget), TransactionStatus.Success)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // price is not equal to priceBase + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(swapBaseToTargetInvalid.timestamp, Seq(swapBaseToTargetInvalid), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // swapFee is not equal to baseFee + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(swapBaseToTargetInvalid2.timestamp, Seq(swapBaseToTargetInvalid2), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // swap base token with not enough depositing target token + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder1.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder1))), + TestBlock.createWithTxStatus(swapBaseToTargetInvalid3.timestamp, Seq(swapBaseToTargetInvalid3), TransactionStatus.ContractMapValueInsufficient)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // swap base token with not enough holding base token + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder2.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder2))), + TestBlock.createWithTxStatus(swapBaseToTargetInvalid4.timestamp, Seq(swapBaseToTargetInvalid4), TransactionStatus.ContractMapValueInsufficient)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // swap base token with wrong deadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositTarget.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget)), + TestBlock.create(setOrder.timestamp, Seq(setOrder))), + TestBlock.createWithTxStatus(swapBaseToTargetInvalid5.timestamp, Seq(swapBaseToTargetInvalid5), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // swap base token more than maxBase + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositTarget.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget)), + TestBlock.create(setOrder.timestamp, Seq(setOrder))), + TestBlock.createWithTxStatus(swapBaseToTargetInvalid6.timestamp, Seq(swapBaseToTargetInvalid6), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // swap base token less than minBase + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositTarget.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget)), + TestBlock.create(setOrder.timestamp, Seq(setOrder))), + TestBlock.createWithTxStatus(swapBaseToTargetInvalid7.timestamp, Seq(swapBaseToTargetInvalid7), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // swap base token with wrong order id + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositTarget.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget)), + TestBlock.create(setOrder.timestamp, Seq(setOrder))), + TestBlock.createWithTxStatus(swapBaseToTargetInvalid8.timestamp, Seq(swapBaseToTargetInvalid8), TransactionStatus.ContractStateMapNotDefined)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.ContractStateMapNotDefined + } + // swap base token after order close + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(closeOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder, closeOrder))), + TestBlock.createWithTxStatus(swapBaseToTarget.timestamp, Seq(swapBaseToTarget), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVStableSwapSameTokenId: Gen[(GenesisTransaction, RC, RC, EC, EC, EC, EC)] = for { + (master, ts, fee) <- basicContractTestGen() + genesis <- genesisVStableSwapGen(master, ts) + vStableSwapContract <- vStableSwapContractGen() + // Register base token + regTokenBase <- registerToken(master, 1000000, 100, "init", fee, ts) + tokenBaseContractId = regTokenBase.contractId + tokenBaseId = tokenIdFromBytes(tokenBaseContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + description <- validDescStringGen + initVStableSwapDataStack <- initVStableSwapDataStackGen(tokenBaseId.arr, tokenBaseId.arr, 5, 1, 1) + regVStableSwapContract <- registerVStableSwapGen(master, vStableSwapContract, initVStableSwapDataStack, description, fee, ts + 2) + issueTokenBase <- issueToken(master, tokenBaseContractId, 1000000, fee, ts + 3) + depositBase <- depositToken(master, tokenBaseContractId, master.toAddress.bytes.arr, regVStableSwapContract.contractId.bytes.arr, 1000, fee, ts + 4) + withdrawBase0 <- withdrawToken(master, tokenBaseContractId, regVStableSwapContract.contractId.bytes.arr, master.toAddress.bytes.arr, 0, fee, ts + 5) + withdrawBaseInvalid <- withdrawToken(master, tokenBaseContractId, regVStableSwapContract.contractId.bytes.arr, master.toAddress.bytes.arr, 1000, fee, ts + 5) + } yield (genesis, regTokenBase, regVStableSwapContract, issueTokenBase, depositBase, withdrawBase0, withdrawBaseInvalid) + + property("unable to deposit when token id is duplicated") { + forAll(preconditionsAndVStableSwapSameTokenId) { case (genesis: GenesisTransaction, regBase: RC, regVStableSwap: RC, issueBase: EC, depositBase: EC, _, _) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(issueBase.timestamp, Seq(regBase, regVStableSwap, issueBase))), + TestBlock.createWithTxStatus(depositBase.timestamp, Seq(depositBase), TransactionStatus.Failed)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + + forAll(preconditionsAndVStableSwapSameTokenId) { case (genesis: GenesisTransaction, regBase: RC, regVStableSwap: RC, issueBase: EC, _, withdraw0: EC, _) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(issueBase.timestamp, Seq(regBase, regVStableSwap, issueBase))), + TestBlock.createWithTxStatus(withdraw0.timestamp, Seq(withdraw0), TransactionStatus.Success)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + } + + forAll(preconditionsAndVStableSwapSameTokenId) { case (genesis: GenesisTransaction, regBase: RC, regVStableSwap: RC, issueBase: EC, _, _, withdrawInvalid: EC) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(issueBase.timestamp, Seq(regBase, regVStableSwap, issueBase))), + TestBlock.createWithTxStatus(withdrawInvalid.timestamp, Seq(withdrawInvalid), TransactionStatus.ContractTokenBalanceInsufficient)) { blockDiffEi => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractTokenBalanceInsufficient + } + } + } +} diff --git a/src/test/scala/vsys/blockchain/state/contract/vstableswap/ExecuteVStableSwapValidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vstableswap/ExecuteVStableSwapValidDiffTest.scala new file mode 100644 index 000000000..bd0b288c4 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vstableswap/ExecuteVStableSwapValidDiffTest.scala @@ -0,0 +1,521 @@ +package vsys.blockchain.state.contract.vstableswap + +import com.google.common.primitives.{Longs, Ints} +import vsys.account.ContractAccount.tokenIdFromBytes +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.token.SystemContractGen +import vsys.blockchain.contract.vstableswap.{VStableSwapContractGen, VStableSwapFunctionHelperGen} +import vsys.blockchain.state.diffs._ +import vsys.blockchain.state._ +import vsys.blockchain.contract.{DataEntry, DataType} +import vsys.blockchain.transaction.contract.{ExecuteContractFunctionTransaction => EC, RegisterContractTransaction => RC} +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} + +class ExecuteVStableSwapValidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with SystemContractGen + with VStableSwapContractGen + with VStableSwapFunctionHelperGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val preconditionsAndVStableSwapDepositToken: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC)] = for { + (genesis, _, _, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, _, _, _) + <- createBaseTokenTargetTokenAndInitVStableSwap( + 1000, // totalSupplyBase + 1, // unityBase + 1000, // issueAmountBase + 1000, // totalSupplyTarget + 1, // unityTarget + 1000, // issueAmountTarget + 5, // maxOrderPerUser + 1, // unitPriceBase + 1) // unitPriceTarget + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget) + + property("V Stable Swap able to deposit") { + forAll(preconditionsAndVStableSwapDepositToken) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC) => + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(regVStableSwap.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase))), + TestBlock.createWithTxStatus(depositBase.timestamp, Seq(depositBase), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val (contractTokenBaseBalanceKey, _) = getContractTokenBalanceKeys(regBase.contractId.bytes.arr, regTarget.contractId.bytes.arr, + regVStableSwap.contractId.bytes.arr) + + newState.tokenAccountBalance(contractTokenBaseBalanceKey) shouldEqual 1000L + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (masterTokenBaseBalanceKey, _) = getUserTokenBalanceKeys(regBase.contractId.bytes.arr, regTarget.contractId.bytes.arr, master) + + newState.tokenAccountBalance(masterTokenBaseBalanceKey) shouldEqual 0L + } + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(regVStableSwap.timestamp, Seq(regBase, regTarget, regVStableSwap, issueTarget))), + TestBlock.createWithTxStatus(depositBase.timestamp, Seq(depositTarget), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val (_, contractTokenTargetBalanceKey) = getContractTokenBalanceKeys(regBase.contractId.bytes.arr, regTarget.contractId.bytes.arr, + regVStableSwap.contractId.bytes.arr) + + newState.tokenAccountBalance(contractTokenTargetBalanceKey) shouldEqual 1000L + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (_, masterTokenTargetBalanceKey) = getUserTokenBalanceKeys(regBase.contractId.bytes.arr, regTarget.contractId.bytes.arr, master) + + newState.tokenAccountBalance(masterTokenTargetBalanceKey) shouldEqual 0L + } + } + } + + val preconditionsAndVStableSwapWithdrawToken: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, _) + <- createBaseTokenTargetTokenAndInitVStableSwap( + 1000, // totalSupplyBase + 1, // unityBase + 1000, // issueAmountBase + 1000, // totalSupplyTarget + 1, // unityTarget + 1000, // issueAmountTarget + 5, // maxOrderPerUser + 1, // unitPriceBase + 1) // unitPriceTarget + withdrawBase <- withdrawToken(master, regTokenBase.contractId, regVStableSwapContract.contractId.bytes.arr, master.toAddress.bytes.arr, 500L, fee, ts + 7) + withdrawTarget <- withdrawToken(master, regTokenTarget.contractId, regVStableSwapContract.contractId.bytes.arr, master.toAddress.bytes.arr, 500L, fee, ts + 8) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, withdrawBase, withdrawTarget) + + property("V Stable Swap able to withdraw") { + forAll(preconditionsAndVStableSwapWithdrawToken) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, withdrawBase: EC, withdrawTarget: EC) => + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositBase.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, depositBase))), + TestBlock.createWithTxStatus(withdrawBase.timestamp, Seq(withdrawBase), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val (contractTokenBaseBalanceKey, _) = getContractTokenBalanceKeys(regBase.contractId.bytes.arr, regTarget.contractId.bytes.arr, + regVStableSwap.contractId.bytes.arr) + + newState.tokenAccountBalance(contractTokenBaseBalanceKey) shouldEqual 500L + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (masterTokenBaseBalanceKey, _) = getUserTokenBalanceKeys(regBase.contractId.bytes.arr, regTarget.contractId.bytes.arr, master) + + newState.tokenAccountBalance(masterTokenBaseBalanceKey) shouldEqual 500L + } + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(regVStableSwap.timestamp, Seq(regBase, regTarget, regVStableSwap, issueTarget, depositTarget))), + TestBlock.createWithTxStatus(withdrawTarget.timestamp, Seq(withdrawTarget), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val (_, contractTokenTargetBalanceKey) = getContractTokenBalanceKeys(regBase.contractId.bytes.arr, regTarget.contractId.bytes.arr, + regVStableSwap.contractId.bytes.arr) + + newState.tokenAccountBalance(contractTokenTargetBalanceKey) shouldEqual 500L + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (_, masterTokenTargetBalanceKey) = getUserTokenBalanceKeys(regBase.contractId.bytes.arr, regTarget.contractId.bytes.arr, master) + + newState.tokenAccountBalance(masterTokenTargetBalanceKey) shouldEqual 500L + } + } + } + + val preconditionsAndVStableSwapSupersedeAndSetOrder: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, user, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attach) + <- createBaseTokenTargetTokenAndInitVStableSwap( + 1000, // totalSupplyBase + 1, // unityBase + 1000, // issueAmountBase + 1000, // totalSupplyTarget + 1, // unityTarget + 1000, // issueAmountTarget + 5, // maxOrderPerUser + 1, // unitPriceBase + 1) // unitPriceTarget + supersede <- supersedeVStableSwapGen(master, regVStableSwapContract.contractId, user.toAddress, attach, fee, ts + 5) + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 1, 1, 0, 100, 0, 100, 1, 1, 500, 500, attach, fee, ts + 6) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, supersede, setOrder) + + property("V Stable Swap able to supersede and set order") { + forAll(preconditionsAndVStableSwapSupersedeAndSetOrder) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, supersede: EC, setOrder: EC) => + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(depositBase.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, supersede))), + TestBlock.createWithTxStatus(setOrder.timestamp, Seq(setOrder), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (_, baseTokenIdKey, targetTokenIdKey, maxOrderPerUserKey, unitPriceBaseKey, unitPriceTargetKey) = getStableSwapContractStateVarKeys(regVStableSwap.contractId.bytes.arr) + + newState.contractInfo(baseTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regBase.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(targetTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regTarget.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(maxOrderPerUserKey) shouldBe Some(DataEntry(Longs.toByteArray(5), DataType.Amount)) + newState.contractInfo(unitPriceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + newState.contractInfo(unitPriceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + + val (baseTokenBalanceKey, targetTokenBalanceKey, userOrdersKey, orderOwnerKey, feeBaseKey, feeTargetKey, minBaseKey, maxBaseKey, + minTargetKey, maxTargetKey, priceBaseKey, priceTargetKey, baseTokenLockedKey, targetTokenLockedKey, orderStatusKey) = getStableSwapContractStateMapKeys(regVStableSwap.contractId.bytes.arr, setOrder.id.arr, master) + + newState.contractNumInfo(baseTokenBalanceKey) shouldBe 500L + newState.contractNumInfo(targetTokenBalanceKey) shouldBe 500L + newState.contractNumInfo(userOrdersKey) shouldBe 1L + newState.contractInfo(orderOwnerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(feeBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractInfo(feeTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractInfo(minBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(minTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(priceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractInfo(priceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractNumInfo(baseTokenLockedKey) shouldBe 500L + newState.contractNumInfo(targetTokenLockedKey) shouldBe 500L + newState.contractInfo(orderStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + } + } + } + + val preconditionsAndVStableSwapSetAndUpdateOrder: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attach) + <- createBaseTokenTargetTokenAndInitVStableSwap( + 1000, // totalSupplyBase + 1, // unityBase + 1000, // issueAmountBase + 1000, // totalSupplyTarget + 1, // unityTarget + 1000, // issueAmountTarget + 5, // maxOrderPerUser + 1, // unitPriceBase + 1) // unitPriceTarget + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 0, 100, 0, 100, 1, 1, 500, 500, attach, fee, ts + 5) + updateOrder <- updateVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 10, 10, 10, 100, 10, 100, 5, 5, attach, fee, ts + 6) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, updateOrder) + + property("V Stable Swap able to set and update orders") { + forAll(preconditionsAndVStableSwapSetAndUpdateOrder) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, updateOrder: EC) => + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(updateOrder.timestamp, Seq(updateOrder), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (makerKey, baseTokenIdKey, targetTokenIdKey, maxOrderPerUserKey, unitPriceBaseKey, unitPriceTargetKey) = getStableSwapContractStateVarKeys(regVStableSwap.contractId.bytes.arr) + + newState.contractInfo(makerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(baseTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regBase.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(targetTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regTarget.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(maxOrderPerUserKey) shouldBe Some(DataEntry(Longs.toByteArray(5), DataType.Amount)) + newState.contractInfo(unitPriceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + newState.contractInfo(unitPriceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + + val (baseTokenBalanceKey, targetTokenBalanceKey, userOrdersKey, orderOwnerKey, feeBaseKey, feeTargetKey, minBaseKey, maxBaseKey, + minTargetKey, maxTargetKey, priceBaseKey, priceTargetKey, baseTokenLockedKey, targetTokenLockedKey, orderStatusKey) = getStableSwapContractStateMapKeys(regVStableSwap.contractId.bytes.arr, setOrder.id.arr, master) + + newState.contractNumInfo(baseTokenBalanceKey) shouldBe 500L + newState.contractNumInfo(targetTokenBalanceKey) shouldBe 500L + newState.contractNumInfo(userOrdersKey) shouldBe 1L + newState.contractInfo(orderOwnerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(feeBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractInfo(feeTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractInfo(minBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractInfo(maxBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(minTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(10L), DataType.Amount)) + newState.contractInfo(maxTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(priceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(5L), DataType.Amount)) + newState.contractInfo(priceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(5L), DataType.Amount)) + newState.contractNumInfo(baseTokenLockedKey) shouldBe 500L + newState.contractNumInfo(targetTokenLockedKey) shouldBe 500L + newState.contractInfo(orderStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + } + } + } + + val preconditionsAndVStableSwapSetAndOrderDeposit: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attach) + <- createBaseTokenTargetTokenAndInitVStableSwap( + 1000, // totalSupplyBase + 1, // unityBase + 1000, // issueAmountBase + 1000, // totalSupplyTarget + 1, // unityTarget + 1000, // issueAmountTarget + 5, // maxOrderPerUser + 1, // unitPriceBase + 1) // unitPriceTarget + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 5, 5, 0, 100, 0, 100, 1, 1, 500, 500, attach, fee, ts + 5) + orderDeposit <- orderDepositVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 100, 100, attach, fee, ts + 6) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, orderDeposit) + + property("V Stable Swap able to set and deposit to orders") { + forAll(preconditionsAndVStableSwapSetAndOrderDeposit) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, orderDeposit: EC) => + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(orderDeposit.timestamp, Seq(orderDeposit), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (makerKey, baseTokenIdKey, targetTokenIdKey, maxOrderPerUserKey, unitPriceBaseKey, unitPriceTargetKey) = getStableSwapContractStateVarKeys(regVStableSwap.contractId.bytes.arr) + + newState.contractInfo(makerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(baseTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regBase.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(targetTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regTarget.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(maxOrderPerUserKey) shouldBe Some(DataEntry(Longs.toByteArray(5), DataType.Amount)) + newState.contractInfo(unitPriceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + newState.contractInfo(unitPriceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + + val (baseTokenBalanceKey, targetTokenBalanceKey, userOrdersKey, orderOwnerKey, feeBaseKey, feeTargetKey, minBaseKey, maxBaseKey, + minTargetKey, maxTargetKey, priceBaseKey, priceTargetKey, baseTokenLockedKey, targetTokenLockedKey, orderStatusKey) = getStableSwapContractStateMapKeys(regVStableSwap.contractId.bytes.arr, setOrder.id.arr, master) + + newState.contractNumInfo(baseTokenBalanceKey) shouldBe 400L + newState.contractNumInfo(targetTokenBalanceKey) shouldBe 400L + newState.contractNumInfo(userOrdersKey) shouldBe 1L + newState.contractInfo(orderOwnerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(feeBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(5L), DataType.Amount)) + newState.contractInfo(feeTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(5L), DataType.Amount)) + newState.contractInfo(minBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(minTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(priceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractInfo(priceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractNumInfo(baseTokenLockedKey) shouldBe 600L + newState.contractNumInfo(targetTokenLockedKey) shouldBe 600L + newState.contractInfo(orderStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + } + } + } + + val preconditionsAndVStableSwapSetAndOrderWithdraw: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attach) + <- createBaseTokenTargetTokenAndInitVStableSwap( + 1000, // totalSupplyBase + 1, // unityBase + 1000, // issueAmountBase + 1000, // totalSupplyTarget + 1, // unityTarget + 1000, // issueAmountTarget + 5, // maxOrderPerUser + 1, // unitPriceBase + 1) // unitPriceTarget + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 5, 5, 0, 100, 0, 100, 1, 1, 500, 500, attach, fee, ts + 5) + orderWithdraw <- orderWithdrawVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 100, 100, attach, fee, ts + 6) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, orderWithdraw) + + property("V Stable Swap able to set and withdraw from orders") { + forAll(preconditionsAndVStableSwapSetAndOrderWithdraw) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, orderWithdraw: EC) => + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(orderWithdraw.timestamp, Seq(orderWithdraw), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (makerKey, baseTokenIdKey, targetTokenIdKey, maxOrderPerUserKey, unitPriceBaseKey, unitPriceTargetKey) = getStableSwapContractStateVarKeys(regVStableSwap.contractId.bytes.arr) + + newState.contractInfo(makerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(baseTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regBase.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(targetTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regTarget.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(maxOrderPerUserKey) shouldBe Some(DataEntry(Longs.toByteArray(5), DataType.Amount)) + newState.contractInfo(unitPriceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + newState.contractInfo(unitPriceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + + val (baseTokenBalanceKey, targetTokenBalanceKey, userOrdersKey, orderOwnerKey, feeBaseKey, feeTargetKey, minBaseKey, maxBaseKey, + minTargetKey, maxTargetKey, priceBaseKey, priceTargetKey, baseTokenLockedKey, targetTokenLockedKey, orderStatusKey) = getStableSwapContractStateMapKeys(regVStableSwap.contractId.bytes.arr, setOrder.id.arr, master) + + newState.contractNumInfo(baseTokenBalanceKey) shouldBe 600L + newState.contractNumInfo(targetTokenBalanceKey) shouldBe 600L + newState.contractNumInfo(userOrdersKey) shouldBe 1L + newState.contractInfo(orderOwnerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(feeBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(5L), DataType.Amount)) + newState.contractInfo(feeTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(5L), DataType.Amount)) + newState.contractInfo(minBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(minTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(priceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractInfo(priceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractNumInfo(baseTokenLockedKey) shouldBe 400L + newState.contractNumInfo(targetTokenLockedKey) shouldBe 400L + newState.contractInfo(orderStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + } + } + } + + val preconditionsAndVStableSwapSetAndOrderClose: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attach) + <- createBaseTokenTargetTokenAndInitVStableSwap( + 1000, // totalSupplyBase + 1, // unityBase + 1000, // issueAmountBase + 1000, // totalSupplyTarget + 1, // unityTarget + 1000, // issueAmountTarget + 5, // maxOrderPerUser + 1, // unitPriceBase + 1) // unitPriceTarget + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 1, 1, 0, 100, 0, 100, 1, 1, 500, 500, attach, fee, ts + 5) + orderClose <- closeVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, attach, fee, ts + 6) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, orderClose) + + property("V Stable Swap able to close orders") { + forAll(preconditionsAndVStableSwapSetAndOrderClose) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, orderClose: EC) => + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(orderClose.timestamp, Seq(orderClose), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (makerKey, baseTokenIdKey, targetTokenIdKey, maxOrderPerUserKey, unitPriceBaseKey, unitPriceTargetKey) = getStableSwapContractStateVarKeys(regVStableSwap.contractId.bytes.arr) + + newState.contractInfo(makerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + + newState.contractInfo(baseTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regBase.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(targetTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regTarget.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(maxOrderPerUserKey) shouldBe Some(DataEntry(Longs.toByteArray(5), DataType.Amount)) + newState.contractInfo(unitPriceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + newState.contractInfo(unitPriceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + + val (baseTokenBalanceKey, targetTokenBalanceKey, userOrdersKey, orderOwnerKey, feeBaseKey, feeTargetKey, minBaseKey, maxBaseKey, + minTargetKey, maxTargetKey, priceBaseKey, priceTargetKey, baseTokenLockedKey, targetTokenLockedKey, orderStatusKey) = getStableSwapContractStateMapKeys(regVStableSwap.contractId.bytes.arr, setOrder.id.arr, master) + + newState.contractNumInfo(baseTokenBalanceKey) shouldBe 1000L + newState.contractNumInfo(targetTokenBalanceKey) shouldBe 1000L + newState.contractNumInfo(userOrdersKey) shouldBe 0L + newState.contractInfo(orderOwnerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(feeBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractInfo(feeTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractInfo(minBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(minTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(priceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractInfo(priceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractNumInfo(baseTokenLockedKey) shouldBe 0L + newState.contractNumInfo(targetTokenLockedKey) shouldBe 0L + newState.contractInfo(orderStatusKey) shouldBe Some(DataEntry(Array(0.toByte), DataType.Boolean)) + } + } + } + + val preconditionsAndVStableSwapSetAndSwapBaseToTarget: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attach) + <- createBaseTokenTargetTokenAndInitVStableSwap( + 1000, // totalSupplyBase + 1, // unityBase + 1000, // issueAmountBase + 1000, // totalSupplyTarget + 1, // unityTarget + 1000, // issueAmountTarget + 5, // maxOrderPerUser + 1, // unitPriceBase + 1) // unitPriceTarget + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 0, 100, 0, 100, 1, 1, 500, 500, attach, fee, ts + 5) + swapBaseToTarget <- swapBaseToTargetVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 100, 0, 1, ts + 10, attach, fee, ts + 6) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, swapBaseToTarget) + + property("V Stable Swap able to swap base to target") { + forAll(preconditionsAndVStableSwapSetAndSwapBaseToTarget) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, swapBaseToTarget: EC) => + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(swapBaseToTarget.timestamp, Seq(swapBaseToTarget), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (makerKey, baseTokenIdKey, targetTokenIdKey, maxOrderPerUserKey, unitPriceBaseKey, unitPriceTargetKey) = getStableSwapContractStateVarKeys(regVStableSwap.contractId.bytes.arr) + + newState.contractInfo(makerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(baseTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regBase.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(targetTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regTarget.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(maxOrderPerUserKey) shouldBe Some(DataEntry(Longs.toByteArray(5), DataType.Amount)) + newState.contractInfo(unitPriceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + newState.contractInfo(unitPriceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + + val (baseTokenBalanceKey, targetTokenBalanceKey, userOrdersKey, orderOwnerKey, feeBaseKey, feeTargetKey, minBaseKey, maxBaseKey, + minTargetKey, maxTargetKey, priceBaseKey, priceTargetKey, baseTokenLockedKey, targetTokenLockedKey, orderStatusKey) = getStableSwapContractStateMapKeys(regVStableSwap.contractId.bytes.arr, setOrder.id.arr, master) + + newState.contractNumInfo(baseTokenBalanceKey) shouldBe 400L + newState.contractNumInfo(targetTokenBalanceKey) shouldBe 600L + newState.contractNumInfo(userOrdersKey) shouldBe 1L + newState.contractInfo(orderOwnerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(feeBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(feeTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(minBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(minTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(priceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractInfo(priceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractNumInfo(baseTokenLockedKey) shouldBe 600L + newState.contractNumInfo(targetTokenLockedKey) shouldBe 400L + newState.contractInfo(orderStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + } + } + } + + val preconditionsAndVStableSwapSetAndSwapTargetToBase: Gen[(GenesisTransaction, RC, RC, RC, EC, EC, EC, EC, EC, EC)] = for { + (genesis, _, master, _, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, fee, ts, attach) + <- createBaseTokenTargetTokenAndInitVStableSwap( + 1000, // totalSupplyBase + 1, // unityBase + 1000, // issueAmountBase + 1000, // totalSupplyTarget + 1, // unityTarget + 1000, // issueAmountTarget + 5, // maxOrderPerUser + 1, // unityPriceBase + 1) // unitPriceTarget + setOrder <- setOrderVStableSwapGen(master, regVStableSwapContract.contractId, 0, 0, 0, 100, 0, 100, 1, 1, 500, 500, attach, fee, ts + 5) + swapTargetToBase <- swapTargetToBaseVStableSwapGen(master, regVStableSwapContract.contractId, setOrder.id.arr, 100, 0, 1, ts + 10, attach, fee, ts + 6) + } yield (genesis, regTokenBase, regTokenTarget, regVStableSwapContract, issueTokenBase, issueTokenTarget, depositBase, depositTarget, setOrder, swapTargetToBase) + + property("V Stable Swap able to swap target to base") { + forAll(preconditionsAndVStableSwapSetAndSwapTargetToBase) { case (genesis: GenesisTransaction, regBase: RC, regTarget: RC, + regVStableSwap: RC, issueBase: EC, issueTarget: EC, depositBase: EC, depositTarget: EC, setOrder: EC, swapTargetToBase: EC) => + assertDiffAndState(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(setOrder.timestamp, Seq(regBase, regTarget, regVStableSwap, issueBase, issueTarget, depositBase, depositTarget, setOrder))), + TestBlock.createWithTxStatus(swapTargetToBase.timestamp, Seq(swapTargetToBase), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = regBase.proofs.firstCurveProof.explicitGet().publicKey + + val (makerKey, baseTokenIdKey, targetTokenIdKey, maxOrderPerUserKey, unitPriceBaseKey, unitPriceTargetKey) = getStableSwapContractStateVarKeys(regVStableSwap.contractId.bytes.arr) + + newState.contractInfo(makerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(baseTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regBase.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(targetTokenIdKey) shouldBe Some(DataEntry(tokenIdFromBytes(regTarget.contractId.bytes.arr, Ints.toByteArray(0)).right.get.arr, DataType.TokenId)) + newState.contractInfo(maxOrderPerUserKey) shouldBe Some(DataEntry(Longs.toByteArray(5), DataType.Amount)) + newState.contractInfo(unitPriceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + newState.contractInfo(unitPriceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1), DataType.Amount)) + + val (baseTokenBalanceKey, targetTokenBalanceKey, userOrdersKey, orderOwnerKey, feeBaseKey, feeTargetKey, minBaseKey, maxBaseKey, + minTargetKey, maxTargetKey, priceBaseKey, priceTargetKey, baseTokenLockedKey, targetTokenLockedKey, orderStatusKey) = getStableSwapContractStateMapKeys(regVStableSwap.contractId.bytes.arr, setOrder.id.arr, master) + + newState.contractNumInfo(baseTokenBalanceKey) shouldBe 600L + newState.contractNumInfo(targetTokenBalanceKey) shouldBe 400L + newState.contractNumInfo(userOrdersKey) shouldBe 1L + newState.contractInfo(orderOwnerKey) shouldBe Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(feeBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(feeTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(minBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(minTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(0L), DataType.Amount)) + newState.contractInfo(maxTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(100L), DataType.Amount)) + newState.contractInfo(priceBaseKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractInfo(priceTargetKey) shouldBe Some(DataEntry(Longs.toByteArray(1L), DataType.Amount)) + newState.contractNumInfo(baseTokenLockedKey) shouldBe 400L + newState.contractNumInfo(targetTokenLockedKey) shouldBe 600L + newState.contractInfo(orderStatusKey) shouldBe Some(DataEntry(Array(1.toByte), DataType.Boolean)) + } + } + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/state/contract/vstableswap/RegisterVStableSwapDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vstableswap/RegisterVStableSwapDiffTest.scala new file mode 100644 index 000000000..d212a7d46 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vstableswap/RegisterVStableSwapDiffTest.scala @@ -0,0 +1,59 @@ +package vsys.blockchain.state.contract.vstableswap + +import cats.Monoid +import com.google.common.primitives.{Bytes, Ints} +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.vstableswap.VStableSwapContractGen +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen} +import vsys.blockchain.contract._ +import vsys.blockchain.state._ +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.contract.RegisterContractTransaction + +class RegisterVStableSwapDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with VStableSwapContractGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val stableSwapContract: Gen[Contract] = vStableSwapContractGen() + + val preconditionsAndStableSwapContractTest: Gen[(GenesisTransaction, RegisterContractTransaction, Long)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contract <- stableSwapContract + description <- validDescStringGen + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + dataStack <- initVStableSwapDataStackGen(tokenId.arr, tokenId.arr, 5, 1, 1) + regContract <- registerVStableSwapGen(master, contract, dataStack, description, fee + 10000000000L, ts) + genesis <- genesisVStableSwapGen(master, ts) + } yield (genesis, regContract, fee + 10000000000L) + + property("register stable swap contract function transactions doesn't break invariant") { + forAll(preconditionsAndStableSwapContractTest) { case (genesis, reg: RegisterContractTransaction, fee: Long) => + assertDiffAndState(Seq(TestBlock.create(Seq(genesis))), TestBlock.create(Seq(reg))) { (blockDiff, newState) => + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + totalPortfolioDiff.balance shouldBe -fee + totalPortfolioDiff.effectiveBalance shouldBe -fee + val master = reg.proofs.firstCurveProof.explicitGet().publicKey + val contractId = reg.contractId.bytes + val makerKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte))) + val tokenIdKey = ByteStr(Bytes.concat(contractId.arr, Array(1.toByte))) + val tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + val (_, masterTxs) = newState.accountTransactionIds(master, 2, 0) + masterTxs.size shouldBe 2 // genesis, reg + newState.contractContent(contractId) shouldEqual Some((2, reg.id, ContractVStableSwap.contract)) + newState.contractInfo(makerKey) shouldEqual Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + newState.contractInfo(tokenIdKey) shouldEqual Some(DataEntry(tokenId.arr, DataType.TokenId)) + } + } + } +} diff --git a/src/test/scala/vsys/blockchain/state/contract/vstableswap/VStableSwapOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vstableswap/VStableSwapOpcDiffTest.scala new file mode 100644 index 000000000..b45123b9c --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vstableswap/VStableSwapOpcDiffTest.scala @@ -0,0 +1,95 @@ +package vsys.blockchain.state.contract.vstableswap + +import com.google.common.primitives.Ints +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import vsys.blockchain.state.EitherExt2 +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidOPCData} +import vsys.blockchain.contract._ +import vsys.blockchain.contract.vstableswap.{VStableSwapContractGen, VStableSwapFunction} +import vsys.blockchain.transaction.contract._ + +class VStableSwapOpcDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with VStableSwapContractGen + with VStableSwapFunction { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val regContract: Gen[Contract] = + Gen.const(Contract.buildContract(ContractVStableSwap.contract.languageCode, ContractVStableSwap.contract.languageVersion, + ContractVStableSwap.contract.trigger, ContractVStableSwap.contract.descriptor, ContractVStableSwap.contract.stateVar, + ContractVStableSwap.contract.stateMap, ContractVStableSwap.contract.textual).explicitGet()) + + val preconditionsAndRegContract: Gen[(GenesisTransaction, RegisterContractTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contract <- regContract + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + data <- initVStableSwapDataStackGen(tokenId.arr, tokenId.arr, 5, 1, 1) + description <- validDescStringGen + genesis <- genesisVStableSwapGen(master, ts) + create <- registerVStableSwapGen(master, contract, data, description, fee, ts + 1) + } yield (genesis, create) + + property("register contract transaction pass OpcFunDiff"){ + forAll(preconditionsAndRegContract) { case (genesis, create) => + assertOpcFuncDifferEi(2, None, create) { opcFunDiffEi => + opcFunDiffEi shouldBe an[Right[_, _]] + } + assertDiffEi(Seq(TestBlock.create(Seq(genesis))), TestBlock.createWithTxStatus(Seq(create), TransactionStatus.Success)) { blockDiffEi => + blockDiffEi shouldBe an[Right[_, _]] + } + } + } + + val regWrongParaContract: Gen[Contract] = + Gen.const(Contract.buildContract(ContractVStableSwap.contract.languageCode, ContractVStableSwap.contract.languageVersion, + wrongDataTrigger, ContractVStableSwap.contract.descriptor, ContractVStableSwap.contract.stateVar, + ContractVStableSwap.contract.stateMap, ContractVStableSwap.contract.textual).explicitGet()) + val preconditionsAndRegContractWrongPara: Gen[RegisterContractTransaction] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contract <- regWrongParaContract + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + data <- initVStableSwapDataStackGen(tokenId.arr, tokenId.arr, 5, 1, 1) + description <- validDescStringGen + create <- registerVStableSwapGen(master, contract, data, description, fee, ts) + } yield create + + property("register contract transaction cannot pass due to wrong list of parameters"){ + forAll(preconditionsAndRegContractWrongPara) { case create => + assertOpcFuncDifferEi(2, None, create) { opcFunDiffEi => + opcFunDiffEi shouldBe Left(ContractDataTypeMismatch) + } + } + } + + val regWrongOpcFunContract: Gen[Contract] = + Gen.const(Contract.buildContract(ContractVStableSwap.contract.languageCode, ContractVStableSwap.contract.languageVersion, + wrongOpcTrigger, ContractVStableSwap.contract.descriptor, ContractVStableSwap.contract.stateVar, + ContractVStableSwap.contract.stateMap, ContractVStableSwap.contract.textual).explicitGet()) + val preconditionsAndRegContractWrongFun: Gen[RegisterContractTransaction] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + contract <- regWrongOpcFunContract + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + data <- initVStableSwapDataStackGen(tokenId.arr, tokenId.arr, 5, 1, 1) + description <- validDescStringGen + create <- registerVStableSwapGen(master, contract, data, description, fee, ts) + } yield create + + property("register contract transaction cannot pass due to wrong opcode"){ + forAll(preconditionsAndRegContractWrongFun) { case create => + assertOpcFuncDifferEi(2, None, create) { opcFunDiffEi => + opcFunDiffEi shouldBe Left(ContractInvalidOPCData) + } + } + } +} diff --git a/src/test/scala/vsys/blockchain/state/contract/vswap/ExecuteVSwapInvalidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vswap/ExecuteVSwapInvalidDiffTest.scala new file mode 100644 index 000000000..e2abafac7 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vswap/ExecuteVSwapInvalidDiffTest.scala @@ -0,0 +1,435 @@ +package vsys.blockchain.state.contract.vswap + +import com.google.common.primitives.Ints +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.token.{SystemContractGen, TokenContractGen} +import vsys.blockchain.contract.vswap.{VSwapContractGen, VSwapFunctionHelperGen} +import vsys.blockchain.contract.ContractGenHelper.basicContractTestGen +import vsys.blockchain.contract.DataEntry +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.contract._ +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import vsys.blockchain.state._ + +class ExecuteVSwapInvalidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with TokenContractGen + with SystemContractGen + with VSwapContractGen + with VSwapFunctionHelperGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val preconditionsAndVSwapDepositAndWithdrawLiquidityTokens: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, _, _, _, regLiquidity, regVSwap, _, _, issueLiquidity, _, _, depositLiquidity, fee, ts, attach) <- createABLiquidityTokenAndInitVSwap(1000, 1, 1000, 1000, 1, 1000, + 1000, 1000,10, 100, 100) + withdrawLiquidity <- withdrawToken(master, regLiquidity.contractId, regVSwap.contractId.bytes.arr, master.toAddress.bytes.arr, 100L, fee, ts + 12) + withdrawInvalidLiquidity <- withdrawToken(master, regLiquidity.contractId, regVSwap.contractId.bytes.arr, master.toAddress.bytes.arr, 2000L, fee, ts + 12) + } yield (genesis, genesis2, regLiquidity, regVSwap, issueLiquidity, depositLiquidity, withdrawLiquidity, withdrawInvalidLiquidity) + + // withdraw liquidity tokens, withdraw token A and B have been local tested successfully + property("withdraw liquidity tokens more than depositing in vswap contract") { + forAll(preconditionsAndVSwapDepositAndWithdrawLiquidityTokens) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueLiquidity: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, withdrawLiquidity: ExecuteContractFunctionTransaction, withdrawInvalidLiquidity: ExecuteContractFunctionTransaction) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositLiquidity.timestamp, Seq(regLiquidity, regVSwap, issueLiquidity, depositLiquidity))), + TestBlock.createWithTxStatus(withdrawLiquidity.timestamp, Seq(withdrawLiquidity), TransactionStatus.Success)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositLiquidity.timestamp, Seq(regLiquidity, regVSwap, issueLiquidity, depositLiquidity))), + TestBlock.createWithTxStatus(withdrawInvalidLiquidity.timestamp, Seq(withdrawInvalidLiquidity), TransactionStatus.ContractTokenBalanceInsufficient)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractTokenBalanceInsufficient + } + } + } + + val preconditionsAndVSwapSetSwap: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, _, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap(1000, 1, 1000, 1000, 1, 1000, 1000, 1000,9, 100, 100) + + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 10L, 10L, attach, fee + 10000000000L, ts) + setInvalidSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 10L, attach, fee + 10000000000L, ts) + setInvalidSwap2 <- setSwapVSwapGen(master, regVSwap.contractId, 8L, 10L, attach, fee + 10000000000L, ts) + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, setInvalidSwap, setInvalidSwap2) + + property("set swap more than deposit") { + forAll(preconditionsAndVSwapSetSwap) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, setInvalidSwap: ExecuteContractFunctionTransaction, setInvalidSwap2: ExecuteContractFunctionTransaction) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositLiquidity.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(setSwap), TransactionStatus.Success)) { (blockDiff) => + blockDiff.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // set swap token A more than deposit, set swap token B more than deposit has been local tested successfully + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositLiquidity.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity))), + TestBlock.createWithTxStatus(setInvalidSwap.timestamp, Seq(setInvalidSwap), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // set swap less than minimum liquidity (amountADesired), set swap less than minimum liquidity (amountBDesired) has been local tested successfully + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositLiquidity.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity))), + TestBlock.createWithTxStatus(setInvalidSwap2.timestamp, Seq(setInvalidSwap2), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVSwapAddLiquidity: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap(10000, 1, 10000, 10000, 1, 10000, + 10000, 1000,10, 3000, 3000) + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts) + + addLiquidity <- addLiquidityVSwapGen(master, regVSwap.contractId, 3000L, 2000L, 900L, 900L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + addInvalidLiquidity <- addLiquidityVSwapGen(master, regVSwap.contractId, 2001, 2001L, 900L, 900L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + addInvalidLiquidity2 <- addLiquidityVSwapGen(master, regVSwap.contractId, 2000L, 2000L, 9000L, 900L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + addInvalidLiquidity3 <- addLiquidityVSwapGen(master, regVSwap.contractId, 2000L, 2000L, 900L, 900L, ts - 1000000000000L, attach, fee + 10000000000L, ts) + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, addLiquidity, addInvalidLiquidity, addInvalidLiquidity2, addInvalidLiquidity3) + + property("unable to add liquidity") { + forAll(preconditionsAndVSwapAddLiquidity) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, addLiquidity: ExecuteContractFunctionTransaction, + addInvalidLiquidity: ExecuteContractFunctionTransaction, addInvalidLiquidity2: ExecuteContractFunctionTransaction, addInvalidLiquidity3: ExecuteContractFunctionTransaction) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(addLiquidity.timestamp, Seq(addLiquidity), TransactionStatus.Success)) { (blockDiff) => + blockDiff.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // add liquidity more than tokenADepositAmount, add liquidity more than tokenBDepositAmount has been locally tested successfully + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(addInvalidLiquidity.timestamp, Seq(addInvalidLiquidity), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // add liquidity less than amountAMin, add liquidity less than amountBMin has been locally tested successfully + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(addInvalidLiquidity2.timestamp, Seq(addInvalidLiquidity2), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // add liquidity with a wrong deadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(addInvalidLiquidity3.timestamp, Seq(addInvalidLiquidity3), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVSwapRemoveLiquidity: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, _, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap(10000, 1, 10000, 10000, 1, + 10000, 10000, 1000,10, 2000, 2000) + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts) + + removeLiquidity <- removeLiquidityVSwapGen(master, regVSwap.contractId, 100L, 100L, 100L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + removeInvalidLiquidity <- removeLiquidityVSwapGen(master, regVSwap.contractId, 2000L, 100L, 100L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + removeInvalidLiquidity2 <- removeLiquidityVSwapGen(master, regVSwap.contractId, 100L, 200L, 100L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + removeInvalidLiquidity3 <- removeLiquidityVSwapGen(master, regVSwap.contractId, 100L, 100L, 100L, ts - 1000000000000L, attach, fee + 10000000000L, ts) + + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, removeLiquidity, removeInvalidLiquidity, removeInvalidLiquidity2, removeInvalidLiquidity3) + + property("unable to remove liquidity") { + forAll(preconditionsAndVSwapRemoveLiquidity) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, + removeLiquidity: ExecuteContractFunctionTransaction, removeInvalidLiquidity: ExecuteContractFunctionTransaction, removeInvalidLiquidity2: ExecuteContractFunctionTransaction, removeInvalidLiquidity3: ExecuteContractFunctionTransaction) => + + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(removeLiquidity.timestamp, Seq(removeLiquidity), TransactionStatus.Success)) { (blockDiff) => + blockDiff.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // remove liquidity more than in pool + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(removeInvalidLiquidity.timestamp, Seq(removeInvalidLiquidity), TransactionStatus.ContractMapValueInsufficient)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.ContractMapValueInsufficient + } + // remove liquidity with wrong amountAMin, remove liquidity with wrong amountBMin has been local tested successfully + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(removeInvalidLiquidity2.timestamp, Seq(removeInvalidLiquidity2), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // remove liquidity with wrong removeLiquidityDeadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(removeInvalidLiquidity3.timestamp, Seq(removeInvalidLiquidity3), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVSwapSwapTokenForExactBaseToken: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, _, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap(10000, 1, 10000, 10000, 1, 10000, + 10000, 1000,10, 2000, 2000) + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts) + + swapTokenForExactBaseToken <- swapTokenForExactBaseTokenVSwapGen(master, regVSwap.contractId, 100L, 112L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + swapInvalidTokenForExactBaseToken <- swapTokenForExactBaseTokenVSwapGen(master, regVSwap.contractId, 100L, 111L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + swapInvalidTokenForExactBaseToken2 <- swapTokenForExactBaseTokenVSwapGen(master, regVSwap.contractId, 100L, 112L, ts - 1000000000000L, attach, fee + 10000000000L, ts) + + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, swapTokenForExactBaseToken, swapInvalidTokenForExactBaseToken, swapInvalidTokenForExactBaseToken2) + + property("unable to swap tokens for exact base token") { + forAll(preconditionsAndVSwapSwapTokenForExactBaseToken) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, swapTokenForExactBaseToken: ExecuteContractFunctionTransaction, swapInvalidTokenForExactBaseToken: ExecuteContractFunctionTransaction, swapInvalidTokenForExactBaseToken2: ExecuteContractFunctionTransaction) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(swapTokenForExactBaseToken), TransactionStatus.Success)) { (blockDiff) => + blockDiff.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // swap tokens for exact base token with wrong total supply + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(swapInvalidTokenForExactBaseToken.timestamp, Seq(swapInvalidTokenForExactBaseToken), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + // swap tokens for exact base token with wrong swapDeadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(swapInvalidTokenForExactBaseToken2.timestamp, Seq(swapInvalidTokenForExactBaseToken2), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + } + + val preconditionsAndVSwapSwapExactTokenForBaseToken: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, _, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap(10000, 1, 10000, 10000, 1, 10000, + 10000, 1000,10, 2000, 2000) + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts) + + swapExactTokenForBaseToken <- swapExactTokenForBaseTokenVSwapGen(master, regVSwap.contractId, 496, 990L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + swapInvalidExactTokenForBaseToken <- swapExactTokenForBaseTokenVSwapGen(master, regVSwap.contractId, 497, 990L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + swapInvalidExactTokenForBaseToken2 <- swapExactTokenForBaseTokenVSwapGen(master, regVSwap.contractId, 496, 990L, ts - 1000000000000L, attach, fee + 10000000000L, ts) + + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, swapExactTokenForBaseToken, swapInvalidExactTokenForBaseToken, swapInvalidExactTokenForBaseToken2) + + property("unable to swap exact token for base token") { + forAll(preconditionsAndVSwapSwapExactTokenForBaseToken) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, swapExactTokenForBaseToken: ExecuteContractFunctionTransaction, + swapInvalidExactTokenForBaseToken: ExecuteContractFunctionTransaction, swapInvalidExactTokenForBaseToken2: ExecuteContractFunctionTransaction) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(swapExactTokenForBaseToken), TransactionStatus.Success)) { (blockDiff) => + blockDiff.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // swap exact token for base token with wrong total supply + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(swapInvalidExactTokenForBaseToken.timestamp, Seq(swapInvalidExactTokenForBaseToken), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // swap exact token for base token with wrong deadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(swapInvalidExactTokenForBaseToken2.timestamp, Seq(swapInvalidExactTokenForBaseToken2), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVSwapSwapTokenForExactTargetToken: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, _, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap(10000, 1, 10000, 10000, 1, 10000, + 10000, 1000,10, 2000, 2000) + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts) + + swapTokenForExactTargetToken <- swapTokenForExactTargetTokenVSwapGen(master, regVSwap.contractId, 100L, 112L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + swapInvalidTokenForExactTargetToken <- swapTokenForExactTargetTokenVSwapGen(master, regVSwap.contractId, 100L, 111L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + swapInvalidTokenForExactTargetToken2 <- swapTokenForExactTargetTokenVSwapGen(master, regVSwap.contractId, 100L, 112L, ts - 1000000000000L, attach, fee + 10000000000L, ts) + + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, swapTokenForExactTargetToken, swapInvalidTokenForExactTargetToken, swapInvalidTokenForExactTargetToken2) + + property("unable to swap token for exact target token") { + forAll(preconditionsAndVSwapSwapTokenForExactTargetToken) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, swapTokenForExactTargetToken: ExecuteContractFunctionTransaction, + swapInvalidTokenForExactTargetToken: ExecuteContractFunctionTransaction, swapInvalidTokenForExactTargetToken2: ExecuteContractFunctionTransaction) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(regTokenA.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(swapTokenForExactTargetToken), TransactionStatus.Success)) { (blockDiff) => + blockDiff.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // swap token for exact target token with wrong total supply + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(swapInvalidTokenForExactTargetToken.timestamp, Seq(swapInvalidTokenForExactTargetToken), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // swap token for exact target token with wrong deadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(swapInvalidTokenForExactTargetToken2.timestamp, Seq(swapInvalidTokenForExactTargetToken2), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVSwapSwapExactTokenForTargetToken: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap(10000, 1, 10000, 10000, 1, 10000, + 10000, 1000,10, 2000, 2000) + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts) + + swapExactTokenForTargetToken <- swapExactTokenForTargetTokenVSwapGen(master, regVSwap.contractId, 90L, 100L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + swapInvalidExactTokenForTargetToken <- swapExactTokenForTargetTokenVSwapGen(master, regVSwap.contractId, 91L, 100L, ts + 1000000000000L, attach, fee + 10000000000L, ts) + + swapInvalidExactTokenForTargetToken2 <- swapExactTokenForTargetTokenVSwapGen(master, regVSwap.contractId, 90L, 100L, ts - 1000000000000L, attach, fee + 10000000000L, ts) + + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, swapExactTokenForTargetToken, swapInvalidExactTokenForTargetToken, swapInvalidExactTokenForTargetToken2) + + property("unable to swap exact token for target token") { + forAll(preconditionsAndVSwapSwapExactTokenForTargetToken) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, swapExactTokenForTargetToken: ExecuteContractFunctionTransaction, + swapInvalidExactTokenForTargetToken: ExecuteContractFunctionTransaction, swapInvalidExactTokenForTargetToken2: ExecuteContractFunctionTransaction) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(swapExactTokenForTargetToken), TransactionStatus.Success)) { (blockDiff) => + blockDiff.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Success + } + // swap exact token for target token with wrong total supply + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(swapInvalidExactTokenForTargetToken.timestamp, Seq(swapInvalidExactTokenForTargetToken), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.explicitGet().txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + // swap exact token for target token with wrong deadline + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(setSwap.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(swapInvalidExactTokenForTargetToken2.timestamp, Seq(swapInvalidExactTokenForTargetToken2), TransactionStatus.Failed)) { (blockDiffEi, _) => + blockDiffEi.txsDiff.contractNumDB.isEmpty shouldBe true + blockDiffEi.txsDiff.portfolios.isEmpty shouldBe false + blockDiffEi.txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } + + val preconditionsAndVSwapDepositSameInputTokens: Gen[(GenesisTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction)] = for { + (master, ts, fee) <- basicContractTestGen() + genesis <- genesisVSwapGen(master, ts) + vSwapContract <- vSwapContractGen() + // register base token + regTokenAContract <- registerToken(master, 1000000000, 1000, "init", fee + 10000000000L, ts) + tokenAContractId = regTokenAContract.contractId + tokenAId = tokenIdFromBytes(tokenAContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + + // issue base token + attach <- genBoundedString(2, ExecuteContractFunctionTransaction.MaxDescriptionSize) + issueTokenA <- issueToken(master, tokenAContractId, 1000000000, fee, ts + 5) + + // register VSwap contract with same token Ids + description <- validDescStringGen + initVSwapDataStack: Seq[DataEntry] <- initVSwapDataStackGen(tokenAId.arr, tokenAId.arr, tokenAId.arr, 100) + regVSwapContract <- registerVSwapGen(master, vSwapContract, initVSwapDataStack, description, fee + 10000000000L, ts + 4) + vSwapContractId = regVSwapContract.contractId + + invalidDeposit <- depositToken(master, tokenAContractId, master.toAddress.bytes.arr, vSwapContractId.bytes.arr, 1000000000, fee + 10000000000L, ts + 9) + } yield (genesis, regTokenAContract, regVSwapContract, issueTokenA, invalidDeposit) + + property("unable to deposit tokens when 3 input token ids are the same") { + forAll(preconditionsAndVSwapDepositSameInputTokens) { case (genesis: GenesisTransaction, regTokenAContract: RegisterContractTransaction, regVSwapContract: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + invalidDeposit: ExecuteContractFunctionTransaction) => + assertDiffEi(Seq(TestBlock.create(genesis.timestamp, Seq(genesis)), TestBlock.create(issueTokenA.timestamp, Seq(regTokenAContract, regVSwapContract, issueTokenA))), + TestBlock.createWithTxStatus(invalidDeposit.timestamp, Seq(invalidDeposit), TransactionStatus.Failed)) { (blockDiffEi) => + blockDiffEi.explicitGet().txsDiff.txStatus shouldBe TransactionStatus.Failed + } + } + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/state/contract/vswap/ExecuteVSwapValidDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vswap/ExecuteVSwapValidDiffTest.scala new file mode 100644 index 000000000..5b7309648 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vswap/ExecuteVSwapValidDiffTest.scala @@ -0,0 +1,540 @@ +package vsys.blockchain.state.contract.vswap + +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.{DataEntry, DataType} +import vsys.blockchain.contract.token.{SystemContractGen, TokenContractGen} +import vsys.blockchain.contract.vswap.{VSwapContractGen, VSwapFunctionHelperGen} +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.contract._ +import vsys.blockchain.state._ +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen, TransactionStatus} +import com.google.common.primitives.Longs + +class ExecuteVSwapValidDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with TokenContractGen + with SystemContractGen + with VSwapContractGen + with VSwapFunctionHelperGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val preconditionsAndVSwapDepositAndWithdrawLiquidityTokens: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + // register 3 tokens and VSwap contract, then deposit all 3 tokens into contract + // the function will issue and deposit the entire supply of liquidity tokens + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap( + 1000, // total supply of token A + 1, // unity of token A + 1000, // issue amount of token A + 1000, // total supply of token B + 1, // unity of token B + 1000, // issue amount of token B + 1000, // total supply of liquidity token + 1000, // unity of liquidity token + 10, // minimum liquidity + 1000, // deposit amount of token A + 1000) // deposit amount of token B + //withdraw tokens + withdrawTokenA <- withdrawToken(master, regTokenA.contractId, regVSwap.contractId.bytes.arr, master.toAddress.bytes.arr, 10L, fee, ts + 10) + withdrawTokenB <- withdrawToken(master, regTokenB.contractId, regVSwap.contractId.bytes.arr, master.toAddress.bytes.arr, 10L, fee, ts + 11) + withdrawLiquidity <- withdrawToken(master, regLiquidity.contractId, regVSwap.contractId.bytes.arr, master.toAddress.bytes.arr, 10L, fee, ts + 12) + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, + withdrawTokenA, withdrawTokenB, withdrawLiquidity) + + property("vswap able to withdraw balance") { + forAll(preconditionsAndVSwapDepositAndWithdrawLiquidityTokens) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, withdrawA: ExecuteContractFunctionTransaction, withdrawB: ExecuteContractFunctionTransaction, + withdrawLiquidity: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(regTokenA.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity))), + TestBlock.createWithTxStatus(withdrawLiquidity.timestamp, Seq(withdrawA, withdrawB, withdrawLiquidity), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = withdrawLiquidity.proofs.firstCurveProof.explicitGet().publicKey + val vSwapContractId = regVSwap.contractId.bytes.arr + + val tokenAContractId = regTokenA.contractId.bytes.arr + val tokenBContractId = regTokenB.contractId.bytes.arr + val liquidityContractId = regLiquidity.contractId.bytes.arr + + val (contractTokenABalanceKey, contractTokenBBalanceKey, contractLiquidityBalanceKey) = getContractTokenBalanceKeys(tokenAContractId, tokenBContractId, liquidityContractId, vSwapContractId) + val (masterTokenABalanceKey, masterTokenBBalanceKey, masterLiquidityBalanceKey) = getUserTokenBalanceKeys(tokenAContractId, tokenBContractId, liquidityContractId, master) + + newState.tokenAccountBalance(masterTokenABalanceKey) shouldBe 10L + newState.tokenAccountBalance(contractTokenABalanceKey) shouldBe 990L + + newState.tokenAccountBalance(masterTokenBBalanceKey) shouldBe 10L + newState.tokenAccountBalance(contractTokenBBalanceKey) shouldBe 990L + + newState.tokenAccountBalance(masterLiquidityBalanceKey) shouldBe 10L + newState.tokenAccountBalance(contractLiquidityBalanceKey) shouldBe 990L + } + } + } + + val preconditionsAndVSwapSetSwap: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap( + 1000, // totalSupplyA + 1, // unityA + 1000, // issueAmountA + 1000, // totalSupplyB + 1, //unityB + 1000, // issueAmountB + 1000, // liquidityTotalSupply + 1000, // liquidityUnity + 9, // minimumLiquidity + 100, // tokenADepositAmount + 100) // tokenBDepositAmount + // set swap + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 10L, 10L, attach, fee + 10000000000L, ts + 10) + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap) + + property("vswap able to setswap") { + forAll(preconditionsAndVSwapSetSwap) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(depositLiquidity.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(setSwap), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = setSwap.proofs.firstCurveProof.explicitGet().publicKey + val vSwapContractId = regVSwap.contractId.bytes.arr + + val (swapStatusKey, minimumLiquidityKey, tokenAReservedKey, tokenBReservedKey, totalSupplyKey, liquidityTokenLeft) = getSwapContractStateVarKeys(vSwapContractId) + val (masterStateMapTokenABalanceKey, masterStateMapTokenBBalanceKey, masterStateMapLiquidityTokenBalanceKey) = getSwapContractStateMapKeys(vSwapContractId, master) + + newState.contractInfo(swapStatusKey) shouldEqual Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(minimumLiquidityKey) shouldEqual Some(DataEntry(Longs.toByteArray(9), DataType.Amount)) + newState.contractNumInfo(tokenAReservedKey) shouldEqual 10L + newState.contractNumInfo(tokenBReservedKey) shouldEqual 10L + newState.contractNumInfo(totalSupplyKey) shouldEqual 10L // sqrt(10 * 10) + newState.contractNumInfo(liquidityTokenLeft) shouldEqual 990L // 1000 - sqrt(10*10) + + newState.contractNumInfo(masterStateMapTokenABalanceKey) shouldEqual 90L + newState.contractNumInfo(masterStateMapTokenBBalanceKey) shouldEqual 90L + newState.contractNumInfo(masterStateMapLiquidityTokenBalanceKey) shouldEqual 1L // sqrt(10 * 10) - 9 + + + + } + } + } + + val preconditionsAndVSwapAddLiquidity: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap( + 10000, // totalSupplyA + 1, // unityA + 10000, // issueAmountA + 10000, // totalSupplyB + 1, // unityB + 10000, // issueAmountB + 10000, // liquidityTotalSupply + 1000, // liquidityUnity + 10, // minimumLiquidity + 2000, // tokenADepositAmount + 2000) // tokenBDepositAmount + + // set swap -> amountADesired, amountBDesired + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts + 10) + + // add liquidity -> amountADesired, amountBDesired, amountAMin, amountBMin + addLiquidity <- addLiquidityVSwapGen(master, regVSwap.contractId, 1000L, 1000L, 900L, 900L, ts + 1000000000000L, attach, fee + 10000000000L, ts + 11) + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, addLiquidity) + + property("vswap able to add liquidity") { + forAll(preconditionsAndVSwapAddLiquidity) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, addLiquidity: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(regTokenA.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(addLiquidity), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = setSwap.proofs.firstCurveProof.explicitGet().publicKey + val vSwapContractId = regVSwap.contractId.bytes.arr + + val (swapStatusKey, minimumLiquidityKey, tokenAReservedKey, tokenBReservedKey, totalSupplyKey, liquidityTokenLeft) = getSwapContractStateVarKeys(vSwapContractId) + val (masterStateMapTokenABalanceKey, masterStateMapTokenBBalanceKey, masterStateMapLiquidityTokenBalanceKey) = getSwapContractStateMapKeys(vSwapContractId, master) + + newState.contractInfo(swapStatusKey) shouldEqual Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(minimumLiquidityKey) shouldEqual Some(DataEntry(Longs.toByteArray(10), DataType.Amount)) + newState.contractNumInfo(tokenAReservedKey) shouldEqual 2000L + newState.contractNumInfo(tokenBReservedKey) shouldEqual 2000L + newState.contractNumInfo(totalSupplyKey) shouldEqual 2000L + newState.contractNumInfo(liquidityTokenLeft) shouldEqual 8000L + + newState.contractNumInfo(masterStateMapTokenABalanceKey) shouldEqual 0L + newState.contractNumInfo(masterStateMapTokenBBalanceKey) shouldEqual 0L + newState.contractNumInfo(masterStateMapLiquidityTokenBalanceKey) shouldEqual 1990L + } + } + } + + val preconditionsAndVSwapRemoveLiquidity: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap( + 10000, // totalSupplyA + 1, // unityA + 10000, // issueAmountA + 10000, // totalSupplyB + 1, // unityB + 10000, // issueAmountB + 10000, // liquidityTotalSupply + 1000, // liquidityUnity + 10, // minimumLiquidity + 2000, // tokenADepositAmount + 2000) // tokenBDepositAmount + + // set swap -> amountADesired, amountBDesired + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts + 10) + + // add liquidity -> amountADesired, amountBDesired, amountAMin, amountBMin + addLiquidity <- addLiquidityVSwapGen(master, regVSwap.contractId, 1000L, 1000L, 900L, 900L, ts + 1000000000000L, attach, fee + 10000000000L, ts + 11) + + // remove liquidity -> liquidity, amountAMin, amountBMin + removeLiquidity <- removeLiquidityVSwapGen(master, regVSwap.contractId, 1000L, 1000L, 1000L, ts + 1000000000000L, attach, fee + 10000000000L, ts + 12) + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, addLiquidity, removeLiquidity) + + property("vswap able to remove liquidity") { + forAll(preconditionsAndVSwapRemoveLiquidity) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, addLiquidity: ExecuteContractFunctionTransaction, + removeLiquidity: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(regTokenA.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap, addLiquidity))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(removeLiquidity), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = setSwap.proofs.firstCurveProof.explicitGet().publicKey + val vSwapContractId = regVSwap.contractId.bytes.arr + + val (swapStatusKey, minimumLiquidityKey, tokenAReservedKey, tokenBReservedKey, totalSupplyKey, liquidityTokenLeft) = getSwapContractStateVarKeys(vSwapContractId) + val (masterStateMapTokenABalanceKey, masterStateMapTokenBBalanceKey, masterStateMapLiquidityTokenBalanceKey) = getSwapContractStateMapKeys(vSwapContractId, master) + + newState.contractInfo(swapStatusKey) shouldEqual Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(minimumLiquidityKey) shouldEqual Some(DataEntry(Longs.toByteArray(10), DataType.Amount)) + newState.contractNumInfo(tokenAReservedKey) shouldEqual 1000L + newState.contractNumInfo(tokenBReservedKey) shouldEqual 1000L + newState.contractNumInfo(totalSupplyKey) shouldEqual 1000L + newState.contractNumInfo(liquidityTokenLeft) shouldEqual 9000L + + newState.contractNumInfo(masterStateMapTokenABalanceKey) shouldEqual 1000L + newState.contractNumInfo(masterStateMapTokenBBalanceKey) shouldEqual 1000L + newState.contractNumInfo(masterStateMapLiquidityTokenBalanceKey) shouldEqual 990L + } + } + } + + val preconditionsAndVSwapSwapTokenForExactBaseToken: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap( + 10000, // totalSupplyA + 1, // unityA + 10000, // issueAmountA + 10000, // totalSupplyB + 1, // unityB + 10000, // issueAmountB + 10000, // liquidityTotalSupply + 1000, // liquidityUnity + 10, // minimumLiquidity + 2000, // tokenADepositAmount + 2000) // tokenBDepositAmount + + // set swap -> amountADesired, amountBDesired + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts + 10) + + // swap for 100 base tokens -> amountOut, amountInMax, deadline + swapTokenForExactBaseToken <- swapTokenForExactBaseTokenVSwapGen(master, regVSwap.contractId, 100L, 112L, ts + 1000000000000L, attach, fee + 10000000000L, ts + 11) + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, swapTokenForExactBaseToken) + + property("vswap able to swap tokens for exact base token") { + forAll(preconditionsAndVSwapSwapTokenForExactBaseToken) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, swapTokenForExactBaseToken: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(regTokenA.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(swapTokenForExactBaseToken), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = setSwap.proofs.firstCurveProof.explicitGet().publicKey + val vSwapContractId = regVSwap.contractId.bytes.arr + + val (swapStatusKey, minimumLiquidityKey, tokenAReservedKey, tokenBReservedKey, totalSupplyKey, liquidityTokenLeft) = getSwapContractStateVarKeys(vSwapContractId) + val (masterStateMapTokenABalanceKey, masterStateMapTokenBBalanceKey, masterStateMapLiquidityTokenBalanceKey) = getSwapContractStateMapKeys(vSwapContractId, master) + + newState.contractInfo(swapStatusKey) shouldEqual Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(minimumLiquidityKey) shouldEqual Some(DataEntry(Longs.toByteArray(10), DataType.Amount)) + newState.contractNumInfo(tokenAReservedKey) shouldEqual 900L + newState.contractNumInfo(tokenBReservedKey) shouldEqual 1112L + newState.contractNumInfo(totalSupplyKey) shouldEqual 1000L + newState.contractNumInfo(liquidityTokenLeft) shouldEqual 9000L + + newState.contractNumInfo(masterStateMapTokenABalanceKey) shouldEqual 1100L + newState.contractNumInfo(masterStateMapTokenBBalanceKey) shouldEqual 888L + newState.contractNumInfo(masterStateMapLiquidityTokenBalanceKey) shouldEqual 990L + } + } + } + + val preconditionsAndVSwapSwapExactTokenForBaseToken: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap( + 10000, // totalSupplyA + 1, // unityA + 10000, // issueAmountA + 10000, // totalSupplyB + 1, // unityB + 10000, // issueAmountB + 10000, // liquidityTotalSupply + 1000, // liquidityUnity + 10, // minimumLiquidity + 2000, // tokenADepositAmount + 2000) // tokenBDepositAmount + + // set swap -> amountADesired, amountBDesired + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts + 10) + + // swap 100 tokens for base tokens -> amountOutMin, amountIn, deadline + swapExactTokenForBaseToken <- swapExactTokenForBaseTokenVSwapGen(master, regVSwap.contractId, 90L, 100L, ts + 1000000000000L, attach, fee + 10000000000L, ts + 11) + + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, swapExactTokenForBaseToken) + + property("vswap able to swap exact token for base token") { + forAll(preconditionsAndVSwapSwapExactTokenForBaseToken) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, swapExactTokenForBaseToken: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(regTokenA.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(swapExactTokenForBaseToken), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = setSwap.proofs.firstCurveProof.explicitGet().publicKey + val vSwapContractId = regVSwap.contractId.bytes.arr + + val (swapStatusKey, minimumLiquidityKey, tokenAReservedKey, tokenBReservedKey, totalSupplyKey, liquidityTokenLeft) = getSwapContractStateVarKeys(vSwapContractId) + val (masterStateMapTokenABalanceKey, masterStateMapTokenBBalanceKey, masterStateMapLiquidityTokenBalanceKey) = getSwapContractStateMapKeys(vSwapContractId, master) + + newState.contractInfo(swapStatusKey) shouldEqual Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(minimumLiquidityKey) shouldEqual Some(DataEntry(Longs.toByteArray(10), DataType.Amount)) + newState.contractNumInfo(tokenAReservedKey) shouldEqual 910L + newState.contractNumInfo(tokenBReservedKey) shouldEqual 1100L + newState.contractNumInfo(totalSupplyKey) shouldEqual 1000L + newState.contractNumInfo(liquidityTokenLeft) shouldEqual 9000L + + newState.contractNumInfo(masterStateMapTokenABalanceKey) shouldEqual 1090L + newState.contractNumInfo(masterStateMapTokenBBalanceKey) shouldEqual 900L + newState.contractNumInfo(masterStateMapLiquidityTokenBalanceKey) shouldEqual 990L + } + } + } + + val preconditionsAndVSwapSwapTokenForExactTargetToken: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap( + 10000, // totalSupplyA + 1, // unityA + 10000, // issueAmountA + 10000, // totalSupplyB + 1, // unityB + 10000, // issueAmountB + 10000, // liquidityTotalSupply + 1000, // liquidityUnity + 10, // minimumLiquidity + 2000, // tokenADepositAmount + 2000) // tokenBDepositAmount + + // set swap -> amountADesired, amountBDesired + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts + 10) + + // swap for 100 target tokens + swapTokenForExactTargetToken <- swapTokenForExactTargetTokenVSwapGen(master, regVSwap.contractId, 100L, 112L, ts + 1000000000000L, attach, fee + 10000000000L, ts + 11) + + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, swapTokenForExactTargetToken) + + property("vswap able to swap token for exact target token") { + forAll(preconditionsAndVSwapSwapTokenForExactTargetToken) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, swapTokenForExactTargetToken: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(regTokenA.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(swapTokenForExactTargetToken), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = setSwap.proofs.firstCurveProof.explicitGet().publicKey + val vSwapContractId = regVSwap.contractId.bytes.arr + + val (swapStatusKey, minimumLiquidityKey, tokenAReservedKey, tokenBReservedKey, totalSupplyKey, liquidityTokenLeft) = getSwapContractStateVarKeys(vSwapContractId) + val (masterStateMapTokenABalanceKey, masterStateMapTokenBBalanceKey, masterStateMapLiquidityTokenBalanceKey) = getSwapContractStateMapKeys(vSwapContractId, master) + + newState.contractInfo(swapStatusKey) shouldEqual Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(minimumLiquidityKey) shouldEqual Some(DataEntry(Longs.toByteArray(10), DataType.Amount)) + newState.contractNumInfo(tokenAReservedKey) shouldEqual 1112L + newState.contractNumInfo(tokenBReservedKey) shouldEqual 900L + newState.contractNumInfo(totalSupplyKey) shouldEqual 1000L + newState.contractNumInfo(liquidityTokenLeft) shouldEqual 9000L + + newState.contractNumInfo(masterStateMapTokenABalanceKey) shouldEqual 888L + newState.contractNumInfo(masterStateMapTokenBBalanceKey) shouldEqual 1100L + newState.contractNumInfo(masterStateMapLiquidityTokenBalanceKey) shouldEqual 990L + } + } + } + + val preconditionsAndVSwapSwapExactTokenForTargetToken: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap( + 10000, // totalSupplyA + 1, // unityA + 10000, // issueAmountA + 10000, // totalSupplyB + 1, // unityB + 10000, // issueAmountB + 10000, // liquidiityTotalSupply + 1000, // liquidityUnity + 10, // minimumLiquidity + 2000, // tokenADepositAmount + 2000) // tokenBDepositAmount + + // set swap -> amountADesired, amountBDesired + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 1000L, 1000L, attach, fee + 10000000000L, ts + 10) + + // swap 100 tokens for target token -> amountOutMin, amountIn + swapExactTokenForTargetToken <- swapExactTokenForTargetTokenVSwapGen(master, regVSwap.contractId, 90L, 100L, ts + 1000000000000L, attach, fee + 10000000000L, ts + 11) + + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, swapExactTokenForTargetToken) + + property("vswap able to swap exact token for target token") { + forAll(preconditionsAndVSwapSwapExactTokenForTargetToken) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, swapExactTokenForTargetToken: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(regTokenA.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(setSwap.timestamp, Seq(swapExactTokenForTargetToken), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + val master = setSwap.proofs.firstCurveProof.explicitGet().publicKey + val vSwapContractId = regVSwap.contractId.bytes.arr + + val (swapStatusKey, minimumLiquidityKey, tokenAReservedKey, tokenBReservedKey, totalSupplyKey, liquidityTokenLeft) = getSwapContractStateVarKeys(vSwapContractId) + val (masterStateMapTokenABalanceKey, masterStateMapTokenBBalanceKey, masterStateMapLiquidityTokenBalanceKey) = getSwapContractStateMapKeys(vSwapContractId, master) + + newState.contractInfo(swapStatusKey) shouldEqual Some(DataEntry(Array(1.toByte), DataType.Boolean)) + newState.contractInfo(minimumLiquidityKey) shouldEqual Some(DataEntry(Longs.toByteArray(10), DataType.Amount)) + newState.contractNumInfo(tokenAReservedKey) shouldEqual 1100L + newState.contractNumInfo(tokenBReservedKey) shouldEqual 910L + newState.contractNumInfo(totalSupplyKey) shouldEqual 1000L + newState.contractNumInfo(liquidityTokenLeft) shouldEqual 9000L + + newState.contractNumInfo(masterStateMapTokenABalanceKey) shouldEqual 900L + newState.contractNumInfo(masterStateMapTokenBBalanceKey) shouldEqual 1090L + newState.contractNumInfo(masterStateMapLiquidityTokenBalanceKey) shouldEqual 990L + } + } + } + + val preconditionsAndAddRemoveLiquidity: Gen[(GenesisTransaction, GenesisTransaction, RegisterContractTransaction, + RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, + ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction, ExecuteContractFunctionTransaction)] = for { + (genesis, genesis2, master, user, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, fee, ts, attach) <- + createABLiquidityTokenAndInitVSwap( + 1000000000000000000L, // totalSupplyA + 100000000, // unityA + 1000000000000000000L, // issueAmountA + 100000000000L, // totalSupplyB + 1000, // unityB + 100000000000L, // issueAmountB + 1000000000000000000L, // liquidiityTotalSupply + 1000, // liquidityUnity + 10, // minimumLiquidity + 1017988017151648L + 73842642848352L, // tokenADepositAmount + 99001450413L + 998444587L) // tokenBDepositAmount + + // set swap -> amountADesired, amountBDesired + setSwap <- setSwapVSwapGen(master, regVSwap.contractId, 73842642848352L, 998444587L, attach, fee + 10000000000L, ts + 10) + + addLiquidity <- addLiquidityVSwapGen(master, regVSwap.contractId, 100000000L, 1350L, 99000000L, 1340L, ts + 1000000000000L, attach, fee + 10000000000L, ts + 11) + + removeLiquidity <- removeLiquidityVSwapGen(master, regVSwap.contractId, 135764305914L, 36821321424176L, 498222293L, ts + 1000000000000L, attach, fee + 10000000000L, ts + 12) + + } yield (genesis, genesis2, regTokenA, regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, + depositB, depositLiquidity, setSwap, addLiquidity, removeLiquidity) + + property("vswap test add liquidity to uneven pool") { + forAll(preconditionsAndAddRemoveLiquidity) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, addLiquidity: ExecuteContractFunctionTransaction, _) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(regTokenA.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(addLiquidity.timestamp, Seq(addLiquidity), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + } + } + } + + property("vswap test remove liquidity from uneven pool") { + forAll(preconditionsAndAddRemoveLiquidity) { case (genesis: GenesisTransaction, genesis2: GenesisTransaction, regTokenA: RegisterContractTransaction, + regTokenB: RegisterContractTransaction, regLiquidity: RegisterContractTransaction, regVSwap: RegisterContractTransaction, issueTokenA: ExecuteContractFunctionTransaction, + issueTokenB: ExecuteContractFunctionTransaction, issueLiquidity: ExecuteContractFunctionTransaction, depositA: ExecuteContractFunctionTransaction, depositB: ExecuteContractFunctionTransaction, + depositLiquidity: ExecuteContractFunctionTransaction, setSwap: ExecuteContractFunctionTransaction, _, removeLiquidity: ExecuteContractFunctionTransaction) => + assertDiffAndStateCorrectBlockTime(Seq(TestBlock.create(genesis.timestamp, Seq(genesis, genesis2)), TestBlock.create(regTokenA.timestamp, Seq(regTokenA, + regTokenB, regLiquidity, regVSwap, issueTokenA, issueTokenB, issueLiquidity, depositA, depositB, depositLiquidity, setSwap))), + TestBlock.createWithTxStatus(removeLiquidity.timestamp, Seq(removeLiquidity), TransactionStatus.Success)) { (blockDiff, newState) => + blockDiff.txsDiff.txStatus shouldBe TransactionStatus.Success + + } + } + } +} \ No newline at end of file diff --git a/src/test/scala/vsys/blockchain/state/contract/vswap/RegisterVSwapContractDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vswap/RegisterVSwapContractDiffTest.scala new file mode 100644 index 000000000..a2a9cb32a --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vswap/RegisterVSwapContractDiffTest.scala @@ -0,0 +1,77 @@ +package vsys.blockchain.state.contract.vswap + +import cats.Monoid +import com.google.common.primitives.{Bytes, Ints} +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.block.TestBlock +import vsys.blockchain.contract.{Contract, ContractGenHelper, ContractVSwap, DataEntry, DataType} +import vsys.blockchain.contract.vswap.VSwapContractGen +import vsys.blockchain.state.diffs.assertDiffAndState +import vsys.blockchain.state._ +import vsys.blockchain.transaction.{GenesisTransaction, TransactionGen} +import vsys.blockchain.transaction.contract.RegisterContractTransaction + +class RegisterVSwapContractDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with VSwapContractGen { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val languageCode: String = "vdds" + val languageVersion: Int = 2 + + val preconditionAndBuildVSwapContract: Gen[(Array[Byte], Array[Byte], Seq[Array[Byte]], + Seq[Array[Byte]], Seq[Array[Byte]], Seq[Array[Byte]], Seq[Array[Byte]])] = for { + langCode <- ContractGenHelper.languageCodeGen(languageCode) + langVer <- ContractGenHelper.languageVersionGen(languageVersion) + init <- Gen.const(ContractVSwap.contract.trigger) + descriptor <- Gen.const(ContractVSwap.contract.descriptor) + stateVar <- Gen.const(ContractVSwap.contract.stateVar) + stateMap <- Gen.const(ContractVSwap.contract.stateMap) + textual <- Gen.const(ContractVSwap.contract.textual) + } yield (langCode, langVer, init, descriptor, stateVar, stateMap, textual) + + property("register v-swap contract build doesn't break invariant"){ + forAll(preconditionAndBuildVSwapContract) { case (langCode, langVer, init, descriptor, stateVar, stateMap, textual) => + Contract.buildContract(langCode, langVer, init, descriptor, stateVar, stateMap, textual) shouldBe an[Right[_, _]] + } + } + + val validContract: Gen[Contract] = vSwapContractGen() + val preconditionsAndRegContractTest: Gen[(GenesisTransaction, RegisterContractTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + genesis <- genesisVSwapGen(master, ts) + contract <- validContract + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + data: Seq[DataEntry] <- initVSwapDataStackGen(tokenId.arr, tokenId.arr, tokenId.arr, 1) + description <- validDescStringGen + create <- registerVSwapGen(master, contract, data, description, fee, ts + 1) + } yield (genesis, create) + + property("register v-swap contract transaction doesn't break invariant") { + forAll(preconditionsAndRegContractTest) { case (genesis, reg: RegisterContractTransaction) => + assertDiffAndState(Seq(TestBlock.create(Seq(genesis))), TestBlock.create(Seq(reg))) { (blockDiff, newState) => + val totalPortfolioDiff: Portfolio = Monoid.combineAll(blockDiff.txsDiff.portfolios.values) + totalPortfolioDiff.balance shouldBe -reg.transactionFee + totalPortfolioDiff.effectiveBalance shouldBe -reg.transactionFee + val master = reg.proofs.firstCurveProof.explicitGet().publicKey + val contractId = reg.contractId.bytes + val makerKey = ByteStr(Bytes.concat(contractId.arr, Array(0.toByte))) + + val (_, masterTxs) = newState.accountTransactionIds(master, 2, 0) + masterTxs.size shouldBe 2 // genesis, reg + newState.contractTokens(contractId) shouldBe 0 + newState.contractContent(contractId) shouldEqual Some((2, reg.id, ContractVSwap.contract)) + + newState.contractInfo(makerKey) shouldEqual Some(DataEntry(master.toAddress.bytes.arr, DataType.Address)) + } + } + } +} diff --git a/src/test/scala/vsys/blockchain/state/contract/vswap/VSwapContractOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/contract/vswap/VSwapContractOpcDiffTest.scala new file mode 100644 index 000000000..94de96c0d --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/contract/vswap/VSwapContractOpcDiffTest.scala @@ -0,0 +1,81 @@ +package vsys.blockchain.state.contract.vswap + +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.blockchain.contract.{Contract, ContractGenHelper, DataEntry} +import vsys.blockchain.contract.ContractVSwap._ +import vsys.utils.serialization.Deser +import com.google.common.primitives.Ints +import org.scalacheck.{Gen, Shrink} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount +import vsys.blockchain.contract.vswap.{VSwapContractGen, VSwapFunction} +import vsys.blockchain.state._ +import vsys.blockchain.state.diffs._ +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidOPCData} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.blockchain.transaction.TransactionGen + +class VSwapContractOpcDiffTest extends PropSpec + with PropertyChecks + with GeneratorDrivenPropertyChecks + with Matchers + with TransactionGen + with VSwapContractGen + with VSwapFunction { + + private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) + + val regWrongOpcFunContract: Gen[Contract] = + Gen.const(Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + wrongOpcTrigger, + Seq(supersedeFunc, setSwapFunc, addLiquidityFunc, removeLiquidityFunc, swapTokenForExactBaseTokenFunc, + swapExactTokenForBaseTokenFunc, swapTokenForExactTargetTokenFunc, swapExactTokenForTargetTokenFunc), + stateVarSeq, stateMapSeq, Seq(triggerTextual, descriptorTextual, stateVarTextual, stateMapTextual)).explicitGet()) + + val regWrongDataContract: Gen[Contract] = + Gen.const(Contract.buildContract(Deser.serilizeString("vdds"), Ints.toByteArray(2), + wrongDataTrigger, + Seq(supersedeFunc, setSwapFunc, addLiquidityFunc, removeLiquidityFunc, swapTokenForExactBaseTokenFunc, + swapExactTokenForBaseTokenFunc, swapTokenForExactTargetTokenFunc, swapExactTokenForTargetTokenFunc), + stateVarSeq, stateMapSeq, Seq(triggerTextual, descriptorTextual, stateVarTextual, stateMapTextual)).explicitGet()) + + val validContract: Gen[Contract] = vSwapContractGen() + val preconditionsAndRegContractWrongFun: Gen[(RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction)] = for { + (master, ts, fee) <- ContractGenHelper.basicContractTestGen() + validContract <- validContract + wrongOpcContract <- regWrongOpcFunContract + wrongDataContract <- regWrongDataContract + + tokenId = tokenIdFromBytes(ContractAccount.systemContractId.bytes.arr, Ints.toByteArray(0)).explicitGet() + data: Seq[DataEntry] <- initVSwapDataStackGen(tokenId.arr, tokenId.arr, tokenId.arr, 1) + description <- validDescStringGen + validCreate <- registerVSwapGen(master, validContract, data, description, fee, ts + 1) + wrongOpcCreate <- registerVSwapGen(master, wrongOpcContract, data, description, fee + 10000000000L, ts+3) + wrongDataCreate <- registerVSwapGen(master, wrongDataContract, data, description, fee + 10000000000L, ts+3) + } yield (validCreate, wrongOpcCreate, wrongDataCreate) + + property("register contract transaction cannot pass due to wrong opcode"){ + forAll(preconditionsAndRegContractWrongFun) { case (validCreate, wrongOpcCreate, _) => + assertOpcFuncDifferEi(2, None, validCreate) { opcFunDiffEi => + opcFunDiffEi shouldBe an[Right[_, _]] + } + + assertOpcFuncDifferEi(2, None, wrongOpcCreate) { opcFunDiffEi => + opcFunDiffEi shouldBe Left(ContractInvalidOPCData) + } + } + } + + property("register contract transaction cannot pass due to wrong list of parameters"){ + forAll(preconditionsAndRegContractWrongFun) { case (_, _, wrongDataCreate) => + assertOpcFuncDifferEi(2, None, wrongDataCreate) { opcFunDiffEi => + opcFunDiffEi shouldBe Left(ContractDataTypeMismatch) + } + } + } +} + + + + diff --git a/src/test/scala/vsys/blockchain/state/diffs/CommonValidationContractTest.scala b/src/test/scala/vsys/blockchain/state/diffs/CommonValidationContractTest.scala index 5c41af212..89787b99c 100644 --- a/src/test/scala/vsys/blockchain/state/diffs/CommonValidationContractTest.scala +++ b/src/test/scala/vsys/blockchain/state/diffs/CommonValidationContractTest.scala @@ -9,7 +9,7 @@ import vsys.account.ContractAccount.tokenIdFromBytes import vsys.blockchain.block.TestBlock import vsys.blockchain.contract.{Contract, ContractGenHelper, DataEntry} import vsys.blockchain.contract.channel.PaymentChannelContractGen -import vsys.blockchain.contract.token.TokenContractGen +import vsys.blockchain.contract.token.{TokenContractGen, TokenContractV2Gen} import vsys.blockchain.transaction.contract.RegisterContractTransaction import vsys.blockchain.transaction.{GenesisTransaction, PaymentTransaction, TransactionGen} import vsys.settings.TestFunctionalitySettings @@ -20,14 +20,16 @@ class CommonValidationContractTest extends PropSpec with Matchers with TransactionGen with TokenContractGen - with PaymentChannelContractGen { + with PaymentChannelContractGen + with TokenContractV2Gen { private implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) val tContract: Gen[Contract] = tokenContractGen(true) val paymentChannelContract: Gen[Contract] = paymentChannelContractGen() + val tContractV2: Gen[Contract] = tokenContractV2Gen(true) - val preconditionsAndContract: Gen[(GenesisTransaction, PaymentTransaction, RegisterContractTransaction, RegisterContractTransaction)] = for { + val preconditionsAndContract: Gen[(GenesisTransaction, PaymentTransaction, RegisterContractTransaction, RegisterContractTransaction, RegisterContractTransaction)] = for { (master, ts, fee) <- ContractGenHelper.basicContractTestGen() contract <- tContract dataStack: Seq[DataEntry] <- initTokenDataStackGen(1L, 1L, "init") @@ -41,10 +43,12 @@ class CommonValidationContractTest extends PropSpec dataForPaymentChannel: Seq[DataEntry] <- initPaymentChannelContractDataStackGen(sysTokenId.arr) // Register a payment channel that supports VSYS regPaymentChannel <- registerPaymentChannelGen(master, pContract, dataForPaymentChannel, description, fee + 10000000000L, ts + 3) - } yield (genesis, transfer, regTokenContract, regPaymentChannel) + contractV2 <- tContractV2 + regTokenContractV2 <- registerTokenGen(master, contractV2, dataStack, description, fee + 10000000000L, ts + 2) + } yield (genesis, transfer, regTokenContract, regPaymentChannel, regTokenContractV2) property("disallows contract related tx before allowed height") { - forAll(preconditionsAndContract) { case (genesis, _, regToken: RegisterContractTransaction, _) => + forAll(preconditionsAndContract) { case (genesis, _, regToken: RegisterContractTransaction, _, _) => assertDiffEi(Seq(TestBlock.create(Seq(genesis))), TestBlock.create(Seq(regToken)), TestFunctionalitySettings.ContractDisabled) { blockDiffEi => blockDiffEi should produce("must not appear before height") } @@ -52,11 +56,20 @@ class CommonValidationContractTest extends PropSpec } property("disallows deposit withdraw contract related tx before allowed height") { - forAll(preconditionsAndContract) { case (g1, p, regToken: RegisterContractTransaction, regChannel: RegisterContractTransaction) => + forAll(preconditionsAndContract) { case (g1, p, regToken: RegisterContractTransaction, regChannel: RegisterContractTransaction, _) => assertDiffEi(Seq(TestBlock.create(Seq(g1)), TestBlock.create(Seq(p))), TestBlock.create(Seq(regToken, regChannel)), TestFunctionalitySettings.ContractDisabled) { blockDiffEi => blockDiffEi should produce("deposit withdraw contracts must not appear before height") } } } + property("disallows exchange contract related tx before allowed height") { + forAll(preconditionsAndContract) { case (g1, p, regToken: RegisterContractTransaction, regChannel: RegisterContractTransaction, regTokenV2: RegisterContractTransaction) => + assertDiffEi(Seq(TestBlock.create(Seq(g1)), TestBlock.create(Seq(p)), TestBlock.create(Seq(regToken))), + TestBlock.create(Seq(regChannel, regTokenV2)), TestFunctionalitySettings.ContractDisabled) { blockDiffEi => + blockDiffEi should produce("exchange contracts must not appear before height") + } + } + } + } diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/AssertOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/AssertOpcDiffTest.scala new file mode 100644 index 000000000..004d135a9 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/AssertOpcDiffTest.scala @@ -0,0 +1,94 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Ints, Longs} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.PrivateKeyAccount +import vsys.blockchain.contract.{ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.StateWriterImpl +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidCaller, + ContractInvalidSigner, GenericError} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.{Left, Right} + +class AssertOpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + val state: StateWriterImpl = newState() + + val tx: RegisterContractTransaction = RegisterContractTransaction.create( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L).right.get + + val executionContext: ExecutionContext = ExecutionContext.fromRegConTx( + state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx).right.get + + val account: Array[Byte] = PrivateKeyAccount( + Array.fill(TransactionParser.KeyLength)(0)).toAddress.bytes.arr + + val account1: Array[Byte] = PrivateKeyAccount( + Array.fill(TransactionParser.KeyLength)(1)).toAddress.bytes.arr + + property("test assert opcs") { + AssertOpcDiff.assertTrue(DataEntry(Array(1.toByte), DataType.Boolean)) should be (Right(OpcDiff.empty)) + AssertOpcDiff.assertTrue(DataEntry(Array(0.toByte), DataType.Boolean)) should be ( + Left(GenericError(s"Invalid Assert (Boolean True): " + + s"Value ${DataEntry(Array(0.toByte), DataType.Boolean).json} is False"))) + AssertOpcDiff.assertTrue(DataEntry(Array(1.toByte), DataType.ShortBytes)) should be (Left(ContractDataTypeMismatch)) + + AssertOpcDiff.gtEq0(DataEntry(Longs.toByteArray(1), DataType.Amount)) should be (Right(OpcDiff.empty)) + AssertOpcDiff.gtEq0(DataEntry(Longs.toByteArray(0), DataType.Amount)) should be (Right(OpcDiff.empty)) + AssertOpcDiff.gtEq0(DataEntry(Longs.toByteArray(-1), DataType.Amount)) should be ( + Left(GenericError(s"Invalid Assert (gteq0): " + + s"Value ${DataEntry(Longs.toByteArray(-1), DataType.Amount).json} is negative"))) + + AssertOpcDiff.ltEq(DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Amount)) should be (Right(OpcDiff.empty)) + AssertOpcDiff.ltEq(DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.ShortBytes)) should be (Left(ContractDataTypeMismatch)) + AssertOpcDiff.ltEq(DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(0), DataType.Amount)) should be ( + Left(GenericError(s"Invalid Assert (lteq0): " + + s"Value ${DataEntry(Longs.toByteArray(1), DataType.Amount).json} " + + s"is larger than ${DataEntry(Longs.toByteArray(0), DataType.Amount).json}"))) + + AssertOpcDiff.ltInt64(DataEntry(Longs.toByteArray(1), DataType.Amount)) should be (Right(OpcDiff.empty)) + AssertOpcDiff.ltInt64(DataEntry(Longs.toByteArray(0), DataType.ShortBytes)) should be ( + Left(ContractDataTypeMismatch)) + + AssertOpcDiff.gt0(DataEntry(Longs.toByteArray(1), DataType.Amount)) should be (Right(OpcDiff.empty)) + AssertOpcDiff.gt0(DataEntry(Longs.toByteArray(0), DataType.Amount)) should be ( + Left(GenericError(s"Invalid Assert (gt0): " + + s"Value ${DataEntry(Longs.toByteArray(0), DataType.Amount).json} is non-positive"))) + AssertOpcDiff.gt0(DataEntry(Longs.toByteArray(-1), DataType.Amount)) should be ( + Left(GenericError(s"Invalid Assert (gt0): " + + s"Value ${DataEntry(Longs.toByteArray(-1), DataType.Amount).json} is non-positive"))) + + AssertOpcDiff.equal(DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Amount)) should be (Right(OpcDiff.empty)) + AssertOpcDiff.equal(DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Ints.toByteArray(1), DataType.Int32)) should be ( + Left(GenericError(s"Invalid Assert (eq): DataEntry " + + s"${DataEntry(Longs.toByteArray(1), DataType.Amount).json} " + + s"is not equal to ${DataEntry(Ints.toByteArray(1), DataType.Int32).json}"))) + + AssertOpcDiff.isCallerOrigin(executionContext)( + DataEntry(account, DataType.Address)) should be (Right(OpcDiff.empty)) + AssertOpcDiff.isCallerOrigin(executionContext)( + DataEntry(account, DataType.ContractAccount)) should be (Left(ContractDataTypeMismatch)) + AssertOpcDiff.isCallerOrigin(executionContext)( + DataEntry(account1, DataType.Address)) should be (Left(ContractInvalidCaller)) + + AssertOpcDiff.isSignerOrigin(executionContext)( + DataEntry(account, DataType.Address)) should be (Right(OpcDiff.empty)) + AssertOpcDiff.isSignerOrigin(executionContext)( + DataEntry(account, DataType.ContractAccount)) should be (Left(ContractDataTypeMismatch)) + AssertOpcDiff.isSignerOrigin(executionContext)( + DataEntry(account1, DataType.Address)) should be (Left(ContractInvalidSigner)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/BasicOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/BasicOpcDiffTest.scala new file mode 100644 index 000000000..69fe4fd11 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/BasicOpcDiffTest.scala @@ -0,0 +1,209 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Ints, Longs, Shorts} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.blockchain.contract.{DataEntry, DataType} +import vsys.blockchain.contract.DataType.MaxShortBytesLength +import vsys.blockchain.transaction.ValidationError +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractUnsupportedOPC, InvalidDataEntry} + +import scala.util.{Left, Right} + +class BasicOpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + property("test basic opcs") { + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Amount), + BasicOpcDiff.add) should be (Right(DataEntry(Longs.toByteArray(2), DataType.Amount))) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.add) should be (Left(ContractDataTypeMismatch)) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Amount), + BasicOpcDiff.add) should be (Left(ValidationError.OverflowError)) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.ShortText), + DataEntry(Longs.toByteArray(1), DataType.ShortText), + BasicOpcDiff.add) should be (Left(ContractUnsupportedOPC)) + + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(1).toByteArray.length.toShort) ++ BigInt(1).toByteArray, DataType.BigInteger), + DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), + BasicOpcDiff.add) should be (Right(DataEntry( + Shorts.toByteArray(BigInt(1).toByteArray.length.toShort) ++ BigInt(1).toByteArray, DataType.BigInteger))) + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.add) should be (Left(ContractDataTypeMismatch)) + + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Amount), + BasicOpcDiff.minus) should be (Right(DataEntry(Longs.toByteArray(0), DataType.Amount))) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(2), DataType.Amount), + BasicOpcDiff.minus) should be (Left(ValidationError.OverflowError)) + BasicOpcDiff.numBiOperation( + DataEntry(DataType.arrayShortLengthToByteArray(BigInt(1).toByteArray) ++ BigInt(1).toByteArray, DataType.BigInteger), + DataEntry(DataType.arrayShortLengthToByteArray(BigInt(2).toByteArray) ++ BigInt(2).toByteArray, DataType.BigInteger), + BasicOpcDiff.minus) should be (Right(DataEntry(DataType.arrayShortLengthToByteArray(BigInt(-1).toByteArray) ++ BigInt(-1).toByteArray, DataType.BigInteger))) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.minus) should be (Left(ContractDataTypeMismatch)) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Amount), + DataEntry(Longs.toByteArray(-1), DataType.Amount), + BasicOpcDiff.minus) should be (Left(ValidationError.OverflowError)) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.ShortText), + DataEntry(Longs.toByteArray(1), DataType.ShortText), + BasicOpcDiff.minus) should be (Left(ContractUnsupportedOPC)) + + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(1).toByteArray.length.toShort) ++ BigInt(1).toByteArray, DataType.BigInteger), + DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), + BasicOpcDiff.minus) should be (Right(DataEntry( + Shorts.toByteArray(BigInt(1).toByteArray.length.toShort) ++ BigInt(1).toByteArray, DataType.BigInteger))) + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.minus) should be (Left(ContractDataTypeMismatch)) + + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Amount), + BasicOpcDiff.multiply) should be (Right(DataEntry(Longs.toByteArray(1), DataType.Amount))) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.multiply) should be (Left(ContractDataTypeMismatch)) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Amount), + DataEntry(Longs.toByteArray(2), DataType.Amount), + BasicOpcDiff.multiply) should be (Left(ValidationError.OverflowError)) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.ShortText), + DataEntry(Longs.toByteArray(1), DataType.ShortText), + BasicOpcDiff.multiply) should be (Left(ContractUnsupportedOPC)) + + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(2).toByteArray.length.toShort) ++ BigInt(2).toByteArray, DataType.BigInteger), + DataEntry(Shorts.toByteArray(BigInt(2).toByteArray.length.toShort) ++ BigInt(2).toByteArray, DataType.BigInteger), + BasicOpcDiff.multiply) should be (Right(DataEntry( + Shorts.toByteArray(BigInt(4).toByteArray.length.toShort) ++ BigInt(4).toByteArray, DataType.BigInteger))) + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray((BigInt(1) << 255*8).toByteArray.length.toShort) ++ (BigInt(1) << 255*8).toByteArray, DataType.BigInteger), + DataEntry(Shorts.toByteArray((BigInt(1) << 255*8).toByteArray.length.toShort) ++ (BigInt(1) << 255*8).toByteArray, DataType.BigInteger), + BasicOpcDiff.multiply) should be (Left(ValidationError.OverflowError)) + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.multiply) should be (Left(ContractDataTypeMismatch)) + + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Amount), + BasicOpcDiff.divide) should be (Right(DataEntry(Longs.toByteArray(1), DataType.Amount))) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.divide) should be (Left(ContractDataTypeMismatch)) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(0), DataType.Amount), + BasicOpcDiff.divide) should be (Left(ValidationError.OverflowError)) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.ShortText), + DataEntry(Longs.toByteArray(1), DataType.ShortText), + BasicOpcDiff.divide) should be (Left(ContractUnsupportedOPC)) + + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(1).toByteArray.length.toShort) ++ BigInt(1).toByteArray, DataType.BigInteger), + DataEntry(Shorts.toByteArray(BigInt(2).toByteArray.length.toShort) ++ BigInt(2).toByteArray, DataType.BigInteger), + BasicOpcDiff.divide) should be (Right(DataEntry( + Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger))) + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(1).toByteArray.length.toShort) ++ BigInt(1).toByteArray, DataType.BigInteger), + DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), + BasicOpcDiff.divide) should be (Left(ValidationError.OverflowError)) + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.divide) should be (Left(ContractDataTypeMismatch)) + + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(0), DataType.Amount), + BasicOpcDiff.minimum) should be (Right(DataEntry(Longs.toByteArray(0), DataType.Amount))) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.minimum) should be (Left(ContractDataTypeMismatch)) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.ShortText), + DataEntry(Longs.toByteArray(1), DataType.ShortText), + BasicOpcDiff.minimum) should be (Left(ContractUnsupportedOPC)) + + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(2).toByteArray.length.toShort) ++ BigInt(2).toByteArray, DataType.BigInteger), + DataEntry(Shorts.toByteArray(BigInt(1).toByteArray.length.toShort) ++ BigInt(1).toByteArray, DataType.BigInteger), + BasicOpcDiff.minimum) should be (Right(DataEntry( + Shorts.toByteArray(BigInt(1).toByteArray.length.toShort) ++ BigInt(1).toByteArray, DataType.BigInteger))) + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.minimum) should be (Left(ContractDataTypeMismatch)) + + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(0), DataType.Amount), + BasicOpcDiff.maximum) should be (Right(DataEntry(Longs.toByteArray(1), DataType.Amount))) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.maximum) should be (Left(ContractDataTypeMismatch)) + BasicOpcDiff.numBiOperation( + DataEntry(Longs.toByteArray(1), DataType.ShortText), + DataEntry(Longs.toByteArray(1), DataType.ShortText), + BasicOpcDiff.maximum) should be (Left(ContractUnsupportedOPC)) + + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(2).toByteArray.length.toShort) ++ BigInt(2).toByteArray, DataType.BigInteger), + DataEntry(Shorts.toByteArray(BigInt(1).toByteArray.length.toShort) ++ BigInt(1).toByteArray, DataType.BigInteger), + BasicOpcDiff.maximum) should be (Right(DataEntry( + Shorts.toByteArray(BigInt(2).toByteArray.length.toShort) ++ BigInt(2).toByteArray, DataType.BigInteger))) + BasicOpcDiff.numBiOperation( + DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), + DataEntry(Longs.toByteArray(1), DataType.Timestamp), + BasicOpcDiff.maximum) should be (Left(ContractDataTypeMismatch)) + + BasicOpcDiff.sqrt(DataEntry(Longs.toByteArray(1), DataType.Amount)) should be (Left(ContractUnsupportedOPC)) + BasicOpcDiff.sqrt(DataEntry(Shorts.toByteArray(BigInt(-1).toByteArray.length.toShort) ++ BigInt(-1).toByteArray, DataType.BigInteger) + ) should be (Left(ValidationError.OverflowError)) + BasicOpcDiff.sqrt(DataEntry(Shorts.toByteArray(BigInt(2).toByteArray.length.toShort) ++ BigInt(2).toByteArray, DataType.BigInteger) + ) should be (Right(DataEntry(Shorts.toByteArray(BigInt(1).toByteArray.length.toShort) ++ BigInt(1).toByteArray, DataType.BigInteger))) + BasicOpcDiff.sqrt(DataEntry(Shorts.toByteArray(BigInt(4).toByteArray.length.toShort) ++ BigInt(4).toByteArray, DataType.BigInteger) + ) should be (Right(DataEntry(Shorts.toByteArray(BigInt(2).toByteArray.length.toShort) ++ BigInt(2).toByteArray, DataType.BigInteger))) + + BasicOpcDiff.concat( + DataEntry(Longs.toByteArray(1), DataType.Amount), + DataEntry(Longs.toByteArray(1), DataType.Amount)) should be ( + Right(DataEntry.create(Longs.toByteArray(1) ++ Longs.toByteArray(1), + DataType.ShortBytes).right.get)) + BasicOpcDiff.concat( + DataEntry(Array.fill(MaxShortBytesLength){0}, DataType.ShortBytes), + DataEntry(Array.fill(1){0}, DataType.ShortBytes), + ) should be (Left(InvalidDataEntry)) + + BasicOpcDiff.constantGet(Array(DataType.Amount.id.toByte) ++ Longs.toByteArray(1), + ) should be (Right(DataEntry(Longs.toByteArray(1), DataType.Amount))) + BasicOpcDiff.constantGet(Array(DataType.Amount.id.toByte) ++ Ints.toByteArray(1), + ) should be (Left(InvalidDataEntry)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/CDBVOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/CDBVOpcDiffTest.scala new file mode 100644 index 000000000..b15063fbd --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/CDBVOpcDiffTest.scala @@ -0,0 +1,126 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.Longs +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.PrivateKeyAccount +import vsys.blockchain.contract.{ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.{ByteStr, StateWriterImpl} +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidStateMap, + ContractInvalidStateVariable, ContractMapValueInsufficient, InvalidDataEntry} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.Left + +class CDBVOpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + val state: StateWriterImpl = newState() + + val tx: RegisterContractTransaction = RegisterContractTransaction.create( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L).right.get + + val executionContext: ExecutionContext = ExecutionContext.fromRegConTx( + state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx).right.get + + property("test CDBV opcs") { + ByteStr(CDBVOpcDiff.set(executionContext)( + Array[Byte](0.toByte, 3.toByte), DataEntry( + Longs.toByteArray(0), DataType.Amount)).right.get.contractDB( + ByteStr(tx.contractId.bytes.arr ++ Array[Byte](0.toByte)))) shouldEqual ByteStr(DataEntry( + Longs.toByteArray(0), DataType.Amount).bytes) + CDBVOpcDiff.set(executionContext)( + Array[Byte](0.toByte, 2.toByte), DataEntry( + Longs.toByteArray(0), DataType.Amount)) should be (Left(ContractInvalidStateVariable)) + + ByteStr(CDBVOpcDiff.mapSet(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Longs.toByteArray(0), DataType.Amount), + DataEntry(Longs.toByteArray(0), DataType.Amount)).right.get.contractDB( + ByteStr(tx.contractId.bytes.arr ++ Array[Byte](0.toByte) ++ DataEntry( + Longs.toByteArray(0), DataType.Amount).bytes))) shouldEqual ByteStr(DataEntry( + Longs.toByteArray(0), DataType.Amount).bytes) + CDBVOpcDiff.mapSet(executionContext)( + Array[Byte](0.toByte, 3.toByte, 2.toByte), DataEntry( + Longs.toByteArray(0), DataType.Amount), DataEntry( + Longs.toByteArray(0), DataType.Amount)) should be (Left(ContractInvalidStateMap)) + + CDBVOpcDiff.mapValueAdd(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Longs.toByteArray(0), DataType.Amount), DataEntry( + Longs.toByteArray(0), DataType.Amount)).right.get.contractNumDB( + ByteStr(tx.contractId.bytes.arr ++ Array[Byte](0.toByte) ++ DataEntry( + Longs.toByteArray(0), DataType.Amount).bytes)) shouldEqual 0 + CDBVOpcDiff.mapValueAdd(executionContext)( + Array[Byte](0.toByte, 2.toByte, 2.toByte), DataEntry( + Longs.toByteArray(0), DataType.ShortBytes), DataEntry( + Longs.toByteArray(0), DataType.Amount)) should be (Left(ContractInvalidStateMap)) + CDBVOpcDiff.mapValueAdd(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Longs.toByteArray(0), DataType.Amount), DataEntry( + Longs.toByteArray(-1), DataType.Amount)) should be (Left(InvalidDataEntry)) + CDBVOpcDiff.mapValueAdd(executionContext)( + Array[Byte](0.toByte, 3.toByte, 4.toByte), DataEntry( + Longs.toByteArray(1), DataType.Amount), DataEntry( + Longs.toByteArray(1), DataType.Int32)) should be (Left(ContractDataTypeMismatch)) + + CDBVOpcDiff.mapValueMinus(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Longs.toByteArray(0), DataType.Amount), DataEntry( + Longs.toByteArray(0), DataType.Amount)).right.get.contractNumDB( + ByteStr(tx.contractId.bytes.arr ++ Array[Byte](0.toByte) ++ DataEntry( + Longs.toByteArray(0), DataType.Amount).bytes)) shouldEqual 0 + CDBVOpcDiff.mapValueMinus(executionContext)( + Array[Byte](0.toByte, 2.toByte, 2.toByte), DataEntry( + Longs.toByteArray(0), DataType.ShortBytes), DataEntry( + Longs.toByteArray(0), DataType.Amount)) should be (Left(ContractInvalidStateMap)) + CDBVOpcDiff.mapValueMinus(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Longs.toByteArray(0), DataType.Amount), DataEntry( + Longs.toByteArray(-1), DataType.Amount)) should be (Left(InvalidDataEntry)) + CDBVOpcDiff.mapValueMinus(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Longs.toByteArray(1), DataType.Amount), DataEntry( + Longs.toByteArray(1), DataType.Amount)) should be (Left(ContractMapValueInsufficient)) + CDBVOpcDiff.mapValueMinus(executionContext)( + Array[Byte](0.toByte, 3.toByte, 4.toByte), DataEntry( + Longs.toByteArray(1), DataType.Amount), DataEntry( + Longs.toByteArray(1), DataType.Int32)) should be (Left(ContractDataTypeMismatch)) + + CDBVOpcDiff.valueAdd(executionContext)( + Array[Byte](0.toByte, 3.toByte), DataEntry( + Longs.toByteArray(0), DataType.Amount)).right.get.contractNumDB( + ByteStr(tx.contractId.bytes.arr ++ Array[Byte](0.toByte))) shouldEqual 0 + CDBVOpcDiff.valueAdd(executionContext)( + Array[Byte](0.toByte, 2.toByte), DataEntry( + Longs.toByteArray(0), DataType.ShortBytes)) should be (Left(ContractInvalidStateVariable)) + CDBVOpcDiff.valueAdd(executionContext)( + Array[Byte](0.toByte, 3.toByte), DataEntry( + Longs.toByteArray(-1), DataType.Amount)) should be (Left(InvalidDataEntry)) + CDBVOpcDiff.valueAdd(executionContext)( + Array[Byte](0.toByte, 4.toByte), DataEntry( + Longs.toByteArray(1), DataType.Int32)) should be (Left(ContractDataTypeMismatch)) + + CDBVOpcDiff.valueMinus(executionContext)( + Array[Byte](0.toByte, 3.toByte), DataEntry( + Longs.toByteArray(0), DataType.Amount)).right.get.contractNumDB( + ByteStr(tx.contractId.bytes.arr ++ Array[Byte](0.toByte))) shouldEqual 0 + CDBVOpcDiff.valueMinus(executionContext)( + Array[Byte](0.toByte, 2.toByte), DataEntry( + Longs.toByteArray(0), DataType.ShortBytes)) should be (Left(ContractInvalidStateVariable)) + CDBVOpcDiff.valueMinus(executionContext)( + Array[Byte](0.toByte, 3.toByte), DataEntry( + Longs.toByteArray(-1), DataType.Amount)) should be (Left(InvalidDataEntry)) + CDBVOpcDiff.valueMinus(executionContext)( + Array[Byte](0.toByte, 3.toByte), DataEntry( + Longs.toByteArray(1), DataType.Amount)) should be (Left(ContractMapValueInsufficient)) + CDBVOpcDiff.valueMinus(executionContext)( + Array[Byte](0.toByte, 4.toByte), DataEntry( + Longs.toByteArray(1), DataType.Int32)) should be (Left(ContractDataTypeMismatch)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/CDBVROpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/CDBVROpcDiffTest.scala new file mode 100644 index 000000000..da3daff1e --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/CDBVROpcDiffTest.scala @@ -0,0 +1,87 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Ints, Longs} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.PrivateKeyAccount +import vsys.blockchain.contract.{ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.StateWriterImpl +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError._ +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.Left + +class CDBVROpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + val state: StateWriterImpl = newState() + + val tx: RegisterContractTransaction = RegisterContractTransaction.create( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L).right.get + + val executionContext: ExecutionContext = ExecutionContext.fromRegConTx( + state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx).right.get + + property("test CDBVR opcs") { + CDBVROpcDiff.get(executionContext)( + Array[Byte](0.toByte, 3.toByte), Seq.empty, 0) should be (Left(ContractStateVariableNotDefined)) + CDBVROpcDiff.get(executionContext)( + Array[Byte](0.toByte, 3.toByte), Seq.empty, 1) should be (Left(ContractLocalVariableIndexOutOfRange)) + + CDBVROpcDiff.mapGet(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Longs.toByteArray(1), DataType.Amount), Seq.empty, 0) should be ( + Left(ContractStateMapNotDefined)) + CDBVROpcDiff.mapGet(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Ints.toByteArray(1), DataType.Int32), Seq.empty, 0) should be ( + Left(ContractInvalidStateMap)) + CDBVROpcDiff.mapGet(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Longs.toByteArray(1), DataType.Amount), Seq.empty, 1) should be ( + Left(ContractLocalVariableIndexOutOfRange)) + + CDBVROpcDiff.mapGetOrDefault(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Longs.toByteArray(1), DataType.Amount), Seq.empty, 0) should be ( + Right(Seq(DataEntry(Longs.toByteArray(0), DataType.Amount)))) + CDBVROpcDiff.mapGetOrDefault(executionContext)( + Array[Byte](0.toByte, 9.toByte, 9.toByte), DataEntry( + Longs.toByteArray(1), DataType.Timestamp), Seq.empty, 0) should be ( + Right(Seq(DataEntry(Longs.toByteArray(0), DataType.Timestamp)))) + CDBVROpcDiff.mapGetOrDefault(executionContext)( + Array[Byte](0.toByte, 9.toByte, 10.toByte), DataEntry( + Longs.toByteArray(1), DataType.Timestamp), Seq.empty, 0) should be ( + Right(Seq(DataEntry(Array(0.toByte), DataType.Boolean)))) + CDBVROpcDiff.mapGetOrDefault(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Ints.toByteArray(1), DataType.Int32), Seq.empty, 0) should be ( + Left(ContractInvalidStateMap)) + CDBVROpcDiff.mapGetOrDefault(executionContext)( + Array[Byte](0.toByte, 4.toByte, 4.toByte), DataEntry( + Ints.toByteArray(1), DataType.Int32), Seq.empty, 0) should be ( + Left(ContractStateMapNotDefined)) + CDBVROpcDiff.mapGetOrDefault(executionContext)( + Array[Byte](0.toByte, 3.toByte, 3.toByte), DataEntry( + Longs.toByteArray(1), DataType.Amount), Seq.empty, 1) should be ( + Left(ContractLocalVariableIndexOutOfRange)) + + CDBVROpcDiff.getOrDefault(executionContext)( + Array[Byte](0.toByte, 3.toByte), Seq.empty, 0) should be ( + Right(Seq(DataEntry(Longs.toByteArray(0), DataType.Amount)))) + CDBVROpcDiff.getOrDefault(executionContext)( + Array[Byte](0.toByte, 9.toByte), Seq.empty, 0) should be ( + Right(Seq(DataEntry(Longs.toByteArray(0), DataType.Timestamp)))) + CDBVROpcDiff.getOrDefault(executionContext)( + Array[Byte](0.toByte, 4.toByte), Seq.empty, 0) should be ( + Left(ContractStateVariableNotDefined)) + CDBVROpcDiff.getOrDefault(executionContext)( + Array[Byte](0.toByte, 3.toByte), Seq.empty, 1) should be ( + Left(ContractLocalVariableIndexOutOfRange)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/CompareOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/CompareOpcDiffTest.scala new file mode 100644 index 000000000..c3f319da8 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/CompareOpcDiffTest.scala @@ -0,0 +1,27 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Ints, Longs} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.blockchain.contract.{DataEntry, DataType} +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractUnsupportedOPC} + +import scala.util.{Left, Right} + +class CompareOpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + property("test compare opcs") { + CompareOpcDiff.numBiComparation(DataEntry(Longs.toByteArray(1), DataType.Amount), DataEntry( + Longs.toByteArray(1), DataType.Amount), CompareOpcDiff.ge) should be ( + Right(DataEntry(Array(1.toByte), DataType.Boolean))) + CompareOpcDiff.numBiComparation(DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Longs.toByteArray(1), DataType.Amount), CompareOpcDiff.ge) should be ( + Right(DataEntry(Array(0.toByte), DataType.Boolean))) + CompareOpcDiff.numBiComparation(DataEntry(Ints.toByteArray(0), DataType.Balance), DataEntry( + Ints.toByteArray(1), DataType.Balance), CompareOpcDiff.ge) should be ( + Left(ContractUnsupportedOPC)) + CompareOpcDiff.numBiComparation(DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(1), DataType.Int32), CompareOpcDiff.ge) should be ( + Left(ContractDataTypeMismatch)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/ConvertOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/ConvertOpcDiffTest.scala new file mode 100644 index 000000000..3ab1da810 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/ConvertOpcDiffTest.scala @@ -0,0 +1,238 @@ +package vsys.blockchain.state.opcdiffs + +import org.scalatest.{Matchers, PropSpec} +import com.google.common.primitives.{Ints, Longs, Shorts} +import vsys.blockchain.contract.{DataEntry, DataType} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import vsys.blockchain.transaction.ValidationError + +class ConvertOpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + val AmountDataTypeObj = DataEntry(Array(3), DataType.DataTypeObj) + val Int32DataTypeObj = DataEntry(Array(4), DataType.DataTypeObj) + val TimestampDataTypeObj = DataEntry(Array(9), DataType.DataTypeObj) + val BigIntDataTypeObj = DataEntry(Array(14), DataType.DataTypeObj) + + property("test convertions of all numerical types to Amount") { + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray(BigInt(1000).toByteArray.length.toShort) ++ BigInt(1000).toByteArray, DataType.BigInteger), AmountDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(1000), DataType.Amount)) + ) + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(1000), DataType.Int32), AmountDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(1000), DataType.Amount)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(1000), DataType.Timestamp), AmountDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(1000), DataType.Amount)) + ) + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), AmountDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(0), DataType.Amount)) + ) + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(0), DataType.Int32), AmountDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(0), DataType.Amount)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(0), DataType.Timestamp), AmountDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(0), DataType.Amount)) + ) + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray(BigInt(Long.MaxValue).toByteArray.length.toShort) ++ BigInt(Long.MaxValue).toByteArray, DataType.BigInteger), AmountDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Amount)) + ) + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(Int.MaxValue), DataType.Int32), AmountDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(Int.MaxValue), DataType.Amount)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Timestamp), AmountDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Amount)) + ) + } + property("test convertions of all numerical types to Int32") { + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray(BigInt(1000).toByteArray.length.toShort) ++ BigInt(1000).toByteArray, DataType.BigInteger), Int32DataTypeObj) should be ( + Right(DataEntry(Ints.toByteArray(1000), DataType.Int32)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(1000), DataType.Amount), Int32DataTypeObj) should be ( + Right(DataEntry(Ints.toByteArray(1000), DataType.Int32)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(1000), DataType.Timestamp), Int32DataTypeObj) should be ( + Right(DataEntry(Ints.toByteArray(1000), DataType.Int32)) + ) + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), Int32DataTypeObj) should be ( + Right(DataEntry(Ints.toByteArray(0), DataType.Int32)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(0), DataType.Amount), Int32DataTypeObj) should be ( + Right(DataEntry(Ints.toByteArray(0), DataType.Int32)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(0), DataType.Timestamp), Int32DataTypeObj) should be ( + Right(DataEntry(Ints.toByteArray(0), DataType.Int32)) + ) + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray(BigInt(Int.MaxValue).toByteArray.length.toShort) ++ BigInt(Int.MaxValue).toByteArray, DataType.BigInteger), Int32DataTypeObj) should be ( + Right(DataEntry(Ints.toByteArray(Int.MaxValue), DataType.Int32)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(Int.MaxValue), DataType.Amount), Int32DataTypeObj) should be ( + Right(DataEntry(Ints.toByteArray(Int.MaxValue), DataType.Int32)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(Int.MaxValue), DataType.Timestamp), Int32DataTypeObj) should be ( + Right(DataEntry(Ints.toByteArray(Int.MaxValue), DataType.Int32)) + ) + } + property("test convertion of all numerical types to Timestamp") { + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray(BigInt(1000).toByteArray.length.toShort) ++ BigInt(1000).toByteArray, DataType.BigInteger), TimestampDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(1000), DataType.Timestamp)) + ) + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(1000), DataType.Int32), TimestampDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(1000), DataType.Timestamp)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(1000), DataType.Amount), TimestampDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(1000), DataType.Timestamp)) + ) + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray(BigInt(0).toByteArray.length.toShort) ++ BigInt(0).toByteArray, DataType.BigInteger), TimestampDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(0), DataType.Timestamp)) + ) + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(0), DataType.Int32), TimestampDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(0), DataType.Timestamp)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(0), DataType.Amount), TimestampDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(0), DataType.Timestamp)) + ) + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray(BigInt(Long.MaxValue).toByteArray.length.toShort) ++ BigInt(Long.MaxValue).toByteArray, DataType.BigInteger), TimestampDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(Long.MaxValue), DataType.Timestamp)) + ) + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(Int.MaxValue), DataType.Int32), TimestampDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(Int.MaxValue), DataType.Timestamp)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(Int.MaxValue), DataType.Amount), TimestampDataTypeObj) should be ( + Right(DataEntry(Longs.toByteArray(Int.MaxValue), DataType.Timestamp)) + ) + } + property("test convertion of all numberical types to BigInt") { + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(1000), DataType.Amount), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(1000).toByteArray.length.toShort) ++ BigInt(1000).toByteArray, DataType.BigInteger)) + ) + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(1000), DataType.Int32), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(1000).toByteArray.length.toShort) ++ BigInt(1000).toByteArray, DataType.BigInteger)) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(1000), DataType.Timestamp), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(1000).toByteArray.length.toShort) ++ BigInt(1000).toByteArray, DataType.BigInteger)) + ) + } + property("test overflow to Int32") { + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray((BigInt(Int.MaxValue) + 1).toByteArray.length.toShort) ++ (BigInt(Int.MaxValue) + 1).toByteArray, DataType.BigInteger), Int32DataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(Int.MaxValue + 1), DataType.Amount), Int32DataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(Int.MaxValue + 1), DataType.Timestamp), Int32DataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray((BigInt(Long.MaxValue) * BigInt(Long.MaxValue)).toByteArray.length.toShort) ++ (BigInt(Long.MaxValue) * BigInt(Long.MaxValue)).toByteArray, DataType.BigInteger), Int32DataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + } + property("test overflow to Amount") { + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray((BigInt(Long.MaxValue) + 1).toByteArray.length.toShort) ++ (BigInt(Long.MaxValue) + 1).toByteArray, DataType.BigInteger), AmountDataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + BasicOpcDiff.convertion(DataEntry((BigInt(Long.MaxValue) + 1).toByteArray, DataType.Amount), AmountDataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + BasicOpcDiff.convertion(DataEntry((BigInt(Long.MaxValue) + 1).toByteArray, DataType.Timestamp), AmountDataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray((BigInt(Long.MaxValue) * BigInt(Long.MaxValue)).toByteArray.length.toShort) ++ (BigInt(Long.MaxValue) * BigInt(Long.MaxValue)).toByteArray, DataType.BigInteger), AmountDataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + } + property("test overflow to Timestamp") { + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray((BigInt(Long.MaxValue) + 1).toByteArray.length.toShort) ++ (BigInt(Long.MaxValue) + 1).toByteArray, DataType.BigInteger), TimestampDataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + BasicOpcDiff.convertion(DataEntry((BigInt(Long.MaxValue) + 1).toByteArray, DataType.Amount), TimestampDataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + BasicOpcDiff.convertion(DataEntry((BigInt(Long.MaxValue) + 1).toByteArray, DataType.Timestamp), TimestampDataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + BasicOpcDiff.convertion(DataEntry(Shorts.toByteArray((BigInt(Long.MaxValue) * BigInt(Long.MaxValue)).toByteArray.length.toShort) ++ (BigInt(Long.MaxValue) * BigInt(Long.MaxValue)).toByteArray, DataType.BigInteger), TimestampDataTypeObj) should be ( + Left(ValidationError.OverflowError) + ) + } + property("test different length numbers for conversion to BigInt") { + val l: List[Long] = List(1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, + 1000000000, 10000000000L, 100000000000L, 1000000000000L, 10000000000000L, 100000000000000L, + 1000000000000000L, 10000000000000000L, 100000000000000000L, 1000000000000000000L) + for (a <- l) { + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(a), DataType.Amount), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(a).toByteArray.length.toShort) ++ BigInt(a).toByteArray, DataType.BigInteger)) + ) + } + for (b <- l.slice(0, 9)) { + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(b.toInt), DataType.Int32), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(b).toByteArray.length.toShort) ++ BigInt(b).toByteArray, DataType.BigInteger)) + ) + } + for (c <- l) { + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(c), DataType.Timestamp), BigIntDataTypeObj) should be( + Right(DataEntry(Shorts.toByteArray(BigInt(c).toByteArray.length.toShort) ++ BigInt(c).toByteArray, DataType.BigInteger)) + ) + } + } + + property("test different length negative numbers for conversion to BigInt") { + val l: List[Long] = List(1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, + 1000000000, 10000000000L, 100000000000L, 1000000000000L, 10000000000000L, 100000000000000L, + 1000000000000000L, 10000000000000000L, 100000000000000000L, 1000000000000000000L) + for (a <- l) { + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(-a), DataType.Amount), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(-a).toByteArray.length.toShort) ++ BigInt(-a).toByteArray, DataType.BigInteger)) + ) + } + for (b <- l.slice(0, 9)) { + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(-b.toInt), DataType.Int32), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(-b).toByteArray.length.toShort) ++ BigInt(-b).toByteArray, DataType.BigInteger)) + ) + } + for (c <- l) { + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(-c), DataType.Timestamp), BigIntDataTypeObj) should be( + Right(DataEntry(Shorts.toByteArray(BigInt(-c).toByteArray.length.toShort) ++ BigInt(-c).toByteArray, DataType.BigInteger)) + ) + } + } + + property("test different array length numbers for conversion to BigInt") { + val l: List[Long] = List(Math.pow(2, 0).toLong, Math.pow(2, 8).toLong, Math.pow(2, 16).toLong, Math.pow(2, 24).toLong, + (Math.pow(2, 32)/2 - 1).toLong, Math.pow(2, 32).toLong, Math.pow(2, 40).toLong, Math.pow(2, 48).toLong, + Math.pow(2, 56).toLong, (Math.pow(2, 64)/2 - 1).toLong) + for (a <- l) { + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(a), DataType.Amount), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(a).toByteArray.length.toShort) ++ BigInt(a).toByteArray, DataType.BigInteger)) + ) + } + for (b <- l.slice(0, 4)) { + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(b.toInt), DataType.Int32), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(b).toByteArray.length.toShort) ++ BigInt(b).toByteArray, DataType.BigInteger)) + ) + } + for (c <- l) { + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(c), DataType.Timestamp), BigIntDataTypeObj) should be( + Right(DataEntry(Shorts.toByteArray(BigInt(c).toByteArray.length.toShort) ++ BigInt(c).toByteArray, DataType.BigInteger)) + ) + } + } + + property("test different array length negative numbers for conversion to BigInt") { + val l: List[Long] = List(Math.pow(2, 0).toLong, Math.pow(2, 8).toLong, Math.pow(2, 16).toLong, Math.pow(2, 24).toLong, + (Math.pow(2, 32)/2 - 1).toLong, Math.pow(2, 32).toLong, Math.pow(2, 40).toLong, Math.pow(2, 48).toLong, + Math.pow(2, 56).toLong, (Math.pow(2, 64)/2 - 1).toLong) + for (a <- l) { + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(-a), DataType.Amount), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(-a).toByteArray.length.toShort) ++ BigInt(-a).toByteArray, DataType.BigInteger)) + ) + } + for (b <- l.slice(0, 4)) { + BasicOpcDiff.convertion(DataEntry(Ints.toByteArray(-b.toInt), DataType.Int32), BigIntDataTypeObj) should be ( + Right(DataEntry(Shorts.toByteArray(BigInt(-b).toByteArray.length.toShort) ++ BigInt(-b).toByteArray, DataType.BigInteger)) + ) + } + for (c <- l) { + BasicOpcDiff.convertion(DataEntry(Longs.toByteArray(-c), DataType.Timestamp), BigIntDataTypeObj) should be( + Right(DataEntry(Shorts.toByteArray(BigInt(-c).toByteArray.length.toShort) ++ BigInt(-c).toByteArray, DataType.BigInteger)) + ) + } + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/IfOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/IfOpcDiffTest.scala new file mode 100644 index 000000000..fb91cdb12 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/IfOpcDiffTest.scala @@ -0,0 +1,81 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Longs, Shorts} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.PrivateKeyAccount +import vsys.blockchain.contract.{ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.StateWriterImpl +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidOPCData} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.{Left, Right} + +class IfOpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + val state: StateWriterImpl = newState() + + val tx: RegisterContractTransaction = RegisterContractTransaction.create( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L).right.get + + val executionContext: ExecutionContext = ExecutionContext.fromRegConTx( + state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx).right.get + + property("if opcs") { + IfOpcDiff.executeOpcBlock(executionContext, + DataEntry(Longs.toByteArray(1), DataType.Amount), + Seq.empty) should be (Left(ContractDataTypeMismatch)) + IfOpcDiff.executeOpcBlock(executionContext, + DataEntry(Shorts.toByteArray(0), DataType.OpcBlock), + Seq.empty) should be (Right((OpcDiff(Map(),Map(),Map(),Map(),Map(),Map(),Map(),Map()),List()))) + IfOpcDiff.executeOpcBlock(executionContext, + DataEntry(Array[Byte](0, 2, 0, 1), DataType.OpcBlock), + Seq.empty) should be (Left(ContractInvalidOPCData)) + + IfOpcDiff.runCondition( + DataEntry(Array(1.toByte), DataType.Boolean) + ) should be (Right(true)) + IfOpcDiff.runCondition( + DataEntry(Array(0.toByte), DataType.Boolean) + ) should be (Right(false)) + IfOpcDiff.runCondition( + DataEntry(Longs.toByteArray(1), DataType.Amount) + ) should be (Left(ContractDataTypeMismatch)) + + IfOpcDiff.parseBytes(executionContext)( + Array[Byte](1.toByte, 0.toByte, 1.toByte), + Seq[DataEntry](DataEntry(Array(1.toByte), DataType.Boolean), + DataEntry(Shorts.toByteArray(0), DataType.OpcBlock))) should be ( + Right((OpcDiff(Map(),Map(),Map(),Map(),Map(),Map(),Map(),Map()), + List(DataEntry(Array(1.toByte), DataType.Boolean), + DataEntry(Shorts.toByteArray(0), DataType.OpcBlock))))) + IfOpcDiff.parseBytes(executionContext)( + Array[Byte](1.toByte, 0.toByte, 1.toByte), + Seq[DataEntry](DataEntry(Array(0.toByte), DataType.Boolean), + DataEntry(Shorts.toByteArray(0), DataType.OpcBlock))) should be ( + Right((OpcDiff(Map(),Map(),Map(),Map(),Map(),Map(),Map(),Map()), + List(DataEntry(Array(0.toByte), DataType.Boolean), + DataEntry(Shorts.toByteArray(0), DataType.OpcBlock))))) + IfOpcDiff.parseBytes(executionContext)( + Array[Byte](2.toByte, 0.toByte, 1.toByte, 2.toByte), + Seq[DataEntry](DataEntry(Array(1.toByte), DataType.Boolean), + DataEntry(Shorts.toByteArray(0), DataType.OpcBlock), + DataEntry(Shorts.toByteArray(1), DataType.OpcBlock))) should be ( + Right((OpcDiff(Map(),Map(),Map(),Map(),Map(),Map(),Map(),Map()), + List(DataEntry(Array(1.toByte), DataType.Boolean), + DataEntry(Shorts.toByteArray(0), DataType.OpcBlock), + DataEntry(Shorts.toByteArray(1), DataType.OpcBlock))))) + IfOpcDiff.parseBytes(executionContext)( + Array[Byte](2.toByte, 0.toByte, 1.toByte, 2.toByte), + Seq[DataEntry](DataEntry(Array(0.toByte), DataType.Boolean), + DataEntry(Shorts.toByteArray(0), DataType.OpcBlock), + DataEntry(Array[Byte](0, 2, 0, 1), DataType.OpcBlock))) should be ( + Left(ContractInvalidOPCData)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/LoadOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/LoadOpcDiffTest.scala new file mode 100644 index 000000000..ec95a52a6 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/LoadOpcDiffTest.scala @@ -0,0 +1,77 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Ints, Longs, Shorts} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.PrivateKeyAccount +import vsys.blockchain.contract.{ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.StateWriterImpl +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError.ContractLocalVariableIndexOutOfRange +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.{Left, Right} + +class LoadOpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + val state: StateWriterImpl = newState() + + val tx: RegisterContractTransaction = RegisterContractTransaction.create( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L).right.get + + val executionContext: ExecutionContext = ExecutionContext.fromRegConTx( + state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx).right.get + + val account: Array[Byte] = PrivateKeyAccount( + Array.fill(TransactionParser.KeyLength)(0)).toAddress.bytes.arr + + property("test load opcs") { + + LoadOpcDiff.signer(executionContext)( + Seq.empty, 0) should be (Right(Seq(DataEntry( + account, DataType.Address)))) + LoadOpcDiff.signer(executionContext)( + Seq.empty, 1) should be (Left(ContractLocalVariableIndexOutOfRange)) + + LoadOpcDiff.caller(executionContext)( + Seq.empty, 0) should be (Right(Seq(DataEntry( + account, DataType.Address)))) + LoadOpcDiff.caller(executionContext)( + Seq.empty, 1) should be (Left(ContractLocalVariableIndexOutOfRange)) + + LoadOpcDiff.timestamp(executionContext)( + Seq.empty, 0) should be (Right(Seq(DataEntry( + Longs.toByteArray(0), DataType.Timestamp)))) + LoadOpcDiff.timestamp(executionContext)( + Seq.empty, 1) should be (Left(ContractLocalVariableIndexOutOfRange)) + + LoadOpcDiff.lastTokenIndex(executionContext)( + Seq.empty, 0) should be (Right(Seq(DataEntry( + Ints.toByteArray(-1), DataType.Int32)))) + LoadOpcDiff.lastTokenIndex(executionContext)( + Seq.empty, 1) should be (Left(ContractLocalVariableIndexOutOfRange)) + + LoadOpcDiff.transactionId(executionContext)( + Seq.empty, 0) should be (Right(Seq(DataEntry( + Shorts.toByteArray(tx.id.arr.length.toShort) ++ tx.id.arr, DataType.ShortBytes)))) + LoadOpcDiff.transactionId(executionContext)( + Seq.empty, 1) should be (Left(ContractLocalVariableIndexOutOfRange)) + + LoadOpcDiff.signerPublicKey(executionContext)( + Seq.empty, 0) should be (Right(Seq(DataEntry( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)).publicKey, DataType.PublicKey)))) + LoadOpcDiff.signerPublicKey(executionContext)( + Seq.empty, 1) should be (Left(ContractLocalVariableIndexOutOfRange)) + + LoadOpcDiff.height(executionContext)( + Seq.empty, 0) should be (Right(Seq(DataEntry( + Ints.toByteArray(1), DataType.Int32)))) + LoadOpcDiff.height(executionContext)( + Seq.empty, 1) should be (Left(ContractLocalVariableIndexOutOfRange)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/ReturnOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/ReturnOpcDiffTest.scala new file mode 100644 index 000000000..8d71e292a --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/ReturnOpcDiffTest.scala @@ -0,0 +1,30 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.Longs +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.PrivateKeyAccount +import vsys.blockchain.contract.{ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError.ContractUnsupportedOPC +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.Left + +class ReturnOpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + property("test return opcs") { + + val state = newState() + + val tx = RegisterContractTransaction.create(PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L) + + ReturnOpcDiff.value(ExecutionContext.fromRegConTx(state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx.right.get).right.get)(Seq.empty, 0) should be ( + Left(ContractUnsupportedOPC)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/SystemTransferDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/SystemTransferDiffTest.scala new file mode 100644 index 000000000..d15cea9e0 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/SystemTransferDiffTest.scala @@ -0,0 +1,101 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Ints, Longs} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.{ContractAccount, PrivateKeyAccount} +import vsys.blockchain.contract.{CallType, ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.{ByteStr, StateWriterImpl} +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidAmount, + ContractTokenBalanceInsufficient, GenericError, InvalidContractAddress} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.{Left, Right} + +class SystemTransferDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + val state: StateWriterImpl = newState() + + val tx: RegisterContractTransaction = RegisterContractTransaction.create( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L).right.get + + val executionContext: ExecutionContext = ExecutionContext.fromRegConTx( + state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx).right.get + + val account: Array[Byte] = PrivateKeyAccount( + Array.fill(TransactionParser.KeyLength)(0)).toAddress.bytes.arr + + val account1: Array[Byte] = PrivateKeyAccount( + Array.fill(TransactionParser.KeyLength)(1)).toAddress.bytes.arr + + property("test system transfer opcs") { + SystemTransferDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Trigger, 2) should be ( + Right(OpcDiff.empty)) + SystemTransferDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, DataEntry( + ContractAccount.fromId(ByteStr(Array.emptyByteArray)).bytes.arr, DataType.ContractAccount), + DataEntry(account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Trigger, 2) should be ( + Left(InvalidContractAddress)) + SystemTransferDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Trigger, 1) should be ( + Right(OpcDiff.empty)) + SystemTransferDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, + DataEntry(account1, DataType.Address), + DataEntry(ContractAccount.fromId(ByteStr(Array.emptyByteArray)).bytes.arr, DataType.ContractAccount), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Trigger, 1) should be ( + Left(InvalidContractAddress)) + SystemTransferDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Trigger, 3) should be ( + Left(GenericError("Invalid Call Index"))) + SystemTransferDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Function, 1) should be ( + Left(GenericError("Invalid Call Type"))) + + SystemTransferDiff.transfer(executionContext)(DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Ints.toByteArray(0), DataType.Int32)) should be ( + Left(ContractDataTypeMismatch)) + SystemTransferDiff.transfer(executionContext)(DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(1), DataType.Amount)) should be ( + Left(ContractTokenBalanceInsufficient)) + SystemTransferDiff.transfer(executionContext)(DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(-1), DataType.Amount)) should be ( + Left(ContractInvalidAmount)) + SystemTransferDiff.transfer(executionContext)(DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount)).right.get.relatedAddress( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(1)).toAddress) shouldEqual true + SystemTransferDiff.transfer(executionContext)(DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount)).right.get.portfolios( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(1)).toAddress).balance shouldEqual 0 + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/TDBAOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/TDBAOpcDiffTest.scala new file mode 100644 index 000000000..b72cf8e45 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/TDBAOpcDiffTest.scala @@ -0,0 +1,117 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Ints, Longs} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.{ContractAccount, PrivateKeyAccount} +import vsys.blockchain.contract.{CallType, ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.{ByteStr, StateWriterImpl} +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidTokenIndex, + GenericError, InvalidContractAddress} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.{Left, Right} + +class TDBAOpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + val state: StateWriterImpl = newState() + + val tx: RegisterContractTransaction = RegisterContractTransaction.create( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L).right.get + + val executionContext: ExecutionContext = ExecutionContext.fromRegConTx( + state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx).right.get + + val account: Array[Byte] = PrivateKeyAccount( + Array.fill(TransactionParser.KeyLength)(0)).toAddress.bytes.arr + + val account1: Array[Byte] = PrivateKeyAccount( + Array.fill(TransactionParser.KeyLength)(1)).toAddress.bytes.arr + + property("test TDBA opcs") { + + TDBAOpcDiff.deposit(executionContext)(DataEntry( + account, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount)) should be ( + Left(ContractInvalidTokenIndex)) + TDBAOpcDiff.deposit(executionContext)(DataEntry( + account, DataType.Address), + DataEntry(Ints.toByteArray(0), DataType.Int32)) should be ( + Left(ContractDataTypeMismatch)) + + TDBAOpcDiff.withdraw(executionContext)(DataEntry( + account, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount)) should be ( + Left(ContractInvalidTokenIndex)) + TDBAOpcDiff.withdraw(executionContext)(DataEntry( + account, DataType.Address), + DataEntry(Ints.toByteArray(0), DataType.Int32)) should be ( + Left(ContractDataTypeMismatch)) + + TDBAOpcDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Trigger, 2) should be ( + Right(OpcDiff.empty)) + TDBAOpcDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, DataEntry( + ContractAccount.fromId(ByteStr(Array.emptyByteArray)).bytes.arr, DataType.ContractAccount), + DataEntry(account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Trigger, 2) should be ( + Left(InvalidContractAddress)) + TDBAOpcDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Trigger, 1) should be ( + Right(OpcDiff.empty)) + TDBAOpcDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, + DataEntry(account1, DataType.Address), + DataEntry(ContractAccount.fromId(ByteStr(Array.emptyByteArray)).bytes.arr, DataType.ContractAccount), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Trigger, 1) should be ( + Left(InvalidContractAddress)) + TDBAOpcDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Trigger, 3) should be ( + Left(GenericError("Invalid Call Index"))) + TDBAOpcDiff.getTriggerCallOpcDiff(executionContext, OpcDiff.empty, DataEntry( + account, DataType.Address), DataEntry( + account1, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), DataEntry( + Ints.toByteArray(0), DataType.Int32), CallType.Function, 1) should be ( + Left(GenericError("Invalid Call Type"))) + + TDBAOpcDiff.contractTransfer(executionContext)(DataEntry( + Longs.toByteArray(0), DataType.Amount), + DataEntry(account, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount)) should be (Left(ContractDataTypeMismatch)) + TDBAOpcDiff.contractTransfer(executionContext)( + DataEntry(account, DataType.Address), + DataEntry(account, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount)) should be (Left(ContractInvalidTokenIndex)) + + TDBAOpcDiff.basicTransfer(executionContext)(DataEntry( + Longs.toByteArray(0), DataType.Amount), + DataEntry(account, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount)) should be (Left(ContractDataTypeMismatch)) + TDBAOpcDiff.basicTransfer(executionContext)( + DataEntry(account, DataType.Address), + DataEntry(account, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount)) should be (Left(ContractInvalidTokenIndex)) + + TDBAOpcDiff.transfer(executionContext)(DataEntry( + Longs.toByteArray(0), DataType.Amount), + DataEntry(account, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount)) should be (Left(ContractDataTypeMismatch)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/TDBAROpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/TDBAROpcDiffTest.scala new file mode 100644 index 000000000..93e19b28b --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/TDBAROpcDiffTest.scala @@ -0,0 +1,50 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Ints, Longs} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.PrivateKeyAccount +import vsys.blockchain.contract.{ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.StateWriterImpl +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidTokenIndex} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.Left + +class TDBAROpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + val state: StateWriterImpl = newState() + + val tx: RegisterContractTransaction = RegisterContractTransaction.create( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L).right.get + + val executionContext: ExecutionContext = ExecutionContext.fromRegConTx( + state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx).right.get + + val account: Array[Byte] = PrivateKeyAccount( + Array.fill(TransactionParser.KeyLength)(0)).toAddress.bytes.arr + + property("test TDBAR opcs") { + + TDBAROpcDiff.balance(executionContext)(DataEntry( + account, DataType.Address), + DataEntry(Ints.toByteArray(0), DataType.Int32), Seq.empty, 0) should be ( + Left(ContractInvalidTokenIndex)) + TDBAROpcDiff.balance(executionContext)(DataEntry( + account, DataType.Address), + DataEntry(Longs.toByteArray(0), DataType.Amount), Seq.empty, 0) should be ( + Left(ContractDataTypeMismatch)) + + TDBAROpcDiff.balanceWithoutTokenIndex( + executionContext)(DataEntry( + account, DataType.Address), + Seq.empty, 0) should be ( + Left(ContractInvalidTokenIndex)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/TDBOpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/TDBOpcDiffTest.scala new file mode 100644 index 000000000..f083ca651 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/TDBOpcDiffTest.scala @@ -0,0 +1,59 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Bytes, Ints, Longs} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.ContractAccount.tokenIdFromBytes +import vsys.account.PrivateKeyAccount +import vsys.blockchain.contract.{ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.{ByteStr, StateWriterImpl} +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidTokenIndex, ContractInvalidTokenInfo} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.Left + +class TDBOpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + property("test TDB opcs") { + + val state: StateWriterImpl = newState() + + val tx: RegisterContractTransaction = RegisterContractTransaction.create( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L).right.get + + val executionContext: ExecutionContext = ExecutionContext.fromRegConTx( + state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx).right.get + + val tokenID: ByteStr = tokenIdFromBytes(executionContext.contractId.bytes.arr, Ints.toByteArray(0)).right.get + + TDBOpcDiff.newToken(executionContext)(DataEntry( + Ints.toByteArray(0), DataType.Int32), DataEntry( + Longs.toByteArray(0), DataType.Amount), DataEntry( + Array.fill(13){1}, DataType.ShortText)) should be (Left(ContractDataTypeMismatch)) + TDBOpcDiff.newToken(executionContext)(DataEntry( + Longs.toByteArray(0), DataType.Amount), DataEntry( + Longs.toByteArray(0), DataType.Amount), DataEntry( + Array.fill(13){1}, DataType.ShortText)) should be (Left(ContractInvalidTokenInfo)) + TDBOpcDiff.newToken(executionContext)(DataEntry( + Longs.toByteArray(-1), DataType.Amount), DataEntry( + Longs.toByteArray(1), DataType.Amount), DataEntry( + Array.fill(13){1}, DataType.ShortText)) should be (Left(ContractInvalidTokenInfo)) + TDBOpcDiff.newToken(executionContext)(DataEntry( + Longs.toByteArray(0), DataType.Amount), DataEntry( + Longs.toByteArray(1), DataType.Amount), DataEntry( + Array.fill(13){1}, DataType.ShortText)).right.get.tokenDB( + ByteStr(Bytes.concat(tokenID.arr, Array(0.toByte)))) shouldEqual DataEntry( + Longs.toByteArray(0), DataType.Amount).bytes + + TDBOpcDiff.split(executionContext)(DataEntry( + Longs.toByteArray(0), DataType.Amount)) should be (Left(ContractInvalidTokenIndex)) + TDBOpcDiff.split(executionContext)(DataEntry( + Ints.toByteArray(0), DataType.Int32)) should be (Left(ContractDataTypeMismatch)) + } +} diff --git a/src/test/scala/vsys/blockchain/state/opcdiffs/TDBROpcDiffTest.scala b/src/test/scala/vsys/blockchain/state/opcdiffs/TDBROpcDiffTest.scala new file mode 100644 index 000000000..590c121d1 --- /dev/null +++ b/src/test/scala/vsys/blockchain/state/opcdiffs/TDBROpcDiffTest.scala @@ -0,0 +1,73 @@ +package vsys.blockchain.state.opcdiffs + +import com.google.common.primitives.{Ints, Longs} +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} +import org.scalatest.{Matchers, PropSpec} +import vsys.account.PrivateKeyAccount +import vsys.blockchain.contract.{ContractPermitted, DataEntry, DataType, ExecutionContext} +import vsys.blockchain.state.StateWriterImpl +import vsys.blockchain.state.diffs.newState +import vsys.blockchain.transaction.TransactionParser +import vsys.blockchain.transaction.ValidationError.{ContractDataTypeMismatch, ContractInvalidTokenIndex, + ContractLocalVariableIndexOutOfRange} +import vsys.blockchain.transaction.contract.RegisterContractTransaction +import vsys.settings.TestFunctionalitySettings + +import scala.util.Left + +class TDBROpcDiffTest extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers { + + val state: StateWriterImpl = newState() + + val tx: RegisterContractTransaction = RegisterContractTransaction.create( + PrivateKeyAccount(Array.fill(TransactionParser.KeyLength)(0)), + ContractPermitted.contract, Seq(DataEntry(Longs.toByteArray(-1), DataType.Amount)), + "vsys", 10000L, 100, 1L).right.get + + val executionContext: ExecutionContext = ExecutionContext.fromRegConTx( + state, TestFunctionalitySettings.Enabled, Option(0L), + 1L, 1, tx).right.get + + property("test TDBR opcs") { + + TDBROpcDiff.max(executionContext)(DataEntry( + Longs.toByteArray(0), DataType.Amount), Seq.empty, 0) should be ( + Left(ContractDataTypeMismatch)) + TDBROpcDiff.max(executionContext)(DataEntry( + Ints.toByteArray(0), DataType.Int32), Seq.empty, 1) should be ( + Left(ContractLocalVariableIndexOutOfRange)) + TDBROpcDiff.max(executionContext)(DataEntry( + Ints.toByteArray(0), DataType.Int32), Seq.empty, 0) should be ( + Left(ContractInvalidTokenIndex)) + + TDBROpcDiff.total(executionContext)(DataEntry( + Longs.toByteArray(0), DataType.Amount), Seq.empty, 0) should be ( + Left(ContractDataTypeMismatch)) + TDBROpcDiff.total(executionContext)(DataEntry( + Ints.toByteArray(0), DataType.Int32), Seq.empty, 1) should be ( + Left(ContractLocalVariableIndexOutOfRange)) + TDBROpcDiff.total(executionContext)(DataEntry( + Ints.toByteArray(0), DataType.Int32), Seq.empty, 0) should be ( + Left(ContractInvalidTokenIndex)) + + TDBROpcDiff.unity(executionContext)(DataEntry( + Longs.toByteArray(0), DataType.Amount), Seq.empty, 0) should be ( + Left(ContractDataTypeMismatch)) + TDBROpcDiff.unity(executionContext)(DataEntry( + Ints.toByteArray(0), DataType.Int32), Seq.empty, 1) should be ( + Left(ContractLocalVariableIndexOutOfRange)) + TDBROpcDiff.unity(executionContext)(DataEntry( + Ints.toByteArray(0), DataType.Int32), Seq.empty, 0) should be ( + Left(ContractInvalidTokenIndex)) + + TDBROpcDiff.desc(executionContext)(DataEntry( + Longs.toByteArray(0), DataType.Amount), Seq.empty, 0) should be ( + Left(ContractDataTypeMismatch)) + TDBROpcDiff.desc(executionContext)(DataEntry( + Ints.toByteArray(0), DataType.Int32), Seq.empty, 1) should be ( + Left(ContractLocalVariableIndexOutOfRange)) + TDBROpcDiff.desc(executionContext)(DataEntry( + Ints.toByteArray(0), DataType.Int32), Seq.empty, 0) should be ( + Left(ContractInvalidTokenIndex)) + } +} diff --git a/src/test/scala/vsys/blockchain/transaction/TransactionGen.scala b/src/test/scala/vsys/blockchain/transaction/TransactionGen.scala index 8de2a8513..0b7ac661f 100644 --- a/src/test/scala/vsys/blockchain/transaction/TransactionGen.scala +++ b/src/test/scala/vsys/blockchain/transaction/TransactionGen.scala @@ -2,11 +2,12 @@ package vsys.blockchain.transaction import org.scalacheck.Gen.{alphaLowerChar, frequency, numChar} import org.scalacheck.{Arbitrary, Gen} +import scorex.crypto.encode.Base58 import vsys.account.PublicKeyAccount._ import vsys.account._ import vsys.blockchain.consensus.SPoSCalc._ import vsys.blockchain.contract._ -import vsys.blockchain.database.{DataType => DatabaseDataType, Entry} +import vsys.blockchain.database.{Entry, DataType => DatabaseDataType} import vsys.blockchain.state._ import vsys.blockchain.transaction.contract._ import vsys.blockchain.transaction.database.DbPutTransaction @@ -39,6 +40,10 @@ trait TransactionGen { val ntpTimestampGen: Gen[Long] = Gen.choose(1, 1000).map(NTP.correctedTime() - _) val accountGen: Gen[PrivateKeyAccount] = bytes32gen.map(seed => PrivateKeyAccount(seed)) + + val fakeAddressGen: Gen[String] = bytes32gen.map(seed => 'A' +: Base58.encode(seed.tail)) + + val fakeAccountGen: Gen[String] = bytes32gen.map(seed => 'C' +: Base58.encode(seed.tail)) val contractAccountGen: Gen[ContractAccount] = Gen.const(ContractAccount.fromId(ByteStr(bytes32gen.sample.get))) @@ -80,7 +85,7 @@ trait TransactionGen { val feeScaleGen: Gen[Short] = Gen.const(100) val slotidGen: Gen[Int] = Gen.choose(0, TestFunctionalitySettings.Enabled.numOfSlots - 1) val attachmentGen: Gen[Array[Byte]] = genBoundedBytes(0, PaymentTransaction.MaxAttachmentSize) - val txStatusGen: Gen[TransactionStatus.Value] = Gen.const(TransactionStatus.Success) + val bigIntGen: Gen[Array[Byte]] = genBoundedBytes(1, DataType.MaxBigIntLength) val entryGen: Gen[Entry] = for { data: String <- entryDataStringGen } yield Entry.buildEntry(data, DatabaseDataType.ByteArray).explicitGet() diff --git a/src/test/scala/vsys/settings/BlockchainSettingsSpecification.scala b/src/test/scala/vsys/settings/BlockchainSettingsSpecification.scala index 44e2ad98a..3fff03863 100644 --- a/src/test/scala/vsys/settings/BlockchainSettingsSpecification.scala +++ b/src/test/scala/vsys/settings/BlockchainSettingsSpecification.scala @@ -33,6 +33,7 @@ class BlockchainSettingsSpecification extends FlatSpec with Matchers { | allow-leased-balance-transfer-until = 17 | allow-contract-transaction-after-height = 0 | allow-deposit-withdraw-contract-after-height = 0 + | allow-exchange-contract-after-height = 0 | num-of-slots = 5 | minting-speed = 5 | } @@ -51,6 +52,7 @@ class BlockchainSettingsSpecification extends FlatSpec with Matchers { | } | state { | tx-type-account-tx-ids = on + | tx-contract-tx-ids = on | } | } |}""".stripMargin)) @@ -63,6 +65,7 @@ class BlockchainSettingsSpecification extends FlatSpec with Matchers { settings.functionalitySettings.mintingSpeed should be (5) settings.functionalitySettings.allowContractTransactionAfterHeight should be (0) settings.functionalitySettings.allowDepositWithdrawContractAfterHeight should be (0) + settings.functionalitySettings.allowExchangeContractAfterHeight should be (0) settings.genesisSettings.blockTimestamp should be(1460678400000L) settings.genesisSettings.timestamp should be(1460678400000L) settings.genesisSettings.signature should be(ByteStr.decodeBase58("BASE58BLKSGNATURE").toOption) @@ -84,6 +87,7 @@ class BlockchainSettingsSpecification extends FlatSpec with Matchers { | type = TESTNET | state { | tx-type-account-tx-ids = off + | tx-contract-tx-ids = off | } | } |}""".stripMargin)) @@ -95,6 +99,7 @@ class BlockchainSettingsSpecification extends FlatSpec with Matchers { settings.functionalitySettings.mintingSpeed should be (1) settings.functionalitySettings.allowContractTransactionAfterHeight should be (4236000) // same as the setting settings.functionalitySettings.allowDepositWithdrawContractAfterHeight should be (12550000) + settings.functionalitySettings.allowExchangeContractAfterHeight should be (18030000) settings.genesisSettings.blockTimestamp should be(1535356447650226656L) settings.genesisSettings.timestamp should be(1535356447650226656L) settings.genesisSettings.averageBlockDelay should be(60.seconds) @@ -137,6 +142,7 @@ class BlockchainSettingsSpecification extends FlatSpec with Matchers { settings.functionalitySettings.mintingSpeed should be (1) settings.functionalitySettings.allowContractTransactionAfterHeight should be (6100000) // same as the setting settings.functionalitySettings.allowDepositWithdrawContractAfterHeight should be (13140520) + settings.functionalitySettings.allowExchangeContractAfterHeight should be (25416184) settings.genesisSettings.blockTimestamp should be(1543286357457333127L) settings.genesisSettings.timestamp should be(1543286357457333127L) settings.genesisSettings.signature should be(ByteStr.decodeBase58("3yYNd7quEWaWytrAug4yGwQvpL3PVJegf9d9NTv9PVE3ouBYJs5PTQqxCjd294uK1zPLj6G5Tk447LqFMWdSFvaQ").toOption) diff --git a/src/test/scala/vsys/settings/TestFunctionalitySettings.scala b/src/test/scala/vsys/settings/TestFunctionalitySettings.scala index 3b012ff45..6e41b9328 100644 --- a/src/test/scala/vsys/settings/TestFunctionalitySettings.scala +++ b/src/test/scala/vsys/settings/TestFunctionalitySettings.scala @@ -5,13 +5,15 @@ object TestFunctionalitySettings { numOfSlots = 60, // easy to test the release case later mintingSpeed = 1, allowContractTransactionAfterHeight = 0, - allowDepositWithdrawContractAfterHeight = 0 + allowDepositWithdrawContractAfterHeight = 0, + allowExchangeContractAfterHeight = 0 ) val ContractDisabled = FunctionalitySettings( numOfSlots = 60, // easy to test the release case later mintingSpeed = 1, allowContractTransactionAfterHeight = 2, - allowDepositWithdrawContractAfterHeight = 3 + allowDepositWithdrawContractAfterHeight = 3, + allowExchangeContractAfterHeight = 4 ) } \ No newline at end of file diff --git a/src/test/scala/vsys/settings/TestStateSettings.scala b/src/test/scala/vsys/settings/TestStateSettings.scala index 601009f44..6c048ddb4 100644 --- a/src/test/scala/vsys/settings/TestStateSettings.scala +++ b/src/test/scala/vsys/settings/TestStateSettings.scala @@ -2,9 +2,11 @@ package vsys.settings object TestStateSettings { val AllOn = StateSettings( - txTypeAccountTxIds = true + txTypeAccountTxIds = true, + txContractTxIds = true ) val AllOff = StateSettings( - txTypeAccountTxIds = false + txTypeAccountTxIds = false, + txContractTxIds = false ) } diff --git a/vsys-testnet.conf b/vsys-testnet.conf index 1a7e00f36..fd85739fd 100644 --- a/vsys-testnet.conf +++ b/vsys-testnet.conf @@ -1,12 +1,13 @@ vsys { - #directory = "./vsys-data" - data-directory = '/opt/vsys/data' + #directory = 'your local path' + #data-directory = 'your local path' logging-level = INFO network { - known-peers = ["54.193.47.112:9923","13.56.200.72:9923","18.218.106.1:9923","3.17.78.253:9923","34.222.191.174:9923"] + known-peers = ["gemmer.vcoin.systems:9923","vnode.vcoin.systems:9923","gemmer.vos.systems:9923","vnode.vos.systems:9923",] black-list-residence-time = 30s peers-broadcast-interval = 5s connection-timeout = 30s + port = 9923 } wallet { password = ""