diff --git a/rust-mandelbrot-nft/.dockerignore b/rust-mandelbrot-nft/.dockerignore new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/rust-mandelbrot-nft/.dockerignore @@ -0,0 +1 @@ +target diff --git a/rust-mandelbrot-nft/.gitignore b/rust-mandelbrot-nft/.gitignore new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/rust-mandelbrot-nft/.gitignore @@ -0,0 +1 @@ +target diff --git a/rust-mandelbrot-nft/Cargo.lock b/rust-mandelbrot-nft/Cargo.lock new file mode 100644 index 00000000..9f981e1b --- /dev/null +++ b/rust-mandelbrot-nft/Cargo.lock @@ -0,0 +1,417 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "json" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "wasi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "png" +version = "0.17.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0e7f4c94ec26ff209cee506314212639d6c91b80afb82984819fafce9df01c" +dependencies = [ + "bitflags", + "crc32fast", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "proc-macro2" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rust-mandelbrot-nft" +version = "0.1.0" +dependencies = [ + "hyper", + "json", + "png", + "tokio", +] + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tokio" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce653fb475565de9f6fb0614b28bca8df2c430c0cf84bcd9c843f15de5414cc" +dependencies = [ + "libc", + "mio", + "num_cpus", + "once_cell", + "pin-project-lite", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/rust-mandelbrot-nft/Cargo.toml b/rust-mandelbrot-nft/Cargo.toml new file mode 100644 index 00000000..df8af02c --- /dev/null +++ b/rust-mandelbrot-nft/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rust-mandelbrot-nft" +version = "0.1.0" +edition = "2021" +authors = ["Andy Thomason"] + +[dependencies] +json = "0.12" +hyper = { version = "0.14", features = ["http1", "runtime", "client"] } +tokio = { version = "1.18", features = ["macros", "rt-multi-thread"] } +png = "0.17" diff --git a/rust-mandelbrot-nft/Dockerfile b/rust-mandelbrot-nft/Dockerfile new file mode 100644 index 00000000..bc2e5aaa --- /dev/null +++ b/rust-mandelbrot-nft/Dockerfile @@ -0,0 +1,7 @@ +# syntax=docker.io/docker/dockerfile:1.4 +FROM cartesi/toolchain:0.11.0 as dapp-build + +WORKDIR /opt/cartesi/dapp + +COPY . . +RUN cargo build -Z build-std=std,core,alloc,panic_abort,proc_macro --target riscv64ima-cartesi-linux-gnu.json --release diff --git a/rust-mandelbrot-nft/README.md b/rust-mandelbrot-nft/README.md new file mode 100644 index 00000000..1ded4b41 --- /dev/null +++ b/rust-mandelbrot-nft/README.md @@ -0,0 +1,61 @@ +# Echo Rust DApp + +This example implements the same behavior as the [Echo DApp written in Python](../echo-python), but here the back-end is written in Rust. As the other example, the DApp simply copies (or "echoes") each input received as a corresponding output notice. + +## Interacting with the application + +We can use the [frontend-console](../frontend-console) application to interact with the DApp. +Ensure that the [application has already been built](../frontend-console/README.md#building) before using it. + +First, go to a separate terminal window and switch to the `frontend-console` directory: + +```shell +cd frontend-console +``` + +Then, send an input as follows: + +```shell +yarn start input send --payload "Hello there" +``` + +In order to verify the notices generated by your inputs, run the command: + +```shell +yarn start notice list +``` + +The response should be something like this: + +```json +[{ "epoch": "0", "input": "1", "notice": "0", "payload": "Hello there" }] +``` + +## Running the back-end in host mode + +When developing an application, it is often important to easily test and debug it. For that matter, it is possible to run the Cartesi Rollups environment in [host mode](../README.md#host-mode), so that the DApp's back-end can be executed directly on the host machine, allowing it to be debugged using regular development tools such as an IDE. + +This DApp is written in Rust, so you need to have rust installed in order to recompile it. + +In order to start the echo-rust back-end, run the following commands in a dedicated terminal: + +```shell +cd echo-rust/ +ROLLUP_HTTP_SERVER_URL="http://127.0.0.1:5004" cargo run +``` + +This will run the echo-rust back-end and send the corresponding notices to port `5004`. + +You can also use a tool like [entr](https://eradman.com/entrproject/) to restart it automatically when the code changes. For example: + +```shell +ls src/*.rs | ROLLUP_HTTP_SERVER_URL="http://127.0.0.1:5004" entr -r cargo run +``` + +After the back-end successfully starts, it should print an output like the following: + +```log +Starting echo-dapp: Sending finish +``` + +After that, you can interact with the application normally [as explained above](#interacting-with-the-application). diff --git a/rust-mandelbrot-nft/dapp.json b/rust-mandelbrot-nft/dapp.json new file mode 100644 index 00000000..f40a1caa --- /dev/null +++ b/rust-mandelbrot-nft/dapp.json @@ -0,0 +1,8 @@ +{ + "fs": { + "files": [ + "target/riscv64ima-cartesi-linux-gnu/release/echo-backend", + "entrypoint.sh" + ] + } +} diff --git a/rust-mandelbrot-nft/docker-bake.hcl b/rust-mandelbrot-nft/docker-bake.hcl new file mode 120000 index 00000000..bfe3894d --- /dev/null +++ b/rust-mandelbrot-nft/docker-bake.hcl @@ -0,0 +1 @@ +../base.hcl \ No newline at end of file diff --git a/rust-mandelbrot-nft/docker-bake.override.hcl b/rust-mandelbrot-nft/docker-bake.override.hcl new file mode 100644 index 00000000..2094a337 --- /dev/null +++ b/rust-mandelbrot-nft/docker-bake.override.hcl @@ -0,0 +1,23 @@ + +target "dapp" { +} + +variable "TAG" { + default = "devel" +} + +variable "DOCKER_ORGANIZATION" { + default = "cartesi" +} + +target "server" { + tags = ["${DOCKER_ORGANIZATION}/dapp:echo-rust-${TAG}-server"] +} + +target "console" { + tags = ["${DOCKER_ORGANIZATION}/dapp:echo-rust-${TAG}-console"] +} + +target "machine" { + tags = ["${DOCKER_ORGANIZATION}/dapp:echo-rust-${TAG}-machine"] +} diff --git a/rust-mandelbrot-nft/docker-compose.override.yml b/rust-mandelbrot-nft/docker-compose.override.yml new file mode 100644 index 00000000..a9966937 --- /dev/null +++ b/rust-mandelbrot-nft/docker-compose.override.yml @@ -0,0 +1,5 @@ +version: "3" + +services: + server_manager: + image: ${DAPP_IMAGE:-cartesi/dapp:echo-rust-0.9.1-server} diff --git a/rust-mandelbrot-nft/entrypoint.sh b/rust-mandelbrot-nft/entrypoint.sh new file mode 100755 index 00000000..4c81e9d3 --- /dev/null +++ b/rust-mandelbrot-nft/entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Copyright 2022 Cartesi Pte. Ltd. +# +# SPDX-License-Identifier: Apache-2.0 +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use +# this file except in compliance with the License. You may obtain a copy of the +# License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +set -e +rollup-init ./target/riscv64ima-cartesi-linux-gnu/release/echo-backend diff --git a/rust-mandelbrot-nft/hello_rust.png b/rust-mandelbrot-nft/hello_rust.png new file mode 100644 index 00000000..05ad60c3 Binary files /dev/null and b/rust-mandelbrot-nft/hello_rust.png differ diff --git a/rust-mandelbrot-nft/riscv64ima-cartesi-linux-gnu.json b/rust-mandelbrot-nft/riscv64ima-cartesi-linux-gnu.json new file mode 100644 index 00000000..05a06334 --- /dev/null +++ b/rust-mandelbrot-nft/riscv64ima-cartesi-linux-gnu.json @@ -0,0 +1,35 @@ +{ + "arch": "riscv64", + "code-model": "medium", + "cpu": "generic-rv64", + "crt-static-respected": true, + "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n64-S128", + "dynamic-linking": true, + "env": "gnu", + "executables": true, + "features": "+m,+a", + "has-rpath": true, + "is-builtin": false, + "llvm-abiname": "lp64", + "llvm-target": "riscv64", + "max-atomic-width": 64, + "os": "linux", + "position-independent-executables": true, + "relro-level": "full", + "target-family": [ + "unix" + ], + "linker-flavor": "gcc", + "linker": "riscv64-cartesi-linux-gnu-gcc", + "pre-link-args": { + "gcc": [] + }, + "post-link-args": { + "gcc": [ + "-Wl,--allow-multiple-definition", + "-Wl,--start-group,-lc,-lm,-lgcc,-lstdc++,-lsupc++,--end-group" + ] + }, + "target-pointer-width": "64", + "panic-strategy": "abort" +} diff --git a/rust-mandelbrot-nft/src/main.rs b/rust-mandelbrot-nft/src/main.rs new file mode 100644 index 00000000..04e11ace --- /dev/null +++ b/rust-mandelbrot-nft/src/main.rs @@ -0,0 +1,264 @@ +// Copyright 2022 Cartesi Pte. Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use +// this file except in compliance with the License. You may obtain a copy of the +// License at http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + + +// Example submission: +// yarn start input send --payload "(-0.1:0.9:4.0)" + + +use json::{object, JsonValue}; +use std::env; + +fn from_hex(s: &str) -> String { + assert!(s.starts_with("0x")); + + s[2..].as_bytes() + .chunks_exact(2) + .map(|c| { + let d0 = if c[0] > b'9' { c[0] + 9 } else { c[0] } & 0x0f; + let d1 = if c[1] > b'9' { c[1] + 9 } else { c[1] } & 0x0f; + (d0 * 16 + d1) as char + }).collect::() +} + +fn from_hex_vec(s: &str) -> Vec { + assert!(s.starts_with("0x")); + + s[2..].as_bytes() + .chunks_exact(2) + .map(|c| { + let d0 = if c[0] > b'9' { c[0] + 9 } else { c[0] } & 0x0f; + let d1 = if c[1] > b'9' { c[1] + 9 } else { c[1] } & 0x0f; + d0 * 16 + d1 + }).collect::>() +} + +fn to_hex(b: &[u8]) -> String { + let mut res = String::with_capacity(b.len()*2+2); + res.push_str("0x"); + for byte in b { + res.push(b"0123456789abcdef"[(byte>>4) as usize] as char); + res.push(b"0123456789abcdef"[(byte&0x0f) as usize] as char); + } + res +} + +#[test] +fn hex() { + assert_eq!(from_hex("0x48656c6c6f"), "Hello"); + + assert_eq!(to_hex(b"Hello"), "0x48656c6c6f"); +} + +fn escape_time(cx: f64, cy: f64) -> u8 { + let mut zx = cx; + let mut zy = cy; + let mut res = 0; + loop { + (zx, zy) = (zx*zx - zy*zy + cx, 2.0*zy*zx + cy ); + if zx*zx + zy*zy > 4.0 || res == 255 { + return res; + } + res += 1; + } +} + +fn gen_nft(params: &str) -> Vec { + const SIZE : usize = 1024; + println!("params={}", params); + + let (px, py, zoom, id) = if params.starts_with("0x28") { + // Command line: use text. + // Question: how do we send hex from the command line tool? + let params = from_hex(params); + println!("gen_bft: params={}", params); + let params = params.strip_prefix("(").unwrap(); + let params = params.strip_suffix(")").unwrap(); + let mut it = params.split(":").map(|x| x.parse().unwrap()); + + let px : f64 = it.next().unwrap(); + let py : f64 = it.next().unwrap(); + let zoom : f64 = it.next().unwrap(); + + (px, py, zoom, 0) + } else { + // In-chain: use binary. 4x256 bit fixed point integers. + let bytes = from_hex_vec(params); + let mut it = bytes + .chunks(32) + .map(|x| u64::from_be_bytes(x[24..32].try_into().unwrap())); + let px = it.next().unwrap() as f64 / 1000000.0; + let py = it.next().unwrap() as f64 / 1000000.0; + let zoom = it.next().unwrap() as f64 / 1000000.0; + let id = it.next().unwrap(); + (px, py, zoom, id) + }; + + println!("x,y,zoom,id={},{},{},{}", px, py, zoom, id); + + let zoom : f64 = 1.0 / zoom / (SIZE as f64) / 2.0; + + let mut w = Vec::new(); + { + let mut encoder = png::Encoder::new(& mut w, SIZE as u32, SIZE as u32); // Width is 2 pixels and height is 1. + encoder.set_color(png::ColorType::Rgba); + encoder.set_depth(png::BitDepth::Eight); + + let mut data = [0_u8; SIZE*SIZE*4]; + + for y in 0..SIZE { + let cy = (y as i32 - (SIZE/2) as i32) as f64 * zoom + py; + for x in 0..SIZE { + let cx = (x as i32 - (SIZE/2) as i32) as f64 * zoom + px; + let t = escape_time(cx, cy); + data[(y*SIZE+x)*4+0] = t << 4; + data[(y*SIZE+x)*4+1] = t << 2; + data[(y*SIZE+x)*4+2] = t; + data[(y*SIZE+x)*4+3] = 0xff; + } + } + + let mut writer = encoder.write_header().unwrap(); + writer.write_image_data(&data).unwrap(); + } + + // Write the image + // TODO: write to IPFS. + let filename = format!("/tmp/mandelbrot-{}.png", params); + std::fs::write(&filename, &w).unwrap(); + println!("written {} size={}", filename, w.len()); + filename.into_bytes() +} + +async fn print_response( + response: hyper::Response, +) -> Result<(), Box> +where + ::Error: 'static, + ::Error: std::error::Error, +{ + let response_status = response.status().as_u16(); + let response_body = hyper::body::to_bytes(response).await?; + println!( + "Received notice status {} body {}", + response_status, + std::str::from_utf8(&response_body)? + ); + Ok(()) +} + +fn process_initial(metadata: &JsonValue) -> Option { + let epoch_index = metadata["epoch_index"].as_u64()?; + let input_index = metadata["input_index"].as_u64()?; + + if epoch_index == 0 && input_index == 0 { + let msg_sender = metadata["msg_sender"].as_str()?; + println!("Captured rollup address: {}", msg_sender); + return Some(msg_sender.to_string()); + } + + return None; +} + +pub async fn handle_advance( + client: &hyper::Client, + server_addr: &str, + request: JsonValue, +) -> Result<&'static str, Box> { + println!("Received advance request data {}", &request); + let payload = request["data"]["payload"] + .as_str() + .ok_or("Missing payload")?; + + println!("Adding notice"); + + let res = gen_nft(payload); + + let notice = object! {"payload" => to_hex(&res)}; + + let req = hyper::Request::builder() + .method(hyper::Method::POST) + .header(hyper::header::CONTENT_TYPE, "application/json") + .uri(format!("{}/notice", server_addr)) + .body(hyper::Body::from(notice.dump()))?; + let response = client.request(req).await?; + + print_response(response).await?; + + Ok("accept") +} + +pub async fn handle_inspect( + client: &hyper::Client, + server_addr: &str, + request: JsonValue, +) -> Result<&'static str, Box> { + println!("Received inspect request data {}", &request); + let payload = request["data"]["payload"] + .as_str() + .ok_or("Missing payload")?; + println!("Adding report"); + let report = object! {"payload" => format!("{}", payload)}; + let req = hyper::Request::builder() + .method(hyper::Method::POST) + .header(hyper::header::CONTENT_TYPE, "application/json") + .uri(format!("{}/report", server_addr)) + .body(hyper::Body::from(report.dump()))?; + let response = client.request(req).await?; + print_response(response).await?; + Ok("accept") +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = hyper::Client::new(); + let server_addr = env::var("ROLLUP_HTTP_SERVER_URL")?; + + let mut status = "accept"; + let mut _rollup_address = String::new(); + loop { + println!("Sending finish"); + let response = object! {"status" => status.clone()}; + let request = hyper::Request::builder() + .method(hyper::Method::POST) + .header(hyper::header::CONTENT_TYPE, "application/json") + .uri(format!("{}/finish", &server_addr)) + .body(hyper::Body::from(response.dump()))?; + let response = client.request(request).await?; + println!("Received finish status {}", response.status()); + + if response.status() == hyper::StatusCode::ACCEPTED { + println!("No pending rollup request, trying again"); + } else { + let body = hyper::body::to_bytes(response).await?; + let utf = std::str::from_utf8(&body)?; + let req = json::parse(utf)?; + + if let Some(address) = process_initial(&req["data"]["metadata"]) { + _rollup_address = address; + continue; + } + + let request_type = req["request_type"] + .as_str() + .ok_or("request_type is not a string")?; + status = match request_type { + "advance_state" => handle_advance(&client, &server_addr[..], req).await?, + "inspect_state" => handle_inspect(&client, &server_addr[..], req).await?, + &_ => { + eprintln!("Unknown request type"); + "reject" + } + }; + } + } +}