diff --git a/Cargo.lock b/Cargo.lock index 69de0a891..6a4ea267f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,7 +150,7 @@ dependencies = [ "derivative", "digest 0.10.7", "rayon", - "sha2", + "sha2 0.10.8", "tracing", ] @@ -460,6 +460,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -472,6 +478,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + [[package]] name = "bitvec" version = "1.0.1" @@ -600,7 +612,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.21", "serde", "serde_json", "thiserror", @@ -646,7 +658,7 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "textwrap", "unicode-width", ] @@ -739,7 +751,7 @@ checksum = "23738e11972c7643e4ec947840fc463b6a571afcd3e735bdfce7d03c7a784aca" dependencies = [ "async-trait", "lazy_static", - "nom", + "nom 7.1.3", "pathdiff", "serde", ] @@ -807,6 +819,15 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +[[package]] +name = "crossbeam-channel" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -832,6 +853,31 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.4.2", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook 0.3.17", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -842,6 +888,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "lab", + "phf 0.11.2", +] + [[package]] name = "csv" version = "1.3.0" @@ -863,6 +919,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "deltae" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" + [[package]] name = "derivative" version = "2.2.0" @@ -903,6 +965,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -952,7 +1034,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e6185b6dc9dddc4db0dedd2e213047e93bcbf7a0fb092abc4c4e4f3195efdb4" dependencies = [ - "base64", + "base64 0.21.7", "hyper", "pin-project", "rand 0.8.5", @@ -974,6 +1056,29 @@ dependencies = [ "subtle", ] +[[package]] +name = "filedescriptor" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" +dependencies = [ + "libc", + "thiserror", + "winapi", +] + +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "fnv" version = "1.0.7" @@ -1381,6 +1486,12 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "lab" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" + [[package]] name = "lazy_static" version = "1.4.0" @@ -1396,6 +1507,17 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall", +] + [[package]] name = "lock_api" version = "0.4.11" @@ -1447,6 +1569,21 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "memmem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "merlin" version = "3.0.0" @@ -1481,6 +1618,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", + "log", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -1517,7 +1655,7 @@ dependencies = [ "postcard", "serde", "serde_json", - "sha2", + "sha2 0.10.8", "snmalloc-rs", "tokio", "tracing", @@ -1569,6 +1707,7 @@ dependencies = [ "ark-std", "clap 4.4.18", "nexus-nova", + "nexus-tui", "nexus-vm", "snmalloc-rs", "tracing", @@ -1610,11 +1749,10 @@ dependencies = [ "cargo_metadata", "clap 4.4.18", "nexus-config", - "nexus-network", "nexus-prover", "nexus-riscv", "nexus-tools-dev", - "serde_json", + "nexus-tui", "tracing", ] @@ -1632,6 +1770,14 @@ dependencies = [ "tracing-subscriber 0.3.18", ] +[[package]] +name = "nexus-tui" +version = "0.1.0" +dependencies = [ + "anyhow", + "superconsole", +] + [[package]] name = "nexus-vm" version = "0.1.0" @@ -1645,12 +1791,34 @@ dependencies = [ "ark-std", "elf", "nexus-riscv", - "num-derive", + "num-derive 0.4.2", "num-traits", "serde", "thiserror", ] +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "5.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" +dependencies = [ + "memchr", + "version_check", +] + [[package]] name = "nom" version = "7.1.3" @@ -1682,6 +1850,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num-derive" version = "0.4.2" @@ -1755,6 +1934,24 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "ordered-float" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-float" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -1817,6 +2014,121 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e1288dbd7786462961e69bfd4df7848c1e37e8b74303dbdab82c3a9cdd2809" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1381c29a877c6d34b8c176e734f35d7f7f5b3adaefe940cb4d1bb7af94678e2e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "pest_meta" +version = "2.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0934d6907f148c22a3acbda520c7eed243ad7487a30f51f6ce52b58b7077a8a" +dependencies = [ + "once_cell", + "pest", + "sha2 0.10.8", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.4" @@ -2022,7 +2334,18 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom 0.2.12", + "libredox", + "thiserror", ] [[package]] @@ -2081,7 +2404,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.21", ] [[package]] @@ -2105,6 +2428,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.21" @@ -2114,6 +2446,15 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.196" @@ -2184,6 +2525,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "sha2" version = "0.10.8" @@ -2239,6 +2593,37 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook 0.3.17", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2254,6 +2639,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -2336,6 +2727,20 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "superconsole" +version = "0.2.0" +source = "git+https://github.com/facebookincubator/superconsole.git?rev=8254a5c#8254a5cd67b1c1aa127436d96d9618695350e354" +dependencies = [ + "anyhow", + "crossbeam-channel", + "crossterm", + "itertools 0.10.5", + "termwiz", + "thiserror", + "unicode-segmentation", +] + [[package]] name = "syn" version = "1.0.109" @@ -2364,6 +2769,70 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "terminfo" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da31aef70da0f6352dbcb462683eb4dd2bfad01cf3fc96cf204547b9a839a585" +dependencies = [ + "dirs", + "fnv", + "nom 5.1.3", + "phf 0.11.2", + "phf_codegen", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "termwiz" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e302bfaa2555ca7fb55eee19051ad43e510153b19cb880d6da5acb65a72ab9" +dependencies = [ + "anyhow", + "base64 0.13.1", + "bitflags 1.3.2", + "cfg-if", + "filedescriptor", + "finl_unicode", + "fixedbitset", + "hex", + "lazy_static", + "libc", + "log", + "memmem", + "nix", + "num-derive 0.3.3", + "num-traits", + "ordered-float 3.9.2", + "pest", + "pest_derive", + "phf 0.10.1", + "regex", + "semver 0.11.0", + "sha2 0.9.9", + "signal-hook 0.1.17", + "siphasher", + "terminfo", + "termios", + "thiserror", + "ucd-trie", + "unicode-segmentation", + "vtparse", + "wezterm-bidi", + "wezterm-color-types", + "wezterm-dynamic 0.1.0", + "winapi", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -2595,6 +3064,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -2616,6 +3091,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" version = "0.1.11" @@ -2658,6 +3139,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vtparse" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0" +dependencies = [ + "utf8parse", +] + [[package]] name = "walkdir" version = "2.4.0" @@ -2753,6 +3243,65 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wezterm-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0a6e355560527dd2d1cf7890652f4f09bb3433b6aadade4c9b5ed76de5f3ec" +dependencies = [ + "log", + "wezterm-dynamic 0.2.0", +] + +[[package]] +name = "wezterm-color-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6e7a483dd2785ba72705c51e8b1be18300302db2a78368dac9bc8773857777" +dependencies = [ + "csscolorparser", + "deltae", + "lazy_static", + "wezterm-dynamic 0.1.0", +] + +[[package]] +name = "wezterm-dynamic" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75e78c0cc60a76de5d93f9dad05651105351e151b6446ab305514945d7588aa" +dependencies = [ + "log", + "ordered-float 3.9.2", + "strsim", + "thiserror", + "wezterm-dynamic-derive", +] + +[[package]] +name = "wezterm-dynamic" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb128bacfa86734e07681fb6068e34c144698e84ee022d6e009145d1abb77b5" +dependencies = [ + "log", + "ordered-float 4.2.0", + "strsim", + "thiserror", + "wezterm-dynamic-derive", +] + +[[package]] +name = "wezterm-dynamic-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9f5ef318442d07b3d071f9f43ea40b80992f87faee14bb4d017b6991c307f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index a1f912145..adc040033 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "vm", "tools", "tools/tools-dev", + "tools/tui", "config", "config/serde_wrapper", "prover", diff --git a/config/bases/rust.toml b/config/bases/rust.toml index 9c3cc29cb..e7dc75000 100644 --- a/config/bases/rust.toml +++ b/config/bases/rust.toml @@ -1,9 +1,7 @@ RUST_LOG="""\ nexus-network=debug,\ nexus-prover=info,\ -nexus-tools-dev=debug,\ -nexus-nova::sequential=debug,\ -nexus-nova::pcd=debug\ +nexus-tools-dev=debug\ """ RUST_BACKTRACE="full" diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 62adf5fa4..824a2b423 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -19,6 +19,7 @@ zstd = { version = "0.12", default-features = false } nexus-vm = { path = "../vm" } nexus-nova = { path = "../nova", features = ["spartan"] } +nexus-tui = { path = "../tools/tui" } tracing = "0.1" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } diff --git a/prover/src/lib.rs b/prover/src/lib.rs index b71e25ba3..c7d5f941e 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -47,41 +47,34 @@ pub fn run(opts: &VMOpts, pow: bool) -> Result { } pub fn prove_seq(pp: &SeqPP, trace: Trace) -> Result { - let k = trace.k; + // let k = trace.k; let tr = Tr(trace); let icount = tr.instructions(); let z_0 = tr.input(0)?; let mut proof = IVCProof::new(pp, &z_0); - println!("\nProving Execution Trace:"); - println!("step. {:7} {:8} {:32} time", "pc", "mem[pc]", "inst"); - - let start = Instant::now(); - let num_steps = tr.steps(); - for i in 0..num_steps { - print!("{:4}. {:51}", i, format!("{} instructions...", k)); - io::stdout().flush().unwrap(); - let t = Instant::now(); - proof = IVCProof::prove_step(proof, &tr)?; + let mut term = nexus_tui::TerminalHandle::new(); + let mut term_ctx = term + .context("Computing") + .on_step(|step| format!("step {step}")) + .num_steps(num_steps) + .with_loading_bar("Proving") + .completion_header("Proved") + .completion_stats(move |elapsed| { + format!( + "{num_steps} step(s) in {elapsed}; {:.2} instructions / second", + icount as f32 / elapsed.as_secs_f32() + ) + }); + + for _ in 0..num_steps { + let _guard = term_ctx.display_step(); - println!( - "{:?} {:0.2}%", - t.elapsed(), - ((i + 1) as f32) * 100.0 / (num_steps as f32) - ); + proof = IVCProof::prove_step(proof, &tr)?; } - println!( - "\nProof Complete: {:.2} instructions / second", - icount as f64 / start.elapsed().as_secs_f64() - ); - print!("\nVerifying Proof... "); - io::stdout().flush().unwrap(); - let t = Instant::now(); - proof.verify(num_steps).expect("verify"); // TODO add verify errors? - println!("{:?}", t.elapsed()); Ok(proof) } @@ -89,19 +82,44 @@ pub fn prove_par(pp: ParPP, trace: Trace) -> Result { let k = trace.k; let tr = Tr(trace); - let steps = tr.steps(); - println!("\nproving {steps} steps..."); - - let start = Instant::now(); - - let mut vs = (0..steps) + let num_steps = tr.steps(); + assert!((num_steps + 1).is_power_of_two()); + + let on_step = move |iter: usize| { + let b = (num_steps + 1).ilog2(); + let a = b - 1 - (num_steps - iter).ilog2(); + + let step = 2usize.pow(a + 1) * iter - (2usize.pow(a) - 1) * (2usize.pow(b + 1) - 1); + let step_type = if iter <= num_steps / 2 { + "leaf" + } else if iter == num_steps - 1 { + "root" + } else { + "node" + }; + format!("{step_type} {step}") + }; + + let mut term = nexus_tui::TerminalHandle::new(); + let mut term_ctx = term + .context("Computing") + .on_step(on_step) + .num_steps(num_steps) + .with_loading_bar("Proving") + .completion_header("Proved") + .completion_stats(move |elapsed| { + format!( + "tree root in {elapsed}; {:.2} instructions / second", + (k * num_steps) as f32 / elapsed.as_secs_f32() + ) + }); + + let mut vs = (0..num_steps) .step_by(2) .map(|i| { - print!("leaf step {i}... "); - io::stdout().flush().unwrap(); - let t = Instant::now(); + let _guard = term_ctx.display_step(); + let v = PCDNode::prove_step(&pp, &tr, i, &tr.input(i)?)?; - println!("{:?}", t.elapsed()); Ok(v) }) .collect::, ProofError>>()?; @@ -110,29 +128,16 @@ pub fn prove_par(pp: ParPP, trace: Trace) -> Result { if vs.len() == 1 { break; } - println!("proving {} vertex steps", vs.len() / 2); vs = vs .chunks(2) .map(|ab| { - print!("vertex step ... "); - io::stdout().flush().unwrap(); - let t = Instant::now(); + let _guard = term_ctx.display_step(); + let c = PCDNode::prove_from(&pp, &tr, &ab[0], &ab[1])?; - println!("{:?}", t.elapsed()); Ok(c) }) .collect::, ProofError>>()?; } - println!( - "\nProof Complete: {:.2} instructions / second", - (k * tr.steps()) as f64 / start.elapsed().as_secs_f64() - ); - - print!("\nVerifying root... "); - io::stdout().flush().unwrap(); - let t = Instant::now(); - vs[0].verify(&pp)?; - println!("{:?}", t.elapsed()); Ok(vs.into_iter().next().unwrap()) } diff --git a/prover/src/pp.rs b/prover/src/pp.rs index 61828ae1f..7af23f945 100644 --- a/prover/src/pp.rs +++ b/prover/src/pp.rs @@ -69,6 +69,11 @@ pub fn gen_to_file(k: usize, par: bool, pp_file: &str) -> Result<(), ProofError> path = ?pp_file, "Generating public parameters", ); + let mut term = nexus_tui::TerminalHandle::new(); + let mut term_ctx = term + .context("Setting up") + .on_step(|_step| "public parameters".into()); + let _guard = term_ctx.display_step(); if par { let pp: ParPP = gen_vm_pp(k)?; @@ -85,12 +90,18 @@ pub fn gen_or_load(gen: bool, k: usize, pp_file: &str) -> Result, Pro where SP: SetupParams + Sync, { - let t = std::time::Instant::now(); + let mut term = nexus_tui::TerminalHandle::new(); + let pp: PP = if gen { tracing::info!( target: LOG_TARGET, "Generating public parameters", ); + let mut term_ctx = term + .context("Setting up") + .on_step(|_step| "public parameters".into()); + let _guard = term_ctx.display_step(); + gen_vm_pp(k)? } else { tracing::info!( @@ -98,13 +109,15 @@ where path = ?pp_file, "Loading public parameters", ); + let mut term_ctx = term + .context("Loading") + .on_step(|_step| "public parameters".into()); + let _guard = term_ctx.display_step(); + load_pp(pp_file)? }; - tracing::info!( - target: LOG_TARGET, - "Done in {:?}", - t.elapsed(), - ); + drop(term); + show_pp(&pp); Ok(pp) } diff --git a/tools/Cargo.toml b/tools/Cargo.toml index b6b1363ba..135d829c3 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -24,11 +24,11 @@ tracing = { version = "0.1", default-features = false, features = ["std"] } cargo_metadata = "0.18.1" clap.workspace = true -serde_json.workspace = true + nexus-tools-dev = { path = "./tools-dev", default-features = false } nexus-config = { path = "../config" } nexus-riscv = { path = "../riscv" } nexus-prover = { path = "../prover" } -nexus-network = { path = "../network" } +nexus-tui = { path = "./tui" } ark-serialize.workspace = true diff --git a/tools/src/command/prove.rs b/tools/src/command/prove.rs index 69cfb54b2..efe27c0d5 100644 --- a/tools/src/command/prove.rs +++ b/tools/src/command/prove.rs @@ -7,7 +7,6 @@ use anyhow::Context; use ark_serialize::CanonicalSerialize; use nexus_config::vm as vm_config; -use nexus_network::client::Client; use nexus_tools_dev::{ command::common::{ prove::{CommonProveArgs, LocalProveArgs, ProveArgs}, @@ -56,18 +55,22 @@ pub fn handle_command(args: ProveArgs) -> anyhow::Result<()> { } } -fn request_prove(path: &Path, url: &str) -> anyhow::Result<()> { +fn request_prove(_path: &Path, _url: &str) -> anyhow::Result<()> { // TODO: network errors cannot be converted to anyhow. - let client = Client::new(url).map_err(|err| anyhow::anyhow!("url is invalid: {err}"))?; - let proof = client - .submit_proof("account".to_string(), path) - .map_err(|err| anyhow::anyhow!("failed to send request: {err}"))?; - - tracing::info!( + // let client = Client::new(url).map_err(|err| anyhow::anyhow!("url is invalid: {err}"))?; + // let proof = client + // .submit_proof("account".to_string(), path) + // .map_err(|err| anyhow::anyhow!("failed to send request: {err}"))?; + + // tracing::info!( + // target: LOG_TARGET, + // hash = %proof.hash, + // "Prove request submitted", + // ); + tracing::warn!( target: LOG_TARGET, - hash = %proof.hash, - "Prove request submitted", + "Networking commands are disabled", ); Ok(()) @@ -114,20 +117,20 @@ fn local_prove( let state = nexus_prover::pp::gen_or_load(false, DEFAULT_K, path_str)?; let root = nexus_prover::prove_par(state, trace)?; - let mut buf = Vec::new(); - root.serialize_compressed(&mut buf)?; - let current_dir = std::env::current_dir()?; - let path = current_dir.join("nexus-proof.json"); + let path = current_dir.join("nexus-proof"); tracing::info!( target: LOG_TARGET, - path = %path.to_string_lossy(), - "Storing the proof", + path = %path.display(), + "Saving the proof", ); - let proof = nexus_network::api::Proof { proof: Some(buf), ..Default::default() }; - let serialized = serde_json::to_vec(&proof)?; - std::fs::write(path, serialized)?; + let mut term = nexus_tui::TerminalHandle::new(); + let mut context = term.context("Saving").on_step(|_step| "proof".into()); + let _guard = context.display_step(); + + let file = std::fs::File::create(path)?; + root.serialize_compressed(file)?; } else { let state = nexus_prover::pp::gen_or_load(false, DEFAULT_K, path_str)?; nexus_prover::prove_seq(&state, trace)?; diff --git a/tools/src/command/request.rs b/tools/src/command/request.rs index 79ac4cb16..d5b04b1cf 100644 --- a/tools/src/command/request.rs +++ b/tools/src/command/request.rs @@ -1,6 +1,5 @@ use anyhow::Context; -use nexus_network::client::Client; use nexus_tools_dev::command::common::RequestArgs; use crate::LOG_TARGET; @@ -12,32 +11,36 @@ pub fn handle_command(args: RequestArgs) -> anyhow::Result<()> { request_proof(hash, &url) } -fn request_proof(hash: String, url: &str) -> anyhow::Result<()> { - let current_dir = std::env::current_dir()?; - let path = current_dir.join("nexus-proof.json"); - let path_str = path.to_str().context("path is not valid utf8")?; - - // TODO: network errors cannot be converted to anyhow. - let client = Client::new(url).map_err(|err| anyhow::anyhow!("url is invalid: {err}"))?; - let proof = client - .fetch_proof(&hash) - .map_err(|err| anyhow::anyhow!("failed to send request: {err}"))?; - - if proof.total_nodes > proof.complete_nodes { - tracing::info!( - target: LOG_TARGET, - "Proof is not complete: {}/{}", - proof.complete_nodes, - proof.total_nodes, - ); - } else { - tracing::info!( - target: LOG_TARGET, - "Storing proof to {path_str}", - ); - let serialized = serde_json::to_vec(&proof)?; - std::fs::write(path_str, serialized)?; - } +fn request_proof(_hash: String, _url: &str) -> anyhow::Result<()> { + // let current_dir = std::env::current_dir()?; + // let path = current_dir.join("nexus-proof.json"); + // let path_str = path.to_str().context("path is not valid utf8")?; + + // // TODO: network errors cannot be converted to anyhow. + // let client = Client::new(url).map_err(|err| anyhow::anyhow!("url is invalid: {err}"))?; + // let proof = client + // .fetch_proof(&hash) + // .map_err(|err| anyhow::anyhow!("failed to send request: {err}"))?; + + // if proof.total_nodes > proof.complete_nodes { + // tracing::info!( + // target: LOG_TARGET, + // "Proof is not complete: {}/{}", + // proof.complete_nodes, + // proof.total_nodes, + // ); + // } else { + // tracing::info!( + // target: LOG_TARGET, + // "Storing proof to {path_str}", + // ); + // let serialized = serde_json::to_vec(&proof)?; + // std::fs::write(path_str, serialized)?; + // } + tracing::warn!( + target: LOG_TARGET, + "Networking commands are disabled", + ); Ok(()) } diff --git a/tools/src/command/verify.rs b/tools/src/command/verify.rs index 7c5701897..8a65abb67 100644 --- a/tools/src/command/verify.rs +++ b/tools/src/command/verify.rs @@ -4,7 +4,6 @@ use std::{ path::{Path, PathBuf}, }; -use anyhow::Context; use ark_serialize::CanonicalDeserialize; use nexus_config::vm as vm_config; use nexus_prover::types::PCDNode; @@ -21,10 +20,7 @@ pub fn handle_command(args: VerifyArgs) -> anyhow::Result<()> { fn verify_proof(path: &Path, k: usize, pp_file: Option) -> anyhow::Result<()> { let file = File::open(path)?; let reader = BufReader::new(file); - let proof: nexus_network::api::Proof = serde_json::from_reader(reader)?; - - let serialized_proof = proof.proof.context("invalid proof object")?; - let root = PCDNode::deserialize_compressed(serialized_proof.as_slice())?; + let root = PCDNode::deserialize_compressed(reader)?; let path = pp_file .as_deref() @@ -32,14 +28,22 @@ fn verify_proof(path: &Path, k: usize, pp_file: Option) -> anyhow::Resu .unwrap_or_else(|| format_params_file(vm_config::NovaImpl::Parallel, k)); let state = nexus_prover::pp::gen_or_load(false, k, &path)?; + let mut term = nexus_tui::TerminalHandle::new(); + let mut ctx = term.context("Verifying").on_step(|_step| "proof".into()); + let guard = ctx.display_step(); + match root.verify(&state) { Ok(_) => { + drop(guard); + tracing::info!( target: LOG_TARGET, "Proof is valid", ); } Err(err) => { + guard.abort(); + tracing::error!( target: LOG_TARGET, err = ?err, diff --git a/tools/tools-dev/src/command/common/verify.rs b/tools/tools-dev/src/command/common/verify.rs index bdbfa563d..3bc2a15ef 100644 --- a/tools/tools-dev/src/command/common/verify.rs +++ b/tools/tools-dev/src/command/common/verify.rs @@ -9,7 +9,7 @@ pub struct VerifyArgs { pub pp_file: Option, /// File containing completed proof - #[arg(default_value = "nexus-proof.json")] + #[arg(default_value = "nexus-proof")] pub file: PathBuf, #[arg(short)] diff --git a/tools/tools-dev/src/command/dev/common_impl/prove.rs b/tools/tools-dev/src/command/dev/common_impl/prove.rs index 174b4560f..039970513 100644 --- a/tools/tools-dev/src/command/dev/common_impl/prove.rs +++ b/tools/tools-dev/src/command/dev/common_impl/prove.rs @@ -49,6 +49,13 @@ pub fn handle_command(args: ProveArgs) -> anyhow::Result<()> { }; request_prove(&path, &url) } else { + // build artifact if needed + if release { + cargo(None, ["build", "--release"])?; + } else { + cargo(None, ["build"])?; + } + let LocalProveArgs { k, pp_file, nova_impl } = local_args; let k = k.unwrap_or(vm_config.k); let nova_impl = nova_impl.unwrap_or(vm_config.nova_impl); diff --git a/tools/tools-dev/src/lib.rs b/tools/tools-dev/src/lib.rs index 978196300..bb35c2b3f 100644 --- a/tools/tools-dev/src/lib.rs +++ b/tools/tools-dev/src/lib.rs @@ -1,8 +1,6 @@ use clap::{Parser, Subcommand}; use tracing::level_filters::LevelFilter; -use tracing_subscriber::{ - filter::Targets, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, -}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; pub mod command; pub mod utils; @@ -38,14 +36,11 @@ pub fn setup_logger() -> tracing::subscriber::DefaultGuard { let _ = dotenvy::from_path(nexus_config::constants::CONFIG_ENV_PATH); }; - let filter = if let Ok(filter) = EnvFilter::try_from_default_env() { - filter.boxed() - } else { - Targets::new() - .with_target("r1cs", LevelFilter::OFF) - .with_default(LevelFilter::INFO) - .boxed() - }; + let filter = EnvFilter::builder() + .with_default_directive(LevelFilter::WARN.into()) + .from_env() + .unwrap() + .add_directive("r1cs=off".parse().unwrap()); tracing_subscriber::registry() .with( diff --git a/tools/tui/Cargo.toml b/tools/tui/Cargo.toml new file mode 100644 index 000000000..e06280d92 --- /dev/null +++ b/tools/tui/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "nexus-tui" +edition.workspace = true +version.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +publish.workspace = true + +[dependencies] +anyhow = "1.0" +superconsole = { git = "https://github.com/facebookincubator/superconsole.git", rev = "8254a5c" } diff --git a/tools/tui/src/action.rs b/tools/tui/src/action.rs new file mode 100644 index 000000000..b4c96df00 --- /dev/null +++ b/tools/tui/src/action.rs @@ -0,0 +1,44 @@ +use super::component::FmtDuration; + +pub(crate) struct Action { + pub iter: usize, + pub iter_num: usize, + + pub step_header: &'static str, + pub step_trailing: Box String + Send>, + pub loading_bar_header: Option<&'static str>, + + pub completion_header: &'static str, + pub completion_trailing: Box String + Send>, +} + +impl Action { + pub(crate) fn show_progress(&self) -> bool { + self.loading_bar_header.is_some() + } + + pub(crate) fn next_iter(&mut self) { + let next_iter = self.iter + 1; + assert!(next_iter <= self.iter_num); + + self.iter = next_iter; + } + + pub(crate) fn is_finished(&self) -> bool { + self.iter == self.iter_num + } +} + +impl Default for Action { + fn default() -> Self { + Self { + iter: 0, + iter_num: 1, + step_header: "", + step_trailing: Box::new(|_step| String::new()), + loading_bar_header: None, + completion_header: "Finished", + completion_trailing: Box::new(|elapsed| format!("in {elapsed}")), + } + } +} diff --git a/tools/tui/src/component/loading.rs b/tools/tui/src/component/loading.rs new file mode 100644 index 000000000..8248799a2 --- /dev/null +++ b/tools/tui/src/component/loading.rs @@ -0,0 +1,66 @@ +use std::{cell::RefCell, time::Duration}; + +use superconsole::{style::Stylize, Component, Dimensions, DrawMode, Line, Lines, Span}; + +use crate::action::Action; + +const WIDTH: usize = "=======> ".len() - 1; + +pub struct LoadingBar<'a> { + pub time_spent: Duration, + + action: &'a RefCell, +} + +impl Component for LoadingBar<'_> { + fn draw_unchecked(&self, _: Dimensions, _: DrawMode) -> anyhow::Result { + let action = self.action.borrow(); + + let res = if !action.is_finished() { + if !action.show_progress() { + return Ok(Lines::new()); + } + let iteration = action.iter; + let total = action.iter_num; + + let heading_span = Span::new_styled( + action + .loading_bar_header + .unwrap_or_default() + .to_owned() + .cyan() + .bold(), + )?; + + let percentage = iteration as f64 / total as f64; + let amount = (percentage * WIDTH as f64).ceil() as usize; + + let loading_bar = format!( + " [{test:=>bar_amt$}{empty:padding_amt$}] {}/{}: ...", + iteration, + total, + test = ">", + empty = "", + bar_amt = amount, + padding_amt = WIDTH - amount, + ); + let loading = Span::new_unstyled(loading_bar)?; + Line::from_iter([heading_span, loading]) + } else { + let elapsed = self.time_spent; + + let heading_span = Span::new_styled(action.completion_header.to_owned().blue().bold())?; + let completion_span = Span::new_unstyled((action.completion_trailing)(elapsed.into()))?; + + Line::from_iter([heading_span, Span::padding(1), completion_span]) + }; + + Ok(Lines(vec![res])) + } +} + +impl<'a> LoadingBar<'a> { + pub fn new(action: &'a RefCell) -> Self { + Self { action, time_spent: Duration::ZERO } + } +} diff --git a/tools/tui/src/component/mod.rs b/tools/tui/src/component/mod.rs new file mode 100644 index 000000000..e2a55f383 --- /dev/null +++ b/tools/tui/src/component/mod.rs @@ -0,0 +1,89 @@ +use std::{cell::RefCell, fmt, ops::Deref, time}; + +use superconsole::{Component, Dimensions, DrawMode, Lines}; + +use crate::action::Action; + +mod loading; +mod timer; + +#[derive(Debug)] +pub struct FmtDuration(time::Duration); + +impl From for FmtDuration { + fn from(d: time::Duration) -> Self { + Self(d) + } +} + +impl Deref for FmtDuration { + type Target = time::Duration; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl fmt::Display for FmtDuration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let secs = self.as_secs(); + if secs == 0 { + write!(f, "{}ms", self.as_millis()) + } else if secs >= 60 { + write!(f, "{}min{}s", secs / 60, secs % 60) + } else { + let secs = self.as_secs_f32(); + write!(f, "{:.1}s", secs) + } + } +} + +pub struct Compositor<'a> { + pub timer: timer::Timer<'a>, + pub loading_bar: loading::LoadingBar<'a>, +} + +impl Component for Compositor<'_> { + fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result { + let mut lines = self.timer.draw_unchecked(dimensions, mode)?; + lines.pad_lines_left(2); + + let action = self.timer.action.borrow(); + let is_finished = action.is_finished(); + if !action.show_progress() && !is_finished { + return Ok(lines); + } + let mut loading = self.loading_bar.draw_unchecked(dimensions, mode)?; + + let step_len = action.step_header.len(); + let loading_len = if is_finished { + action.completion_header.len() + } else { + action.loading_bar_header.unwrap_or_default().len() + }; + + let padding = if step_len >= loading_len { + step_len - loading_len + 2 + } else { + 2usize.saturating_sub(loading_len - step_len) + }; + loading.pad_lines_left(padding); + + lines.0.extend(loading.0); + + Ok(lines) + } +} + +impl<'a> Compositor<'a> { + pub fn new(action: &'a RefCell) -> Self { + Self { + timer: timer::Timer::new(action), + loading_bar: loading::LoadingBar::new(action), + } + } + + pub fn finalize(&mut self) -> anyhow::Result { + self.draw_unchecked(Dimensions::default(), DrawMode::Final) + } +} diff --git a/tools/tui/src/component/timer.rs b/tools/tui/src/component/timer.rs new file mode 100644 index 000000000..e6bc9888f --- /dev/null +++ b/tools/tui/src/component/timer.rs @@ -0,0 +1,87 @@ +use std::{ + cell::{Cell, RefCell}, + time::Instant, +}; + +use superconsole::{ + style::{Color, Stylize}, + Component, Dimensions, DrawMode, Line, Lines, Span, +}; + +use super::FmtDuration; +use crate::action::Action; + +pub struct Timer<'a> { + pub start: Instant, + pub(super) action: &'a RefCell, + + num_dots: Cell, + last_tick: Instant, + color: Color, +} + +impl Component for Timer<'_> { + fn draw_unchecked(&self, _: Dimensions, _: DrawMode) -> anyhow::Result { + let action = self.action.borrow(); + if action.is_finished() { + return Ok(Lines::new()); + } + let elapsed: FmtDuration = self.last_tick.elapsed().into(); + let action = self.action.borrow(); + + let heading_span = Span::new_styled(action.step_header.to_owned().bold().with(self.color))?; + let mut trailing = (action.step_trailing)(action.iter); + if !trailing.is_empty() { + trailing.insert(0, ' '); + } + let trailing_span = Span::new_unstyled(trailing)?; + + // dots + let num_dots = self.num_dots.get(); + let dots = Span::new_unstyled(format!( + " {dot:.>num_dots$}{empty:>num_spaces$}", + dot = '.', + num_dots = num_dots, + empty = "", + num_spaces = 4 - num_dots, + ))?; + self.num_dots.set(((num_dots + 1) % 4).max(1)); + + let elapsed_span = Span::new_styled(elapsed.to_string().bold())?; + + let line = Line::from_iter([heading_span, trailing_span, dots, elapsed_span]); + Ok(Lines(vec![line])) + } +} + +impl<'a> Timer<'a> { + pub fn new(action: &'a RefCell) -> Self { + let start = Instant::now(); + Self { + start, + action, + num_dots: Cell::new(1), + last_tick: start, + color: if action.borrow().show_progress() { + Color::Blue + } else { + Color::Cyan + }, + } + } + + pub fn reset(&mut self) { + *self = Self::new(self.action); + } + + pub fn finalize(&mut self) -> anyhow::Result { + self.last_tick = self.start; + self.num_dots.set(3); + self.color = Color::Blue; + + let mut lines = self.draw_unchecked(Dimensions::default(), DrawMode::Normal)?; + lines.pad_lines_left(2); + + Ok(lines) + } +} diff --git a/tools/tui/src/lib.rs b/tools/tui/src/lib.rs new file mode 100644 index 000000000..b9ed25a6c --- /dev/null +++ b/tools/tui/src/lib.rs @@ -0,0 +1,7 @@ +mod action; +mod component; + +mod thread; + +pub mod terminal; +pub use terminal::TerminalHandle; diff --git a/tools/tui/src/terminal.rs b/tools/tui/src/terminal.rs new file mode 100644 index 000000000..0a9e860f8 --- /dev/null +++ b/tools/tui/src/terminal.rs @@ -0,0 +1,137 @@ +use std::sync::mpsc; + +use super::{action::Action, component::FmtDuration, thread as tui_thread}; + +pub struct TerminalHandle { + thread: tui_thread::ThreadHandle, + ctx_sender: Option>, +} + +impl Default for TerminalHandle { + fn default() -> Self { + Self::new() + } +} + +impl TerminalHandle { + pub fn new() -> Self { + Self { + thread: tui_thread::ThreadHandle::new(), + ctx_sender: None, + } + } + + pub fn context(&mut self, step_header: &'static str) -> TerminalContext<'_> { + let _ = self.ctx_sender.take(); + TerminalContext { + term: self, + action: Some(Action { step_header, ..Default::default() }), + steps_left: 1, + } + } +} + +pub struct TerminalContext<'a> { + term: &'a mut TerminalHandle, + action: Option, + steps_left: usize, +} + +impl TerminalContext<'_> { + pub fn with_loading_bar(self, loading_header: &'static str) -> Self { + Self { + action: self.action.map(|action| Action { + loading_bar_header: Some(loading_header), + ..action + }), + ..self + } + } + + pub fn num_steps(self, num_steps: usize) -> Self { + assert!(num_steps > 0); + Self { + action: self + .action + .map(|action| Action { iter_num: num_steps, ..action }), + steps_left: num_steps, + ..self + } + } + + pub fn on_step(self, on_step: F) -> Self + where + F: Fn(usize) -> String + Send + 'static, + { + Self { + action: self.action.map(|action| Action { + step_trailing: Box::new(on_step), + ..action + }), + ..self + } + } + + pub fn completion_header(self, completion_header: &'static str) -> Self { + Self { + action: self + .action + .map(|action| Action { completion_header, ..action }), + ..self + } + } + + pub fn completion_stats(self, on_completion: F) -> Self + where + F: Fn(FmtDuration) -> String + Send + 'static, + { + Self { + action: self.action.map(|action| Action { + completion_trailing: Box::new(on_completion), + ..action + }), + ..self + } + } + + pub fn display_step(&mut self) -> Guard<'_> { + self.steps_left = self + .steps_left + .checked_sub(1) + .expect("steps number overflow"); + let ctx_sender = &mut self.term.ctx_sender; + + let sender = if let Some(action) = self.action.take() { + ctx_sender.get_or_insert_with(|| { + let (tx, rx) = mpsc::channel(); + let _ = self.term.thread.sender().send((action, rx)); + tx + }) + } else { + let tx = ctx_sender.as_ref().unwrap(); + let _ = tx.send(()); + tx + } + .into(); + + Guard { sender } + } +} + +pub struct Guard<'a> { + sender: Option<&'a mpsc::Sender<()>>, +} + +impl Guard<'_> { + pub fn abort(mut self) { + let _ = self.sender.take(); + } +} + +impl Drop for Guard<'_> { + fn drop(&mut self) { + if let Some(sender) = self.sender { + let _ = sender.send(()); + } + } +} diff --git a/tools/tui/src/thread.rs b/tools/tui/src/thread.rs new file mode 100644 index 000000000..ff79cb169 --- /dev/null +++ b/tools/tui/src/thread.rs @@ -0,0 +1,102 @@ +use std::mem::ManuallyDrop; +use std::time::Duration; +use std::{ + cell::RefCell, + sync::mpsc::{self, Receiver, RecvTimeoutError, SyncSender}, + thread::JoinHandle, +}; + +use superconsole::SuperConsole; + +use super::{action::Action, component::Compositor}; + +pub(crate) type Payload = (Action, Receiver<()>); + +pub(crate) struct ThreadHandle { + sender: ManuallyDrop>, + join_handle: ManuallyDrop>, +} + +impl ThreadHandle { + pub fn new() -> Self { + let (sender, receiver) = mpsc::sync_channel(0); + + let join_handle = std::thread::spawn(move || thread_main(receiver)); + Self { + sender: ManuallyDrop::new(sender), + join_handle: ManuallyDrop::new(join_handle), + } + } + + pub fn sender(&self) -> &SyncSender { + &self.sender + } +} + +impl Drop for ThreadHandle { + fn drop(&mut self) { + let Self { sender, join_handle } = self; + // SAFETY: struct fields are not accessible once `drop` returns. + unsafe { ManuallyDrop::drop(sender) }; + let join_handle = unsafe { ManuallyDrop::take(join_handle) }; + + let _ = join_handle.join(); + } +} + +fn thread_main(receiver: Receiver) { + let Some(mut superconsole) = SuperConsole::new() else { + return; + }; + + while let Ok((action, _receiver)) = receiver.recv() { + let _ = handle_action(&mut superconsole, action, _receiver); + } +} + +fn handle_action( + superconsole: &mut SuperConsole, + action: Action, + receiver: Receiver<()>, +) -> anyhow::Result<()> { + const SLEEP_DURATION: Duration = Duration::from_millis(100); + + let mut total_elapsed = Duration::ZERO; + let iter_num = action.iter_num; + + let action = RefCell::new(action); + let mut component = Compositor::new(&action); + for i in 0..iter_num { + component.timer.reset(); + + loop { + superconsole.render(&component)?; + + match receiver.recv_timeout(SLEEP_DURATION) { + Ok(_) => break, + Err(RecvTimeoutError::Disconnected) => anyhow::bail!("sender dropped"), + Err(RecvTimeoutError::Timeout) => {} + } + } + total_elapsed += component.timer.start.elapsed(); + let step_line = component.timer.finalize()?; + + superconsole.render(&component)?; + superconsole.emit(step_line); + + action.borrow_mut().next_iter(); + + if i + 1 < iter_num { + // wait for the next step. + receiver.recv()?; + } + } + + component.loading_bar.time_spent = total_elapsed; + let bar_line = component.finalize()?; + + superconsole.render(&component)?; + superconsole.emit(bar_line); + + Ok(()) +} diff --git a/vm/src/trace.rs b/vm/src/trace.rs index 7f8cde511..8db47fd9d 100644 --- a/vm/src/trace.rs +++ b/vm/src/trace.rs @@ -163,7 +163,7 @@ pub fn trace(vm: &mut NexusVM, k: usize, pow: bool) -> Result { if vm.inst.opcode == HALT { if pow { let count = trace.blocks.len(); - if count.next_power_of_two() == count { + if count.next_power_of_two() == count + 1 { break; } } else {