tacryt-socryp 8 месяцев назад
Родитель
Сommit
270b2b9c66
89 измененных файлов с 11207 добавлено и 2293 удалено
  1. 3 0
      .env
  2. 6599 0
      Cargo.lock
  3. 0 1
      Cargo.toml
  4. 8 4
      Makefile
  5. 16 0
      README.md
  6. 10 4
      crates/hoonc/Cargo.toml
  7. 10 7
      crates/hoonc/src/lib.rs
  8. 6 2
      crates/hoonc/src/main.rs
  9. 1 0
      crates/kernels/Cargo.toml
  10. 3 0
      crates/kernels/src/lib.rs
  11. 8 0
      crates/kernels/src/miner.rs
  12. 8 6
      crates/nockapp/Cargo.toml
  13. 2 16
      crates/nockapp/src/drivers/exit.rs
  14. 4 2
      crates/nockapp/src/drivers/npc.rs
  15. 7 1
      crates/nockapp/src/drivers/one_punch.rs
  16. 168 87
      crates/nockapp/src/kernel/form.rs
  17. 4 2
      crates/nockapp/src/nockapp/driver.rs
  18. 228 114
      crates/nockapp/src/nockapp/mod.rs
  19. 9 2
      crates/nockapp/src/utils/mod.rs
  20. 43 48
      crates/nockapp/src/utils/slogger.rs
  21. 4 4
      crates/nockchain-bitcoin-sync/src/lib.rs
  22. 3 2
      crates/nockchain-libp2p-io/Cargo.toml
  23. 46 132
      crates/nockchain-libp2p-io/src/nc.rs
  24. 20 0
      crates/nockchain-libp2p-io/src/p2p.rs
  25. 25 3
      crates/nockchain-libp2p-io/src/p2p_util.rs
  26. 2 1
      crates/nockchain/Cargo.toml
  27. 57 19
      crates/nockchain/src/lib.rs
  28. 1 1
      crates/nockchain/src/main.rs
  29. 268 0
      crates/nockchain/src/mining.rs
  30. 0 32
      crates/nockvm/rust/assert_no_alloc/Cargo.toml
  31. 0 21
      crates/nockvm/rust/assert_no_alloc/LICENSE
  32. 0 151
      crates/nockvm/rust/assert_no_alloc/README.md
  33. 0 37
      crates/nockvm/rust/assert_no_alloc/examples/main.rs
  34. 0 305
      crates/nockvm/rust/assert_no_alloc/src/lib.rs
  35. 0 153
      crates/nockvm/rust/assert_no_alloc/tests/test.rs
  36. 2 1
      crates/nockvm/rust/nockvm/Cargo.toml
  37. 562 489
      crates/nockvm/rust/nockvm/src/interpreter.rs
  38. 13 11
      crates/nockvm/rust/nockvm/src/jets.rs
  39. 1 17
      crates/nockvm/rust/nockvm/src/lib.rs
  40. 29 28
      crates/nockvm/rust/nockvm/src/mem.rs
  41. 8 0
      crates/nockvm/rust/nockvm/src/noun.rs
  42. 20 24
      crates/nockvm/rust/nockvm/src/trace.rs
  43. 3 2
      crates/nockvm/rust/nockvm/src/unifying_equality.rs
  44. 0 1
      crates/nockvm/rust/nockvm_crypto/Cargo.toml
  45. 16 1
      crates/zkvm-jetpack/src/form/belt.rs
  46. 214 0
      crates/zkvm-jetpack/src/form/felt.rs
  47. 17 0
      crates/zkvm-jetpack/src/form/math/base.rs
  48. 127 0
      crates/zkvm-jetpack/src/form/math/bpoly.rs
  49. 173 0
      crates/zkvm-jetpack/src/form/math/fext.rs
  50. 30 0
      crates/zkvm-jetpack/src/form/math/mary.rs
  51. 2 0
      crates/zkvm-jetpack/src/form/math/mod.rs
  52. 36 21
      crates/zkvm-jetpack/src/form/math/tip5.rs
  53. 2 0
      crates/zkvm-jetpack/src/form/mod.rs
  54. 208 0
      crates/zkvm-jetpack/src/hot.rs
  55. 5 6
      crates/zkvm-jetpack/src/jets/bp_jets.rs
  56. 305 0
      crates/zkvm-jetpack/src/jets/cheetah_jets.rs
  57. 121 0
      crates/zkvm-jetpack/src/jets/fext_jets.rs
  58. 86 0
      crates/zkvm-jetpack/src/jets/mary_jets.rs
  59. 4 0
      crates/zkvm-jetpack/src/jets/mod.rs
  60. 178 0
      crates/zkvm-jetpack/src/jets/verifier_jets.rs
  61. 1 0
      crates/zkvm-jetpack/src/lib.rs
  62. 11 0
      crates/zkvm-jetpack/src/utils.rs
  63. 125 56
      hoon/apps/dumbnet/inner.hoon
  64. 26 13
      hoon/apps/dumbnet/lib/consensus.hoon
  65. 8 7
      hoon/apps/dumbnet/lib/miner.hoon
  66. 10 4
      hoon/apps/dumbnet/lib/types.hoon
  67. 40 0
      hoon/apps/dumbnet/miner.hoon
  68. 1 0
      hoon/apps/dumbnet/outer.hoon
  69. 5 1
      hoon/common/nock-prover.hoon
  70. 5 1
      hoon/common/nock-verifier.hoon
  71. 1 1
      hoon/common/pow.hoon
  72. 117 36
      hoon/common/stark/prover.hoon
  73. 202 74
      hoon/common/stark/verifier.hoon
  74. 1 0
      hoon/common/table/memory.hoon
  75. 20 28
      hoon/common/table/prover/compute.hoon
  76. 42 23
      hoon/common/table/prover/memory.hoon
  77. 74 64
      hoon/common/table/verifier/compute.hoon
  78. 43 7
      hoon/common/table/verifier/memory.hoon
  79. 374 116
      hoon/common/tx-engine.hoon
  80. 1 1
      hoon/common/zeke.hoon
  81. 137 45
      hoon/common/ztd/eight.hoon
  82. 4 2
      hoon/common/ztd/five.hoon
  83. 21 1
      hoon/common/ztd/four.hoon
  84. 21 6
      hoon/common/ztd/one.hoon
  85. 6 0
      hoon/common/ztd/seven.hoon
  86. 173 47
      hoon/common/ztd/three.hoon
  87. BIN
      hoon/constraints/constraints-0.jam
  88. 5 2
      hoon/dat/constraints.hoon
  89. 8 0
      hoon/dat/softed-constraints.hoon

+ 3 - 0
.env

@@ -0,0 +1,3 @@
+RUST_LOG=info,nockchain=debug,nockchain_libp2p_io=info,libp2p=info,libp2p_quic=info
+MINIMAL_LOG_FORMAT=true
+MINING_PUBKEY=EHmKL2U3vXfS5GYAY5aVnGdukfDWwvkQPCZXnjvZVShsSQi3UAuA4tQQpVwGJMzc9FfpTY8pLDkqhBGfWutiF4prrCktUH9oAWJxkXQBzAavKDc95NR3DjmYwnnw8GuugnK

+ 6599 - 0
Cargo.lock

@@ -0,0 +1,6599 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[package]]
+name = "aead"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+dependencies = [
+ "crypto-common",
+ "generic-array",
+]
+
+[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "aes-siv"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e08d0cdb774acd1e4dac11478b1a0c0d203134b2aab0ba25eb430de9b18f8b9"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "cmac",
+ "ctr",
+ "dbl",
+ "digest",
+ "zeroize",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anes"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
+
+[[package]]
+name = "anstream"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+dependencies = [
+ "anstyle",
+ "once_cell",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+
+[[package]]
+name = "argon2"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
+dependencies = [
+ "base64ct",
+ "blake2",
+ "cpufeatures",
+ "password-hash",
+]
+
+[[package]]
+name = "arrayref"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "asn1-rs"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60"
+dependencies = [
+ "asn1-rs-derive",
+ "asn1-rs-impl",
+ "displaydoc",
+ "nom",
+ "num-traits",
+ "rusticata-macros",
+ "thiserror 2.0.12",
+ "time",
+]
+
+[[package]]
+name = "asn1-rs-derive"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "synstructure",
+]
+
+[[package]]
+name = "asn1-rs-impl"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "assert_no_alloc"
+version = "1.1.2"
+dependencies = [
+ "backtrace",
+ "log",
+]
+
+[[package]]
+name = "async-io"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059"
+dependencies = [
+ "async-lock",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix 0.38.44",
+ "slab",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "asynchronous-codec"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233"
+dependencies = [
+ "bytes",
+ "futures-sink",
+ "futures-util",
+ "memchr",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "attohttpc"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2"
+dependencies = [
+ "http 0.2.12",
+ "log",
+ "url",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "autotools"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef941527c41b0fc0dd48511a8154cd5fc7e29200a0ff8b7203c5d777dbc795cf"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "axum"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
+dependencies = [
+ "async-trait",
+ "axum-core 0.4.5",
+ "bytes",
+ "futures-util",
+ "http 1.3.1",
+ "http-body",
+ "http-body-util",
+ "itoa",
+ "matchit 0.7.3",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "sync_wrapper",
+ "tower 0.5.2",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5"
+dependencies = [
+ "axum-core 0.5.2",
+ "bytes",
+ "form_urlencoded",
+ "futures-util",
+ "http 1.3.1",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "itoa",
+ "matchit 0.8.4",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower 0.5.2",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http 1.3.1",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http 1.3.1",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "bardecoder"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "757eeab9757d5ddd88aa1420449f3ce92631339b7f5b3785808da7a148de35d9"
+dependencies = [
+ "anyhow",
+ "image",
+ "log",
+ "newtype_derive",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "base-x"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
+
+[[package]]
+name = "base58ck"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f"
+dependencies = [
+ "bitcoin-internals",
+ "bitcoin_hashes",
+]
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "base64ct"
+version = "1.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3"
+
+[[package]]
+name = "bech32"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d"
+
+[[package]]
+name = "bincode"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
+dependencies = [
+ "bincode_derive",
+ "serde",
+ "unty",
+]
+
+[[package]]
+name = "bincode_derive"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09"
+dependencies = [
+ "virtue",
+]
+
+[[package]]
+name = "bindgen"
+version = "0.69.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
+dependencies = [
+ "bitflags 2.9.1",
+ "cexpr",
+ "clang-sys",
+ "itertools 0.12.1",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash 1.1.0",
+ "shlex",
+ "syn 2.0.101",
+ "which",
+]
+
+[[package]]
+name = "bit_field"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+
+[[package]]
+name = "bitcoin"
+version = "0.32.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad8929a18b8e33ea6b3c09297b687baaa71fb1b97353243a3f1029fad5c59c5b"
+dependencies = [
+ "base58ck",
+ "bech32",
+ "bitcoin-internals",
+ "bitcoin-io",
+ "bitcoin-units",
+ "bitcoin_hashes",
+ "hex-conservative",
+ "hex_lit",
+ "secp256k1",
+ "serde",
+]
+
+[[package]]
+name = "bitcoin-internals"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bitcoin-io"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
+
+[[package]]
+name = "bitcoin-units"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2"
+dependencies = [
+ "bitcoin-internals",
+ "serde",
+]
+
+[[package]]
+name = "bitcoin_hashes"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
+dependencies = [
+ "bitcoin-io",
+ "hex-conservative",
+ "serde",
+]
+
+[[package]]
+name = "bitcoincore-rpc"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee"
+dependencies = [
+ "bitcoincore-rpc-json",
+ "jsonrpc",
+ "log",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "bitcoincore-rpc-json"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a"
+dependencies = [
+ "bitcoin",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+
+[[package]]
+name = "bitvec"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
+dependencies = [
+ "funty",
+ "radium",
+ "tap",
+ "wyz",
+]
+
+[[package]]
+name = "blake2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "blake3"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "cc",
+ "cfg-if",
+ "constant_time_eq",
+ "serde",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bs58"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
+
+[[package]]
+name = "bytemuck"
+version = "1.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "camino"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo-platform"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "semver 1.0.26",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "cassowary"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
+
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
+[[package]]
+name = "castaway"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
+dependencies = [
+ "rustversion",
+]
+
+[[package]]
+name = "cbor4ii"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "472931dd4dfcc785075b09be910147f9c6258883fc4591d0dac6116392b2daa6"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "chrono"
+version = "0.4.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-link",
+]
+
+[[package]]
+name = "ciborium"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
+[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+
+[[package]]
+name = "cmac"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa"
+dependencies = [
+ "cipher",
+ "dbl",
+ "digest",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+
+[[package]]
+name = "compact_str"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
+dependencies = [
+ "castaway",
+ "cfg-if",
+ "itoa",
+ "rustversion",
+ "ryu",
+ "static_assertions",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "constant_time_eq"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
+
+[[package]]
+name = "convert_case"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "coolor"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "980c2afde4af43d6a05c5be738f9eae595cff86dce1f38f88b95058a98c027f3"
+dependencies = [
+ "crossterm 0.29.0",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core2"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "criterion"
+version = "0.5.1"
+source = "git+https://github.com/vlovich/criterion.rs.git?rev=9b485aece85a3546126b06cc25d33e14aba829b3#9b485aece85a3546126b06cc25d33e14aba829b3"
+dependencies = [
+ "anes",
+ "cast",
+ "ciborium",
+ "clap",
+ "criterion-plot",
+ "is-terminal",
+ "itertools 0.14.0",
+ "num-traits",
+ "once_cell",
+ "oorandom",
+ "plotters",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "tinytemplate",
+ "walkdir",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.5.0"
+source = "git+https://github.com/vlovich/criterion.rs.git?rev=9b485aece85a3546126b06cc25d33e14aba829b3#9b485aece85a3546126b06cc25d33e14aba829b3"
+dependencies = [
+ "cast",
+ "itertools 0.14.0",
+]
+
+[[package]]
+name = "crokey"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5282b45c96c5978c8723ea83385cb9a488b64b7d175733f48d07bf9da514a863"
+dependencies = [
+ "crokey-proc_macros",
+ "crossterm 0.29.0",
+ "once_cell",
+ "serde",
+ "strict",
+]
+
+[[package]]
+name = "crokey-proc_macros"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ea0218d3fedf0797fa55676f1964ef5d27103d41ed0281b4bbd2a6e6c3d8d28"
+dependencies = [
+ "crossterm 0.29.0",
+ "proc-macro2",
+ "quote",
+ "strict",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "crossbeam"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "crossterm"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
+dependencies = [
+ "bitflags 2.9.1",
+ "crossterm_winapi",
+ "mio 1.0.3",
+ "parking_lot",
+ "rustix 0.38.44",
+ "signal-hook",
+ "signal-hook-mio",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
+dependencies = [
+ "bitflags 2.9.1",
+ "crossterm_winapi",
+ "derive_more",
+ "document-features",
+ "mio 1.0.3",
+ "parking_lot",
+ "rustix 1.0.7",
+ "signal-hook",
+ "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 = "crunchy"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "rand_core 0.6.4",
+ "typenum",
+]
+
+[[package]]
+name = "ctr"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "rustc_version 0.4.1",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "darling"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "dashmap"
+version = "6.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+ "hashbrown 0.14.5",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
+
+[[package]]
+name = "data-encoding-macro"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d"
+dependencies = [
+ "data-encoding",
+ "data-encoding-macro-internal",
+]
+
+[[package]]
+name = "data-encoding-macro-internal"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
+dependencies = [
+ "data-encoding",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "dbl"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "der"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[package]]
+name = "der-parser"
+version = "10.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
+dependencies = [
+ "asn1-rs",
+ "displaydoc",
+ "nom",
+ "num-bigint",
+ "num-traits",
+ "rusticata-macros",
+]
+
+[[package]]
+name = "deranged"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "derive_more"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "unicode-xid",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "dirs"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "document-features"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "dogstatsd"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "faeb22dbbf6aef8e687ad83f9cd9fd893ac5c0f36d2e46fb0cbcf7ae19640ec0"
+dependencies = [
+ "chrono",
+ "retry",
+]
+
+[[package]]
+name = "dtoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04"
+
+[[package]]
+name = "dynasm"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0cecff24995c8a5a3c3169cff4c733fe7d91aedf5d8cc96238738bfe53186b8"
+dependencies = [
+ "bitflags 2.9.1",
+ "byteorder",
+ "lazy_static",
+ "proc-macro-error2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "dynasmrt"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f5eab96b8688bcbf1d2354bcfe0261005ac1dd0616747152ada34948d4e9582"
+dependencies = [
+ "byteorder",
+ "dynasm",
+ "fnv",
+ "memmap2",
+]
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "rand_core 0.6.4",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "enum-as-inner"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "equix"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194df1f219a987430956f20faaf702fd4d434b1b2f7300014119854184107ac7"
+dependencies = [
+ "arrayvec",
+ "hashx",
+ "num-traits",
+ "thiserror 2.0.12",
+ "visibility",
+]
+
+[[package]]
+name = "equix-latency"
+version = "0.1.0"
+dependencies = [
+ "equix",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "event-listener"
+version = "5.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
+dependencies = [
+ "event-listener",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "exr"
+version = "1.73.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
+dependencies = [
+ "bit_field",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+
+[[package]]
+name = "filetime"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "libredox",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "fixed-capacity-vec"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b31a14f5ee08ed1a40e1252b35af18bed062e3f39b69aab34decde36bc43e40"
+
+[[package]]
+name = "flate2"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
+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 = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "fsevent-sys"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "funty"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-bounded"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91f328e7fb845fc832912fb6a34f40cf6d1888c92f974d1893a54e97b5ff542e"
+dependencies = [
+ "futures-timer",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+ "num_cpus",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-lite"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "futures-rustls"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb"
+dependencies = [
+ "futures-io",
+ "rustls",
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-timer"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generator"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "libc",
+ "log",
+ "rustversion",
+ "windows 0.61.1",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gif"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "glob"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
+
+[[package]]
+name = "gnort"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1033ebf8ca0e0a249d8996a5979dcfd276d43b2818cb6045993a844250c49516"
+dependencies = [
+ "dashmap",
+ "derive_more",
+ "dogstatsd",
+ "governor",
+ "maplit",
+ "nonzero_ext",
+ "once_cell",
+ "thiserror 2.0.12",
+ "tracing",
+]
+
+[[package]]
+name = "governor"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be93b4ec2e4710b04d9264c0c7350cdd62a8c20e5e4ac732552ebb8f0debe8eb"
+dependencies = [
+ "cfg-if",
+ "dashmap",
+ "futures-sink",
+ "futures-timer",
+ "futures-util",
+ "getrandom 0.3.3",
+ "no-std-compat",
+ "nonzero_ext",
+ "parking_lot",
+ "portable-atomic",
+ "quanta",
+ "rand 0.9.1",
+ "smallvec",
+ "spinning_top",
+ "web-time",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http 1.3.1",
+ "indexmap 2.9.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "half"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash",
+]
+
+[[package]]
+name = "hashx"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6a2ec826c7346da402e77c8d647d6586249a561ba36674ddb91dc82abf930b8"
+dependencies = [
+ "arrayvec",
+ "blake2",
+ "dynasmrt",
+ "fixed-capacity-vec",
+ "hex",
+ "rand_core 0.9.3",
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "hermit-abi"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+
+[[package]]
+name = "hermit-abi"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hex-conservative"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
+dependencies = [
+ "arrayvec",
+]
+
+[[package]]
+name = "hex-literal"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcaaec4551594c969335c98c903c1397853d4198408ea609190f420500f6be71"
+
+[[package]]
+name = "hex_lit"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd"
+
+[[package]]
+name = "hickory-proto"
+version = "0.25.0-alpha.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d00147af6310f4392a31680db52a3ed45a2e0f68eb18e8c3fe5537ecc96d9e2"
+dependencies = [
+ "async-recursion",
+ "async-trait",
+ "cfg-if",
+ "data-encoding",
+ "enum-as-inner",
+ "futures-channel",
+ "futures-io",
+ "futures-util",
+ "idna",
+ "ipnet",
+ "once_cell",
+ "rand 0.9.1",
+ "socket2",
+ "thiserror 2.0.12",
+ "tinyvec",
+ "tokio",
+ "tracing",
+ "url",
+]
+
+[[package]]
+name = "hickory-resolver"
+version = "0.25.0-alpha.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5762f69ebdbd4ddb2e975cd24690bf21fe6b2604039189c26acddbc427f12887"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "hickory-proto",
+ "ipconfig",
+ "moka",
+ "once_cell",
+ "parking_lot",
+ "rand 0.9.1",
+ "resolv-conf",
+ "smallvec",
+ "thiserror 2.0.12",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "hkdf"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+dependencies = [
+ "hmac",
+]
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "home"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "hoonc"
+version = "0.2.0"
+dependencies = [
+ "bytes",
+ "clap",
+ "dirs",
+ "futures",
+ "nockapp",
+ "nockvm",
+ "nockvm_macros",
+ "tempfile",
+ "tokio",
+ "tracing",
+ "walkdir",
+]
+
+[[package]]
+name = "http"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http 1.3.1",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http 1.3.1",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2",
+ "http 1.3.1",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-timeout"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
+dependencies = [
+ "hyper",
+ "hyper-util",
+ "pin-project-lite",
+ "tokio",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http 1.3.1",
+ "http-body",
+ "hyper",
+ "libc",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core 0.61.2",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "ibig"
+version = "0.3.6"
+dependencies = [
+ "cfg-if",
+ "criterion",
+ "num-traits",
+ "rand 0.8.5",
+ "serde",
+ "serde_test",
+ "static_assertions",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
+
+[[package]]
+name = "icu_properties"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "potential_utf",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
+
+[[package]]
+name = "icu_provider"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "if-addrs"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "if-watch"
+version = "3.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdf9d64cfcf380606e64f9a0bcf493616b65331199f984151a6fa11a7b3cde38"
+dependencies = [
+ "async-io",
+ "core-foundation",
+ "fnv",
+ "futures",
+ "if-addrs",
+ "ipnet",
+ "log",
+ "netlink-packet-core",
+ "netlink-packet-route",
+ "netlink-proto",
+ "netlink-sys",
+ "rtnetlink",
+ "system-configuration",
+ "tokio",
+ "windows 0.53.0",
+]
+
+[[package]]
+name = "igd-next"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d06464e726471718db9ad3fefc020529fabcde03313a0fc3967510e2db5add12"
+dependencies = [
+ "async-trait",
+ "attohttpc",
+ "bytes",
+ "futures",
+ "http 1.3.1",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "log",
+ "rand 0.9.1",
+ "tokio",
+ "url",
+ "xmltree",
+]
+
+[[package]]
+name = "image"
+version = "0.24.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "exr",
+ "gif",
+ "jpeg-decoder",
+ "num-traits",
+ "png",
+ "qoi",
+ "tiff",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.15.3",
+]
+
+[[package]]
+name = "indoc"
+version = "2.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
+
+[[package]]
+name = "inotify"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
+dependencies = [
+ "bitflags 1.3.2",
+ "inotify-sys",
+ "libc",
+]
+
+[[package]]
+name = "inotify-sys"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "instability"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d"
+dependencies = [
+ "darling",
+ "indoc",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "intmap"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "210870d5399600055a955c020cc97e462203c234dc2b103b5da5d80fdbd5eed5"
+
+[[package]]
+name = "ipconfig"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f"
+dependencies = [
+ "socket2",
+ "widestring",
+ "windows-sys 0.48.0",
+ "winreg",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
+
+[[package]]
+name = "is-terminal"
+version = "0.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
+dependencies = [
+ "hermit-abi 0.5.1",
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "json"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
+
+[[package]]
+name = "jsonrpc"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf"
+dependencies = [
+ "base64 0.13.1",
+ "minreq",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "kernels"
+version = "0.1.0"
+
+[[package]]
+name = "kqueue"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a"
+dependencies = [
+ "kqueue-sys",
+ "libc",
+]
+
+[[package]]
+name = "kqueue-sys"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+]
+
+[[package]]
+name = "lazy-regex"
+version = "3.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126"
+dependencies = [
+ "lazy-regex-proc_macros",
+ "once_cell",
+ "regex",
+]
+
+[[package]]
+name = "lazy-regex-proc_macros"
+version = "3.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "regex",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
+[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
+
+[[package]]
+name = "libc"
+version = "0.2.172"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
+
+[[package]]
+name = "libloading"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.53.0",
+]
+
+[[package]]
+name = "libp2p"
+version = "0.56.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "bytes",
+ "either",
+ "futures",
+ "futures-timer",
+ "getrandom 0.2.16",
+ "libp2p-allow-block-list",
+ "libp2p-connection-limits",
+ "libp2p-core",
+ "libp2p-dns",
+ "libp2p-identify",
+ "libp2p-identity",
+ "libp2p-kad",
+ "libp2p-mdns",
+ "libp2p-memory-connection-limits",
+ "libp2p-metrics",
+ "libp2p-ping",
+ "libp2p-quic",
+ "libp2p-request-response",
+ "libp2p-swarm",
+ "libp2p-tcp",
+ "libp2p-tls",
+ "libp2p-upnp",
+ "multiaddr",
+ "pin-project",
+ "rw-stream-sink",
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "libp2p-allow-block-list"
+version = "0.5.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+]
+
+[[package]]
+name = "libp2p-connection-limits"
+version = "0.5.1"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+]
+
+[[package]]
+name = "libp2p-core"
+version = "0.43.1"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "either",
+ "fnv",
+ "futures",
+ "futures-timer",
+ "libp2p-identity",
+ "multiaddr",
+ "multihash",
+ "multistream-select",
+ "parking_lot",
+ "pin-project",
+ "quick-protobuf",
+ "rand 0.8.5",
+ "rw-stream-sink",
+ "thiserror 2.0.12",
+ "tracing",
+ "unsigned-varint",
+ "web-time",
+]
+
+[[package]]
+name = "libp2p-dns"
+version = "0.44.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "async-trait",
+ "futures",
+ "hickory-resolver",
+ "libp2p-core",
+ "libp2p-identity",
+ "parking_lot",
+ "smallvec",
+ "tracing",
+]
+
+[[package]]
+name = "libp2p-identify"
+version = "0.47.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "asynchronous-codec",
+ "either",
+ "futures",
+ "futures-bounded",
+ "futures-timer",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "quick-protobuf",
+ "quick-protobuf-codec",
+ "smallvec",
+ "thiserror 2.0.12",
+ "tracing",
+]
+
+[[package]]
+name = "libp2p-identity"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbb68ea10844211a59ce46230909fd0ea040e8a192454d4cc2ee0d53e12280eb"
+dependencies = [
+ "bs58",
+ "ed25519-dalek",
+ "hkdf",
+ "multihash",
+ "quick-protobuf",
+ "rand 0.8.5",
+ "sha2",
+ "thiserror 2.0.12",
+ "tracing",
+ "zeroize",
+]
+
+[[package]]
+name = "libp2p-kad"
+version = "0.47.1"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "asynchronous-codec",
+ "bytes",
+ "either",
+ "fnv",
+ "futures",
+ "futures-bounded",
+ "futures-timer",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "quick-protobuf",
+ "quick-protobuf-codec",
+ "rand 0.8.5",
+ "sha2",
+ "smallvec",
+ "thiserror 2.0.12",
+ "tracing",
+ "uint",
+ "web-time",
+]
+
+[[package]]
+name = "libp2p-mdns"
+version = "0.47.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "futures",
+ "hickory-proto",
+ "if-watch",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "rand 0.8.5",
+ "smallvec",
+ "socket2",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "libp2p-memory-connection-limits"
+version = "0.4.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "memory-stats",
+ "sysinfo 0.33.1",
+ "tracing",
+]
+
+[[package]]
+name = "libp2p-metrics"
+version = "0.17.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "futures",
+ "libp2p-core",
+ "libp2p-identify",
+ "libp2p-identity",
+ "libp2p-kad",
+ "libp2p-ping",
+ "libp2p-swarm",
+ "pin-project",
+ "prometheus-client",
+ "web-time",
+]
+
+[[package]]
+name = "libp2p-ping"
+version = "0.46.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "rand 0.8.5",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "libp2p-quic"
+version = "0.12.1"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "if-watch",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-tls",
+ "quinn",
+ "rand 0.8.5",
+ "ring",
+ "rustls",
+ "socket2",
+ "thiserror 2.0.12",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "libp2p-request-response"
+version = "0.28.1"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "async-trait",
+ "cbor4ii",
+ "futures",
+ "futures-bounded",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "rand 0.8.5",
+ "serde",
+ "smallvec",
+ "tracing",
+]
+
+[[package]]
+name = "libp2p-swarm"
+version = "0.47.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "either",
+ "fnv",
+ "futures",
+ "futures-timer",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm-derive",
+ "lru",
+ "multistream-select",
+ "rand 0.8.5",
+ "smallvec",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "libp2p-swarm-derive"
+version = "0.35.1"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "heck",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "libp2p-tcp"
+version = "0.43.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "if-watch",
+ "libc",
+ "libp2p-core",
+ "socket2",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "libp2p-tls"
+version = "0.6.2"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "futures",
+ "futures-rustls",
+ "libp2p-core",
+ "libp2p-identity",
+ "rcgen",
+ "ring",
+ "rustls",
+ "rustls-webpki",
+ "thiserror 2.0.12",
+ "x509-parser",
+ "yasna",
+]
+
+[[package]]
+name = "libp2p-upnp"
+version = "0.4.1"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "igd-next",
+ "libp2p-core",
+ "libp2p-swarm",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags 2.9.1",
+ "libc",
+ "redox_syscall",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
+
+[[package]]
+name = "litemap"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
+
+[[package]]
+name = "litrs"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+
+[[package]]
+name = "loom"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
+dependencies = [
+ "cfg-if",
+ "generator",
+ "scoped-tls",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "lru"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
+dependencies = [
+ "hashbrown 0.15.3",
+]
+
+[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
+[[package]]
+name = "maplit"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
+[[package]]
+name = "matchit"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "memmap2"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memory-stats"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c73f5c649995a115e1a0220b35e4df0a1294500477f97a91d0660fb5abeb574a"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "minimad"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9c5d708226d186590a7b6d4a9780e2bdda5f689e0d58cd17012a298efd745d2"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "minreq"
+version = "2.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0d2aaba477837b46ec1289588180fabfccf0c3b1d1a0c6b1866240cd6cd5ce9"
+dependencies = [
+ "log",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+dependencies = [
+ "libc",
+ "log",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
+dependencies = [
+ "libc",
+ "log",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "moka"
+version = "0.12.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+ "loom",
+ "parking_lot",
+ "portable-atomic",
+ "rustc_version 0.4.1",
+ "smallvec",
+ "tagptr",
+ "thiserror 1.0.69",
+ "uuid",
+]
+
+[[package]]
+name = "multiaddr"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961"
+dependencies = [
+ "arrayref",
+ "byteorder",
+ "data-encoding",
+ "libp2p-identity",
+ "multibase",
+ "multihash",
+ "percent-encoding",
+ "serde",
+ "static_assertions",
+ "unsigned-varint",
+ "url",
+]
+
+[[package]]
+name = "multibase"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404"
+dependencies = [
+ "base-x",
+ "data-encoding",
+ "data-encoding-macro",
+]
+
+[[package]]
+name = "multihash"
+version = "0.19.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d"
+dependencies = [
+ "core2",
+ "unsigned-varint",
+]
+
+[[package]]
+name = "multistream-select"
+version = "0.13.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "bytes",
+ "futures",
+ "pin-project",
+ "smallvec",
+ "tracing",
+ "unsigned-varint",
+]
+
+[[package]]
+name = "murmur3"
+version = "0.5.2"
+dependencies = [
+ "murmur3-sys",
+ "quickcheck",
+]
+
+[[package]]
+name = "murmur3-sys"
+version = "0.1.0"
+dependencies = [
+ "bindgen",
+]
+
+[[package]]
+name = "netlink-packet-core"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4"
+dependencies = [
+ "anyhow",
+ "byteorder",
+ "netlink-packet-utils",
+]
+
+[[package]]
+name = "netlink-packet-route"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66"
+dependencies = [
+ "anyhow",
+ "bitflags 1.3.2",
+ "byteorder",
+ "libc",
+ "netlink-packet-core",
+ "netlink-packet-utils",
+]
+
+[[package]]
+name = "netlink-packet-utils"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34"
+dependencies = [
+ "anyhow",
+ "byteorder",
+ "paste",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "netlink-proto"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72452e012c2f8d612410d89eea01e2d9b56205274abb35d53f60200b2ec41d60"
+dependencies = [
+ "bytes",
+ "futures",
+ "log",
+ "netlink-packet-core",
+ "netlink-sys",
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "netlink-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23"
+dependencies = [
+ "bytes",
+ "futures",
+ "libc",
+ "log",
+ "tokio",
+]
+
+[[package]]
+name = "newtype_derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac8cd24d9f185bb7223958d8c1ff7a961b74b1953fd05dba7cc568a63b3861ec"
+dependencies = [
+ "rustc_version 0.1.7",
+]
+
+[[package]]
+name = "nix"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "no-std-compat"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
+
+[[package]]
+name = "nockapp"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "assert_no_alloc",
+ "async-trait",
+ "axum 0.8.4",
+ "bincode",
+ "bitvec",
+ "blake3",
+ "byteorder",
+ "bytes",
+ "chrono",
+ "clap",
+ "dirs",
+ "either",
+ "futures",
+ "getrandom 0.2.16",
+ "gnort",
+ "ibig",
+ "intmap",
+ "nockvm",
+ "nockvm_macros",
+ "opentelemetry",
+ "opentelemetry-otlp",
+ "opentelemetry_sdk",
+ "rand 0.8.5",
+ "serde",
+ "tempfile",
+ "termimad",
+ "thiserror 2.0.12",
+ "tokio",
+ "tokio-util",
+ "tonic",
+ "tracing",
+ "tracing-opentelemetry",
+ "tracing-subscriber",
+ "tracing-test",
+ "yaque",
+]
+
+[[package]]
+name = "nockchain"
+version = "0.1.0"
+dependencies = [
+ "bitcoincore-rpc",
+ "bs58",
+ "clap",
+ "equix",
+ "hoonc",
+ "kernels",
+ "libp2p",
+ "nockapp",
+ "nockchain-bitcoin-sync",
+ "nockchain-libp2p-io",
+ "nockvm",
+ "nockvm_macros",
+ "tempfile",
+ "termcolor",
+ "tokio",
+ "tracing",
+ "tracing-test",
+ "vergen",
+ "zkvm-jetpack",
+]
+
+[[package]]
+name = "nockchain-bitcoin-sync"
+version = "0.1.0"
+dependencies = [
+ "bitcoincore-rpc",
+ "ibig",
+ "nockapp",
+ "nockvm",
+ "nockvm_macros",
+ "tokio",
+ "tracing",
+ "zkvm-jetpack",
+]
+
+[[package]]
+name = "nockchain-libp2p-io"
+version = "0.1.0"
+dependencies = [
+ "bs58",
+ "bytes",
+ "either",
+ "equix",
+ "futures",
+ "gnort",
+ "hickory-proto",
+ "hickory-resolver",
+ "ibig",
+ "libp2p",
+ "nockapp",
+ "nockvm",
+ "nockvm_macros",
+ "serde",
+ "serde_bytes",
+ "tokio",
+ "tracing",
+ "void",
+]
+
+[[package]]
+name = "nockchain-wallet"
+version = "0.1.0"
+dependencies = [
+ "bardecoder",
+ "clap",
+ "crossterm 0.28.1",
+ "either",
+ "getrandom 0.2.16",
+ "image",
+ "kernels",
+ "nockapp",
+ "nockvm",
+ "nockvm_macros",
+ "qrcode",
+ "ratatui",
+ "tempfile",
+ "termimad",
+ "thiserror 2.0.12",
+ "tokio",
+ "tracing",
+ "tracing-subscriber",
+ "zkvm-jetpack",
+]
+
+[[package]]
+name = "nockvm"
+version = "0.1.0"
+dependencies = [
+ "assert_no_alloc",
+ "autotools",
+ "bitvec",
+ "cc",
+ "criterion",
+ "either",
+ "ibig",
+ "intmap",
+ "json",
+ "lazy_static",
+ "libc",
+ "memmap2",
+ "murmur3",
+ "nockvm_crypto",
+ "nockvm_macros",
+ "num-derive",
+ "num-traits",
+ "rand 0.8.5",
+ "signal-hook",
+ "slotmap",
+ "static_assertions",
+ "thiserror 2.0.12",
+ "tracing",
+]
+
+[[package]]
+name = "nockvm_crypto"
+version = "0.1.0"
+dependencies = [
+ "aes",
+ "aes-siv",
+ "assert_no_alloc",
+ "curve25519-dalek",
+ "ed25519-dalek",
+ "ibig",
+ "sha1",
+ "sha2",
+ "x25519-dalek",
+]
+
+[[package]]
+name = "nockvm_macros"
+version = "0.1.0"
+dependencies = [
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "nonzero_ext"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
+
+[[package]]
+name = "notify"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486"
+dependencies = [
+ "bitflags 1.3.2",
+ "crossbeam-channel",
+ "filetime",
+ "fsevent-sys",
+ "inotify",
+ "kqueue",
+ "libc",
+ "mio 0.8.11",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi 0.3.9",
+ "libc",
+]
+
+[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "oid-registry"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7"
+dependencies = [
+ "asn1-rs",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "oorandom"
+version = "11.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
+
+[[package]]
+name = "opentelemetry"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab70038c28ed37b97d8ed414b6429d343a8bbf44c9f79ec854f3a643029ba6d7"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "js-sys",
+ "pin-project-lite",
+ "thiserror 1.0.69",
+ "tracing",
+]
+
+[[package]]
+name = "opentelemetry-http"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "http 1.3.1",
+ "opentelemetry",
+ "reqwest",
+]
+
+[[package]]
+name = "opentelemetry-otlp"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76"
+dependencies = [
+ "async-trait",
+ "futures-core",
+ "http 1.3.1",
+ "opentelemetry",
+ "opentelemetry-http",
+ "opentelemetry-proto",
+ "opentelemetry_sdk",
+ "prost",
+ "reqwest",
+ "thiserror 1.0.69",
+ "tokio",
+ "tonic",
+ "tracing",
+]
+
+[[package]]
+name = "opentelemetry-proto"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6"
+dependencies = [
+ "opentelemetry",
+ "opentelemetry_sdk",
+ "prost",
+ "tonic",
+]
+
+[[package]]
+name = "opentelemetry_sdk"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8"
+dependencies = [
+ "async-trait",
+ "futures-channel",
+ "futures-executor",
+ "futures-util",
+ "glob",
+ "opentelemetry",
+ "percent-encoding",
+ "rand 0.8.5",
+ "serde_json",
+ "thiserror 1.0.69",
+ "tokio",
+ "tokio-stream",
+ "tracing",
+]
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "password-hash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
+dependencies = [
+ "base64ct",
+ "rand_core 0.6.4",
+ "subtle",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pem"
+version = "3.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3"
+dependencies = [
+ "base64 0.22.1",
+ "serde",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "plotters"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
+dependencies = [
+ "num-traits",
+ "plotters-backend",
+ "plotters-svg",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "plotters-backend"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
+dependencies = [
+ "plotters-backend",
+]
+
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "polling"
+version = "3.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi 0.4.0",
+ "pin-project-lite",
+ "rustix 0.38.44",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "portable-atomic"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
+
+[[package]]
+name = "potential_utf"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.2.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "proc-macro-error-attr2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "proc-macro-error2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
+dependencies = [
+ "proc-macro-error-attr2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prometheus-client"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf41c1a7c32ed72abe5082fb19505b969095c12da9f5732a4bc9878757fd087c"
+dependencies = [
+ "dtoa",
+ "itoa",
+ "parking_lot",
+ "prometheus-client-derive-encode",
+]
+
+[[package]]
+name = "prometheus-client-derive-encode"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "prost"
+version = "0.13.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.13.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
+dependencies = [
+ "anyhow",
+ "itertools 0.14.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "qrcode"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "166f136dfdb199f98186f3649cf7a0536534a61417a1a30221b492b4fb60ce3f"
+dependencies = [
+ "image",
+]
+
+[[package]]
+name = "quanta"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e"
+dependencies = [
+ "crossbeam-utils",
+ "libc",
+ "once_cell",
+ "raw-cpuid",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "quick-protobuf"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "quick-protobuf-codec"
+version = "0.3.1"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "asynchronous-codec",
+ "bytes",
+ "quick-protobuf",
+ "thiserror 2.0.12",
+ "unsigned-varint",
+]
+
+[[package]]
+name = "quickcheck"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
+dependencies = [
+ "env_logger",
+ "log",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "quinn"
+version = "0.11.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "futures-io",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash 2.1.1",
+ "rustls",
+ "socket2",
+ "thiserror 2.0.12",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e"
+dependencies = [
+ "bytes",
+ "getrandom 0.3.3",
+ "lru-slab",
+ "rand 0.9.1",
+ "ring",
+ "rustc-hash 2.1.1",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.12",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+
+[[package]]
+name = "radium"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
+dependencies = [
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.3",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.9.3",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+dependencies = [
+ "getrandom 0.3.3",
+]
+
+[[package]]
+name = "ratatui"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
+dependencies = [
+ "bitflags 2.9.1",
+ "cassowary",
+ "compact_str",
+ "crossterm 0.28.1",
+ "indoc",
+ "instability",
+ "itertools 0.13.0",
+ "lru",
+ "paste",
+ "strum",
+ "unicode-segmentation",
+ "unicode-truncate",
+ "unicode-width 0.2.0",
+]
+
+[[package]]
+name = "raw-cpuid"
+version = "11.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146"
+dependencies = [
+ "bitflags 2.9.1",
+]
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "rcgen"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2"
+dependencies = [
+ "pem",
+ "ring",
+ "rustls-pki-types",
+ "time",
+ "yasna",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
+dependencies = [
+ "bitflags 2.9.1",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
+dependencies = [
+ "getrandom 0.2.16",
+ "libredox",
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "reqwest"
+version = "0.12.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http 1.3.1",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower 0.5.2",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "windows-registry",
+]
+
+[[package]]
+name = "resolv-conf"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3"
+
+[[package]]
+name = "retry"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1e211f878258887b3e65dd3c8ff9f530fe109f441a117ee0cdc27f341355032"
+dependencies = [
+ "rand 0.9.1",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.16",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rtnetlink"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a552eb82d19f38c3beed3f786bd23aa434ceb9ac43ab44419ca6d67a7e186c0"
+dependencies = [
+ "futures",
+ "log",
+ "netlink-packet-core",
+ "netlink-packet-route",
+ "netlink-packet-utils",
+ "netlink-proto",
+ "netlink-sys",
+ "nix",
+ "thiserror 1.0.69",
+ "tokio",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc-hash"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
+[[package]]
+name = "rustc_version"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
+dependencies = [
+ "semver 0.1.20",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver 1.0.26",
+]
+
+[[package]]
+name = "rusticata-macros"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+dependencies = [
+ "bitflags 2.9.1",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
+dependencies = [
+ "bitflags 2.9.1",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.9.4",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
+dependencies = [
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
+dependencies = [
+ "web-time",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+
+[[package]]
+name = "rw-stream-sink"
+version = "0.4.0"
+source = "git+https://github.com/libp2p/rust-libp2p.git?rev=da0017ee887a868e231ed78c7de892779c17800d#da0017ee887a868e231ed78c7de892779c17800d"
+dependencies = [
+ "futures",
+ "pin-project",
+ "static_assertions",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "secp256k1"
+version = "0.29.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
+dependencies = [
+ "bitcoin_hashes",
+ "rand 0.8.5",
+ "secp256k1-sys",
+ "serde",
+]
+
+[[package]]
+name = "secp256k1-sys"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "semver"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
+
+[[package]]
+name = "semver"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_bytes"
+version = "0.11.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.140"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "serde_test"
+version = "1.0.177"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signal-hook"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-mio"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
+dependencies = [
+ "libc",
+ "mio 1.0.3",
+ "signal-hook",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
+
+[[package]]
+name = "socket2"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "spinning_top"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strict"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f42444fea5b87a39db4218d9422087e66a85d0e7a0963a439b07bcdf91804006"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "strum"
+version = "0.26.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
+dependencies = [
+ "strum_macros",
+]
+
+[[package]]
+name = "strum_macros"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.28.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c2f3ca6693feb29a89724516f016488e9aafc7f37264f898593ee4b942f31b"
+dependencies = [
+ "cfg-if",
+ "core-foundation-sys",
+ "libc",
+ "ntapi",
+ "once_cell",
+ "winapi",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.30.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3"
+dependencies = [
+ "cfg-if",
+ "core-foundation-sys",
+ "libc",
+ "ntapi",
+ "once_cell",
+ "windows 0.52.0",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.33.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+ "memchr",
+ "ntapi",
+ "rayon",
+ "windows 0.57.0",
+]
+
+[[package]]
+name = "system-configuration"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
+dependencies = [
+ "bitflags 2.9.1",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "tagptr"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
+
+[[package]]
+name = "tap"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
+
+[[package]]
+name = "tempfile"
+version = "3.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
+dependencies = [
+ "fastrand",
+ "getrandom 0.3.3",
+ "once_cell",
+ "rustix 1.0.7",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "termimad"
+version = "0.31.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7301d9c2c4939c97f25376b70d3c13311f8fefdee44092fc361d2a98adc2cbb6"
+dependencies = [
+ "coolor",
+ "crokey",
+ "crossbeam",
+ "lazy-regex",
+ "minimad",
+ "serde",
+ "thiserror 2.0.12",
+ "unicode-width 0.1.14",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl 2.0.12",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tiff"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
+[[package]]
+name = "time"
+version = "0.3.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
+dependencies = [
+ "deranged",
+ "itoa",
+ "libc",
+ "num-conv",
+ "num_threads",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
+
+[[package]]
+name = "time-macros"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio 1.0.3",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "hashbrown 0.15.3",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tonic"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
+dependencies = [
+ "async-stream",
+ "async-trait",
+ "axum 0.7.9",
+ "base64 0.22.1",
+ "bytes",
+ "h2",
+ "http 1.3.1",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-timeout",
+ "hyper-util",
+ "percent-encoding",
+ "pin-project",
+ "prost",
+ "socket2",
+ "tokio",
+ "tokio-stream",
+ "tower 0.4.13",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "indexmap 1.9.3",
+ "pin-project",
+ "pin-project-lite",
+ "rand 0.8.5",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-opentelemetry"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a971f6058498b5c0f1affa23e7ea202057a7301dbff68e968b2d578bcbd053"
+dependencies = [
+ "js-sys",
+ "once_cell",
+ "opentelemetry",
+ "opentelemetry_sdk",
+ "smallvec",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+ "tracing-subscriber",
+ "web-time",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "tracing-test"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68"
+dependencies = [
+ "tracing-core",
+ "tracing-subscriber",
+ "tracing-test-macro",
+]
+
+[[package]]
+name = "tracing-test-macro"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568"
+dependencies = [
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "typenum"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
+
+[[package]]
+name = "uint"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e"
+dependencies = [
+ "byteorder",
+ "crunchy",
+ "hex",
+ "static_assertions",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "unicode-truncate"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
+dependencies = [
+ "itertools 0.13.0",
+ "unicode-segmentation",
+ "unicode-width 0.1.14",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+
+[[package]]
+name = "unicode-width"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
+[[package]]
+name = "unsigned-varint"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "unty"
+version = "0.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
+
+[[package]]
+name = "url"
+version = "2.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "uuid"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
+dependencies = [
+ "getrandom 0.3.3",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "vergen"
+version = "8.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566"
+dependencies = [
+ "anyhow",
+ "cargo_metadata",
+ "cfg-if",
+ "regex",
+ "rustc_version 0.4.1",
+ "rustversion",
+ "sysinfo 0.30.13",
+ "time",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "virtue"
+version = "0.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
+
+[[package]]
+name = "visibility"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "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 = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
+
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix 0.38.44",
+]
+
+[[package]]
+name = "widestring"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
+
+[[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-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
+dependencies = [
+ "windows-core 0.52.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538"
+dependencies = [
+ "windows-core 0.53.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
+dependencies = [
+ "windows-core 0.57.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows"
+version = "0.61.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
+dependencies = [
+ "windows-collections",
+ "windows-core 0.61.2",
+ "windows-future",
+ "windows-link",
+ "windows-numerics",
+]
+
+[[package]]
+name = "windows-collections"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
+dependencies = [
+ "windows-core 0.61.2",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd"
+dependencies = [
+ "windows-result 0.1.2",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
+dependencies = [
+ "windows-implement 0.57.0",
+ "windows-interface 0.57.0",
+ "windows-result 0.1.2",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
+dependencies = [
+ "windows-implement 0.60.0",
+ "windows-interface 0.59.1",
+ "windows-link",
+ "windows-result 0.3.4",
+ "windows-strings 0.4.2",
+]
+
+[[package]]
+name = "windows-future"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
+dependencies = [
+ "windows-core 0.61.2",
+ "windows-link",
+ "windows-threading",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+
+[[package]]
+name = "windows-numerics"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
+dependencies = [
+ "windows-core 0.61.2",
+ "windows-link",
+]
+
+[[package]]
+name = "windows-registry"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
+dependencies = [
+ "windows-result 0.3.4",
+ "windows-strings 0.3.1",
+ "windows-targets 0.53.0",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
+dependencies = [
+ "windows_aarch64_gnullvm 0.53.0",
+ "windows_aarch64_msvc 0.53.0",
+ "windows_i686_gnu 0.53.0",
+ "windows_i686_gnullvm 0.53.0",
+ "windows_i686_msvc 0.53.0",
+ "windows_x86_64_gnu 0.53.0",
+ "windows_x86_64_gnullvm 0.53.0",
+ "windows_x86_64_msvc 0.53.0",
+]
+
+[[package]]
+name = "windows-threading"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+
+[[package]]
+name = "winreg"
+version = "0.50.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags 2.9.1",
+]
+
+[[package]]
+name = "writeable"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
+
+[[package]]
+name = "wyz"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
+dependencies = [
+ "tap",
+]
+
+[[package]]
+name = "x25519-dalek"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
+dependencies = [
+ "curve25519-dalek",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "x509-parser"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460"
+dependencies = [
+ "asn1-rs",
+ "data-encoding",
+ "der-parser",
+ "lazy_static",
+ "nom",
+ "oid-registry",
+ "rusticata-macros",
+ "thiserror 2.0.12",
+ "time",
+]
+
+[[package]]
+name = "xml-rs"
+version = "0.8.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda"
+
+[[package]]
+name = "xmltree"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb"
+dependencies = [
+ "xml-rs",
+]
+
+[[package]]
+name = "yaque"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "487f1a92dacd945cc5bc78a8193cc00b9a2cce3c07746ca51533f513843f40d2"
+dependencies = [
+ "futures",
+ "lazy_static",
+ "log",
+ "notify",
+ "rand 0.8.5",
+ "semver 1.0.26",
+ "sysinfo 0.28.4",
+]
+
+[[package]]
+name = "yasna"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
+dependencies = [
+ "time",
+]
+
+[[package]]
+name = "yoke"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+
+[[package]]
+name = "zerotrie"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "zkvm-jetpack"
+version = "0.1.0"
+dependencies = [
+ "argon2",
+ "arrayref",
+ "bytes",
+ "either",
+ "hex-literal",
+ "ibig",
+ "nockapp",
+ "nockvm",
+ "nockvm_macros",
+ "num-traits",
+ "quickcheck",
+ "smallvec",
+ "tracing",
+]
+
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]

+ 0 - 1
Cargo.toml

@@ -7,7 +7,6 @@ members = [
     "crates/nockchain-bitcoin-sync",
     "crates/nockchain-libp2p-io",
     "crates/nockchain",
-    "crates/nockvm/rust/assert_no_alloc",
     "crates/nockvm/rust/ibig",
     "crates/nockvm/rust/murmur3",
     "crates/nockvm/rust/nockvm_macros",

+ 8 - 4
Makefile

@@ -5,8 +5,7 @@ include .env
 export RUST_BACKTRACE ?= full
 export RUST_LOG ?= info,nockchain=debug,nockchain_libp2p_io=info,libp2p=info,libp2p_quic=info
 export MINIMAL_LOG_FORMAT ?= true
-export MINING_PUBKEY ?= EHmKL2U3vXfS5GYAY5aVnGdukfDWwvkQPCZXnjvZVShsSQi3UAuA4tQQpVwGJMzc9FfpTY8pLDkqhBGfWutiF4prrCktUH9oAWJxkXQBzAavKDc95NR3DjmYwnnw8GuugnK
-
+export MINING_PUBKEY ?= 2qwq9dQRZfpFx8BDicghpMRnYGKZsZGxxhh9m362pzpM9aeo276pR1yHZPS41y3CW3vPKxeYM8p8fzZS8GXmDGzmNNCnVNekjrSYogqfEFMqwhHh5iCjaKPaDTwhupWqiXj6
 export
 
 .PHONY: build
@@ -65,7 +64,7 @@ build-trivial: ensure-dirs
 	echo '%trivial' > hoon/trivial.hoon
 	hoonc --arbitrary hoon/trivial.hoon
 
-HOON_TARGETS=assets/dumb.jam assets/wal.jam
+HOON_TARGETS=assets/dumb.jam assets/wal.jam assets/miner.jam
 
 .PHONY: nuke-hoonc-data
 nuke-hoonc-data:
@@ -89,11 +88,16 @@ run-nockchain-leader:  # Run nockchain node in leader mode
 	$(call show_env_vars)
 	mkdir -p test-leader && cd test-leader && rm -f nockchain.sock && RUST_BACKTRACE=1 cargo run --release --bin nockchain -- --fakenet --genesis-leader --npc-socket nockchain.sock --mining-pubkey $(MINING_PUBKEY) --bind /ip4/0.0.0.0/udp/3005/quic-v1 --peer /ip4/127.0.0.1/udp/3006/quic-v1 --new-peer-id --no-default-peers
 
-.PHONY: run-nockchain
+.PHONY: run-nockchain-follower
 run-nockchain:  # Run a nockchain node in follower mode with a mining pubkey
 	$(call show_env_vars)
 	mkdir -p miner-node && cd miner-node && rm -f nockchain.sock && RUST_BACKTRACE=1 cargo run --release --bin nockchain -- --fakenet --genesis-watcher --npc-socket nockchain.sock --mining-pubkey $(MINING_PUBKEY) --bind /ip4/0.0.0.0/udp/3006/quic-v1 --peer /ip4/127.0.0.1/udp/3005/quic-v1 --new-peer-id --no-default-peers
 
+.PHONY: run-nockchain
+run-nockchain:  # Run a nockchain node in follower mode with a mining pubkey
+	$(call show_env_vars)
+	mkdir -p miner-node && cd miner-node && rm -f nockchain.sock && RUST_BACKTRACE=1 cargo run --release --bin nockchain -- --npc-socket nockchain.sock --mining-pubkey $(MINING_PUBKEY) --bind /ip4/0.0.0.0/udp/3006/quic-v1
+
 HOON_SRCS := $(find hoon -type file -name '*.hoon')
 
 ## Build dumb.jam with hoonc

+ 16 - 0
README.md

@@ -94,6 +94,22 @@ To run a Nockchain miner:
 make run-nockchain
 ```
 
+=======
+To run a Nockchain node without mining:
+
+```
+nockchain
+```
+
+To run a Nockchain node and mine to a pubkey:
+
+```
+nockchain --mining_pubkey <your_pubkey> --mine
+```
+
+For launch, make sure you run in a fresh working directory that does not include a .data.nockchain file from testing.
+
+
 ## FAQ
 
 ### Can I use same pubkey if running multiple miners?

+ 10 - 4
crates/hoonc/Cargo.toml

@@ -4,18 +4,24 @@ version = "0.2.0"
 edition.workspace = true
 
 [dependencies]
-nockapp = { workspace = true }
+bincode = { workspace = true }
+blake3 = { workspace = true }
+bytes = { workspace = true }
+clap = { workspace = true, features = ["derive", "cargo", "color", "env"] }
 dirs = { workspace = true }
+futures = { workspace = true }
+nockapp = { workspace = true }
 nockvm = { workspace = true }
 nockvm_macros = { workspace = true }
-clap = { workspace = true, features = ["derive", "cargo", "color", "env"] }
-futures = { workspace = true }
 tempfile = { workspace = true }
+thiserror = { workspace = true }
 tokio = { workspace = true }
 tracing = { workspace = true }
-bytes = { workspace = true }
+tracing-subscriber = { workspace = true }
+# tracing-tracy = { workspace = true, features = ["flush-on-exit"] }
 walkdir = "2.5.0"
 
 [[bin]]
 name = "hoonc"
 path = "src/main.rs"
+

+ 10 - 7
crates/hoonc/src/lib.rs

@@ -1,19 +1,19 @@
-use clap::{arg, command, ColorChoice, Parser};
-use nockvm::interpreter::{self, Context};
 use std::env::current_dir;
 use std::ffi::OsStr;
 use std::path::{Path, PathBuf};
-use tokio::fs::{self, File};
-use tokio::io::AsyncReadExt;
-use tracing::{debug, info, instrument, trace};
-use walkdir::{DirEntry, WalkDir};
 
+use clap::{arg, command, ColorChoice, Parser};
 use nockapp::driver::Operation;
 use nockapp::kernel::boot::{self, default_boot_cli, Cli as BootCli};
 use nockapp::noun::slab::NounSlab;
 use nockapp::{system_data_dir, AtomExt, Noun, NounExt};
+use nockvm::interpreter::{self, Context};
 use nockvm::noun::{Atom, D, T};
 use nockvm_macros::tas;
+use tokio::fs::{self, File};
+use tokio::io::AsyncReadExt;
+use tracing::{debug, info, instrument, trace};
+use walkdir::{DirEntry, WalkDir};
 
 pub const OUT_JAM_NAME: &str = "out.jam";
 
@@ -28,6 +28,9 @@ pub struct ChooCli {
     #[command(flatten)]
     pub boot: BootCli,
 
+    //  TODO: REPRODUCIBILITY:
+    //  make entry path relative to the dependency directory
+    //  we may have to go back to requiring that the entry exists in the dependency directory
     #[arg(help = "Path to file to compile")]
     pub entry: std::path::PathBuf,
 
@@ -340,7 +343,7 @@ pub fn canonicalize_and_string(path: &std::path::Path) -> String {
 
 /// Run the build and verify the output file, used to build files outside of cli.
 pub async fn run_build(
-    nockapp: nockapp::NockApp,
+    mut nockapp: nockapp::NockApp,
     out_path: Option<PathBuf>,
 ) -> Result<Vec<u8>, Error> {
     nockapp.run().await?;

+ 6 - 2
crates/hoonc/src/main.rs

@@ -1,6 +1,6 @@
-use hoonc::*;
 use clap::Parser;
 use futures::FutureExt;
+use hoonc::*;
 use nockapp::kernel::boot;
 use nockvm::mem::{AllocationError, NewStackError};
 
@@ -9,8 +9,12 @@ async fn main() -> Result<(), Error> {
     let cli = ChooCli::parse();
 
     boot::init_default_tracing(&cli.boot.clone());
+    // use tracing_subscriber::layer::SubscriberExt;
+    // tracing::subscriber::set_global_default(
+    //     tracing_subscriber::registry().with(tracing_tracy::TracyLayer::default()),
+    // );
     let result = std::panic::AssertUnwindSafe(async {
-        let (nockapp, _) = initialize_hoonc(cli).await?;
+        let (mut nockapp, _) = initialize_hoonc(cli).await?;
         nockapp.run().await?;
         Ok::<(), Error>(())
     })

+ 1 - 0
crates/kernels/Cargo.toml

@@ -12,3 +12,4 @@ bazel_build = []
 # Specific kernels
 dumb = []
 wallet = []
+miner = []

+ 3 - 0
crates/kernels/src/lib.rs

@@ -3,3 +3,6 @@ pub mod wallet;
 
 #[cfg(feature = "dumb")]
 pub mod dumb;
+
+#[cfg(feature = "miner")]
+pub mod miner;

+ 8 - 0
crates/kernels/src/miner.rs

@@ -0,0 +1,8 @@
+#[cfg(feature = "bazel_build")]
+pub static KERNEL: &[u8] = include_bytes!(env!("MINER_JAM_PATH"));
+
+#[cfg(not(feature = "bazel_build"))]
+pub const KERNEL: &[u8] = include_bytes!(concat!(
+    env!("CARGO_MANIFEST_DIR"),
+    "/../../assets/miner.jam"
+));

+ 8 - 6
crates/nockapp/Cargo.toml

@@ -11,6 +11,7 @@ bazel_build = []
 
 [dependencies]
 anyhow = { workspace = true }
+async-trait = { workspace = true }
 axum = { workspace = true }
 bitvec = { workspace = true }
 blake3 = { workspace = true }
@@ -19,11 +20,11 @@ dirs = { workspace = true }
 ibig = { workspace = true }
 nockvm = { workspace = true }
 nockvm_macros = { workspace = true }
-assert_no_alloc = { workspace = true }
-async-trait = { workspace = true }
+
 bincode = { workspace = true, features = ["serde"] }
 byteorder = { workspace = true }
 bytes = { workspace = true, features = ["serde"] }
+chrono = { workspace = true }
 either = { workspace = true }
 futures = { workspace = true }
 getrandom = { workspace = true }
@@ -31,16 +32,17 @@ gnort = { workspace = true }
 intmap = { workspace = true }
 rand = { workspace = true }
 serde = { workspace = true }
+signal-hook = { workspace = true }
+signal-hook-tokio = { workspace = true, features = ["futures-v0_3"] }
 tempfile = { workspace = true }
 termimad = { workspace = true }
 thiserror = { workspace = true }
-tracing = { workspace = true }
-tracing-test = { workspace = true }
-tracing-subscriber = { workspace = true }
 tokio = { workspace = true, features = ["time", "sync", "signal"] }
 tokio-util = { workspace = true, features = ["rt"] }
+tracing = { workspace = true }
+tracing-subscriber = { workspace = true }
+tracing-test = { workspace = true }
 yaque = { workspace = true }
-chrono = { workspace = true }
 
 opentelemetry.workspace = true
 opentelemetry-otlp.workspace = true

+ 2 - 16
crates/nockapp/src/drivers/exit.rs

@@ -24,24 +24,10 @@ pub fn exit() -> IODriverFn {
                                     if cell.head().eq_bytes(b"exit") && cell.tail().is_atom() {
                                         // Exit with the code provided in the tail
                                         if let Ok(exit_code) = cell.tail().as_atom().and_then(|atom| atom.as_u64()) {
-                                            handle.exit.send(exit_code as usize).await.unwrap_or_else(|err| {
-                                                panic!(
-                                                    "Panicked with {err:?} at {}:{} (git sha: {:?})",
-                                                    file!(),
-                                                    line!(),
-                                                    option_env!("GIT_SHA")
-                                                )
-                                            });
+                                            handle.exit.exit(exit_code as usize).await?;
                                         } else {
                                             // Default to error code 1 if we can't get a valid exit code
-                                            handle.exit.send(1).await.unwrap_or_else(|err| {
-                                                panic!(
-                                                    "Panicked with {err:?} at {}:{} (git sha: {:?})",
-                                                    file!(),
-                                                    line!(),
-                                                    option_env!("GIT_SHA")
-                                                )
-                                            });
+                                            handle.exit.exit(1).await?;
                                         }
                                     }
                                 }

+ 4 - 2
crates/nockapp/src/drivers/npc.rs

@@ -289,6 +289,7 @@ async fn write_message(
 #[cfg(test)]
 mod tests {
     use crate::nockapp::driver::{IOAction, NockAppHandle};
+    use crate::NockAppExit;
 
     use super::*;
     use std::io::{Read, Write};
@@ -512,8 +513,9 @@ mod tests {
 
         // Create channels for driver communication
         let (tx_io, mut rx_io) = mpsc::channel(32);
-        let (tx_effect, rx_effect) = broadcast::channel(32);
-        let (tx_exit, _) = mpsc::channel(1);
+        let (tx_effect_chan, rx_effect) = broadcast::channel(32);
+        let tx_effect = Arc::new(tx_effect_chan);
+        let (tx_exit, _) = NockAppExit::new();
 
         let handle = NockAppHandle {
             io_sender: tx_io,

+ 7 - 1
crates/nockapp/src/drivers/one_punch.rs

@@ -5,7 +5,7 @@ use crate::noun::slab::NounSlab;
 use either::Either::{self, Left, Right};
 use nockvm::noun::D;
 use nockvm_macros::tas;
-use tracing::debug;
+use tracing::{debug, error};
 
 pub enum OnePunchWire {
     Poke,
@@ -114,6 +114,12 @@ async fn handle_effect(
     // Split out root bindings so they don't get dropped early
     let root = unsafe { eff.root() };
     debug!("poke_once_driver: root: {:?}", root);
+
+    if root.is_atom() {
+        error!("No effects were returned from one-shot poke.");
+        return Err(NockAppError::PokeFailed);
+    }
+
     let effect_cell = root.as_cell().unwrap_or_else(|err| {
         panic!(
             "Panicked with {err:?} at {}:{} (git sha: {:?})",

+ 168 - 87
crates/nockapp/src/kernel/form.rs

@@ -3,7 +3,7 @@ use crate::noun::slab::NounSlab;
 use blake3::{Hash, Hasher};
 use byteorder::{LittleEndian, WriteBytesExt};
 use nockvm::hamt::Hamt;
-use nockvm::interpreter::{self, interpret, Error, Mote};
+use nockvm::interpreter::{self, interpret, Error, Mote, NockCancelToken};
 use nockvm::jets::cold::{Cold, Nounable};
 use nockvm::jets::hot::{HotEntry, URBIT_HOT_STATE};
 use nockvm::jets::nock::util::mook;
@@ -12,8 +12,10 @@ use nockvm::mug::met3_usize;
 use nockvm::noun::{Atom, Cell, DirectAtom, IndirectAtom, Noun, Slots, D, T};
 use nockvm::trace::{path_to_cord, write_serf_trace_safe, TraceInfo};
 use nockvm_macros::tas;
+
 use std::any::Any;
 use std::fs::File;
+use std::future::Future;
 use std::path::PathBuf;
 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
 use std::sync::Arc;
@@ -23,7 +25,7 @@ use tracing::{debug, error, info, warn};
 use crate::kernel::checkpoint::{Checkpoint, ExportedState, JamPaths, JammedCheckpoint};
 use crate::nockapp::wire::{wire_to_noun, WireRepr};
 use crate::noun::slam;
-use crate::utils::{create_context, current_da, NOCK_STACK_SIZE};
+use crate::utils::{create_context, current_da, NOCK_STACK_SIZE, NOCK_STACK_SIZE_HUGE};
 use crate::{AtomExt, CrownError, NounExt, Result, ToBytesExt};
 use bincode::config::Configuration;
 
@@ -81,8 +83,10 @@ pub enum SerfAction {
 }
 
 pub(crate) struct SerfThread {
-    handle: std::thread::JoinHandle<()>,
+    handle: Option<std::thread::JoinHandle<()>>,
     action_sender: mpsc::Sender<SerfAction>,
+    pub cancel_token: NockCancelToken,
+    inhibit: Arc<AtomicBool>,
     /// Jam persistence buffer paths.
     pub jam_paths: Arc<JamPaths>,
     /// Buffer toggle for writing to the jam buffer.
@@ -102,6 +106,9 @@ impl SerfThread {
         let (action_sender, action_receiver) = mpsc::channel(1);
         let (buffer_toggle_sender, buffer_toggle_receiver) = oneshot::channel();
         let (event_number_sender, event_number_receiver) = oneshot::channel();
+        let (cancel_token_sender, cancel_token_receiver) = oneshot::channel();
+        let inhibit = Arc::new(AtomicBool::new(false));
+        let inhibit_clone = inhibit.clone();
         std::fs::create_dir_all(jam_paths.0.parent().unwrap_or_else(|| {
             panic!(
                 "Panicked at {}:{} (git sha: {:?})",
@@ -142,43 +149,51 @@ impl SerfThread {
                 event_number_sender
                     .send(serf.event_num.clone())
                     .expect("Could not send event number out of serf thread");
-                serf_loop(serf, action_receiver, buffer_toggle);
+                cancel_token_sender
+                    .send(serf.context.cancel_token())
+                    .expect("Could not send cancel token out of serf thread");
+                serf_loop(serf, action_receiver, buffer_toggle, inhibit_clone);
             })?;
 
         let buffer_toggle = buffer_toggle_receiver.await?;
         let event_number = event_number_receiver.await?;
+        let cancel_token = cancel_token_receiver.await?;
         Ok(SerfThread {
+            inhibit,
             buffer_toggle,
-            handle,
+            handle: Some(handle),
             action_sender,
             jam_paths: jam_paths_cloned,
             event_number,
+            cancel_token,
         })
     }
 
-    // Future which completes when the serf thread finishes. Since rust threads only support polling or blocking joining, not notification,
-    // we have to poll on a timer.
-    pub(crate) async fn finished(&self) {
-        let mut interval = tokio::time::interval(Duration::from_millis(100));
-        loop {
-            interval.tick().await;
-            if self.handle.is_finished() {
-                debug!("Serf finished");
-                break;
+    pub(crate) fn stop(&mut self) -> impl Future<Output = Result<()>> {
+        let action_sender = self.action_sender.clone();
+        let cancel_token = self.cancel_token.clone();
+        let join_handle = self.handle.take().expect("Serf join handle already taken.");
+        let tokio_join_handle = tokio::task::spawn_blocking(move || join_handle.join());
+        self.inhibit.store(true, Ordering::SeqCst);
+        async move {
+            cancel_token.cancel();
+            action_sender
+                .send(SerfAction::Stop)
+                .await
+                .expect("Failed to send stop action");
+            match tokio_join_handle.await {
+                Ok(Ok(())) => Ok(()),
+                Ok(Err(e)) => Err(CrownError::Unknown(format!("Serf thread panicked: {e:?}"))),
+                Err(e) => Err(CrownError::JoinError(e)),
             }
         }
     }
 
-    pub(crate) async fn stop(&self) {
-        self.action_sender
-            .send(SerfAction::Stop)
-            .await
-            .expect("Failed to send stop action");
-        self.finished().await;
-    }
-
-    pub(crate) fn join(self) -> Result<(), Box<dyn Any + Send + 'static>> {
-        self.handle.join()
+    pub(crate) fn join(&mut self) -> Result<(), Box<dyn Any + Send + 'static>> {
+        self.handle
+            .take()
+            .expect("Serf thread already joined")
+            .join()
     }
 
     pub(crate) async fn get_kernel_state_slab(&self) -> Result<NounSlab> {
@@ -197,24 +212,33 @@ impl SerfThread {
         Ok(result_fut.await?)
     }
 
-    pub(crate) async fn peek(&self, ovo: NounSlab) -> Result<NounSlab> {
+    pub(crate) fn peek(&self, ovo: NounSlab) -> impl Future<Output = Result<NounSlab>> {
         let (result, result_fut) = oneshot::channel();
-        self.action_sender
-            .send(SerfAction::Peek { ovo, result })
-            .await?;
-        result_fut.await?
+        let action_sender = self.action_sender.clone();
+        async move {
+            action_sender.send(SerfAction::Peek { ovo, result }).await?;
+            result_fut.await?
+        }
     }
 
-    pub(crate) async fn poke(&self, wire: WireRepr, cause: NounSlab) -> Result<NounSlab> {
+    // We are very carefully ensuring that the future does not contain the &self reference, to allow spawning a task without lifetime issues
+    pub(crate) fn poke(
+        &self,
+        wire: WireRepr,
+        cause: NounSlab,
+    ) -> impl Future<Output = Result<NounSlab>> {
         let (result, result_fut) = oneshot::channel();
-        self.action_sender
-            .send(SerfAction::Poke {
-                wire,
-                cause,
-                result,
-            })
-            .await?;
-        result_fut.await?
+        let action_sender = self.action_sender.clone();
+        async move {
+            action_sender
+                .send(SerfAction::Poke {
+                    wire,
+                    cause,
+                    result,
+                })
+                .await?;
+            result_fut.await?
+        }
     }
 
     pub(crate) fn poke_sync(&self, wire: WireRepr, cause: NounSlab) -> Result<NounSlab> {
@@ -250,12 +274,15 @@ impl SerfThread {
         result_fut.await?
     }
 
-    pub(crate) async fn checkpoint(&self) -> Result<JammedCheckpoint> {
+    pub(crate) fn checkpoint(&self) -> impl Future<Output = Result<JammedCheckpoint>> {
         let (result, result_fut) = oneshot::channel();
-        self.action_sender
-            .send(SerfAction::Checkpoint { result })
-            .await?;
-        Ok(result_fut.await?)
+        let action_sender = self.action_sender.clone();
+        async move {
+            action_sender
+                .send(SerfAction::Checkpoint { result })
+                .await?;
+            Ok(result_fut.await?)
+        }
     }
 }
 
@@ -269,6 +296,7 @@ fn serf_loop(
     mut serf: Serf,
     mut action_receiver: mpsc::Receiver<SerfAction>,
     buffer_toggle: Arc<AtomicBool>,
+    inhibit: Arc<AtomicBool>,
 ) {
     loop {
         let Some(action) = action_receiver.blocking_recv() else {
@@ -277,16 +305,20 @@ fn serf_loop(
         match action {
             SerfAction::LoadState { state, result } => {
                 let res = load_state_from_bytes(&mut serf, &state);
-                result.send(res).expect("Could not send load state result");
+                let _ = result.send(res).map_err(|e| {
+                    debug!("Could not send load state result");
+                    e
+                });
             }
             SerfAction::Stop => {
                 break;
             }
             SerfAction::GetStateBytes { result } => {
                 let state_bytes = create_state_bytes(&mut serf);
-                result
-                    .send(state_bytes)
-                    .expect("Could not send state bytes");
+                let _ = result.send(state_bytes).map_err(|e| {
+                    debug!("Could not send state bytes to dropped channel");
+                    e
+                });
             }
             SerfAction::GetKernelStateSlab { result } => {
                 let kernel_state_noun = serf.arvo.slot(STATE_AXIS);
@@ -298,9 +330,10 @@ fn serf_loop(
                         Ok(slab)
                     },
                 );
-                result
-                    .send(kernel_state_slab)
-                    .expect("Could not send kernel state slab");
+                let _ = result.send(kernel_state_slab).map_err(|e| {
+                    debug!("Tried to send to dropped result channel");
+                    e
+                });
             }
             SerfAction::GetColdStateSlab { result } => {
                 let cold_state_noun = serf.context.cold.into_noun(serf.stack());
@@ -309,41 +342,67 @@ fn serf_loop(
                     slab.copy_into(cold_state_noun);
                     slab
                 };
-                result
-                    .send(cold_state_slab)
-                    .expect("Could not send cold state slab");
+                let _ = result.send(cold_state_slab).map_err(|e| {
+                    debug!("Could not send cold state to dropped channel.");
+                    e
+                });
             }
             SerfAction::Checkpoint { result } => {
                 let checkpoint = create_checkpoint(&mut serf, buffer_toggle.clone());
-                result.send(checkpoint).expect("Could not send checkpoint");
+                //result.send(checkpoint).expect("Could not send checkpoint");
+                if result.send(checkpoint).is_err() {
+                    debug!(
+                        "Checkpoint receiver dropped before receiving result - likely timed out"
+                    );
+                }
             }
             SerfAction::Peek { ovo, result } => {
-                let ovo_noun = ovo.copy_to_stack(serf.stack());
-                let noun_res = serf.peek(ovo_noun);
-                let noun_slab_res = noun_res.map(|noun| {
-                    let mut slab = NounSlab::new();
-                    slab.copy_into(noun);
-                    slab
-                });
-                result
-                    .send(noun_slab_res)
-                    .expect("Failed to send peek result from serf thread");
+                if inhibit.load(Ordering::SeqCst) {
+                    let _ = result
+                        .send(Err(CrownError::Unknown("Serf stopping".to_string())))
+                        .map_err(|e| {
+                            debug!("Tried to send inhibited peek state to dropped channel");
+                            e
+                        });
+                } else {
+                    let ovo_noun = ovo.copy_to_stack(serf.stack());
+                    let noun_res = serf.peek(ovo_noun);
+                    let noun_slab_res = noun_res.map(|noun| {
+                        let mut slab = NounSlab::new();
+                        slab.copy_into(noun);
+                        slab
+                    });
+                    let _ = result.send(noun_slab_res).map_err(|e| {
+                        debug!("Tried to send peek state to dropped channel");
+                        e
+                    });
+                }
             }
             SerfAction::Poke {
                 wire,
                 cause,
                 result,
             } => {
-                let cause_noun = cause.copy_to_stack(serf.stack());
-                let noun_res = serf.poke(wire, cause_noun);
-                let noun_slab_res = noun_res.map(|noun| {
-                    let mut slab = NounSlab::new();
-                    slab.copy_into(noun);
-                    slab
-                });
-                result
-                    .send(noun_slab_res)
-                    .expect("Failed to send poke result from serf thread");
+                if inhibit.load(Ordering::SeqCst) {
+                    let _ = result
+                        .send(Err(CrownError::Unknown("Serf stopping".to_string())))
+                        .map_err(|e| {
+                            debug!("Failed to send inihibited poke result from serf thread");
+                            e
+                        });
+                } else {
+                    let cause_noun = cause.copy_to_stack(serf.stack());
+                    let noun_res = serf.poke(wire, cause_noun);
+                    let noun_slab_res = noun_res.map(|noun| {
+                        let mut slab = NounSlab::new();
+                        slab.copy_into(noun);
+                        slab
+                    });
+                    let _ = result.send(noun_slab_res).map_err(|e| {
+                        debug!("Failed to send poke result from serf thread");
+                        e
+                    });
+                }
             }
         }
     }
@@ -501,8 +560,6 @@ pub struct Kernel {
     pub(crate) serf: SerfThread,
     /// Directory path for storing pma snapshots.
     pma_dir: Arc<PathBuf>,
-    /// Atomic flag for terminating the kernel.
-    pub terminator: Arc<AtomicBool>,
 }
 
 impl Kernel {
@@ -533,11 +590,30 @@ impl Kernel {
             NOCK_STACK_SIZE, jam_paths_arc, kernel_vec, hot_state_vec, trace,
         )
         .await?;
-        let terminator = Arc::new(AtomicBool::new(false));
         Ok(Self {
             serf,
             pma_dir: pma_dir_arc,
-            terminator,
+        })
+    }
+
+    pub async fn load_with_hot_state_huge(
+        pma_dir: PathBuf,
+        jam_paths: JamPaths,
+        kernel: &[u8],
+        hot_state: &[HotEntry],
+        trace: bool,
+    ) -> Result<Self> {
+        let jam_paths_arc = Arc::new(jam_paths);
+        let kernel_vec = Vec::from(kernel);
+        let hot_state_vec = Vec::from(hot_state);
+        let pma_dir_arc = Arc::new(pma_dir);
+        let serf = SerfThread::new(
+            NOCK_STACK_SIZE_HUGE, jam_paths_arc, kernel_vec, hot_state_vec, trace,
+        )
+        .await?;
+        Ok(Self {
+            serf,
+            pma_dir: pma_dir_arc,
         })
     }
 
@@ -591,13 +667,13 @@ impl Kernel {
     }
 
     /// Produces a checkpoint of the kernel state.
-    pub async fn checkpoint(&self) -> Result<JammedCheckpoint> {
-        self.serf.checkpoint().await
+    pub fn checkpoint(&self) -> impl Future<Output = Result<JammedCheckpoint>> {
+        self.serf.checkpoint()
     }
 
-    #[tracing::instrument(name = "nockapp::Kernel::poke", skip(self, cause))]
-    pub async fn poke(&self, wire: WireRepr, cause: NounSlab) -> Result<NounSlab> {
-        self.serf.poke(wire, cause).await
+    // We are very carefully ensuring the future does not contain the "self" reference to ensure no lifetime issues when spawning tasks
+    pub fn poke(&self, wire: WireRepr, cause: NounSlab) -> impl Future<Output = Result<NounSlab>> {
+        self.serf.poke(wire, cause)
     }
 
     pub fn poke_sync(&self, wire: WireRepr, cause: NounSlab) -> Result<NounSlab> {
@@ -608,9 +684,10 @@ impl Kernel {
         self.serf.peek_sync(ovo)
     }
 
-    #[tracing::instrument(name = "nockapp::Kernel::peek", skip_all)]
-    pub async fn peek(&mut self, ovo: NounSlab) -> Result<NounSlab> {
-        self.serf.peek(ovo).await
+    // We are very carefully ensuring the future does not contain the "self" reference to ensure no lifetime issues when spawning tasks
+    #[tracing::instrument(name = "crown::Kernel::peek", skip_all)]
+    pub(crate) fn peek(&self, ovo: NounSlab) -> impl Future<Output = Result<NounSlab>> {
+        self.serf.peek(ovo)
     }
 
     pub async fn create_state_bytes(&self) -> Result<Vec<u8>> {
@@ -629,6 +706,8 @@ pub struct Serf {
     pub arvo: Noun,
     /// The interpreter context.
     pub context: interpreter::Context,
+    /// Cancellation
+    pub cancel_token: NockCancelToken,
     /// The current event number.
     pub event_num: Arc<AtomicU64>,
 }
@@ -677,6 +756,7 @@ impl Serf {
         };
 
         let mut context = create_context(stack, &hot_state, cold, trace_info);
+        let cancel_token = context.cancel_token();
 
         let version = checkpoint
             .as_ref()
@@ -721,6 +801,7 @@ impl Serf {
             arvo,
             context,
             event_num,
+            cancel_token,
         };
 
         if let Some(checkpoint) = checkpoint {
@@ -1050,7 +1131,7 @@ impl Serf {
     ///
     /// Result containing the poke response or an error.
     #[tracing::instrument(level = "info", skip_all, fields(
-        wire_source = wire.source
+        src = wire.source
     ))]
     pub fn poke(&mut self, wire: WireRepr, cause: Noun) -> Result<Noun> {
         let random_bytes = rand::random::<u64>();

+ 4 - 2
crates/nockapp/src/nockapp/driver.rs

@@ -1,12 +1,14 @@
 use crate::noun::slab::NounSlab;
 use futures::future::Future;
 use std::pin::Pin;
+use std::sync::Arc;
 use tokio::sync::{broadcast, mpsc, oneshot, Mutex};
 use tokio::task::JoinSet;
 use tracing::instrument;
 
 use super::error::NockAppError;
 use super::wire::WireRepr;
+use super::NockAppExit;
 
 pub type IODriverFuture = Pin<Box<dyn Future<Output = Result<(), NockAppError>> + Send>>;
 pub type IODriverFn = Box<dyn FnOnce(NockAppHandle) -> IODriverFuture>;
@@ -38,9 +40,9 @@ where
 
 pub struct NockAppHandle {
     pub io_sender: ActionSender,
-    pub effect_sender: EffectSender,
+    pub effect_sender: Arc<EffectSender>,
     pub effect_receiver: Mutex<EffectReceiver>,
-    pub exit: mpsc::Sender<usize>,
+    pub exit: NockAppExit,
 }
 
 /// IO actions sent between [`NockAppHandle`] and [`crate::NockApp`] over channels.

+ 228 - 114
crates/nockapp/src/nockapp/mod.rs

@@ -7,6 +7,7 @@ pub mod wire;
 
 pub use error::NockAppError;
 
+use std::future::Future;
 use std::path::PathBuf;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
@@ -17,7 +18,7 @@ use tokio::sync::{broadcast, mpsc, Mutex, OwnedMutexGuard};
 use tokio::time::{interval, Duration, Interval};
 use tokio::{fs, select};
 use tokio_util::task::TaskTracker;
-use tracing::{debug, error, info, instrument, trace};
+use tracing::{debug, error, info, instrument, trace, warn};
 
 use crate::kernel::form::Kernel;
 use crate::noun::slab::NounSlab;
@@ -26,19 +27,42 @@ use driver::{IOAction, IODriverFn, NockAppHandle, PokeResult};
 use metrics::*;
 use wire::WireRepr;
 
+use futures::stream::StreamExt;
+use signal_hook::consts::signal::*;
+use signal_hook::consts::TERM_SIGNALS;
+use signal_hook_tokio::Signals;
+
 type NockAppResult = Result<(), NockAppError>;
 
+// Error code constants for process exit and signal handling
+// These numbers correspond to the standard Unix-style exit codes
+// Exit code = 128 + signal number
+/// Clean exit, no error
+pub const EXIT_OK: usize = 0;
+/// Unknown signal or error
+pub const EXIT_UNKNOWN: usize = 1;
+/// SIGHUP: Terminal closed or controlling process died
+pub const EXIT_SIGHUP: usize = 129;
+/// SIGINT: Keyboard interrupt (C-c)
+pub const EXIT_SIGINT: usize = 130;
+/// SIGQUIT: Quit from keyboard (core dump)
+pub const EXIT_SIGQUIT: usize = 131;
+/// SIGTERM: Termination signal from OS or process manager
+pub const EXIT_SIGTERM: usize = 143;
+
 pub struct NockApp {
     /// Nock kernel
     pub(crate) kernel: Kernel,
     /// Current join handles for IO drivers (parallel to `drivers`)
     pub(crate) tasks: tokio_util::task::TaskTracker,
-    /// Exit signal sender
-    exit_send: mpsc::Sender<usize>,
-    /// Exit signal receiver
-    exit_recv: mpsc::Receiver<usize>,
+    /// Exit state object
+    exit: NockAppExit,
+    /// Exit state receiver
+    exit_recv: tokio::sync::mpsc::Receiver<NockAppExitStatus>,
     /// Exit status
     exit_status: AtomicBool,
+    /// Abort immediately on signal
+    abort_immediately: AtomicBool,
     /// Save event num sender
     watch_send: Arc<Mutex<tokio::sync::watch::Sender<u64>>>,
     /// Save event num receiver
@@ -48,18 +72,16 @@ pub struct NockApp {
     /// IO action channel sender
     action_channel_sender: mpsc::Sender<IOAction>,
     /// Effect broadcast channel
-    effect_broadcast: broadcast::Sender<NounSlab>,
+    effect_broadcast: Arc<broadcast::Sender<NounSlab>>,
     /// Save interval
     save_interval: Interval,
     /// Mutex to ensure only one save at a time
     pub(crate) save_mutex: Arc<Mutex<()>>,
     /// Shutdown oneshot sender
-    shutdown_send: Option<tokio::sync::oneshot::Sender<NockAppResult>>,
-    /// Shutdown oneshot receiver
-    shutdown_recv: tokio::sync::oneshot::Receiver<NockAppResult>,
-    // cancel_token: tokio_util::sync::CancellationToken,
     pub npc_socket_path: Option<PathBuf>,
     metrics: NockAppMetrics,
+    /// Signals handled by the work loop
+    signals: Signals,
 }
 
 pub enum NockAppRun {
@@ -67,6 +89,60 @@ pub enum NockAppRun {
     Done,
 }
 
+pub enum NockAppExitStatus {
+    Exit(usize),
+    Shutdown(NockAppResult),
+    Done(NockAppResult),
+}
+
+#[derive(Clone)]
+pub struct NockAppExit {
+    sender: tokio::sync::mpsc::Sender<NockAppExitStatus>,
+}
+
+impl NockAppExit {
+    pub fn new() -> (Self, tokio::sync::mpsc::Receiver<NockAppExitStatus>) {
+        let (sender, receiver) = tokio::sync::mpsc::channel(1);
+        (NockAppExit { sender }, receiver)
+    }
+
+    pub fn exit(&self, code: usize) -> impl std::future::Future<Output = NockAppResult> {
+        trace!("NockAppExit exit()");
+        let sender = self.sender.clone();
+        async move {
+            sender
+                .send(NockAppExitStatus::Exit(code))
+                .await
+                .map_err(|_| NockAppError::ChannelClosedError)?;
+            Ok(())
+        }
+    }
+
+    fn shutdown(&self, res: NockAppResult) -> impl Future<Output = NockAppResult> {
+        trace!("NockAppExit shutdown()");
+        let sender = self.sender.clone();
+        async move {
+            sender
+                .send(NockAppExitStatus::Shutdown(res))
+                .await
+                .map_err(|_| NockAppError::ChannelClosedError)?;
+            Ok(())
+        }
+    }
+
+    fn done(&self, res: NockAppResult) -> impl Future<Output = NockAppResult> {
+        trace!("NockAppExit done()");
+        let sender = self.sender.clone();
+        async move {
+            sender
+                .send(NockAppExitStatus::Done(res))
+                .await
+                .map_err(|_| NockAppError::ChannelClosedError)?;
+            Ok(())
+        }
+    }
+}
+
 impl NockApp {
     /// This constructs a Tokio interval, even though it doesn't look like it, a Tokio runtime is _required_.
     pub async fn new(kernel: Kernel, save_interval_duration: Duration) -> Self {
@@ -75,12 +151,12 @@ impl NockApp {
         // the Arc in the serf would result in a race condition!
 
         let (action_channel_sender, action_channel) = mpsc::channel(100);
-        let (effect_broadcast, _) = broadcast::channel(100);
+        let (effect_broadcast_sender, _) = broadcast::channel(100);
+        let effect_broadcast = Arc::new(effect_broadcast_sender);
         // let tasks = Arc::new(Mutex::new(TaskJoinSet::new()));
         // let tasks = TaskJoinSet::new();
         // let tasks = Arc::new(TaskJoinSet::new());
         let tasks = TaskTracker::new();
-        let (exit_send, exit_recv) = mpsc::channel(1);
         let mut save_interval = interval(save_interval_duration);
         save_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); // important so we don't stack ticks when lagging
         let save_mutex = Arc::new(Mutex::new(()));
@@ -88,14 +164,18 @@ impl NockApp {
             tokio::sync::watch::channel(kernel.serf.event_number.load(Ordering::SeqCst));
         let watch_send = Arc::new(Mutex::new(watch_send.clone()));
         let exit_status = AtomicBool::new(false);
-        let (shutdown_send, shutdown_recv) = tokio::sync::oneshot::channel();
+        let abort_immediately = AtomicBool::new(false);
         // let cancel_token = tokio_util::sync::CancellationToken::new();
         let metrics = NockAppMetrics::register(gnort::global_metrics_registry())
             .expect("Failed to register metrics!");
+        let signals = Signals::new(&[TERM_SIGNALS, &[SIGHUP]].concat())
+            .expect("Failed to create signal handler");
+        let (exit, exit_recv) = NockAppExit::new();
         Self {
             kernel,
             tasks,
-            exit_send,
+            abort_immediately,
+            exit,
             exit_recv,
             exit_status,
             watch_send,
@@ -105,11 +185,10 @@ impl NockApp {
             effect_broadcast,
             save_interval,
             save_mutex,
-            shutdown_send: Some(shutdown_send),
-            shutdown_recv,
             // cancel_token,
             npc_socket_path: None,
             metrics,
+            signals,
         }
     }
 
@@ -118,7 +197,7 @@ impl NockApp {
             io_sender: self.action_channel_sender.clone(),
             effect_sender: self.effect_broadcast.clone(),
             effect_receiver: Mutex::new(self.effect_broadcast.subscribe()),
-            exit: self.exit_send.clone(),
+            exit: self.exit.clone(),
         }
     }
 
@@ -129,7 +208,7 @@ impl NockApp {
         let io_sender = self.action_channel_sender.clone();
         let effect_sender = self.effect_broadcast.clone();
         let effect_receiver = Mutex::new(self.effect_broadcast.subscribe());
-        let exit = self.exit_send.clone();
+        let exit = self.exit.clone();
         let fut = driver(NockAppHandle {
             io_sender,
             effect_sender,
@@ -138,6 +217,8 @@ impl NockApp {
         });
         // TODO: Stop using the task tracker for user code?
         self.tasks.spawn(fut);
+
+        debug!("Added IO driver");
     }
 
     /// Purely for testing purposes (injecting delays) for now.
@@ -149,11 +230,12 @@ impl NockApp {
     ) -> Result<tokio::task::JoinHandle<NockAppResult>, NockAppError> {
         let toggle = self.kernel.serf.buffer_toggle.clone();
         let jam_paths = self.kernel.serf.jam_paths.clone();
-        let checkpoint = self.kernel.checkpoint().await?;
-        let bytes = checkpoint.encode()?;
         let send_lock = self.watch_send.clone();
+        let checkpoint_fut = self.kernel.checkpoint();
 
         let join_handle = self.tasks.spawn(async move {
+            let checkpoint = checkpoint_fut.await?;
+            let bytes = checkpoint.encode()?;
             f.await;
             let path = if toggle.load(Ordering::SeqCst) {
                 &jam_paths.1
@@ -243,9 +325,9 @@ impl NockApp {
     }
 
     /// Runs until the nockapp is done (returns exit 0 or an error)
+    /// TODO: we should print most errors rather than exiting immediately
     #[instrument(skip(self))]
-    pub async fn run_no_join(&mut self) -> NockAppResult {
-        debug!("Starting nockapp run");
+    pub async fn run(&mut self) -> NockAppResult {
         // Reset NockApp for next run
         // self.reset();
         // debug!("Reset NockApp for next run");
@@ -253,7 +335,9 @@ impl NockApp {
             let work_res = self.work().await;
             match work_res {
                 Ok(nockapp_run) => match nockapp_run {
-                    crate::nockapp::NockAppRun::Pending => continue,
+                    crate::nockapp::NockAppRun::Pending => {
+                        continue;
+                    }
                     crate::nockapp::NockAppRun::Done => break Ok(()),
                 },
                 Err(NockAppError::Exit(code)) => {
@@ -274,24 +358,6 @@ impl NockApp {
         }
     }
 
-    pub async fn join(self) -> NockAppResult {
-        debug!("Awaiting serf stop");
-        self.kernel.serf.stop().await;
-        debug!("Joining serf thread");
-        self.kernel
-            .serf
-            .join()
-            .map_err(|e| NockAppError::SerfThreadError(e))?;
-        debug!("Serf thread joined");
-        Ok(())
-    }
-
-    pub async fn run(mut self) -> NockAppResult {
-        let res = self.run_no_join().await;
-        self.join().await?;
-        res
-    }
-
     #[instrument(skip(socket))]
     fn cleanup_socket_(socket: &Option<PathBuf>) {
         // Clean up npc socket file if it exists
@@ -311,33 +377,51 @@ impl NockApp {
     }
 
     async fn work(&mut self) -> Result<NockAppRun, NockAppError> {
+        // Track SIGINT (C-c) presses for immediate termination
         // Fires when there is a save interval tick *and* an available permit in the save semaphore
         let save_ready = self
             .save_interval
             .tick()
             .then(|_| self.save_mutex.clone().lock_owned());
         select!(
-        _ = self.kernel.serf.finished() => {
-                debug!("Serf thread exited");
-                Ok(NockAppRun::Done)
-            },
-            shutdown = &mut self.shutdown_recv => {
-                debug!("Shutdown channel received");
-                self.metrics.handle_shutdown.increment();
-                self.cleanup_socket();
-                match shutdown {
-                    Ok(Ok(())) => {
-                        debug!("Shutdown triggered, exiting");
-                        Ok(NockAppRun::Done)
+            exit_status_res = self.exit_recv.recv() => {
+                let Some(exit_status) = exit_status_res else {
+                    error!("Exit channel closed");
+                    return Err(NockAppError::ChannelClosedError)
+                };
+                match exit_status {
+                    NockAppExitStatus::Exit(code) => {
+                        self.metrics.handle_exit.increment();
+                        self.handle_exit(code).await
                     },
-                    Ok(Err(e)) => {
-                        error!("Shutdown triggered with error: {}", e);
-                        Err(e)
+                    NockAppExitStatus::Shutdown(res) => {
+                        self.metrics.handle_shutdown.increment();
+                        let stop_fut = self.kernel.serf.stop();
+                        let exit = self.exit.clone();
+                        self.tasks.spawn(async move {
+                            if let Err(e) = stop_fut.await {
+                                if let Err(e) = exit.done(Err(NockAppError::from(e))).await {
+                                    error!("Error completing shutdown: {e}");
+                                }
+                            } else {
+                                if let Err(e) = exit.done(res).await {
+                                    error!("Error completing shutdown: {e}");
+                                }
+                            }
+                        });
+                        Ok(NockAppRun::Pending)
                     },
-                    // Err(_recv_error) => {},
-                    Err(_recv_error) => {
-                        error!("Shutdown channel closed prematurely");
-                        Err(NockAppError::ChannelClosedError)
+                    NockAppExitStatus::Done(res) => {
+                        match res {
+                            Ok(()) => {
+                                debug!("Shutdown triggered, exiting");
+                                Ok(NockAppRun::Done)
+                            },
+                            Err(e) => {
+                                error!("Shutdown triggered with error: {}", e);
+                                Err(e)
+                            }
+                        }
                     },
                 }
             },
@@ -345,20 +429,38 @@ impl NockApp {
                 self.metrics.handle_save_permit_res.increment();
                 self.handle_save_permit_res(save_guard).await
             },
-            exit = self.exit_recv.recv() => {
-                self.metrics.handle_exit.increment();
-                debug!("Exit signal received");
-                if let Some(code) = exit {
-                    self.handle_exit(code).await
+            maybe_signal = self.signals.next() => {
+                debug!("Signal received");
+                if let Some(signal) = maybe_signal {
+                    let (code, explanation) = match signal {
+                        SIGINT => (EXIT_SIGINT, "SIGINT (C-c): Keyboard interrupt."),
+                        SIGTERM => (EXIT_SIGTERM, "SIGTERM: Termination signal from OS or process manager."),
+                        SIGQUIT => (EXIT_SIGQUIT, "SIGQUIT: Quit from keyboard (core dump)."),
+                        SIGHUP => (EXIT_SIGHUP, "SIGHUP: Terminal closed or controlling process died."),
+                        _ => (EXIT_UNKNOWN, "Unknown signal: default error code 1."),
+                    };
+                    self.metrics.handle_exit.increment();
+                    debug!("Received signal {signal}, code {code}: {explanation}");
+                    loop {
+                        if !self.abort_immediately.load(Ordering::SeqCst) {
+                            if self.abort_immediately.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_ok() {
+                                trace!("Exiting due to signal {signal}");
+                                let exit_fut = self.exit.exit(code);
+                                self.tasks.spawn(exit_fut);
+                                break Ok(NockAppRun::Pending);
+                            }
+                        } else {
+                            std::process::exit(code.try_into().unwrap());
+                        }
+                    }
                 } else {
-                    error!("Exit signal channel closed prematurely");
+                    error!("Signal stream ended unexpectedly");
                     Err(NockAppError::ChannelClosedError)
                 }
-            }
-            // FIXME: This shouldn't be hanging the event loop on the kernel poke/peek etc.
+            },
             action_res = self.action_channel.recv() => {
+                debug!("Action channel received");
                 self.metrics.handle_action.increment();
-                trace!("Action channel received");
                 match action_res {
                     Some(action) => {
                         self.handle_action(action).await;
@@ -392,7 +494,7 @@ impl NockApp {
     }
 
     #[instrument(skip_all)]
-    async fn handle_action(&mut self, action: IOAction) {
+    async fn handle_action(&self, action: IOAction) {
         // Stop processing events if we are exiting
         if self.exit_status.load(Ordering::SeqCst) {
             if let IOAction::Poke { .. } = action {
@@ -419,55 +521,72 @@ impl NockApp {
 
     #[instrument(skip_all)]
     async fn handle_poke(
-        &mut self,
+        &self,
         wire: WireRepr,
         cause: NounSlab,
         ack_channel: tokio::sync::oneshot::Sender<PokeResult>,
     ) {
-        let poke_result = self.kernel.poke(wire, cause).await;
-        match poke_result {
-            Ok(effects) => {
-                let _ = ack_channel.send(PokeResult::Ack);
-                for effect_slab in effects.to_vec() {
-                    let _ = self.effect_broadcast.send(effect_slab);
+        let poke_future = self.kernel.poke(wire, cause);
+        let effect_broadcast = self.effect_broadcast.clone();
+        let _ = self.tasks.spawn(async move {
+            let poke_result = poke_future.await;
+            match poke_result {
+                Ok(effects) => {
+                    let _ = ack_channel.send(PokeResult::Ack);
+                    for effect_slab in effects.to_vec() {
+                        let _ = effect_broadcast.send(effect_slab);
+                    }
+                }
+                Err(_) => {
+                    let _ = ack_channel.send(PokeResult::Nack);
                 }
             }
-            Err(_) => {
-                let _ = ack_channel.send(PokeResult::Nack);
-            }
-        }
+        });
     }
 
     #[instrument(skip_all)]
     async fn handle_peek(
-        &mut self,
+        &self,
         path: NounSlab,
         result_channel: tokio::sync::oneshot::Sender<Option<NounSlab>>,
     ) {
-        let peek_res = self.kernel.peek(path).await;
+        let peek_future = self.kernel.peek(path);
+        let _ = self.tasks.spawn(async move {
+            let peek_res = peek_future.await;
 
-        match peek_res {
-            Ok(res_slab) => {
-                let _ = result_channel.send(Some(res_slab));
-            }
-            Err(e) => {
-                error!("Peek error: {:?}", e);
-                let _ = result_channel.send(None);
+            match peek_res {
+                Ok(res_slab) => {
+                    let _ = result_channel.send(Some(res_slab));
+                }
+                Err(e) => {
+                    error!("Peek error: {:?}", e);
+                    let _ = result_channel.send(None);
+                }
             }
-        }
+        });
+    }
+
+    async fn handle_signal(&mut self, code: usize) -> Result<NockAppRun, NockAppError> {
+        self.kernel.serf.cancel_token.cancel();
+        self.handle_exit(code).await
     }
 
     // TODO: We should explicitly kick off a save somehow
+    // TOOD: :>) spawn a task which awaits the signal stream and if there is a SIGINT, then call std::process::exit(1)
     #[instrument(skip_all)]
     async fn handle_exit(&mut self, code: usize) -> Result<NockAppRun, NockAppError> {
-        // `cargo nextest run`
-        // 2025-01-23T01:11:52.365215Z  INFO nockapp::nockapp: Exit request received, waiting for save checkpoint with event_num 60
-        // 2025-01-23T01:11:52.403120Z ERROR nockapp::nockapp: Action channel closed prematurely
-        // 2025-01-23T01:11:52.403132Z ERROR nockapp::nockapp: Got error running nockapp: ChannelClosedError
-        // test tests::test_compile_test_app ... FAILED
-        // self.action_channel.close();
-        // TODO: See if exit_status is duplicative of what the cancel token is for.
-        self.exit_status.store(true, Ordering::SeqCst);
+        // We should only run handle_exit once, break out if we are already exiting.
+        loop {
+            if self.exit_status.load(Ordering::SeqCst) {
+                return Ok(NockAppRun::Pending);
+            } else if self
+                .exit_status
+                .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
+                .is_ok()
+            {
+                break;
+            }
+        }
 
         // Force an immediate save to ensure we have the latest state
         info!(
@@ -489,14 +608,7 @@ impl NockApp {
 
         let mut recv = self.watch_recv.clone();
         // let cancel_token = self.cancel_token.clone();
-        let shutdown_send = self.shutdown_send.take().unwrap_or_else(|| {
-            panic!(
-                "Panicked at {}:{} (git sha: {:?})",
-                file!(),
-                line!(),
-                option_env!("GIT_SHA")
-            )
-        });
+        let exit = self.exit.clone();
         // self.tasks.close();
         // self.tasks.wait().await;
         // recv from the watch channel until we reach the exit event_num, wrapped up in a future
@@ -505,17 +617,17 @@ impl NockApp {
         // TODO: Break this out as a separate select! handler with no spawn
         self.tasks.spawn(async move {
             recv.wait_for(|&new| {
-                assert!(
-                    new <= exit_event_num,
-                    "new {new:?} exit_event_num {exit_event_num:?}"
-                );
-                new == exit_event_num
+                // assert!(
+                //     new <= exit_event_num,
+                //     "new {new:?} exit_event_num {exit_event_num:?}"
+                // );
+                new >= exit_event_num
             })
             .await
             .expect("Failed to wait for saves to catch up to exit_event_num");
             Self::cleanup_socket_(&socket_path);
             debug!("Save event_num reached, finishing with code {}", code);
-            let shutdown_result = if code == 0 {
+            let shutdown_result = if code == EXIT_OK {
                 Ok(())
             } else {
                 Err(NockAppError::Exit(code))
@@ -524,7 +636,9 @@ impl NockApp {
             // we don't get a race condition where the yielded result is
             // "canceled" instead of the actual result.
             debug!("Sending shutdown result");
-            let _ = shutdown_send.send(shutdown_result);
+            if let Err(e) = exit.shutdown(shutdown_result).await {
+                error!("Error sending shutdown: {e:}")
+            }
         });
         Ok(NockAppRun::Pending)
     }

+ 9 - 2
crates/nockapp/src/utils/mod.rs

@@ -8,7 +8,7 @@ pub use bytes::ToBytes;
 use either::Either;
 pub use error::{CrownError, Result};
 use nockvm::hamt::Hamt;
-use nockvm::interpreter::{self, Context};
+use nockvm::interpreter::{self, Context, NockCancelToken};
 use nockvm::jets::cold::Cold;
 use nockvm::jets::hot::{Hot, HotEntry};
 use nockvm::jets::warm::Warm;
@@ -19,6 +19,8 @@ use nockvm::trace::TraceInfo;
 use slogger::CrownSlogger;
 use std::ptr::copy_nonoverlapping;
 use std::slice::from_raw_parts_mut;
+use std::sync::atomic::AtomicIsize;
+use std::sync::Arc;
 use std::time::{SystemTime, UNIX_EPOCH};
 
 // urbit @da timestamp
@@ -32,7 +34,10 @@ const S1: u128 = 18446744073709551616;
 pub const NOCK_STACK_1KB: usize = 1 << 7;
 
 // nock stack size
-pub const NOCK_STACK_SIZE: usize = (NOCK_STACK_1KB << 10 << 10) * 8; // 2GB
+pub const NOCK_STACK_SIZE: usize = (NOCK_STACK_1KB << 10 << 10) * 8; // 8GB
+
+// HUGE nock stack size
+pub const NOCK_STACK_SIZE_HUGE: usize = (NOCK_STACK_1KB << 10 << 10) * 128; // 32GB
 
 /**
  *   ::  +from-unix: unix seconds to @da
@@ -148,6 +153,7 @@ pub fn create_context(
     let hot = Hot::init(&mut stack, hot_state);
     let warm = Warm::init(&mut stack, &mut cold, &hot);
     let slogger = Box::pin(CrownSlogger {});
+    let cancel = Arc::new(AtomicIsize::new(NockCancelToken::RUNNING_IDLE));
 
     interpreter::Context {
         stack,
@@ -158,5 +164,6 @@ pub fn create_context(
         cache,
         scry_stack: D(0),
         trace_info,
+        running_status: cancel,
     }
 }

+ 43 - 48
crates/nockapp/src/utils/slogger.rs

@@ -1,5 +1,4 @@
 use crate::{CrownError, Result};
-use assert_no_alloc::permit_alloc;
 use either::Either::*;
 use nockvm::interpreter::Slogger;
 use nockvm::jets::list::util::lent;
@@ -13,38 +12,36 @@ pub struct CrownSlogger;
 
 impl Slogger for CrownSlogger {
     fn slog(&mut self, stack: &mut NockStack, pri: u64, tank: Noun) {
-        permit_alloc(|| {
-            let mut buffer = Vec::new();
-            match slog_tank(stack, tank, &mut buffer) {
-                Ok(_) => {
-                    let message = String::from_utf8_lossy(&buffer)
-                        .trim_matches('\0')
-                        .replace('\n', " ")
-                        .to_string();
-                    if !message.is_empty() {
-                        if cfg!(feature = "slog-tracing") {
-                            match pri {
-                                0 => info!(target: "slogger", "{}", message),
-                                1 => warn!(target: "slogger", "{}", message),
-                                2 => debug!(target: "slogger", "{}", message),
-                                3 => trace!(target: "slogger", "{}", message),
-                                _ => info!(target: "slogger", "{}", message),
-                            }
-                        } else {
-                            let _ = writeln!(stderr(), "{}", message);
-                        }
-                    }
-                }
-                Err(e) => {
-                    let err_msg = format!("Failed to slog tank: {}", e);
+        let mut buffer = Vec::new();
+        match slog_tank(stack, tank, &mut buffer) {
+            Ok(_) => {
+                let message = String::from_utf8_lossy(&buffer)
+                    .trim_matches('\0')
+                    .replace('\n', " ")
+                    .to_string();
+                if !message.is_empty() {
                     if cfg!(feature = "slog-tracing") {
-                        error!(target: "slogger", "{}", err_msg);
+                        match pri {
+                            0 => info!(target: "slogger", "{}", message),
+                            1 => warn!(target: "slogger", "{}", message),
+                            2 => debug!(target: "slogger", "{}", message),
+                            3 => trace!(target: "slogger", "{}", message),
+                            _ => info!(target: "slogger", "{}", message),
+                        }
                     } else {
-                        let _ = writeln!(stderr(), "{}", err_msg);
+                        let _ = writeln!(stderr(), "{}", message);
                     }
                 }
             }
-        });
+            Err(e) => {
+                let err_msg = format!("Failed to slog tank: {}", e);
+                if cfg!(feature = "slog-tracing") {
+                    error!(target: "slogger", "{}", err_msg);
+                } else {
+                    let _ = writeln!(stderr(), "{}", err_msg);
+                }
+            }
+        }
     }
 
     fn flog(&mut self, _stack: &mut NockStack, cord: Noun) {
@@ -56,31 +53,29 @@ impl Slogger for CrownSlogger {
                 option_env!("GIT_SHA")
             )
         });
-        permit_alloc(|| {
-            let mut buffer = Vec::new();
-            match slog_cord(cord_atom, &mut buffer) {
-                Ok(_) => {
-                    let message = String::from_utf8_lossy(&buffer)
-                        .trim_matches('\0')
-                        .to_string();
-                    if !message.is_empty() {
-                        if cfg!(feature = "slog-tracing") {
-                            info!(target: "slogger", "{}", message);
-                        } else {
-                            let _ = writeln!(stderr(), "{}", message);
-                        }
-                    }
-                }
-                Err(e) => {
-                    let err_msg = format!("Failed to flog cord: {}", e);
+        let mut buffer = Vec::new();
+        match slog_cord(cord_atom, &mut buffer) {
+            Ok(_) => {
+                let message = String::from_utf8_lossy(&buffer)
+                    .trim_matches('\0')
+                    .to_string();
+                if !message.is_empty() {
                     if cfg!(feature = "slog-tracing") {
-                        error!(target: "slogger", "{}", err_msg);
+                        info!(target: "slogger", "{}", message);
                     } else {
-                        let _ = writeln!(stderr(), "{}", err_msg);
+                        let _ = writeln!(stderr(), "{}", message);
                     }
                 }
             }
-        });
+            Err(e) => {
+                let err_msg = format!("Failed to flog cord: {}", e);
+                if cfg!(feature = "slog-tracing") {
+                    error!(target: "slogger", "{}", err_msg);
+                } else {
+                    let _ = writeln!(stderr(), "{}", err_msg);
+                }
+            }
+        }
     }
 }
 

+ 4 - 4
crates/nockchain-bitcoin-sync/src/lib.rs

@@ -23,7 +23,7 @@ const TEST_GENESIS_BLOCK_HASH: &str =
     "00000000e6c3c75c18bdb06cc39d616d636fca0fc967c29ebf8225ddf7f2fe48";
 const TEST_GENESIS_BLOCK_HEIGHT: u64 = 2048;
 
-const GENESIS_SEAL_MSG: &str = "8K5RhhjnZQChv7cnhEiafkaxBReVhaySKEZatFEg7cagoK96kUo92sz";
+const GENESIS_SEAL_MSG: &str = "2c8Ltbg44dPkEGcNPupcVAtDgD87753M9pG2fg8yC2mTEqg5qAFvvbT";
 
 /// Helper function to get the test block used for fake genesis blocks
 fn get_test_block() -> BlockRef {
@@ -197,7 +197,7 @@ pub fn bitcoin_watcher_driver(
                     let hash_tuple = block_hash_to_belts(&mut poke_slab, &block.hash);
                     let poke_noun = T(
                         &mut poke_slab,
-                        &[D(tas!(b"command")), D(tas!(b"btc-data")), hash_tuple],
+                        &[D(tas!(b"command")), D(tas!(b"btc-data")), D(0), hash_tuple],
                     );
                     poke_slab.set_root(poke_noun);
 
@@ -241,7 +241,7 @@ pub fn bitcoin_watcher_driver(
                     let hash_tuple = block_hash_to_belts(&mut poke_slab, &test_block.hash);
                     let poke_noun = T(
                         &mut poke_slab,
-                        &[D(tas!(b"command")), D(tas!(b"btc-data")), hash_tuple],
+                        &[D(tas!(b"command")), D(tas!(b"btc-data")), D(0), hash_tuple],
                     );
                     poke_slab.set_root(poke_noun);
 
@@ -375,7 +375,7 @@ impl BitcoinWatcher {
                     );
 
                     // Check if the block has enough confirmations
-                    if block_info.confirmations >= 3 {
+                    if block_info.confirmations >= 2 {
                         debug!(
                             "Block has sufficient confirmations ({}), returning",
                             block_info.confirmations

+ 3 - 2
crates/nockchain-libp2p-io/Cargo.toml

@@ -8,14 +8,14 @@ nockapp = { workspace = true }
 nockvm = { workspace = true }
 nockvm_macros = { workspace = true }
 
-bytes = { workspace = true }
 bs58 = { workspace = true }
+bytes = { workspace = true }
 either = { workspace = true }
 equix.workspace = true
 futures = { workspace = true }
 gnort = { workspace = true }
-hickory-resolver = { workspace = true }
 hickory-proto = { workspace = true }
+hickory-resolver = { workspace = true }
 ibig = { workspace = true }
 libp2p = { workspace = true, features = [
     "ping",
@@ -29,6 +29,7 @@ libp2p = { workspace = true, features = [
     "request-response",
     "memory-connection-limits",
     "cbor",
+    "peer-store",
 ] }
 serde = { workspace = true, features = ["alloc", "derive", "serde_derive"] }
 serde_bytes = { workspace = true, features = ["alloc"] }

+ 46 - 132
crates/nockchain-libp2p-io/src/nc.rs

@@ -9,6 +9,7 @@ use futures::{Future, StreamExt};
 use libp2p::identify::Event::Received;
 use libp2p::identity::Keypair;
 use libp2p::kad::NoKnownPeers;
+use libp2p::peer_store::Store;
 use libp2p::request_response::Event::*;
 use libp2p::request_response::Message::*;
 use libp2p::request_response::{self};
@@ -21,8 +22,7 @@ use nockapp::noun::slab::NounSlab;
 use nockapp::utils::make_tas;
 use nockapp::utils::scry::*;
 use nockapp::wire::{Wire, WireRepr};
-use nockapp::NockAppError;
-use nockapp::{AtomExt, NounExt};
+use nockapp::{AtomExt, NockAppError, NounExt};
 use nockvm::noun::{Atom, Noun, D, T};
 use nockvm_macros::tas;
 use serde_bytes::ByteBuf;
@@ -32,7 +32,7 @@ use tracing::{debug, error, info, instrument, trace, warn};
 
 use crate::metrics::NockchainP2PMetrics;
 use crate::p2p::*;
-use crate::p2p_util::{MessageTracker, PeerIdExt};
+use crate::p2p_util::{log_fail2ban_ipv4, log_fail2ban_ipv6, MessageTracker, PeerIdExt};
 use crate::tip5_util::tip5_hash_to_base58;
 
 //TODO This wire is a placeholder for now. The libp2p driver is entangled with the other types of nockchain pokes
@@ -151,36 +151,6 @@ impl<T: 'static> TrackedJoinSet<T> {
     }
 }
 
-#[derive(Debug, Clone)]
-pub struct MiningKeyConfig {
-    pub share: u64,
-    pub m: u64,
-    pub keys: Vec<String>,
-}
-
-impl FromStr for MiningKeyConfig {
-    type Err = String;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        // Expected format: "share,m:key1,key2,key3"
-        let parts: Vec<&str> = s.split(':').collect();
-        if parts.len() != 2 {
-            return Err("Invalid format. Expected 'share,m:key1,key2,key3'".to_string());
-        }
-
-        let share_m: Vec<&str> = parts[0].split(',').collect();
-        if share_m.len() != 2 {
-            return Err("Invalid share,m format".to_string());
-        }
-
-        let share = share_m[0].parse::<u64>().map_err(|e| e.to_string())?;
-        let m = share_m[1].parse::<u64>().map_err(|e| e.to_string())?;
-        let keys: Vec<String> = parts[1].split(',').map(String::from).collect();
-
-        Ok(MiningKeyConfig { share, m, keys })
-    }
-}
-
 const POKE_VERSION: u64 = 0;
 
 #[instrument(skip(keypair, bind, allowed, limits, memory_limits, equix_builder))]
@@ -192,7 +162,6 @@ pub fn make_libp2p_driver(
     memory_limits: Option<memory_connection_limits::Behaviour>,
     initial_peers: &[Multiaddr],
     equix_builder: equix::EquiXBuilder,
-    mining_config: Option<Vec<MiningKeyConfig>>,
     init_complete_tx: Option<tokio::sync::oneshot::Sender<()>>,
 ) -> IODriverFn {
     let initial_peers = Vec::from(initial_peers);
@@ -201,11 +170,20 @@ pub fn make_libp2p_driver(
             .expect("Failed to register metrics!");
 
         Box::pin(async move {
-            let mut swarm = crate::p2p::start_swarm(keypair, bind, allowed, limits, memory_limits)
-                .map_err(|e| {
-                    warn!("Could not create swarm: {}", e);
-                    NockAppError::OtherError
-                })?;
+            let mut swarm =
+                match crate::p2p::start_swarm(keypair, bind, allowed, limits, memory_limits) {
+                    Ok(swarm) => swarm,
+                    Err(e) => {
+                        error!("Could not create swarm: {}", e);
+                        let (_, handle_clone) = handle.dup();
+                        tokio::spawn(async move {
+                            if let Err(e) = handle_clone.exit.exit(1).await {
+                                error!("Failed to send exit signal: {}", e);
+                            }
+                        });
+                        return Err(NockAppError::OtherError);
+                    }
+                };
             let (swarm_tx, mut swarm_rx) = mpsc::channel::<SwarmAction>(1000); // number needs to be high enough to send gossips to peers
             let mut join_set = TrackedJoinSet::<Result<(), NockAppError>>::new();
             let message_tracker = Arc::new(Mutex::new(MessageTracker::new()));
@@ -214,23 +192,6 @@ pub fn make_libp2p_driver(
             let mut initial_peer_retries_remaining = INITIAL_PEER_RETRIES;
             dial_initial_peers(&mut swarm, &initial_peers)?;
 
-            if let Some(configs) = mining_config {
-                if configs.len() == 1
-                    && configs[0].share == 1
-                    && configs[0].m == 1
-                    && configs[0].keys.len() == 1
-                {
-                    // Simple case - use set_mining_key
-                    set_mining_key(&handle, configs[0].keys[0].clone()).await?;
-                } else {
-                    // Advanced case - use set_mining_key_advanced
-                    set_mining_key_advanced(&handle, configs).await?;
-                }
-                enable_mining(&handle, true).await?;
-            } else {
-                enable_mining(&handle, false).await?;
-            }
-
             if let Some(tx) = init_complete_tx {
                 let _ = tx.send(());
                 debug!("libp2p driver initialization complete signal sent");
@@ -320,7 +281,31 @@ pub fn make_libp2p_driver(
                             },
                             SwarmAction::BlockPeer { peer_id } => {
                                 warn!("SAction: Blocking peer {peer_id}");
+                                // Block the peer in the allow_block_list
                                 swarm.behaviour_mut().allow_block_list.block_peer(peer_id);
+                                {
+                                    // get peer IP address from the swarm
+                                    let peer_addresses = swarm.behaviour_mut().peer_store.store().addresses_of_peer(&peer_id);
+                                    if let Some(peer_multi_addrs) = peer_addresses {
+                                        for multi_addr in peer_multi_addrs {
+                                            for protocol in multi_addr.iter() {
+
+                                                match protocol {
+                                                    libp2p::core::multiaddr::Protocol::Ip4(ip) => {
+                                                        log_fail2ban_ipv4(&peer_id, &ip);
+                                                    },
+                                                    libp2p::core::multiaddr::Protocol::Ip6(ip) => {
+                                                        log_fail2ban_ipv6(&peer_id, &ip);
+                                                    },
+                                                    // TODO: Dns?
+                                                    _ => {}
+                                                }
+                                            }
+                                        }
+                                    } else {
+                                        error!("Failed to get peer IP address for peer id: {peer_id}");
+                                    };
+                                }
                                 // Disconnect the peer if they're currently connected
                                 let _ = swarm.disconnect_peer_id(peer_id);
                             },
@@ -467,81 +452,10 @@ impl NockchainResponse {
     }
 }
 
-#[instrument(skip(handle, pubkey))]
-async fn set_mining_key(
-    handle: &NockAppHandle,
-    pubkey: String,
-) -> Result<PokeResult, NockAppError> {
-    let mut set_mining_key_slab = NounSlab::new();
-    let set_mining_key = Atom::from_value(&mut set_mining_key_slab, "set-mining-key")
-        .expect("Failed to create set-mining-key atom");
-    let pubkey_cord =
-        Atom::from_value(&mut set_mining_key_slab, pubkey).expect("Failed to create pubkey atom");
-
-    let set_mining_key_poke = T(
-        &mut set_mining_key_slab,
-        &[D(tas!(b"command")), set_mining_key.as_noun(), pubkey_cord.as_noun()],
-    );
-    set_mining_key_slab.set_root(set_mining_key_poke);
-
-    let wire = NockchainWire::Local;
-    handle.poke(wire.to_wire(), set_mining_key_slab).await
-}
-
-async fn set_mining_key_advanced(
-    handle: &NockAppHandle,
-    configs: Vec<MiningKeyConfig>,
-) -> Result<PokeResult, NockAppError> {
-    let mut set_mining_key_slab = NounSlab::new();
-    let set_mining_key_adv = Atom::from_value(&mut set_mining_key_slab, "set-mining-key-advanced")
-        .expect("Failed to create set-mining-key-advanced atom");
-
-    // Create the list of configs
-    let mut config_list = Vec::new();
-    for config in configs {
-        // Create the list of keys
-        let mut key_list = Vec::new();
-        for key in config.keys {
-            let key_atom =
-                Atom::from_value(&mut set_mining_key_slab, key).expect("Failed to create key atom");
-            key_list.push(key_atom.as_noun());
-        }
-        let keys_cell = T(&mut set_mining_key_slab, &key_list);
-
-        // Create the config tuple [share m keys]
-        let config_tuple = T(
-            &mut set_mining_key_slab,
-            &[D(config.share), D(config.m), keys_cell],
-        );
-        config_list.push(config_tuple);
-    }
-    let configs_cell = T(&mut set_mining_key_slab, &config_list);
-
-    let set_mining_key_poke = T(
-        &mut set_mining_key_slab,
-        &[D(tas!(b"command")), set_mining_key_adv.as_noun(), configs_cell],
-    );
-    set_mining_key_slab.set_root(set_mining_key_poke);
-
-    let wire = NockchainWire::Local;
-    handle.poke(wire.to_wire(), set_mining_key_slab).await
-}
-
-//TODO add %set-mining-key-multisig poke
-#[instrument(skip(handle))]
-async fn enable_mining(handle: &NockAppHandle, enable: bool) -> Result<PokeResult, NockAppError> {
-    let mut enable_mining_slab = NounSlab::new();
-    let enable_mining = Atom::from_value(&mut enable_mining_slab, "enable-mining")
-        .expect("Failed to create enable-mining atom");
-
-    let enable_mining_poke = T(
-        &mut enable_mining_slab,
-        &[D(tas!(b"command")), enable_mining.as_noun(), D(if enable { 0 } else { 1 })],
-    );
-    enable_mining_slab.set_root(enable_mining_poke);
-    let wire = NockchainWire::Local;
-    handle.poke(wire.to_wire(), enable_mining_slab).await
-}
+// fn emit_fail2ban(peer_ip: u128) -> Result<(), NockAppError> {
+//     // get peer ip address
+//     let peer_ip = peer_id.to_base58();
+// }
 
 async fn handle_effect(
     noun_slab: NounSlab,
@@ -659,7 +573,7 @@ async fn handle_effect(
             let effect_cell = unsafe { noun_slab.root().as_cell()? };
             let block_id = effect_cell.tail();
 
-            //add the bad block ID
+            // Add the bad block ID
             let mut tracker = message_tracker.lock().await;
             let peers_to_ban = tracker.process_bad_block_id(block_id)?;
 

+ 20 - 0
crates/nockchain-libp2p-io/src/p2p.rs

@@ -66,6 +66,8 @@ pub const REQ_RES_PROTOCOL_VERSION: &str = "/nockchain-1-req-res";
 pub const KAD_PROTOCOL_VERSION: &str = "/nockchain-1-kad";
 pub const IDENTIFY_PROTOCOL_VERSION: &str = "/nockchain-1-identify";
 
+const PEER_STORE_RECORD_CAPACITY: usize = 10 * 1024;
+
 #[derive(Debug)]
 pub enum SwarmAction {
     SendResponse {
@@ -99,6 +101,8 @@ pub struct NockchainBehaviour {
     connection_limits: connection_limits::Behaviour,
     /// Memory connection limits
     memory_connection_limits: Toggle<memory_connection_limits::Behaviour>,
+    /// Peer store for tracking peer information (including addresses)
+    pub peer_store: libp2p::peer_store::Behaviour<libp2p::peer_store::memory_store::MemoryStore>,
     /// Actual comms
     pub request_response: cbor::Behaviour<NockchainRequest, NockchainResponse>,
 }
@@ -142,6 +146,13 @@ impl NockchainBehaviour {
                 Toggle::<allow_block_list::Behaviour<allow_block_list::AllowedPeers>>::from(
                     allowed,
                 );
+            let peer_store_config = libp2p::peer_store::memory_store::Config::default();
+            let record_capacity = PEER_STORE_RECORD_CAPACITY.try_into().unwrap();
+            let peer_store_config = peer_store_config.set_record_capacity(record_capacity);
+            let peer_store_memory =
+                libp2p::peer_store::memory_store::MemoryStore::new(peer_store_config);
+
+            let peer_store_behaviour = libp2p::peer_store::Behaviour::new(peer_store_memory);
             NockchainBehaviour {
                 ping: ping::Behaviour::default(),
                 identify: identify_behaviour,
@@ -151,6 +162,7 @@ impl NockchainBehaviour {
                 request_response: request_response_behaviour,
                 connection_limits: connection_limits_behaviour,
                 memory_connection_limits,
+                peer_store: peer_store_behaviour,
             }
         }
     }
@@ -216,6 +228,8 @@ pub enum NockchainEvent {
     Kad(kad::Event),
     /// Request or response received from peer
     RequestResponse(request_response::Event<NockchainRequest, NockchainResponse>),
+    /// Peer store events
+    PeerStore(libp2p::peer_store::memory_store::Event),
 }
 
 impl From<identify::Event> for NockchainEvent {
@@ -248,6 +262,12 @@ impl From<request_response::Event<NockchainRequest, NockchainResponse>> for Nock
     }
 }
 
+impl From<libp2p::peer_store::memory_store::Event> for NockchainEvent {
+    fn from(event: libp2p::peer_store::memory_store::Event) -> Self {
+        Self::PeerStore(event)
+    }
+}
+
 ///** Handler for "identify" messages */
 //#[instrument(skip(swarm))]
 pub fn identify_received(

+ 25 - 3
crates/nockchain-libp2p-io/src/p2p_util.rs

@@ -1,17 +1,26 @@
 use std::collections::{BTreeMap, BTreeSet};
+use std::net::{Ipv4Addr, Ipv6Addr};
 use std::str::FromStr;
 
 use libp2p::PeerId;
 use nockapp::noun::slab::NounSlab;
-use nockapp::NockAppError;
-use nockapp::{AtomExt, NounExt};
+use nockapp::{AtomExt, NockAppError, NounExt};
 use nockvm::noun::Noun;
 use nockvm_macros::tas;
-use tracing::debug;
+use tracing::{debug, warn};
 
 use crate::metrics::NockchainP2PMetrics;
 use crate::tip5_util::tip5_hash_to_base58;
 
+// The warn logs are specifically constructed for fail2ban
+// Changing these breaks the integration with the fail2ban regex
+pub fn log_fail2ban_ipv4(peer_id: &PeerId, ip: &Ipv4Addr) {
+    warn!("fail2ban: Blocked peer {peer_id} with IPv4 address: {ip}");
+}
+pub fn log_fail2ban_ipv6(peer_id: &PeerId, ip: &Ipv6Addr) {
+    warn!("fail2ban: Blocked peer {peer_id} with IPv6 address: {ip}");
+}
+
 pub trait PeerIdExt {
     fn from_noun(noun: Noun) -> Result<PeerId, NockAppError>;
 }
@@ -586,4 +595,17 @@ mod tests {
         // (since we removed the peers entirely)
         assert!(!tracker.is_tracking_block_id(other_block_id));
     }
+
+    #[test]
+    fn test_fail2ban_logging() {
+        let peer_id: PeerId = libp2p::PeerId::from_bytes(&[0; 2]).unwrap();
+        assert_eq!("11", peer_id.to_base58());
+        let ipv4_addr = Ipv4Addr::new(192, 168, 1, 1);
+        let ipv6_addr = Ipv6Addr::new(0x2001, 0x0db8, 0x0db8, 0x0db8, 0x0db8, 0x0db8, 0x0db8, 0x1);
+        // Check the display representation of the IP addresses
+        let ipv4_display = format!("{}", ipv4_addr);
+        let ipv6_display = format!("{}", ipv6_addr);
+        assert_eq!(ipv4_display, "192.168.1.1");
+        assert_eq!(ipv6_display, "2001:db8:db8:db8:db8:db8:db8:1");
+    }
 }

+ 2 - 1
crates/nockchain/Cargo.toml

@@ -7,8 +7,8 @@ edition.workspace = true
 
 [dependencies]
 hoonc.workspace = true
+kernels = { workspace = true, features = ["dumb", "miner"] }
 nockapp.workspace = true
-kernels = { workspace = true, features = ["dumb"] }
 nockchain-bitcoin-sync.workspace = true
 nockvm.workspace = true
 nockvm_macros.workspace = true
@@ -17,6 +17,7 @@ bitcoincore-rpc.workspace = true
 bs58.workspace = true
 clap.workspace = true
 equix.workspace = true
+futures.workspace = true
 libp2p = { workspace = true, features = [
     "ping",
     "kad",

+ 57 - 19
crates/nockchain/src/lib.rs

@@ -1,3 +1,5 @@
+pub mod mining;
+
 use std::error::Error;
 use std::fs;
 use std::path::Path;
@@ -6,8 +8,10 @@ use clap::{arg, command, ArgAction, Parser};
 use libp2p::identity::Keypair;
 use libp2p::multiaddr::Multiaddr;
 use libp2p::{allow_block_list, connection_limits, memory_connection_limits, PeerId};
+use nockapp::driver::Operation;
 use nockapp::kernel::boot;
-use nockapp::NockApp;
+use nockapp::wire::Wire;
+use nockapp::{one_punch_driver, NockApp, NounExt};
 use nockchain_bitcoin_sync::{bitcoin_watcher_driver, BitcoinRPCConnection, GenesisNodeType};
 use nockchain_libp2p_io::p2p::{
     MAX_ESTABLISHED_CONNECTIONS, MAX_ESTABLISHED_CONNECTIONS_PER_PEER,
@@ -22,12 +26,13 @@ use std::path::PathBuf;
 use clap::value_parser;
 use colors::*;
 use nockapp::noun::slab::NounSlab;
-use nockchain_libp2p_io::nc::MiningKeyConfig;
 use nockvm::jets::hot::HotEntry;
 use nockvm::noun::{D, T};
 use nockvm_macros::tas;
 use tracing::{debug, info, instrument};
 
+use crate::mining::MiningKeyConfig;
+
 /// Module for handling driver initialization signals
 pub mod driver_init {
     use nockapp::driver::{make_driver, IODriverFn, PokeResult};
@@ -149,10 +154,7 @@ const TESTNET_BACKBONE_NODES: &[&str] = &[];
 // TODO: feature flag testnet/realnet
 /** Backbone nodes for our realnet */
 #[allow(dead_code)]
-const REALNET_BACKBONE_NODES: &[&str] = &[];
-
-/** List of backbone nodes baked into the executable: we'll try to join these unconditionally */
-const BACKBONE_NODES: &[&str] = TESTNET_BACKBONE_NODES;
+const REALNET_BACKBONE_NODES: &[&str] = &["/dnsaddr/nockchain-backbone.zorp.io"];
 
 /** How often we should affirmatively ask other nodes for their heaviest chain */
 const CHAIN_INTERVAL_SECS: u64 = 20;
@@ -160,7 +162,7 @@ const CHAIN_INTERVAL_SECS: u64 = 20;
 /// The height of the bitcoin block that we want to sync our genesis block to
 /// Currently, this is the height of an existing block for testing. It will be
 /// switched to a future block for launch.
-const GENESIS_HEIGHT: u64 = 892723;
+const GENESIS_HEIGHT: u64 = 897767;
 
 /// Command line arguments
 #[derive(Parser, Debug, Clone)]
@@ -243,6 +245,12 @@ pub struct NockchainCli {
 
 impl NockchainCli {
     pub fn validate(&self) -> Result<(), String> {
+        if self.mine && !(self.mining_pubkey.is_some() || self.mining_key_adv.is_some()) {
+            return Err(
+                "Cannot specify mine without either mining_pubkey or mining_key_adv".to_string(),
+            );
+        }
+
         if self.mining_pubkey.is_some() && self.mining_key_adv.is_some() {
             return Err(
                 "Cannot specify both mining_pubkey and mining_key_adv at the same time".to_string(),
@@ -445,7 +453,13 @@ pub async fn init_with_kernel(
         } else { c.max_system_memory_fraction.map(memory_connection_limits::Behaviour::with_max_percentage) }
     });
 
-    let backbone_peers = BACKBONE_NODES
+    let default_backbone_peers = if cli.as_ref().map(|c| c.fakenet).unwrap_or(false) {
+        TESTNET_BACKBONE_NODES
+    } else {
+        REALNET_BACKBONE_NODES
+    };
+
+    let backbone_peers = default_backbone_peers
         .iter()
         .map(|multiaddr_str| {
             multiaddr_str
@@ -484,6 +498,7 @@ pub async fn init_with_kernel(
     let mut driver_signals = driver_init::DriverInitSignals::new();
 
     // Register drivers that need initialization signals
+    let mining_init_tx = driver_signals.register_driver("mining");
     let libp2p_init_tx = driver_signals.register_driver("libp2p");
     let watcher_init_tx = driver_signals.register_driver("bitcoin_watcher");
 
@@ -521,8 +536,40 @@ pub async fn init_with_kernel(
         let watcher_driver =
             bitcoin_watcher_driver(Some(connection), node_type, message, Some(watcher_init_tx));
         nockapp.add_io_driver(watcher_driver).await;
+    } else {
+        // Realnet with no BTC node
+        let mut poke_slab = NounSlab::new();
+        let poke_noun = T(
+            &mut poke_slab,
+            &[D(tas!(b"command")), D(tas!(b"btc-data")), D(0)],
+        );
+        poke_slab.set_root(poke_noun);
+        nockapp
+            .poke(nockapp::wire::SystemWire.to_wire(), poke_slab)
+            .await
+            .expect("Failed to poke for no BTC hash");
     }
 
+    let mining_config = cli.as_ref().and_then(|c| {
+        if let Some(pubkey) = &c.mining_pubkey {
+            Some(vec![MiningKeyConfig {
+                share: 1,
+                m: 1,
+                keys: vec![pubkey.clone()],
+            }])
+        } else if let Some(mining_key_adv) = &c.mining_key_adv {
+            Some(mining_key_adv.clone())
+        } else {
+            None
+        }
+    });
+
+    let mine = cli.as_ref().map_or(false, |c| c.mine);
+
+    let mining_driver =
+        crate::mining::create_mining_driver(mining_config, mine, Some(mining_init_tx));
+    nockapp.add_io_driver(mining_driver).await;
+
     let libp2p_driver = nockchain_libp2p_io::nc::make_libp2p_driver(
         keypair,
         bind_multiaddrs,
@@ -531,17 +578,6 @@ pub async fn init_with_kernel(
         memory_limits,
         &peer_multiaddrs,
         equix_builder,
-        cli.as_ref().and_then(|c| {
-            if let Some(pubkey) = &c.mining_pubkey {
-                Some(vec![nockchain_libp2p_io::nc::MiningKeyConfig {
-                    share: 1,
-                    m: 1,
-                    keys: vec![pubkey.clone()],
-                }])
-            } else {
-                c.mining_key_adv.clone()
-            }
-        }),
         Some(libp2p_init_tx),
     );
     nockapp.add_io_driver(libp2p_driver).await;
@@ -587,6 +623,8 @@ pub async fn init_with_kernel(
         .add_io_driver(nockapp::timer_driver(CHAIN_INTERVAL_SECS, timer_slab))
         .await;
 
+    nockapp.add_io_driver(nockapp::exit_driver()).await;
+
     Ok(nockapp)
 }
 

+ 1 - 1
crates/nockchain/src/main.rs

@@ -12,7 +12,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
     boot::init_default_tracing(&cli.nockapp_cli);
 
     let prover_hot_state = produce_prover_hot_state();
-    let nockchain =
+    let mut nockchain =
         nockchain::init_with_kernel(Some(cli), KERNEL, prover_hot_state.as_slice()).await?;
     nockchain.run().await?;
     Ok(())

+ 268 - 0
crates/nockchain/src/mining.rs

@@ -0,0 +1,268 @@
+use std::str::FromStr;
+
+use kernels::miner::KERNEL;
+use nockapp::kernel::checkpoint::JamPaths;
+use nockapp::kernel::form::Kernel;
+use nockapp::nockapp::driver::{IODriverFn, NockAppHandle, PokeResult};
+use nockapp::nockapp::wire::Wire;
+use nockapp::nockapp::NockAppError;
+use nockapp::noun::slab::NounSlab;
+use nockapp::noun::{AtomExt, NounExt};
+use nockvm::noun::{Atom, D, T};
+use nockvm_macros::tas;
+use tempfile::tempdir;
+use tracing::{instrument, warn};
+
+pub enum MiningWire {
+    Mined,
+    Candidate,
+    SetPubKey,
+    Enable,
+}
+
+impl MiningWire {
+    pub fn verb(&self) -> &'static str {
+        match self {
+            MiningWire::Mined => "mined",
+            MiningWire::SetPubKey => "setpubkey",
+            MiningWire::Candidate => "candidate",
+            MiningWire::Enable => "enable",
+        }
+    }
+}
+
+impl Wire for MiningWire {
+    const VERSION: u64 = 1;
+    const SOURCE: &'static str = "miner";
+
+    fn to_wire(&self) -> nockapp::wire::WireRepr {
+        let tags = vec![self.verb().into()];
+        nockapp::wire::WireRepr::new(MiningWire::SOURCE, MiningWire::VERSION, tags)
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct MiningKeyConfig {
+    pub share: u64,
+    pub m: u64,
+    pub keys: Vec<String>,
+}
+
+impl FromStr for MiningKeyConfig {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        // Expected format: "share,m:key1,key2,key3"
+        let parts: Vec<&str> = s.split(':').collect();
+        if parts.len() != 2 {
+            return Err("Invalid format. Expected 'share,m:key1,key2,key3'".to_string());
+        }
+
+        let share_m: Vec<&str> = parts[0].split(',').collect();
+        if share_m.len() != 2 {
+            return Err("Invalid share,m format".to_string());
+        }
+
+        let share = share_m[0].parse::<u64>().map_err(|e| e.to_string())?;
+        let m = share_m[1].parse::<u64>().map_err(|e| e.to_string())?;
+        let keys: Vec<String> = parts[1].split(',').map(String::from).collect();
+
+        Ok(MiningKeyConfig { share, m, keys })
+    }
+}
+
+pub fn create_mining_driver(
+    mining_config: Option<Vec<MiningKeyConfig>>,
+    mine: bool,
+    init_complete_tx: Option<tokio::sync::oneshot::Sender<()>>,
+) -> IODriverFn {
+    Box::new(move |mut handle| {
+        Box::pin(async move {
+            let Some(configs) = mining_config else {
+                enable_mining(&handle, false).await?;
+
+                if let Some(tx) = init_complete_tx {
+                    tx.send(()).map_err(|_| {
+                        warn!("Could not send driver initialization for mining driver.");
+                        NockAppError::OtherError
+                    })?;
+                }
+
+                return Ok(());
+            };
+            if configs.len() == 1
+                && configs[0].share == 1
+                && configs[0].m == 1
+                && configs[0].keys.len() == 1
+            {
+                set_mining_key(&handle, configs[0].keys[0].clone()).await?;
+            } else {
+                set_mining_key_advanced(&handle, configs).await?;
+            }
+            enable_mining(&handle, mine).await?;
+
+            if let Some(tx) = init_complete_tx {
+                tx.send(()).map_err(|_| {
+                    warn!("Could not send driver initialization for mining driver.");
+                    NockAppError::OtherError
+                })?;
+            }
+
+            if !mine {
+                return Ok(());
+            }
+            let mut next_attempt: Option<NounSlab> = None;
+            let mut current_attempt: tokio::task::JoinSet<()> = tokio::task::JoinSet::new();
+
+            loop {
+                tokio::select! {
+                    effect_res = handle.next_effect() => {
+                        let Ok(effect) = effect_res else {
+                          warn!("Error receiving effect in mining driver: {effect_res:?}");
+                        continue;
+                        };
+                        let Ok(effect_cell) = (unsafe { effect.root().as_cell() }) else {
+                            drop(effect);
+                            continue;
+                        };
+
+                        if effect_cell.head().eq_bytes("mine") {
+                            let candidate_slab = {
+                                let mut slab = NounSlab::new();
+                                slab.copy_into(effect_cell.tail());
+                                slab
+                            };
+                            if !current_attempt.is_empty() {
+                                next_attempt = Some(candidate_slab);
+                            } else {
+                                let (cur_handle, attempt_handle) = handle.dup();
+                                handle = cur_handle;
+                                current_attempt.spawn(mining_attempt(candidate_slab, attempt_handle));
+                            }
+                        }
+                    },
+                    mining_attempt_res = current_attempt.join_next(), if !current_attempt.is_empty()  => {
+                        if let Some(Err(e)) = mining_attempt_res {
+                            warn!("Error during mining attempt: {e:?}");
+                        }
+                        let Some(candidate_slab) = next_attempt else {
+                            continue;
+                        };
+                        next_attempt = None;
+                        let (cur_handle, attempt_handle) = handle.dup();
+                        handle = cur_handle;
+                        current_attempt.spawn(mining_attempt(candidate_slab, attempt_handle));
+
+                    }
+                }
+            }
+        })
+    })
+}
+
+pub async fn mining_attempt(candidate: NounSlab, handle: NockAppHandle) -> () {
+    let snapshot_dir =
+        tokio::task::spawn_blocking(|| tempdir().expect("Failed to create temporary directory"))
+            .await
+            .expect("Failed to create temporary directory");
+    let hot_state = zkvm_jetpack::hot::produce_prover_hot_state();
+    let snapshot_path_buf = snapshot_dir.path().to_path_buf();
+    let jam_paths = JamPaths::new(snapshot_dir.path());
+    // Spawns a new std::thread for this mining attempt
+    let kernel =
+        Kernel::load_with_hot_state_huge(snapshot_path_buf, jam_paths, KERNEL, &hot_state, false)
+            .await
+            .expect("Could not load mining kernel");
+    let effects_slab = kernel
+        .poke(MiningWire::Candidate.to_wire(), candidate)
+        .await
+        .expect("Could not poke mining kernel with candidate");
+    for effect in effects_slab.to_vec() {
+        let Ok(effect_cell) = (unsafe { effect.root().as_cell() }) else {
+            drop(effect);
+            continue;
+        };
+        if effect_cell.head().eq_bytes("command") {
+            handle
+                .poke(MiningWire::Mined.to_wire(), effect)
+                .await
+                .expect("Could not poke nockchain with mined PoW");
+        }
+    }
+}
+
+#[instrument(skip(handle, pubkey))]
+async fn set_mining_key(
+    handle: &NockAppHandle,
+    pubkey: String,
+) -> Result<PokeResult, NockAppError> {
+    let mut set_mining_key_slab = NounSlab::new();
+    let set_mining_key = Atom::from_value(&mut set_mining_key_slab, "set-mining-key")
+        .expect("Failed to create set-mining-key atom");
+    let pubkey_cord =
+        Atom::from_value(&mut set_mining_key_slab, pubkey).expect("Failed to create pubkey atom");
+    let set_mining_key_poke = T(
+        &mut set_mining_key_slab,
+        &[D(tas!(b"command")), set_mining_key.as_noun(), pubkey_cord.as_noun()],
+    );
+    set_mining_key_slab.set_root(set_mining_key_poke);
+
+    handle
+        .poke(MiningWire::SetPubKey.to_wire(), set_mining_key_slab)
+        .await
+}
+
+async fn set_mining_key_advanced(
+    handle: &NockAppHandle,
+    configs: Vec<MiningKeyConfig>,
+) -> Result<PokeResult, NockAppError> {
+    let mut set_mining_key_slab = NounSlab::new();
+    let set_mining_key_adv = Atom::from_value(&mut set_mining_key_slab, "set-mining-key-advanced")
+        .expect("Failed to create set-mining-key-advanced atom");
+
+    // Create the list of configs
+    let mut configs_list = D(0);
+    for config in configs {
+        // Create the list of keys
+        let mut keys_noun = D(0);
+        for key in config.keys {
+            let key_atom =
+                Atom::from_value(&mut set_mining_key_slab, key).expect("Failed to create key atom");
+            keys_noun = T(&mut set_mining_key_slab, &[key_atom.as_noun(), keys_noun]);
+        }
+
+        // Create the config tuple [share m keys]
+        let config_tuple = T(
+            &mut set_mining_key_slab,
+            &[D(config.share), D(config.m), keys_noun],
+        );
+
+        configs_list = T(&mut set_mining_key_slab, &[config_tuple, configs_list]);
+    }
+
+    let set_mining_key_poke = T(
+        &mut set_mining_key_slab,
+        &[D(tas!(b"command")), set_mining_key_adv.as_noun(), configs_list],
+    );
+    set_mining_key_slab.set_root(set_mining_key_poke);
+
+    handle
+        .poke(MiningWire::SetPubKey.to_wire(), set_mining_key_slab)
+        .await
+}
+
+//TODO add %set-mining-key-multisig poke
+#[instrument(skip(handle))]
+async fn enable_mining(handle: &NockAppHandle, enable: bool) -> Result<PokeResult, NockAppError> {
+    let mut enable_mining_slab = NounSlab::new();
+    let enable_mining = Atom::from_value(&mut enable_mining_slab, "enable-mining")
+        .expect("Failed to create enable-mining atom");
+    let enable_mining_poke = T(
+        &mut enable_mining_slab,
+        &[D(tas!(b"command")), enable_mining.as_noun(), D(if enable { 0 } else { 1 })],
+    );
+    enable_mining_slab.set_root(enable_mining_poke);
+    handle
+        .poke(MiningWire::Enable.to_wire(), enable_mining_slab)
+        .await
+}

+ 0 - 32
crates/nockvm/rust/assert_no_alloc/Cargo.toml

@@ -1,32 +0,0 @@
-[package]
-name = "assert_no_alloc"
-version = "1.1.2"
-authors = ["Florian Jung <flo@windfis.ch>"]
-edition = "2018"
-license = "BSD-1-Clause"
-description = "Custom Rust allocator allowing to temporarily disable memory (de)allocations for a thread. Aborts or prints a warning if allocating although forbidden."
-homepage = "https://github.com/Windfisch/rust-assert-no-alloc"
-repository = "https://github.com/Windfisch/rust-assert-no-alloc"
-readme = "README.md"
-keywords = ["allocator", "real-time", "debug", "audio"]
-categories = ["development-tools::debugging"]
-
-[features]
-default = ["warn_debug"]
-warn_debug = []
-warn_release = []
-disable_release = []
-
-# Print a backtrace before aborting the program when an allocation failure happens
-backtrace = ["dep:backtrace"]
-# Use the `log` crate instead of printing to STDERR
-# WARNING: If the allocation failure happens during a logger call, then
-#          depending on the logger's implementation this may block indefinitely
-log = ["dep:log"]
-
-[dependencies]
-backtrace = { version = "0.3", optional = true }
-log = { version = "0.4", optional = true }
-
-[package.metadata.docs.rs]
-features = ["warn_debug"]

+ 0 - 21
crates/nockvm/rust/assert_no_alloc/LICENSE

@@ -1,21 +0,0 @@
-assert_no_alloc -- A custom Rust allocator allowing to temporarily disable
-memory (de)allocations for a thread.
-
-Copyright (c) 2020 Florian Jung <flo@windfis.ch>
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 0 - 151
crates/nockvm/rust/assert_no_alloc/README.md

@@ -1,151 +0,0 @@
-assert_no_alloc
-===============
-
-This crate provides a custom allocator that allows to temporarily disable
-memory (de)allocations for a thread. If a (de)allocation is attempted
-anyway, the program will abort or print a warning.
-
-It uses thread local storage for the "disabled-flag/counter", and thus
-should be thread safe, if the underlying allocator (currently hard-coded
-to `std::alloc::System`) is.
-
-[documentation @ docs.rs](https://docs.rs/assert_no_alloc/1.1.0/assert_no_alloc/),
-[crates.io](https://crates.io/crates/assert_no_alloc)
-
-Rationale
----------
-
-No-allocation-zones are relevant e.g. in real-time scenarios like audio
-callbacks. Allocation and deallocation can take unpredictable amounts of
-time, and thus can *sometimes* lead to audible glitches because the audio
-data is not served in time.
-
-Debugging such problems can be hard, because it is difficult to reproduce
-such problems consistently. Avoiding such problems is also hard, since
-allocation/deallocation is a common thing to do and most libraries are not
-explicit whether certain functions can allocate or not. Also, this might
-even depend on the run-time situation (e.g. a `Vec::push` might allocate,
-but it is guaranteed to not allocate *if* enough space has been `reserve()`d
-before).
-
-To aid the developer in tackling these problems, this crate offers an easy
-way of detecting all forbidden allocations.
-
-How to use
-----------
-
-First, configure the features: `warn_debug` and `warn_release` change the
-behaviour from aborting your program into just printing an error message
-on `stderr`. Aborting is useful for debugging purposes, as it allows you
-to retrieve a stacktrace, while warning is less intrusive.
-
-Note that you need to disable the (default-enabled) `disable_release` feature
-by specify `default-features = false` if you want to use `warn_release`. If
-`disable_release` is set (which is the default), then this crate will do
-nothing if built in `--release` mode.
-
-Second, use the allocator provided by this crate. Add this to `main.rs`:
-
-```rust
-use assert_no_alloc::*;
-
-#[cfg(debug_assertions)] // required when disable_release is set (default)
-#[global_allocator]
-static A: AllocDisabler = AllocDisabler;
-```
-
-Third, wrap code sections that may not allocate like this:
-
-```rust
-use assert_no_alloc::assert_no_alloc;
-
-assert_no_alloc(|| {
-	println!("This code can not allocate.");
-});
-```
-
-Advanced use
-------------
-
-Values can be returned using:
-
-```rust
-use assert_no_alloc::assert_no_alloc;
-
-let answer = assert_no_alloc(|| { 42 });
-```
-
-The effect of `assert_no_alloc` can be overridden using `permit_alloc`:
-
-```rust
-use assert_no_alloc::{assert_no_alloc, permit_alloc};
-
-assert_no_alloc(|| {
-	permit_alloc(|| {
-		// Allocate some memory here. This will work.
-	});
-});
-```
-
-This is useful for test stubs whose code is executed in an `assert_no_alloc`
-context.
-
-Objects that deallocate upon `Drop` can be wrapped in `PermitDrop`:
-
-```rust
-use assert_no_alloc::{PermitDrop, permit_alloc};
-
-let foo = PermitDrop::new(
-    permit_alloc(||
-        Box::new(..)
-    )
-);
-```
-
-Dropping `foo` will not trigger an assertion (but dropping a `Box` would).
-
-`assert_no_alloc()` calls can be nested, with proper panic unwinding handling.
-
-Note that to fully bypass this crate, e.g. when in release mode, you need to
-*both* have the `disable_release` feature flag enabled (which it is by default)
-and to not register `AllocDisabler` as `global_allocator`.
-
-Optional features
------------------
-
-These compile time features are not enabled by default:
-
-- `backtrace` causes a backtrace to be printed before the allocation failure.
-  This backtrace is gathered at runtime, and its accuracy depends on the
-  platform and the compilation options used.
-- `log` uses the `log` crate to write the allocation failure message to the
-  configured logger. If the `backtrace` feature is also enabled, then the
-  backtrace will also be written to the logger This can be useful when using a
-  logger that writes directly to a file or any other place that isn't STDERR.
-
-  The main caveat here is that if the allocation was caused by the logger and if
-  the logger wraps its entire log function in a regular non-entrant mutex, then
-  this may result in a deadlock. Make sure your logger doesn't do this before
-  enabling this feature.
-
-Examples
---------
-
-See [examples/main.rs](https://github.com/Windfisch/rust-assert-no-alloc/blob/master/examples/main.rs) for an example.
-
-You can try out the different feature flags:
-
-- `cargo run --example main` -> memory allocation of 4 bytes failed. Aborted (core dumped)
-- `cargo run --example main  --release --no-default-features` -> same as above.
-- `cargo run --example main --features=warn_debug` -> Tried to (de)allocate memory in a thread that forbids allocator calls! This will not be executed if the above allocation has aborted.
-- `cargo run --example main --features=warn_release --release --no-default-features` -> same as above.
-- `cargo run --example main --release` will not even check for forbidden allocations
-
-Test suite
-----------
-
-The tests will fail to compile with the default features. Run them using:
-
-```bash
-cargo test --features=warn_debug --tests
-```

+ 0 - 37
crates/nockvm/rust/assert_no_alloc/examples/main.rs

@@ -1,37 +0,0 @@
-use assert_no_alloc::*;
-
-#[cfg(debug_assertions)]
-#[global_allocator]
-static A: AllocDisabler = AllocDisabler;
-
-fn main() {
-    println!("Alloc is allowed. Let's allocate some memory...");
-    let vec_can_allocate = vec![42; 10];
-    println!("This will be executed if the above allocation succeeds: {vec_can_allocate:?}");
-
-    println!();
-
-    let fib5 = assert_no_alloc(|| {
-        println!("Alloc is forbidden. Let's calculate something without memory allocations...");
-
-        fn fib(n: u32) -> u32 {
-            if n <= 1 {
-                1
-            } else {
-                fib(n - 1) + fib(n - 2)
-            }
-        }
-
-        fib(5)
-    });
-    println!("\tSuccess, the 5th fibonacci number is {}", fib5);
-    println!();
-
-    assert_no_alloc(|| {
-        println!("Alloc is forbidden. Let's allocate some memory...");
-        let vec_cannot_allocate = vec![42; 100];
-        println!("This will not be executed if the above allocation has aborted. {vec_cannot_allocate:?}");
-    });
-
-    println!("This will not be executed if the above allocation has aborted.");
-}

+ 0 - 305
crates/nockvm/rust/assert_no_alloc/src/lib.rs

@@ -1,305 +0,0 @@
-#![allow(unused)]
-/* assert_no_alloc -- A custom Rust allocator allowing to temporarily disable
- * memory (de)allocations for a thread.
- *
- * Copyright (c) 2020 Florian Jung <flo@windfis.ch>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#![doc = include_str!("../README.md")]
-
-use std::alloc::{GlobalAlloc, Layout, System};
-use std::cell::Cell;
-
-// check for mutually exclusive features.
-#[cfg(all(feature = "disable_release", feature = "warn_release"))]
-compile_error!("disable_release cannot be active at the same time with warn_release");
-
-#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled
-thread_local! {
-    static ALLOC_FORBID_COUNT: Cell<u32> = const { Cell::new(0) };
-    static ALLOC_PERMIT_COUNT: Cell<u32> = const { Cell::new(0) };
-
-    #[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))]
-    static ALLOC_VIOLATION_COUNT: Cell<u32> = const { Cell::new(0) };
-}
-
-#[cfg(all(feature = "disable_release", not(debug_assertions)))] // if disabled
-pub fn assert_no_alloc<T, F: FnOnce() -> T>(func: F) -> T {
-    // no-op
-    func()
-}
-
-#[cfg(all(feature = "disable_release", not(debug_assertions)))] // if disabled
-pub fn permit_alloc<T, F: FnOnce() -> T>(func: F) -> T {
-    // no-op
-    func()
-}
-
-#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled
-/// Calls the `func` closure, but forbids any (de)allocations.
-///
-/// If a call to the allocator is made, the program will abort with an error,
-/// print a warning (depending on the `warn_debug` feature flag. Or ignore
-/// the situation, when compiled in `--release` mode with the `disable_release`
-///feature flag set (which is the default)).
-pub fn assert_no_alloc<T, F: FnOnce() -> T>(func: F) -> T {
-    // RAII guard for managing the forbid counter. This is to ensure correct behaviour
-    // when catch_unwind is used
-    struct Guard;
-    impl Guard {
-        fn new() -> Guard {
-            ALLOC_FORBID_COUNT.with(|c| c.set(c.get() + 1));
-            Guard
-        }
-    }
-    impl Drop for Guard {
-        fn drop(&mut self) {
-            ALLOC_FORBID_COUNT.with(|c| c.set(c.get() - 1));
-        }
-    }
-
-    #[cfg(any(
-        all(feature = "warn_debug", debug_assertions),
-        all(feature = "warn_release", not(debug_assertions))
-    ))] // if warn mode is selected
-    let old_violation_count = violation_count();
-
-    let guard = Guard::new(); // increment the forbid counter
-    let ret = func();
-    std::mem::drop(guard); // decrement the forbid counter
-
-    #[cfg(any(
-        all(feature = "warn_debug", debug_assertions),
-        all(feature = "warn_release", not(debug_assertions))
-    ))] // if warn mode is selected
-    if violation_count() > old_violation_count {
-        eprintln!("Tried to (de)allocate memory in a thread that forbids allocator calls!");
-    }
-
-    ret
-}
-
-/// Calls the `func` closure, but ensures that the forbid and permit counters
-/// are maintained accurately even if a longjmp originates and terminates
-/// within the closure. If you longjmp over this function, we can't fix
-/// anything about it.
-#[cfg(all(feature = "disable_release", not(debug_assertions)))] // if disabled
-pub fn ensure_alloc_counters<T, F: FnOnce() -> T>(func: F) -> T {
-    func()
-}
-
-#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled
-pub fn ensure_alloc_counters<T, F: FnOnce() -> T>(func: F) -> T {
-    let forbid_counter = ALLOC_FORBID_COUNT.with(|c| c.get());
-    let permit_counter = ALLOC_PERMIT_COUNT.with(|c| c.get());
-
-    let ret = func();
-
-    ALLOC_FORBID_COUNT.with(|c| c.set(forbid_counter));
-    ALLOC_PERMIT_COUNT.with(|c| c.set(permit_counter));
-
-    ret
-}
-
-#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled
-/// Calls the `func` closure. Allocations are temporarily allowed, even if this
-/// code runs inside of assert_no_alloc.
-pub fn permit_alloc<T, F: FnOnce() -> T>(func: F) -> T {
-    // RAII guard for managing the permit counter
-    struct Guard;
-    impl Guard {
-        fn new() -> Guard {
-            ALLOC_PERMIT_COUNT.with(|c| c.set(c.get() + 1));
-            Guard
-        }
-    }
-    impl Drop for Guard {
-        fn drop(&mut self) {
-            ALLOC_PERMIT_COUNT.with(|c| c.set(c.get() - 1));
-        }
-    }
-
-    let guard = Guard::new(); // increment the forbid counter
-    let ret = func();
-    std::mem::drop(guard); // decrement the forbid counter
-
-    ret
-}
-
-#[cfg(any(
-    all(feature = "warn_debug", debug_assertions),
-    all(feature = "warn_release", not(debug_assertions))
-))] // if warn mode is selected
-/// Returns the count of allocation warnings emitted so far.
-///
-/// Only available when the `warn_debug` or `warn release` features are enabled.
-pub fn violation_count() -> u32 {
-    ALLOC_VIOLATION_COUNT.with(|c| c.get())
-}
-
-#[cfg(any(
-    all(feature = "warn_debug", debug_assertions),
-    all(feature = "warn_release", not(debug_assertions))
-))] // if warn mode is selected
-/// Resets the count of allocation warnings to zero.
-///
-/// Only available when the `warn_debug` or `warn release` features are enabled.
-pub fn reset_violation_count() {
-    ALLOC_VIOLATION_COUNT.with(|c| c.set(0));
-}
-
-#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled
-/// The custom allocator that handles the checking.
-///
-/// To use this crate, you must add the following in your `main.rs`:
-/// ```rust
-/// use assert_no_alloc::*;
-/// // ...
-/// #[cfg(debug_assertions)]
-/// #[global_allocator]
-/// static A: AllocDisabler = AllocDisabler;
-/// ```
-pub struct AllocDisabler;
-
-#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled
-impl AllocDisabler {
-    #[allow(unused_variables)]
-    fn check(&self, layout: Layout) {
-        let forbid_count = ALLOC_FORBID_COUNT.with(|f| f.get());
-        let permit_count = ALLOC_PERMIT_COUNT.with(|p| p.get());
-        if forbid_count > 0 && permit_count == 0 {
-            #[cfg(any(
-                all(feature = "warn_debug", debug_assertions),
-                all(feature = "warn_release", not(debug_assertions))
-            ))] // if warn mode is selected
-            ALLOC_VIOLATION_COUNT.with(|c| c.set(c.get() + 1));
-
-            #[cfg(any(
-                all(not(feature = "warn_debug"), debug_assertions),
-                all(not(feature = "warn_release"), not(debug_assertions))
-            ))] // if abort mode is selected
-            {
-                #[cfg(all(feature = "log", feature = "backtrace"))]
-                permit_alloc(|| {
-                    log::error!(
-                        "Memory allocation of {} bytes failed from:\n{:?}",
-                        layout.size(),
-                        backtrace::Backtrace::new()
-                    )
-                });
-                #[cfg(all(feature = "log", not(feature = "backtrace")))]
-                permit_alloc(|| log::error!("Memory allocation of {} bytes failed", layout.size()));
-
-                #[cfg(all(not(feature = "log"), feature = "backtrace"))]
-                permit_alloc(|| {
-                    eprintln!(
-                        "Allocation failure from:\n{:?}",
-                        backtrace::Backtrace::new()
-                    )
-                });
-
-                // This handler can be overridden (although as of writing, the API to do so is still
-                // unstable) so we must always call this even when the log feature is enabled
-                std::alloc::handle_alloc_error(layout);
-            }
-        }
-    }
-}
-
-#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled
-unsafe impl GlobalAlloc for AllocDisabler {
-    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
-        self.check(layout);
-        System.alloc(layout)
-    }
-
-    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
-        self.check(layout);
-        System.dealloc(ptr, layout)
-    }
-}
-
-/// Wrapper for objects whose Drop implementation shall be permitted
-/// to (de)allocate.
-///
-/// Typical usage:
-///
-/// ```rust
-/// use assert_no_alloc::{PermitDrop, permit_alloc};
-/// let foo = PermitDrop::new(
-///     permit_alloc(||
-///         Box::new(..)
-///     )
-/// );
-/// ```
-///
-/// Here, creation of the Box is guarded by the explicit `permit_alloc` call,
-/// and destruction of the Box is guarded by PermitDrop. Neither creation nor
-/// destruction will cause an assertion failure from within `assert_no_alloc`.
-pub struct PermitDrop<T>(Option<T>);
-
-impl<T> PermitDrop<T> {
-    pub fn new(t: T) -> PermitDrop<T> {
-        permit_alloc(|| PermitDrop(Some(t)))
-    }
-}
-
-impl<T> std::ops::Deref for PermitDrop<T> {
-    type Target = T;
-    fn deref(&self) -> &T {
-        self.0.as_ref().unwrap_or_else(|| {
-            panic!(
-                "Panicked at {}:{} (git sha: {})",
-                file!(),
-                line!(),
-                option_env!("GIT_SHA").unwrap_or("unknown")
-            )
-        })
-    }
-}
-
-impl<T> std::ops::DerefMut for PermitDrop<T> {
-    fn deref_mut(&mut self) -> &mut T {
-        self.0.as_mut().unwrap_or_else(|| {
-            panic!(
-                "Panicked at {}:{} (git sha: {})",
-                file!(),
-                line!(),
-                option_env!("GIT_SHA").unwrap_or("unknown")
-            )
-        })
-    }
-}
-
-impl<I: Iterator> Iterator for PermitDrop<I> {
-    type Item = I::Item;
-    fn next(&mut self) -> Option<Self::Item> {
-        (**self).next()
-    }
-}
-
-impl<T> Drop for PermitDrop<T> {
-    fn drop(&mut self) {
-        let mut tmp = None;
-        std::mem::swap(&mut tmp, &mut self.0);
-        permit_alloc(|| {
-            std::mem::drop(tmp);
-        });
-    }
-}

+ 0 - 153
crates/nockvm/rust/assert_no_alloc/tests/test.rs

@@ -1,153 +0,0 @@
-// use assert_no_alloc::*;
-
-// #[global_allocator]
-// static A: AllocDisabler = AllocDisabler;
-
-// // TODO: Get this working in miri later
-// #[cfg(all(feature = "warn_debug", debug_assertions))]
-// #[cfg_attr(miri, ignore)]
-// mod test {
-//     use super::*;
-//     use std::panic::catch_unwind;
-
-//     // This is only a kludge; what we actually want to check is "will do_alloc() be optimized out?", e.g. due to
-//     // compiler optimizations turned on in --release mode. We can't do that, the closest we can get is to check
-//     // whether debug_assertions are disabled, which coincidentially also happens in release mode.
-//     #[cfg(not(debug_assertions))]
-//     compile_error!(
-//         "The test suite only works in debug mode. Use `cargo test --features warn_debug`"
-//     );
-
-//     #[cfg(feature = "warn_debug")]
-//     fn check_and_reset() -> bool {
-//         let result = violation_count() > 0;
-//         reset_violation_count();
-//         result
-//     }
-
-//     // Provide a stub check_and_reset() function if warn_debug is disabled. This will never be compiled due to the
-//     // compile_error!() above, but this stub ensures that the output will not be cluttered with spurious error
-//     // messages.
-//     #[cfg(not(feature = "warn_debug"))]
-//     fn check_and_reset() -> bool {
-//         unreachable!()
-//     }
-
-//     fn do_alloc() {
-//         let _tmp: Box<u32> = Box::new(42);
-//     }
-
-//     #[test]
-//     fn ok_noop() {
-//         assert_eq!(check_and_reset(), false);
-//         do_alloc();
-//         assert_eq!(check_and_reset(), false);
-//     }
-
-//     #[test]
-//     fn ok_simple() {
-//         assert_eq!(check_and_reset(), false);
-//         assert_no_alloc(|| {});
-
-//         do_alloc();
-//         assert_eq!(check_and_reset(), false);
-//     }
-
-//     #[test]
-//     fn ok_nested() {
-//         assert_eq!(check_and_reset(), false);
-//         assert_no_alloc(|| {
-//             assert_no_alloc(|| {});
-//         });
-
-//         do_alloc();
-//         assert_eq!(check_and_reset(), false);
-//     }
-
-//     #[test]
-//     fn forbidden_simple() {
-//         assert_eq!(check_and_reset(), false);
-//         assert_no_alloc(|| {
-//             do_alloc();
-//         });
-//         assert_eq!(check_and_reset(), true);
-//     }
-
-//     #[test]
-//     fn forbidden_in_nested() {
-//         assert_eq!(check_and_reset(), false);
-//         assert_no_alloc(|| {
-//             assert_no_alloc(|| {
-//                 do_alloc();
-//             });
-//         });
-//         assert_eq!(check_and_reset(), true);
-//     }
-
-//     #[test]
-//     fn forbidden_after_nested() {
-//         assert_eq!(check_and_reset(), false);
-//         assert_no_alloc(|| {
-//             assert_no_alloc(|| {});
-//             do_alloc();
-//         });
-//         assert_eq!(check_and_reset(), true);
-//     }
-
-//     #[test]
-//     fn unwind_ok() {
-//         assert_eq!(check_and_reset(), false);
-//         assert_no_alloc(|| {
-//             let r = catch_unwind(|| {
-//                 assert_no_alloc(|| {
-//                     panic!();
-//                 });
-//             });
-//             assert!(r.is_err());
-//         });
-//         check_and_reset(); // unwinding might have allocated memory; we don't care about that.
-//         do_alloc();
-//         assert_eq!(check_and_reset(), false);
-//     }
-
-//     #[test]
-//     fn unwind_nested() {
-//         assert_eq!(check_and_reset(), false);
-//         assert_no_alloc(|| {
-//             let r = catch_unwind(|| {
-//                 assert_no_alloc(|| {
-//                     panic!();
-//                 });
-//             });
-//             assert!(r.is_err());
-
-//             check_and_reset(); // unwinding might have allocated memory; we don't care about that.
-//             do_alloc();
-//             assert_eq!(check_and_reset(), true);
-//         });
-//     }
-
-//     #[test]
-//     fn unwind_nested2() {
-//         assert_eq!(check_and_reset(), false);
-//         assert_no_alloc(|| {
-//             assert_no_alloc(|| {
-//                 let r = catch_unwind(|| {
-//                     assert_no_alloc(|| {
-//                         assert_no_alloc(|| {
-//                             panic!();
-//                         });
-//                     });
-//                 });
-//                 assert!(r.is_err());
-
-//                 check_and_reset(); // unwinding might have allocated memory; we don't care about that.
-//                 do_alloc();
-//                 assert_eq!(check_and_reset(), true);
-//             });
-//         });
-//         check_and_reset(); // unwinding might have allocated memory; we don't care about that.
-//         do_alloc();
-//         assert_eq!(check_and_reset(), false);
-//     }
-// }

+ 2 - 1
crates/nockvm/rust/nockvm/Cargo.toml

@@ -10,7 +10,6 @@ missing_safety_doc = "allow"
 # Please keep these alphabetized
 [dependencies]
 # Internal dependencies
-assert_no_alloc = { workspace = true, features = [] }
 ibig.workspace = true
 murmur3.workspace = true
 nockvm_crypto = { workspace = true }
@@ -45,6 +44,8 @@ cc = "1.0"
 default = ["mmap"]
 malloc = []
 mmap = []
+# Dangerous
+no_check_oom = []
 # FOR DEBUGGING MEMORY ISSUES ONLY
 check_all = ["check_acyclic", "check_forwarding", "check_junior"]
 check_acyclic = []

+ 562 - 489
crates/nockvm/rust/nockvm/src/interpreter.rs

@@ -9,14 +9,16 @@ use crate::noun::{Atom, Cell, IndirectAtom, Noun, Slots, D, T};
 use crate::trace::{write_nock_trace, TraceInfo, TraceStack};
 use crate::unifying_equality::unifying_equality;
 use crate::{assert_acyclic, assert_no_forwarding_pointers, assert_no_junior_pointers, flog, noun};
-use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters};
 use bitvec::prelude::{BitSlice, Lsb0};
 use either::*;
 use nockvm_macros::tas;
-use std::ops::DerefMut;
+use std::ops::{DerefMut, Neg};
 use std::pin::Pin;
 use std::result;
+use std::sync::atomic::{AtomicIsize, Ordering};
+use std::sync::Arc;
 use std::time::Instant;
+use tracing::trace;
 
 crate::gdb!();
 
@@ -293,6 +295,39 @@ pub struct Context {
     pub cache: Hamt<Noun>,
     pub scry_stack: Noun,
     pub trace_info: Option<TraceInfo>,
+    pub running_status: Arc<AtomicIsize>,
+}
+
+#[derive(Debug, Clone)]
+pub struct NockCancelToken {
+    running_status: Arc<AtomicIsize>,
+}
+
+impl NockCancelToken {
+    pub const RUNNING_IDLE: isize = 0;
+
+    pub fn cancel(&self) -> bool {
+        loop {
+            let running = self.running_status.load(Ordering::SeqCst);
+            if running == Self::RUNNING_IDLE {
+                trace!("nock cancellation: already idle");
+                break false;
+            } else if running < Self::RUNNING_IDLE {
+                trace!("nock cancellation: already cancelled");
+                break false;
+            } else {
+                trace!("Nock cancellation: cancelling");
+                if let Ok(_) = self.running_status.compare_exchange(
+                    running,
+                    running.neg(),
+                    Ordering::SeqCst,
+                    Ordering::SeqCst,
+                ) {
+                    break true;
+                }
+            }
+        }
+    }
 }
 
 impl Context {
@@ -310,6 +345,12 @@ impl Context {
         self.cache = saved.cache;
     }
 
+    pub fn cancel_token(&self) -> NockCancelToken {
+        NockCancelToken {
+            running_status: self.running_status.clone(),
+        }
+    }
+
     /**
      * For jets that need a stack frame internally.
      *
@@ -386,6 +427,7 @@ pub type Result = result::Result<Noun, Error>;
 
 const BAIL_EXIT: Result = Err(Error::Deterministic(Mote::Exit, D(0)));
 const BAIL_FAIL: Result = Err(Error::NonDeterministic(Mote::Fail, D(0)));
+const BAIL_INTR: Result = Err(Error::NonDeterministic(Mote::Intr, D(0)));
 
 #[allow(unused_variables)]
 #[inline(always)]
@@ -401,6 +443,30 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
     let snapshot = context.save();
     let virtual_frame: *const u64 = context.stack.get_frame_pointer();
     let mut res: Noun = D(0);
+    loop {
+        let running_status = context.running_status.load(Ordering::SeqCst);
+        if running_status >= NockCancelToken::RUNNING_IDLE {
+            if context
+                .running_status
+                .compare_exchange(
+                    running_status,
+                    running_status + 1,
+                    Ordering::SeqCst,
+                    Ordering::SeqCst,
+                )
+                .is_ok()
+            {
+                break;
+            }
+        } else {
+            return Err(exit(
+                context,
+                &snapshot,
+                virtual_frame,
+                Error::NonDeterministic(Mote::Intr, D(0)),
+            ));
+        }
+    }
 
     // Setup stack for Nock computation
     unsafe {
@@ -425,545 +491,552 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
     // ```
     //
     // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use)
-    let nock = assert_no_alloc(|| {
-        ensure_alloc_counters(|| {
-            unsafe {
-                push_formula(&mut context.stack, formula, true)?;
-
-                loop {
-                    let work: NockWork = *context.stack.top();
-                    match work {
-                        NockWork::Done => {
-                            write_trace(context);
+    let nock = unsafe {
+        push_formula(&mut context.stack, formula, true)?;
+
+        loop {
+            let work: NockWork = *context.stack.top();
+            match work {
+                NockWork::Done => {
+                    write_trace(context);
+
+                    let stack = &mut context.stack;
+                    debug_assertions(stack, orig_subject);
+                    debug_assertions(stack, subject);
+                    debug_assertions(stack, res);
+
+                    stack.preserve(&mut context.cache);
+                    stack.preserve(&mut context.cold);
+                    stack.preserve(&mut context.warm);
+                    stack.preserve(&mut res);
+                    stack.frame_pop();
+
+                    debug_assertions(stack, orig_subject);
+                    debug_assertions(stack, res);
+
+                    break Ok(res);
+                }
+                NockWork::Ret => {
+                    write_trace(context);
+
+                    let stack = &mut context.stack;
+                    debug_assertions(stack, orig_subject);
+                    debug_assertions(stack, subject);
+                    debug_assertions(stack, res);
+
+                    stack.preserve(&mut context.cache);
+                    stack.preserve(&mut context.cold);
+                    stack.preserve(&mut context.warm);
+                    stack.preserve(&mut res);
+                    stack.frame_pop();
+
+                    debug_assertions(stack, orig_subject);
+                    debug_assertions(stack, res);
+                }
+                NockWork::WorkCons(mut cons) => match cons.todo {
+                    TodoCons::ComputeHead => {
+                        cons.todo = TodoCons::ComputeTail;
+                        *context.stack.top() = NockWork::WorkCons(cons);
+                        push_formula(&mut context.stack, cons.head, false)?;
+                    }
+                    TodoCons::ComputeTail => {
+                        cons.todo = TodoCons::Cons;
+                        cons.head = res;
+                        *context.stack.top() = NockWork::WorkCons(cons);
+                        push_formula(&mut context.stack, cons.tail, false)?;
+                    }
+                    TodoCons::Cons => {
+                        let stack = &mut context.stack;
+                        res = T(stack, &[cons.head, res]);
+                        stack.pop::<NockWork>();
+                    }
+                },
+                NockWork::Work0(zero) => {
+                    if let Ok(noun) = subject.slot_atom(zero.axis) {
+                        res = noun;
+                        context.stack.pop::<NockWork>();
+                    } else {
+                        // Axis invalid for input Noun
+                        break BAIL_EXIT;
+                    }
+                }
+                NockWork::Work1(once) => {
+                    res = once.noun;
+                    context.stack.pop::<NockWork>();
+                }
+                NockWork::Work2(mut vale) => match vale.todo {
+                    Todo2::ComputeSubject => {
+                        vale.todo = Todo2::ComputeFormula;
+                        *context.stack.top() = NockWork::Work2(vale);
+                        push_formula(&mut context.stack, vale.subject, false)?;
+                    }
+                    Todo2::ComputeFormula => {
+                        vale.todo = Todo2::ComputeResult;
+                        vale.subject = res;
+                        *context.stack.top() = NockWork::Work2(vale);
+                        push_formula(&mut context.stack, vale.formula, false)?;
+                    }
+                    Todo2::ComputeResult => {
+                        let stack = &mut context.stack;
+                        if vale.tail {
+                            stack.pop::<NockWork>();
+                            subject = vale.subject;
+                            push_formula(stack, res, true)?;
+                        } else {
+                            vale.todo = Todo2::RestoreSubject;
+                            std::mem::swap(&mut vale.subject, &mut subject);
+                            *stack.top() = NockWork::Work2(vale);
 
-                            let stack = &mut context.stack;
                             debug_assertions(stack, orig_subject);
                             debug_assertions(stack, subject);
                             debug_assertions(stack, res);
 
-                            stack.preserve(&mut context.cache);
-                            stack.preserve(&mut context.cold);
-                            stack.preserve(&mut context.warm);
-                            stack.preserve(&mut res);
-                            stack.frame_pop();
-
-                            debug_assertions(stack, orig_subject);
-                            debug_assertions(stack, res);
-
-                            break Ok(res);
+                            mean_frame_push(stack, 0);
+                            *stack.push() = NockWork::Ret;
+                            push_formula(stack, res, true)?;
                         }
-                        NockWork::Ret => {
-                            write_trace(context);
-
-                            let stack = &mut context.stack;
-                            debug_assertions(stack, orig_subject);
-                            debug_assertions(stack, subject);
-                            debug_assertions(stack, res);
+                    }
+                    Todo2::RestoreSubject => {
+                        let stack = &mut context.stack;
 
-                            stack.preserve(&mut context.cache);
-                            stack.preserve(&mut context.cold);
-                            stack.preserve(&mut context.warm);
-                            stack.preserve(&mut res);
-                            stack.frame_pop();
+                        subject = vale.subject;
+                        stack.pop::<NockWork>();
 
-                            debug_assertions(stack, orig_subject);
-                            debug_assertions(stack, res);
+                        debug_assertions(stack, orig_subject);
+                        debug_assertions(stack, subject);
+                        debug_assertions(stack, res);
+                    }
+                },
+                NockWork::Work3(mut thee) => match thee.todo {
+                    Todo3::ComputeChild => {
+                        thee.todo = Todo3::ComputeType;
+                        *context.stack.top() = NockWork::Work3(thee);
+                        push_formula(&mut context.stack, thee.child, false)?;
+                    }
+                    Todo3::ComputeType => {
+                        res = if res.is_cell() { D(0) } else { D(1) };
+                        context.stack.pop::<NockWork>();
+                    }
+                },
+                NockWork::Work4(mut four) => match four.todo {
+                    Todo4::ComputeChild => {
+                        four.todo = Todo4::Increment;
+                        *context.stack.top() = NockWork::Work4(four);
+                        push_formula(&mut context.stack, four.child, false)?;
+                    }
+                    Todo4::Increment => {
+                        if let Ok(atom) = res.as_atom() {
+                            res = inc(&mut context.stack, atom).as_noun();
+                            context.stack.pop::<NockWork>();
+                        } else {
+                            // Cannot increment (Nock 4) a cell
+                            break BAIL_EXIT;
                         }
-                        NockWork::WorkCons(mut cons) => match cons.todo {
-                            TodoCons::ComputeHead => {
-                                cons.todo = TodoCons::ComputeTail;
-                                *context.stack.top() = NockWork::WorkCons(cons);
-                                push_formula(&mut context.stack, cons.head, false)?;
-                            }
-                            TodoCons::ComputeTail => {
-                                cons.todo = TodoCons::Cons;
-                                cons.head = res;
-                                *context.stack.top() = NockWork::WorkCons(cons);
-                                push_formula(&mut context.stack, cons.tail, false)?;
-                            }
-                            TodoCons::Cons => {
-                                let stack = &mut context.stack;
-                                res = T(stack, &[cons.head, res]);
-                                stack.pop::<NockWork>();
-                            }
-                        },
-                        NockWork::Work0(zero) => {
-                            if let Ok(noun) = subject.slot_atom(zero.axis) {
-                                res = noun;
-                                context.stack.pop::<NockWork>();
+                    }
+                },
+                NockWork::Work5(mut five) => match five.todo {
+                    Todo5::ComputeLeftChild => {
+                        five.todo = Todo5::ComputeRightChild;
+                        *context.stack.top() = NockWork::Work5(five);
+                        push_formula(&mut context.stack, five.left, false)?;
+                    }
+                    Todo5::ComputeRightChild => {
+                        five.todo = Todo5::TestEquals;
+                        five.left = res;
+                        *context.stack.top() = NockWork::Work5(five);
+                        push_formula(&mut context.stack, five.right, false)?;
+                    }
+                    Todo5::TestEquals => {
+                        let stack = &mut context.stack;
+                        let saved_value_ptr = &mut five.left;
+                        res = if unifying_equality(stack, &mut res, saved_value_ptr) {
+                            D(0)
+                        } else {
+                            D(1)
+                        };
+                        stack.pop::<NockWork>();
+                    }
+                },
+                NockWork::Work6(mut cond) => match cond.todo {
+                    Todo6::ComputeTest => {
+                        cond.todo = Todo6::ComputeBranch;
+                        *context.stack.top() = NockWork::Work6(cond);
+                        push_formula(&mut context.stack, cond.test, false)?;
+                    }
+                    Todo6::ComputeBranch => {
+                        let stack = &mut context.stack;
+                        stack.pop::<NockWork>();
+                        if let Left(direct) = res.as_either_direct_allocated() {
+                            if direct.data() == 0 {
+                                push_formula(stack, cond.zero, cond.tail)?;
+                            } else if direct.data() == 1 {
+                                push_formula(stack, cond.once, cond.tail)?;
                             } else {
-                                // Axis invalid for input Noun
+                                // Test branch of Nock 6 must return 0 or 1
                                 break BAIL_EXIT;
                             }
+                        } else {
+                            // Test branch of Nock 6 must return a direct atom
+                            break BAIL_EXIT;
                         }
-                        NockWork::Work1(once) => {
-                            res = once.noun;
-                            context.stack.pop::<NockWork>();
+                    }
+                },
+                NockWork::Work7(mut pose) => match pose.todo {
+                    Todo7::ComputeSubject => {
+                        pose.todo = Todo7::ComputeResult;
+                        *context.stack.top() = NockWork::Work7(pose);
+                        push_formula(&mut context.stack, pose.subject, false)?;
+                    }
+                    Todo7::ComputeResult => {
+                        let stack = &mut context.stack;
+                        if pose.tail {
+                            stack.pop::<NockWork>();
+                            subject = res;
+                            push_formula(stack, pose.formula, true)?;
+                        } else {
+                            pose.todo = Todo7::RestoreSubject;
+                            pose.subject = subject;
+                            *stack.top() = NockWork::Work7(pose);
+                            subject = res;
+                            push_formula(stack, pose.formula, false)?;
                         }
-                        NockWork::Work2(mut vale) => match vale.todo {
-                            Todo2::ComputeSubject => {
-                                vale.todo = Todo2::ComputeFormula;
-                                *context.stack.top() = NockWork::Work2(vale);
-                                push_formula(&mut context.stack, vale.subject, false)?;
-                            }
-                            Todo2::ComputeFormula => {
-                                vale.todo = Todo2::ComputeResult;
-                                vale.subject = res;
-                                *context.stack.top() = NockWork::Work2(vale);
-                                push_formula(&mut context.stack, vale.formula, false)?;
-                            }
-                            Todo2::ComputeResult => {
+                    }
+                    Todo7::RestoreSubject => {
+                        subject = pose.subject;
+                        context.stack.pop::<NockWork>();
+                    }
+                },
+                NockWork::Work8(mut pins) => match pins.todo {
+                    Todo8::ComputeSubject => {
+                        pins.todo = Todo8::ComputeResult;
+                        *context.stack.top() = NockWork::Work8(pins);
+                        push_formula(&mut context.stack, pins.pin, false)?;
+                    }
+                    Todo8::ComputeResult => {
+                        let stack = &mut context.stack;
+                        if pins.tail {
+                            subject = T(stack, &[res, subject]);
+                            stack.pop::<NockWork>();
+                            push_formula(stack, pins.formula, true)?;
+                        } else {
+                            pins.todo = Todo8::RestoreSubject;
+                            pins.pin = subject;
+                            *stack.top() = NockWork::Work8(pins);
+                            subject = T(stack, &[res, subject]);
+                            push_formula(stack, pins.formula, false)?;
+                        }
+                    }
+                    Todo8::RestoreSubject => {
+                        subject = pins.pin;
+                        context.stack.pop::<NockWork>();
+                    }
+                },
+                NockWork::Work9(mut kale) => {
+                    match kale.todo {
+                        Todo9::ComputeCore => {
+                            kale.todo = Todo9::ComputeResult;
+                            *context.stack.top() = NockWork::Work9(kale);
+                            push_formula(&mut context.stack, kale.core, false)?;
+                        }
+                        Todo9::ComputeResult => {
+                            if let Ok(mut formula) = res.slot_atom(kale.axis) {
+                                if !cfg!(feature = "sham_hints") {
+                                    if let Some((jet, _path)) = context
+                                        .warm
+                                        .find_jet(&mut context.stack, &mut res, &mut formula)
+                                    {
+                                        match jet(context, res) {
+                                            Ok(jet_res) => {
+                                                res = jet_res;
+                                                context.stack.pop::<NockWork>();
+                                                continue;
+                                            }
+                                            Err(JetErr::Punt) => {}
+                                            Err(err) => {
+                                                break Err(err.into());
+                                            }
+                                        }
+                                    }
+                                };
+
                                 let stack = &mut context.stack;
-                                if vale.tail {
+                                if kale.tail {
                                     stack.pop::<NockWork>();
-                                    subject = vale.subject;
-                                    push_formula(stack, res, true)?;
+
+                                    // We could trace on 2 as well, but 2 only comes from Hoon via
+                                    // '.*', so we can assume it's never directly used to invoke
+                                    // jetted code.
+                                    if context.trace_info.is_some() {
+                                        if let Some(path) = context.cold.matches(stack, &mut res) {
+                                            append_trace(stack, path);
+                                        };
+                                    };
+
+                                    subject = res;
+                                    push_formula(stack, formula, true)?;
                                 } else {
-                                    vale.todo = Todo2::RestoreSubject;
-                                    std::mem::swap(&mut vale.subject, &mut subject);
-                                    *stack.top() = NockWork::Work2(vale);
+                                    kale.todo = Todo9::RestoreSubject;
+                                    kale.core = subject;
+                                    *stack.top() = NockWork::Work9(kale);
 
                                     debug_assertions(stack, orig_subject);
                                     debug_assertions(stack, subject);
                                     debug_assertions(stack, res);
 
+                                    subject = res;
                                     mean_frame_push(stack, 0);
                                     *stack.push() = NockWork::Ret;
-                                    push_formula(stack, res, true)?;
+                                    push_formula(stack, formula, true)?;
+
+                                    // We could trace on 2 as well, but 2 only comes from Hoon via
+                                    // '.*', so we can assume it's never directly used to invoke
+                                    // jetted code.
+                                    if context.trace_info.is_some() {
+                                        if let Some(path) = context.cold.matches(stack, &mut res) {
+                                            append_trace(stack, path);
+                                        };
+                                    };
                                 }
+                            } else {
+                                // Axis into core must be atom
+                                break BAIL_EXIT;
                             }
-                            Todo2::RestoreSubject => {
-                                let stack = &mut context.stack;
+                        }
+                        Todo9::RestoreSubject => {
+                            let stack = &mut context.stack;
 
-                                subject = vale.subject;
-                                stack.pop::<NockWork>();
+                            subject = kale.core;
+                            stack.pop::<NockWork>();
 
-                                debug_assertions(stack, orig_subject);
-                                debug_assertions(stack, subject);
-                                debug_assertions(stack, res);
-                            }
-                        },
-                        NockWork::Work3(mut thee) => match thee.todo {
-                            Todo3::ComputeChild => {
-                                thee.todo = Todo3::ComputeType;
-                                *context.stack.top() = NockWork::Work3(thee);
-                                push_formula(&mut context.stack, thee.child, false)?;
-                            }
-                            Todo3::ComputeType => {
-                                res = if res.is_cell() { D(0) } else { D(1) };
-                                context.stack.pop::<NockWork>();
-                            }
-                        },
-                        NockWork::Work4(mut four) => match four.todo {
-                            Todo4::ComputeChild => {
-                                four.todo = Todo4::Increment;
-                                *context.stack.top() = NockWork::Work4(four);
-                                push_formula(&mut context.stack, four.child, false)?;
-                            }
-                            Todo4::Increment => {
-                                if let Ok(atom) = res.as_atom() {
-                                    res = inc(&mut context.stack, atom).as_noun();
+                            debug_assertions(stack, orig_subject);
+                            debug_assertions(stack, subject);
+                            debug_assertions(stack, res);
+                        }
+                    }
+                }
+                NockWork::Work10(mut diet) => {
+                    match diet.todo {
+                        Todo10::ComputeTree => {
+                            diet.todo = Todo10::ComputePatch; // should we compute patch then tree?
+                            *context.stack.top() = NockWork::Work10(diet);
+                            push_formula(&mut context.stack, diet.tree, false)?;
+                        }
+                        Todo10::ComputePatch => {
+                            diet.todo = Todo10::Edit;
+                            diet.tree = res;
+                            *context.stack.top() = NockWork::Work10(diet);
+                            push_formula(&mut context.stack, diet.patch, false)?;
+                        }
+                        Todo10::Edit => {
+                            res = edit(&mut context.stack, diet.axis.as_bitslice(), res, diet.tree);
+                            context.stack.pop::<NockWork>();
+                        }
+                    }
+                }
+                NockWork::Work11D(mut dint) => match dint.todo {
+                    Todo11D::ComputeHint => {
+                        if let Some(ret) =
+                            hint::match_pre_hint(context, subject, dint.tag, dint.hint, dint.body)
+                        {
+                            match ret {
+                                Ok(found) => {
+                                    res = found;
                                     context.stack.pop::<NockWork>();
-                                } else {
-                                    // Cannot increment (Nock 4) a cell
-                                    break BAIL_EXIT;
                                 }
-                            }
-                        },
-                        NockWork::Work5(mut five) => match five.todo {
-                            Todo5::ComputeLeftChild => {
-                                five.todo = Todo5::ComputeRightChild;
-                                *context.stack.top() = NockWork::Work5(five);
-                                push_formula(&mut context.stack, five.left, false)?;
-                            }
-                            Todo5::ComputeRightChild => {
-                                five.todo = Todo5::TestEquals;
-                                five.left = res;
-                                *context.stack.top() = NockWork::Work5(five);
-                                push_formula(&mut context.stack, five.right, false)?;
-                            }
-                            Todo5::TestEquals => {
-                                let stack = &mut context.stack;
-                                let saved_value_ptr = &mut five.left;
-                                res = if unifying_equality(stack, &mut res, saved_value_ptr) {
-                                    D(0)
-                                } else {
-                                    D(1)
-                                };
-                                stack.pop::<NockWork>();
-                            }
-                        },
-                        NockWork::Work6(mut cond) => match cond.todo {
-                            Todo6::ComputeTest => {
-                                cond.todo = Todo6::ComputeBranch;
-                                *context.stack.top() = NockWork::Work6(cond);
-                                push_formula(&mut context.stack, cond.test, false)?;
-                            }
-                            Todo6::ComputeBranch => {
-                                let stack = &mut context.stack;
-                                stack.pop::<NockWork>();
-                                if let Left(direct) = res.as_either_direct_allocated() {
-                                    if direct.data() == 0 {
-                                        push_formula(stack, cond.zero, cond.tail)?;
-                                    } else if direct.data() == 1 {
-                                        push_formula(stack, cond.once, cond.tail)?;
-                                    } else {
-                                        // Test branch of Nock 6 must return 0 or 1
-                                        break BAIL_EXIT;
-                                    }
-                                } else {
-                                    // Test branch of Nock 6 must return a direct atom
-                                    break BAIL_EXIT;
+                                Err(err) => {
+                                    break Err(err);
                                 }
                             }
-                        },
-                        NockWork::Work7(mut pose) => match pose.todo {
-                            Todo7::ComputeSubject => {
-                                pose.todo = Todo7::ComputeResult;
-                                *context.stack.top() = NockWork::Work7(pose);
-                                push_formula(&mut context.stack, pose.subject, false)?;
-                            }
-                            Todo7::ComputeResult => {
-                                let stack = &mut context.stack;
-                                if pose.tail {
-                                    stack.pop::<NockWork>();
-                                    subject = res;
-                                    push_formula(stack, pose.formula, true)?;
-                                } else {
-                                    pose.todo = Todo7::RestoreSubject;
-                                    pose.subject = subject;
-                                    *stack.top() = NockWork::Work7(pose);
-                                    subject = res;
-                                    push_formula(stack, pose.formula, false)?;
+                        } else {
+                            dint.todo = Todo11D::ComputeResult;
+                            *context.stack.top() = NockWork::Work11D(dint);
+                            push_formula(&mut context.stack, dint.hint, false)?;
+                        }
+                    }
+                    Todo11D::ComputeResult => {
+                        if let Some(ret) = hint::match_pre_nock(
+                            context,
+                            subject,
+                            dint.tag,
+                            Some((dint.hint, res)),
+                            dint.body,
+                        ) {
+                            match ret {
+                                Ok(found) => {
+                                    res = found;
+                                    context.stack.pop::<NockWork>();
                                 }
-                            }
-                            Todo7::RestoreSubject => {
-                                subject = pose.subject;
-                                context.stack.pop::<NockWork>();
-                            }
-                        },
-                        NockWork::Work8(mut pins) => match pins.todo {
-                            Todo8::ComputeSubject => {
-                                pins.todo = Todo8::ComputeResult;
-                                *context.stack.top() = NockWork::Work8(pins);
-                                push_formula(&mut context.stack, pins.pin, false)?;
-                            }
-                            Todo8::ComputeResult => {
-                                let stack = &mut context.stack;
-                                if pins.tail {
-                                    subject = T(stack, &[res, subject]);
-                                    stack.pop::<NockWork>();
-                                    push_formula(stack, pins.formula, true)?;
-                                } else {
-                                    pins.todo = Todo8::RestoreSubject;
-                                    pins.pin = subject;
-                                    *stack.top() = NockWork::Work8(pins);
-                                    subject = T(stack, &[res, subject]);
-                                    push_formula(stack, pins.formula, false)?;
+                                Err(err) => {
+                                    break Err(err);
                                 }
                             }
-                            Todo8::RestoreSubject => {
-                                subject = pins.pin;
+                        } else {
+                            if dint.tail {
                                 context.stack.pop::<NockWork>();
+                            } else {
+                                dint.todo = Todo11D::Done;
+                                dint.hint = res;
+                                *context.stack.top() = NockWork::Work11D(dint);
                             }
-                        },
-                        NockWork::Work9(mut kale) => {
-                            match kale.todo {
-                                Todo9::ComputeCore => {
-                                    kale.todo = Todo9::ComputeResult;
-                                    *context.stack.top() = NockWork::Work9(kale);
-                                    push_formula(&mut context.stack, kale.core, false)?;
-                                }
-                                Todo9::ComputeResult => {
-                                    if let Ok(mut formula) = res.slot_atom(kale.axis) {
-                                        if !cfg!(feature = "sham_hints") {
-                                            if let Some((jet, _path)) = context.warm.find_jet(
-                                                &mut context.stack, &mut res, &mut formula,
-                                            ) {
-                                                match jet(context, res) {
-                                                    Ok(jet_res) => {
-                                                        res = jet_res;
-                                                        context.stack.pop::<NockWork>();
-                                                        continue;
-                                                    }
-                                                    Err(JetErr::Punt) => {}
-                                                    Err(err) => {
-                                                        break Err(err.into());
-                                                    }
-                                                }
-                                            }
-                                        };
-
-                                        let stack = &mut context.stack;
-                                        if kale.tail {
-                                            stack.pop::<NockWork>();
-
-                                            // We could trace on 2 as well, but 2 only comes from Hoon via
-                                            // '.*', so we can assume it's never directly used to invoke
-                                            // jetted code.
-                                            if context.trace_info.is_some() {
-                                                if let Some(path) =
-                                                    context.cold.matches(stack, &mut res)
-                                                {
-                                                    append_trace(stack, path);
-                                                };
-                                            };
-
-                                            subject = res;
-                                            push_formula(stack, formula, true)?;
-                                        } else {
-                                            kale.todo = Todo9::RestoreSubject;
-                                            kale.core = subject;
-                                            *stack.top() = NockWork::Work9(kale);
-
-                                            debug_assertions(stack, orig_subject);
-                                            debug_assertions(stack, subject);
-                                            debug_assertions(stack, res);
-
-                                            subject = res;
-                                            mean_frame_push(stack, 0);
-                                            *stack.push() = NockWork::Ret;
-                                            push_formula(stack, formula, true)?;
-
-                                            // We could trace on 2 as well, but 2 only comes from Hoon via
-                                            // '.*', so we can assume it's never directly used to invoke
-                                            // jetted code.
-                                            if context.trace_info.is_some() {
-                                                if let Some(path) =
-                                                    context.cold.matches(stack, &mut res)
-                                                {
-                                                    append_trace(stack, path);
-                                                };
-                                            };
-                                        }
-                                    } else {
-                                        // Axis into core must be atom
-                                        break BAIL_EXIT;
-                                    }
-                                }
-                                Todo9::RestoreSubject => {
-                                    let stack = &mut context.stack;
-
-                                    subject = kale.core;
-                                    stack.pop::<NockWork>();
-
-                                    debug_assertions(stack, orig_subject);
-                                    debug_assertions(stack, subject);
-                                    debug_assertions(stack, res);
-                                }
-                            }
+                            push_formula(&mut context.stack, dint.body, dint.tail)?;
                         }
-                        NockWork::Work10(mut diet) => {
-                            match diet.todo {
-                                Todo10::ComputeTree => {
-                                    diet.todo = Todo10::ComputePatch; // should we compute patch then tree?
-                                    *context.stack.top() = NockWork::Work10(diet);
-                                    push_formula(&mut context.stack, diet.tree, false)?;
-                                }
-                                Todo10::ComputePatch => {
-                                    diet.todo = Todo10::Edit;
-                                    diet.tree = res;
-                                    *context.stack.top() = NockWork::Work10(diet);
-                                    push_formula(&mut context.stack, diet.patch, false)?;
-                                }
-                                Todo10::Edit => {
-                                    res = edit(
-                                        &mut context.stack,
-                                        diet.axis.as_bitslice(),
-                                        res,
-                                        diet.tree,
-                                    );
+                    }
+                    Todo11D::Done => {
+                        if let Some(found) = hint::match_post_nock(
+                            context,
+                            subject,
+                            dint.tag,
+                            Some(dint.hint),
+                            dint.body,
+                            res,
+                        ) {
+                            res = found;
+                        }
+                        context.stack.pop::<NockWork>();
+                    }
+                },
+                NockWork::Work11S(mut sint) => match sint.todo {
+                    Todo11S::ComputeResult => {
+                        if let Some(ret) =
+                            hint::match_pre_nock(context, subject, sint.tag, None, sint.body)
+                        {
+                            match ret {
+                                Ok(found) => {
+                                    res = found;
                                     context.stack.pop::<NockWork>();
                                 }
+                                Err(err) => {
+                                    break Err(err);
+                                }
                             }
+                        } else {
+                            if sint.tail {
+                                context.stack.pop::<NockWork>();
+                            } else {
+                                sint.todo = Todo11S::Done;
+                                *context.stack.top() = NockWork::Work11S(sint);
+                            }
+                            push_formula(&mut context.stack, sint.body, sint.tail)?;
                         }
-                        NockWork::Work11D(mut dint) => match dint.todo {
-                            Todo11D::ComputeHint => {
-                                if let Some(ret) = hint::match_pre_hint(
-                                    context, subject, dint.tag, dint.hint, dint.body,
-                                ) {
-                                    match ret {
-                                        Ok(found) => {
-                                            res = found;
-                                            context.stack.pop::<NockWork>();
-                                        }
-                                        Err(err) => {
-                                            break Err(err);
+                    }
+                    Todo11S::Done => {
+                        if let Some(found) =
+                            hint::match_post_nock(context, subject, sint.tag, None, sint.body, res)
+                        {
+                            res = found;
+                        }
+                        context.stack.pop::<NockWork>();
+                    }
+                },
+                NockWork::Work12(mut scry) => match scry.todo {
+                    Todo12::ComputeReff => {
+                        let stack = &mut context.stack;
+                        scry.todo = Todo12::ComputePath;
+                        *stack.top() = NockWork::Work12(scry);
+                        push_formula(stack, scry.reff, false)?;
+                    }
+                    Todo12::ComputePath => {
+                        let stack = &mut context.stack;
+                        scry.todo = Todo12::Scry;
+                        scry.reff = res;
+                        *stack.top() = NockWork::Work12(scry);
+                        push_formula(stack, scry.path, false)?;
+                    }
+                    Todo12::Scry => {
+                        if let Some(cell) = context.scry_stack.cell() {
+                            scry.path = res;
+                            let scry_stack = context.scry_stack;
+                            let scry_handler = cell.head();
+                            let scry_gate = scry_handler.as_cell()?;
+                            let payload = T(&mut context.stack, &[scry.reff, res]);
+                            let scry_core = T(
+                                &mut context.stack,
+                                &[scry_gate.head(), payload, scry_gate.tail().as_cell()?.tail()],
+                            );
+                            let scry_form = T(&mut context.stack, &[D(9), D(2), D(1), scry_core]);
+
+                            context.scry_stack = cell.tail();
+                            // Alternately, we could use scry_core as the subject and [9 2 0 1] as
+                            // the formula. It's unclear if performance will be better with a purely
+                            // static formula.
+                            match interpret(context, D(0), scry_form) {
+                                Ok(noun) => match noun.as_either_atom_cell() {
+                                    Left(atom) => {
+                                        if atom.as_noun().raw_equals(&D(0)) {
+                                            break Err(Error::ScryBlocked(scry.path));
+                                        } else {
+                                            break Err(Error::ScryCrashed(D(0)));
                                         }
                                     }
-                                } else {
-                                    dint.todo = Todo11D::ComputeResult;
-                                    *context.stack.top() = NockWork::Work11D(dint);
-                                    push_formula(&mut context.stack, dint.hint, false)?;
-                                }
-                            }
-                            Todo11D::ComputeResult => {
-                                if let Some(ret) = hint::match_pre_nock(
-                                    context,
-                                    subject,
-                                    dint.tag,
-                                    Some((dint.hint, res)),
-                                    dint.body,
-                                ) {
-                                    match ret {
-                                        Ok(found) => {
-                                            res = found;
-                                            context.stack.pop::<NockWork>();
-                                        }
-                                        Err(err) => {
-                                            break Err(err);
+                                    Right(cell) => match cell.tail().as_either_atom_cell() {
+                                        Left(_) => {
+                                            let stack = &mut context.stack;
+                                            let hunk =
+                                                T(stack, &[D(tas!(b"hunk")), scry.reff, scry.path]);
+                                            mean_push(stack, hunk);
+                                            break Err(Error::ScryCrashed(D(0)));
                                         }
-                                    }
-                                } else {
-                                    if dint.tail {
-                                        context.stack.pop::<NockWork>();
-                                    } else {
-                                        dint.todo = Todo11D::Done;
-                                        dint.hint = res;
-                                        *context.stack.top() = NockWork::Work11D(dint);
-                                    }
-                                    push_formula(&mut context.stack, dint.body, dint.tail)?;
-                                }
-                            }
-                            Todo11D::Done => {
-                                if let Some(found) = hint::match_post_nock(
-                                    context,
-                                    subject,
-                                    dint.tag,
-                                    Some(dint.hint),
-                                    dint.body,
-                                    res,
-                                ) {
-                                    res = found;
-                                }
-                                context.stack.pop::<NockWork>();
-                            }
-                        },
-                        NockWork::Work11S(mut sint) => match sint.todo {
-                            Todo11S::ComputeResult => {
-                                if let Some(ret) = hint::match_pre_nock(
-                                    context, subject, sint.tag, None, sint.body,
-                                ) {
-                                    match ret {
-                                        Ok(found) => {
-                                            res = found;
+                                        Right(cell) => {
+                                            res = cell.tail();
+                                            context.scry_stack = scry_stack;
                                             context.stack.pop::<NockWork>();
                                         }
-                                        Err(err) => {
-                                            break Err(err);
-                                        }
+                                    },
+                                },
+                                Err(error) => match error {
+                                    Error::Deterministic(_, trace) | Error::ScryCrashed(trace) => {
+                                        break Err(Error::ScryCrashed(trace));
                                     }
-                                } else {
-                                    if sint.tail {
-                                        context.stack.pop::<NockWork>();
-                                    } else {
-                                        sint.todo = Todo11S::Done;
-                                        *context.stack.top() = NockWork::Work11S(sint);
+                                    Error::NonDeterministic(_, _) => {
+                                        break Err(error);
                                     }
-                                    push_formula(&mut context.stack, sint.body, sint.tail)?;
-                                }
-                            }
-                            Todo11S::Done => {
-                                if let Some(found) = hint::match_post_nock(
-                                    context, subject, sint.tag, None, sint.body, res,
-                                ) {
-                                    res = found;
-                                }
-                                context.stack.pop::<NockWork>();
-                            }
-                        },
-                        NockWork::Work12(mut scry) => match scry.todo {
-                            Todo12::ComputeReff => {
-                                let stack = &mut context.stack;
-                                scry.todo = Todo12::ComputePath;
-                                *stack.top() = NockWork::Work12(scry);
-                                push_formula(stack, scry.reff, false)?;
-                            }
-                            Todo12::ComputePath => {
-                                let stack = &mut context.stack;
-                                scry.todo = Todo12::Scry;
-                                scry.reff = res;
-                                *stack.top() = NockWork::Work12(scry);
-                                push_formula(stack, scry.path, false)?;
-                            }
-                            Todo12::Scry => {
-                                if let Some(cell) = context.scry_stack.cell() {
-                                    scry.path = res;
-                                    let scry_stack = context.scry_stack;
-                                    let scry_handler = cell.head();
-                                    let scry_gate = scry_handler.as_cell()?;
-                                    let payload = T(&mut context.stack, &[scry.reff, res]);
-                                    let scry_core = T(
-                                        &mut context.stack,
-                                        &[
-                                            scry_gate.head(),
-                                            payload,
-                                            scry_gate.tail().as_cell()?.tail(),
-                                        ],
-                                    );
-                                    let scry_form =
-                                        T(&mut context.stack, &[D(9), D(2), D(1), scry_core]);
-
-                                    context.scry_stack = cell.tail();
-                                    // Alternately, we could use scry_core as the subject and [9 2 0 1] as
-                                    // the formula. It's unclear if performance will be better with a purely
-                                    // static formula.
-                                    match interpret(context, D(0), scry_form) {
-                                        Ok(noun) => match noun.as_either_atom_cell() {
-                                            Left(atom) => {
-                                                if atom.as_noun().raw_equals(&D(0)) {
-                                                    break Err(Error::ScryBlocked(scry.path));
-                                                } else {
-                                                    break Err(Error::ScryCrashed(D(0)));
-                                                }
-                                            }
-                                            Right(cell) => {
-                                                match cell.tail().as_either_atom_cell() {
-                                                    Left(_) => {
-                                                        let stack = &mut context.stack;
-                                                        let hunk = T(
-                                                            stack,
-                                                            &[
-                                                                D(tas!(b"hunk")),
-                                                                scry.reff,
-                                                                scry.path,
-                                                            ],
-                                                        );
-                                                        mean_push(stack, hunk);
-                                                        break Err(Error::ScryCrashed(D(0)));
-                                                    }
-                                                    Right(cell) => {
-                                                        res = cell.tail();
-                                                        context.scry_stack = scry_stack;
-                                                        context.stack.pop::<NockWork>();
-                                                    }
-                                                }
-                                            }
-                                        },
-                                        Err(error) => match error {
-                                            Error::Deterministic(_, trace)
-                                            | Error::ScryCrashed(trace) => {
-                                                break Err(Error::ScryCrashed(trace));
-                                            }
-                                            Error::NonDeterministic(_, _) => {
-                                                break Err(error);
-                                            }
-                                            Error::ScryBlocked(_) => {
-                                                break BAIL_FAIL;
-                                            }
-                                        },
+                                    Error::ScryBlocked(_) => {
+                                        break BAIL_FAIL;
                                     }
-                                } else {
-                                    // No scry handler
-                                    break BAIL_EXIT;
-                                }
+                                },
                             }
-                        },
-                    };
-                }
+                        } else {
+                            // No scry handler
+                            break BAIL_EXIT;
+                        }
+                    }
+                },
+            };
+        }
+    };
+
+    loop {
+        let running_status = context.running_status.load(Ordering::SeqCst);
+        if running_status < NockCancelToken::RUNNING_IDLE {
+            if context
+                .running_status
+                .compare_exchange(
+                    running_status,
+                    running_status + 1,
+                    Ordering::SeqCst,
+                    Ordering::SeqCst,
+                )
+                .is_ok()
+            {
+                break;
             }
-        })
-    });
+        } else if running_status == NockCancelToken::RUNNING_IDLE {
+            break;
+        } else {
+            if context
+                .running_status
+                .compare_exchange(
+                    running_status,
+                    running_status - 1,
+                    Ordering::SeqCst,
+                    Ordering::SeqCst,
+                )
+                .is_ok()
+            {
+                break;
+            }
+        }
+    }
 
     match nock {
         Ok(res) => Ok(res),

+ 13 - 11
crates/nockvm/rust/nockvm/src/jets.rs

@@ -310,13 +310,15 @@ pub mod util {
     }
 
     pub mod test {
+        use std::sync::atomic::AtomicIsize;
+        use std::sync::Arc;
+
         use super::*;
         use crate::hamt::Hamt;
-        use crate::interpreter::Slogger;
+        use crate::interpreter::{NockCancelToken, Slogger};
         use crate::mem::NockStack;
         use crate::noun::{Atom, Noun, D, T};
         use crate::unifying_equality::unifying_equality;
-        use assert_no_alloc::assert_no_alloc;
         use ibig::UBig;
 
         struct TestSlogger {}
@@ -338,6 +340,7 @@ pub mod util {
             let hot = Hot::init(&mut stack, URBIT_HOT_STATE);
             let cache = Hamt::<Noun>::new(&mut stack);
             let slogger = std::boxed::Box::pin(TestSlogger {});
+            let cancel = Arc::new(AtomicIsize::new(NockCancelToken::RUNNING_IDLE));
 
             Context {
                 stack,
@@ -348,6 +351,7 @@ pub mod util {
                 cache,
                 scry_stack: D(0),
                 trace_info: None,
+                running_status: cancel,
             }
         }
 
@@ -439,15 +443,13 @@ pub mod util {
 
         pub fn assert_jet_size(context: &mut Context, jet: Jet, sam: Noun, siz: usize) {
             let sam = T(&mut context.stack, &[D(0), sam, D(0)]);
-            let res = assert_no_alloc(|| {
-                jet(context, sam).unwrap_or_else(|err| {
-                    panic!(
-                        "Panicked with {err:?} at {}:{} (git sha: {:?})",
-                        file!(),
-                        line!(),
-                        option_env!("GIT_SHA")
-                    )
-                })
+            let res = jet(context, sam).unwrap_or_else(|err| {
+                panic!(
+                    "Panicked with {err:?} at {}:{} (git sha: {:?})",
+                    file!(),
+                    line!(),
+                    option_env!("GIT_SHA")
+                )
             });
             assert!(res.is_atom(), "jet result not atom");
             let res_siz = res

+ 1 - 17
crates/nockvm/rust/nockvm/src/lib.rs

@@ -1,4 +1,4 @@
-// #![feature(ptr_as_ref_unchecked)]
+#![feature(cold_path)]
 #![allow(dead_code)]
 
 extern crate lazy_static;
@@ -49,22 +49,6 @@ pub fn check_endian() {
     }
 }
 
-// Use the allocator from assert_no_alloc.
-//
-// DO NOT COMMENT THIS OUT
-//
-// if you need to allow allocations somewhere for debugging, wrap your debug code in
-// ```
-// permit_alloc( || {
-//   your.code.goes.here()
-// })
-// ```
-//
-// (see https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use)
-#[cfg(debug_assertions)]
-#[global_allocator]
-static A: assert_no_alloc::AllocDisabler = assert_no_alloc::AllocDisabler;
-
 pub(crate) use gdb;
 
 #[cfg(test)]

+ 29 - 28
crates/nockvm/rust/nockvm/src/mem.rs

@@ -1,7 +1,6 @@
 // TODO: fix stack push in PC
 use crate::noun::{Atom, Cell, CellMemory, IndirectAtom, Noun, NounAllocator};
 use crate::{assert_acyclic, assert_no_forwarding_pointers, assert_no_junior_pointers};
-use assert_no_alloc::permit_alloc;
 use either::Either::{self, Left, Right};
 use ibig::Stack;
 use memmap2::MmapMut;
@@ -414,6 +413,8 @@ impl NockStack {
     // Types of size: word (words: usize)
     /// Check if an allocation or pointer retrieval indicates an invalid request or an invalid state
     pub(crate) fn alloc_would_oom_(&self, alloc: Allocation, words: usize) {
+        #[cfg(feature = "no_check_oom")]
+        return;
         let _memory_state = self.memory_state(Some(words));
         if self.pc && !alloc.alloc_type.allowed_when_pc() {
             panic_any(self.cannot_alloc_in_pc(Some(words)));
@@ -734,20 +735,18 @@ impl NockStack {
         // Calculate the pointer offset from the base in words
         let ptr_u64 = ptr as *const u64;
         // We need to permit alloc here for panic reasons
-        assert_no_alloc::permit_alloc(|| {
-            debug_assert!(
-                ptr_u64 >= self.start,
-                "is_in_frame: {} >= {}",
-                ptr_u64 as usize,
-                self.start as usize,
-            );
-            debug_assert!(
-                ptr_u64 < self.start.add(self.size),
-                "is_in_frame: {} < {}",
-                ptr_u64 as usize,
-                self.start.add(self.size) as usize,
-            );
-        });
+        debug_assert!(
+            ptr_u64 >= self.start,
+            "is_in_frame: {} >= {}",
+            ptr_u64 as usize,
+            self.start as usize,
+        );
+        debug_assert!(
+            ptr_u64 < self.start.add(self.size),
+            "is_in_frame: {} < {}",
+            ptr_u64 as usize,
+            self.start.add(self.size) as usize,
+        );
 
         let ptr_offset = (ptr_u64 as usize - self.start as usize) / 8;
 
@@ -806,13 +805,17 @@ impl NockStack {
 
     // Get the offset of the previous alloc pointer
     unsafe fn prev_alloc_offset(&self) -> usize {
-        let prev_alloc_ptr = *self.prev_alloc_pointer_pointer();
-        if prev_alloc_ptr == self.start as *mut u64 {
-            0
-        } else {
-            // Calculate offset from base pointer in words
-            (prev_alloc_ptr as usize - self.start as usize) / 8
-        }
+        // let prev_alloc_ptr = *self.prev_alloc_pointer_pointer();
+        // if prev_alloc_ptr == self.start as *mut u64 {
+        //     0
+        // } else {
+        //     // Calculate offset from base pointer in words
+        //     (prev_alloc_ptr as usize - self.start as usize) / 8
+        // }
+        // seems to be ~5x faster
+        let ptr = *self.prev_alloc_pointer_pointer() as usize;
+        // ptr == start  ⇒  diff==0
+        ((ptr).wrapping_sub(self.start as usize)) >> 3
     }
 
     /** Mutable pointer to a slot in a stack frame: east stack */
@@ -1453,12 +1456,10 @@ impl NockStack {
 
         // Check for null pointers before calculating offsets
         if prev_frame_ptr.is_null() || prev_stack_ptr.is_null() || prev_alloc_ptr.is_null() {
-            permit_alloc(|| {
-                panic!(
-                    "serf: frame_pop: null NockStack pointer f={:p} s={:p} a={:p}",
-                    prev_frame_ptr, prev_stack_ptr, prev_alloc_ptr
-                );
-            });
+            panic!(
+                "serf: frame_pop: null NockStack pointer f={:p} s={:p} a={:p}",
+                prev_frame_ptr, prev_stack_ptr, prev_alloc_ptr
+            );
         }
 
         // Calculate the offsets from base pointer

+ 8 - 0
crates/nockvm/rust/nockvm/src/noun.rs

@@ -1005,6 +1005,14 @@ impl Atom {
         }
     }
 
+    pub fn as_bool(self) -> Result<bool> {
+        if self.is_direct() {
+            Ok(unsafe { self.direct.data() == 0 })
+        } else {
+            Err(Error::NotRepresentable)
+        }
+    }
+
     /** Produce a SoftFloat-compatible ordered pair of 64-bit words */
     pub unsafe fn as_u64_pair(self) -> Result<[u64; 2]> {
         if self.is_direct() {

+ 20 - 24
crates/nockvm/rust/nockvm/src/trace.rs

@@ -124,18 +124,16 @@ pub fn write_serf_trace(info: &mut TraceInfo, name: &str, start: Instant) -> Res
         .as_micros() as f64;
     let dur = Instant::now().saturating_duration_since(start).as_micros() as f64;
 
-    assert_no_alloc::permit_alloc(|| {
-        let obj = object! {
-            "cat" => "event",
-            "name" => name,
-            "ph" => "X",
-            "pid" => info.pid,
-            "tid" => 1,
-            "ts" => ts,
-            "dur" => dur,
-        };
-        obj.write(&mut info.file)
-    })?;
+    let obj = object! {
+        "cat" => "event",
+        "name" => name,
+        "ph" => "X",
+        "pid" => info.pid,
+        "tid" => 1,
+        "ts" => ts,
+        "dur" => dur,
+    };
+    let _ = obj.write(&mut info.file);
     info.file.write_all(",\n".as_bytes())?;
 
     Ok(())
@@ -175,18 +173,16 @@ pub unsafe fn write_nock_trace(
             }
         };
 
-        assert_no_alloc::permit_alloc(|| {
-            let obj = object! {
-                "cat" => "nock",
-                "name" => pc_str,
-                "ph" => "X",
-                "pid" => info.pid,
-                "tid" => 1,
-                "ts" => ts,
-                "dur" => dur,
-            };
-            obj.write(&mut info.file)
-        })?;
+        let obj = object! {
+            "cat" => "nock",
+            "name" => pc_str,
+            "ph" => "X",
+            "pid" => info.pid,
+            "tid" => 1,
+            "ts" => ts,
+            "dur" => dur,
+        };
+        let _ = obj.write(&mut info.file);
         info.file.write_all(",\n".as_bytes())?;
 
         trace_stack = (*trace_stack).next;

+ 3 - 2
crates/nockvm/rust/nockvm/src/unifying_equality.rs

@@ -189,12 +189,12 @@ unsafe fn senior_pointer_first(
             a < high_pointer && a >= low_pointer,
             b < high_pointer && b >= low_pointer,
         ) {
+            (true, false) => break (b, a), // a is in the frame, b is not, so b is senior
+            (false, true) => break (a, b), // b is in the frame, a is not, so a is senior
             (true, true) => {
                 // both pointers are in the same frame, pick arbitrarily (lower in mem)
                 break lower_pointer_first(a, b);
             }
-            (true, false) => break (b, a), // a is in the frame, b is not, so b is senior
-            (false, true) => break (a, b), // b is in the frame, a is not, so a is senior
             (false, false) => {
                 // chase up the stack
                 #[allow(clippy::comparison_chain)]
@@ -228,6 +228,7 @@ unsafe fn senior_pointer_first(
                     // "previous previous" stack pointer. this is the other boundary of the previous allocation arena
                     high_pointer = *(frame_pointer.sub(STACK + 1)) as *const u64;
                 } else {
+                    core::hint::cold_path();
                     panic!("senior_pointer_first: stack_pointer == alloc_pointer");
                 }
             }

+ 0 - 1
crates/nockvm/rust/nockvm_crypto/Cargo.toml

@@ -6,7 +6,6 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-assert_no_alloc.workspace = true
 ibig.workspace = true
 
 # ed25519

+ 16 - 1
crates/zkvm-jetpack/src/form/belt.rs

@@ -1,4 +1,4 @@
-use std::ops::{Add, Mul, Neg, Sub};
+use std::ops::{Add, Div, Mul, Neg, Sub};
 
 use nockvm::noun::Noun;
 use num_traits::Pow;
@@ -56,6 +56,11 @@ impl Belt {
         }
         Ok(ROOTS[log_of_self as usize].into())
     }
+
+    #[inline(always)]
+    pub fn inv(&self) -> Self {
+        Belt(binv(self.0))
+    }
 }
 
 impl Add for Belt {
@@ -110,6 +115,16 @@ impl Pow<usize> for Belt {
     }
 }
 
+impl Div for Belt {
+    type Output = Self;
+
+    #[inline(always)]
+    #[allow(clippy::suspicious_arithmetic_impl)]
+    fn div(self, rhs: Self) -> Self::Output {
+        self * rhs.inv()
+    }
+}
+
 impl PartialEq<u64> for Belt {
     #[inline(always)]
     fn eq(&self, other: &u64) -> bool {

+ 214 - 0
crates/zkvm-jetpack/src/form/felt.rs

@@ -0,0 +1,214 @@
+use core::ops::{Add, Div, Mul, Neg, Sub};
+
+use num_traits::{MulAdd, Pow};
+
+use crate::form::base::*;
+use crate::form::fext::*;
+use crate::form::{Belt, Felt};
+
+impl Felt {
+    #[inline(always)]
+    pub fn zero() -> Self {
+        Felt(Default::default())
+    }
+
+    #[inline(always)]
+    pub fn one() -> Self {
+        Self::from([1, 0, 0])
+    }
+
+    #[inline(always)]
+    pub fn constant(a: u64) -> Self {
+        Felt([Belt(a), Belt::zero(), Belt::zero()])
+    }
+
+    #[inline(always)]
+    pub fn lift(a: Belt) -> Self {
+        Felt([a, Belt::zero(), Belt::zero()])
+    }
+
+    #[inline(always)]
+    pub fn ordered_root(order: u64) -> Result<Self, FieldError> {
+        Ok(Self::constant(Belt(order).ordered_root()?.into()))
+    }
+
+    #[inline(always)]
+    pub fn is_zero(&self) -> bool {
+        self.0.iter().all(|&e| e.is_zero())
+    }
+
+    #[inline(always)]
+    pub fn degree(&self) -> u32 {
+        match self.0.iter().rposition(|&x| x.is_zero()) {
+            Some(i) => i as u32,
+            // TODO: change return to an enum so this can be negative infty?
+            None => 0,
+        }
+    }
+    #[inline(always)]
+    pub fn unpack(self) -> [u64; 3] {
+        [self.0[0].0, self.0[1].0, self.0[2].0]
+    }
+
+    #[inline(always)]
+    pub fn copy_from_slice(&mut self, src: &Felt) {
+        self.0.copy_from_slice(&src.0)
+    }
+
+    // pub fn to_simd(&self) -> std::simd::Simd<[u64; 4]> {
+    //     std::simd::Simd::load_or_default(&self.0)
+    // }
+}
+
+impl core::ops::Index<usize> for Felt {
+    type Output = Belt;
+
+    #[inline(always)]
+    fn index(&self, index: usize) -> &Self::Output {
+        &self.0[index]
+    }
+}
+
+impl core::ops::IndexMut<usize> for Felt {
+    #[inline(always)]
+    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+        &mut self.0[index]
+    }
+}
+
+// impl AsRef<[u64]> for Felt {
+//     fn as_ref(&self) -> &[u64] {
+//         &self.0
+//     }
+// }
+
+impl From<[Belt; 3]> for Felt {
+    #[inline(always)]
+    fn from(f: [Belt; 3]) -> Self {
+        Felt(f)
+    }
+}
+
+impl From<[u64; 3]> for Felt {
+    #[inline(always)]
+    fn from(f: [u64; 3]) -> Self {
+        Felt(f.map(Belt::from))
+    }
+}
+
+impl TryFrom<&[u64]> for Felt {
+    type Error = ();
+
+    #[inline(always)]
+    fn try_from(f: &[u64]) -> Result<Self, Self::Error> {
+        if f.len() != 3 {
+            return Err(());
+        }
+        Ok(Felt([Belt::from(f[0]), Belt::from(f[1]), Belt::from(f[2])]))
+    }
+}
+
+impl From<Felt> for [u64; 3] {
+    #[inline(always)]
+    fn from(f: Felt) -> Self {
+        f.0.map(u64::from)
+    }
+}
+
+impl From<Felt> for [Belt; 3] {
+    #[inline(always)]
+    fn from(f: Felt) -> Self {
+        f.0
+    }
+}
+
+impl<'a> From<&'a Felt> for &'a [Belt] {
+    #[inline(always)]
+    fn from(f: &'a Felt) -> &'a [Belt] {
+        &f.0
+    }
+}
+
+#[cfg(test)]
+impl quickcheck::Arbitrary for Felt {
+    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
+        Felt([Belt::arbitrary(g), Belt::arbitrary(g), Belt::arbitrary(g)])
+    }
+}
+
+impl Add for Felt {
+    type Output = Self;
+
+    #[inline(always)]
+    fn add(self, rhs: Self) -> Self::Output {
+        let mut res: Felt = Felt::zero();
+        fadd(&self, &rhs, &mut res);
+        res
+    }
+}
+
+impl Sub for Felt {
+    type Output = Self;
+
+    #[inline(always)]
+    fn sub(self, rhs: Self) -> Self::Output {
+        let mut res: Felt = Felt::zero();
+        fsub(&self, &rhs, &mut res);
+        res
+    }
+}
+
+impl Mul for Felt {
+    type Output = Self;
+
+    #[inline(always)]
+    fn mul(self, rhs: Self) -> Self::Output {
+        let mut res: Felt = Felt::zero();
+        fmul(&self, &rhs, &mut res);
+        res
+    }
+}
+
+impl Pow<usize> for Felt {
+    type Output = Self;
+
+    #[inline(always)]
+    fn pow(self, rhs: usize) -> Self::Output {
+        let mut res: Felt = Felt::zero();
+        fpow(&self, rhs as u64, &mut res);
+        res
+    }
+}
+
+impl Neg for Felt {
+    type Output = Self;
+
+    #[inline(always)]
+    fn neg(self) -> Self::Output {
+        let mut res: Felt = Felt::zero();
+        fneg(&self, &mut res);
+        res
+    }
+}
+
+impl Div for Felt {
+    type Output = Self;
+
+    #[inline(always)]
+    fn div(self, rhs: Self) -> Self::Output {
+        let mut res: Felt = Felt::zero();
+        fdiv(&self, &rhs, &mut res);
+        res
+    }
+}
+
+impl MulAdd for Felt {
+    type Output = Self;
+    #[inline(always)]
+    fn mul_add(self, a: Self, b: Self) -> Self {
+        let mut res: Felt = Felt::zero();
+        fmul(&self, &a, &mut res);
+        fadd_self(&b, &mut res);
+        res
+    }
+}

+ 17 - 0
crates/zkvm-jetpack/src/form/math/base.rs

@@ -90,3 +90,20 @@ pub fn bpow(mut a: u64, mut b: u64) -> u64 {
     }
     reduce((c as u128) * (a as u128))
 }
+
+#[inline(always)]
+pub fn bdiv(a: u64, b: u64) -> u64 {
+    bmul(a, binv(b))
+}
+
+#[inline(always)]
+pub fn binv(a: u64) -> u64 {
+    // Due to fermat's little theorem, a^(p-1) = 1 (mod p), so a^(p-2) = a^(-1) (mod p)
+    // bpow already checks based, so we skip it here
+    bpow(a, PRIME - 2)
+}
+
+#[test]
+fn test_binv() {
+    assert_eq!(bmul(binv(888), 888), 1);
+}

+ 127 - 0
crates/zkvm-jetpack/src/form/math/bpoly.rs

@@ -287,12 +287,139 @@ pub fn bpoly_zero_extend(a: &[Belt], res: &mut [Belt]) {
     res[a_len..res_len].fill(Belt::zero());
 }
 
+#[inline(always)]
+pub fn bpdvr(a: &[Belt], b: &[Belt], q: &mut [Belt], res: &mut [Belt]) {
+    if a.is_zero() {
+        q.fill(Belt(0));
+        res.fill(Belt(0));
+        return;
+    } else if b.is_zero() {
+        panic!("divide by zero\r");
+    };
+
+    q.fill(Belt(0));
+    res.fill(Belt(0));
+
+    let a_end = a.degree() as usize;
+    let mut r = a[0..(a_end + 1)].to_vec();
+
+    let deg_b = b.degree();
+
+    let mut i = a_end;
+    let end_b = deg_b as usize;
+    let mut deg_r = a.degree();
+    let mut q_index = deg_r.saturating_sub(deg_b);
+
+    while deg_r >= deg_b {
+        let coeff = r[i] / b[end_b];
+        q[q_index as usize] = coeff;
+        for k in 0..(deg_b + 1) {
+            let index = k as usize;
+            if k <= a_end as u32 && k < b.len() as u32 && k <= (i as u32) {
+                r[i - index] = r[i - index] - coeff * b[end_b - index];
+            }
+        }
+        deg_r = deg_r.saturating_sub(1);
+        q_index = q_index.saturating_sub(1);
+        if deg_r == 0 && r[0] == 0 {
+            break;
+        }
+        i -= 1;
+    }
+
+    let r_len = deg_r + 1;
+    res[0..(r_len as usize)].copy_from_slice(&r[0..(r_len as usize)]);
+}
+
+/// Extended Euclidean Algorithm, GCD
+#[inline(always)]
+pub fn bpegcd(a: &[Belt], b: &[Belt], d: &mut [Belt], u: &mut [Belt], v: &mut [Belt]) {
+    let mut m1_u = vec![Belt(0)];
+    let mut m2_u = vec![Belt(1)];
+    let mut m1_v = vec![Belt(1)];
+    let mut m2_v = vec![Belt(0)];
+
+    d.fill(Belt(0));
+    u.fill(Belt(0));
+    v.fill(Belt(0));
+
+    let mut a = a.to_vec();
+    let mut b = b.to_vec();
+
+    while !b.is_zero() {
+        let deg_a = a.degree();
+        let deg_b = b.degree();
+        let deg_q = deg_a.saturating_sub(deg_b);
+        let len_q = deg_q + 1;
+        let len_r = deg_b + 1;
+
+        let mut q = vec![Belt(0); len_q as usize];
+        let mut r = vec![Belt(0); len_r as usize];
+
+        bpdvr(
+            a.as_slice(),
+            b.as_slice(),
+            q.as_mut_slice(),
+            r.as_mut_slice(),
+        );
+
+        a = b;
+        b = r;
+
+        let q_len = q.len();
+        let m1_u_len = m1_u.len() as usize;
+
+        let mut res1_len = q_len + m1_u_len - 1;
+        let mut res1 = vec![Belt(0); res1_len as usize];
+        bpmul(q.as_slice(), m1_u.as_slice(), res1.as_mut_slice());
+
+        let m2_u_len = m2_u.len();
+
+        let len_res2 = std::cmp::max(m2_u_len, res1_len);
+        let mut res2 = vec![Belt(0); len_res2 as usize];
+        bpsub(m2_u.as_slice(), res1.as_slice(), res2.as_mut_slice());
+
+        m2_u = m1_u;
+        m1_u = res2;
+
+        let m1_v_len = m1_v.len() as usize;
+
+        res1.fill(Belt(0));
+        res1_len = q_len + m1_v_len - 1;
+
+        bpmul(q.as_slice(), m1_v.as_slice(), res1.as_mut_slice());
+
+        let m2_v_len = m2_v.len();
+
+        let len_res3 = std::cmp::max(m2_v_len, res1_len);
+        let mut res3 = vec![Belt(0); len_res3 as usize];
+
+        bpsub(m2_v.as_slice(), res1.as_slice(), res3.as_mut_slice());
+
+        m2_v = m1_v;
+        m1_v = res3;
+    }
+
+    let a_len = a.len();
+    d[0..a_len].copy_from_slice(&a[0..a_len]);
+
+    let m2_u_len = m2_u.len();
+    let m2_v_len = m2_v.len();
+
+    u[0..(m2_u_len as usize)].copy_from_slice(&m2_u[0..(m2_u_len as usize)]);
+    v[0..(m2_v_len as usize)].copy_from_slice(&m2_v[0..(m2_v_len as usize)]);
+}
+
 #[inline(always)]
 pub fn normalize_bpoly(a: &mut Vec<Belt>) {
     // normalize result by removing trailing zeros
     if a.len() <= 1 {
         return;
     }
+    if a.is_zero() {
+        *a = vec![Belt(0)];
+        return;
+    }
     for i in (0..a.len()).rev() {
         if a[i].is_zero() {
             a.pop();

+ 173 - 0
crates/zkvm-jetpack/src/form/math/fext.rs

@@ -0,0 +1,173 @@
+use crate::form::bpoly::*;
+use crate::form::poly::*;
+
+//==============================================================================
+// field extension methods
+//==============================================================================
+
+pub fn fadd(a: &Felt, b: &Felt, res: &mut Felt) {
+    for i in 0..3 {
+        res[i] = a[i] + b[i];
+    }
+}
+
+pub fn fadd_(a: &Felt, b: &Felt) -> Felt {
+    let mut res: Felt = Felt::zero();
+    fadd(a, b, &mut res);
+    res
+}
+
+pub fn fadd_self(a: &Felt, res: &mut Felt) {
+    for i in 0..3 {
+        res[i] = a[i] + res[i];
+    }
+}
+
+#[inline(always)]
+pub fn fsub(a: &Felt, b: &Felt, res: &mut Felt) {
+    for i in 0..3 {
+        res[i] = a[i] - b[i];
+    }
+}
+
+pub fn fsub_(a: &Felt, b: &Felt) -> Felt {
+    let mut res: Felt = Felt::zero();
+    fsub(a, b, &mut res);
+    res
+}
+
+#[inline(always)]
+pub fn fneg(a: &Felt, res: &mut Felt) {
+    for i in 0..3 {
+        res[i] = -a[i];
+    }
+}
+
+pub fn fneg_(a: &Felt) -> Felt {
+    let mut res: Felt = Felt::zero();
+    fneg(a, &mut res);
+    res
+}
+
+#[inline(always)]
+pub fn felt_to_bpoly(dat: &Felt) -> BPolyVec {
+    PolyVec(vec![dat.0[0], dat.0[1], dat.0[2]])
+}
+
+pub const IRD: [Belt; 4] = [Belt(1), Belt(18446744069414584320), Belt(0), Belt(1)];
+
+// See hoon comments for full explanation
+#[inline(always)]
+pub fn fmul(a: &Felt, b: &Felt, res: &mut Felt) {
+    let poly_a: &[Belt] = &a.0;
+    let poly_b: &[Belt] = &b.0;
+
+    let a0: Belt = poly_a[0];
+    let a1: Belt = poly_a[1];
+    let a2: Belt = poly_a[2];
+    let b0: Belt = poly_b[0];
+    let b1: Belt = poly_b[1];
+    let b2: Belt = poly_b[2];
+
+    let a0b0: Belt = a0 * b0;
+    let a1b1: Belt = a1 * b1;
+    let a2b2: Belt = a2 * b2;
+    let a0b1_a1b0: Belt = ((a0 + a1) * (b0 + b1) - a0b0) - a1b1;
+    let a1b2_a2b1: Belt = ((a1 + a2) * (b1 + b2) - a1b1) - a2b2;
+    let a0b2_a2b0: Belt = ((a0 + a2) * (b0 + b2) - a0b0) - a2b2;
+
+    res.0[0] = a0b0 - a1b2_a2b1;
+    res.0[1] = (a0b1_a1b0 + a1b2_a2b1) - a2b2;
+    res.0[2] = a0b2_a2b0 + a1b1 + a2b2;
+}
+
+pub fn fmul_(a: &Felt, b: &Felt) -> Felt {
+    let mut res = Felt::zero();
+    fmul(a, b, &mut res);
+    res
+}
+
+pub fn fscal_(a: &Belt, b: &Felt) -> Felt {
+    Felt([b.0[0] * *a, b.0[1] * *a, b.0[2] * *a])
+}
+
+pub fn fscal_self(a: &Belt, b: &mut Felt) {
+    b.0[0] = b.0[0] * *a;
+    b.0[1] = b.0[1] * *a;
+    b.0[2] = b.0[2] * *a;
+}
+
+const POLY: [Belt; 4] = [Belt(1), Belt(18446744069414584320), Belt(0), Belt(1)];
+
+pub const DUV_LEN: usize = 4;
+
+#[inline(always)]
+pub fn finv(a: &Felt, res: &mut Felt) {
+    let poly_a: &[Belt] = a.into();
+
+    let mut poly_d = [Belt(0); DUV_LEN];
+    let mut poly_u = [Belt(0); DUV_LEN];
+    let mut poly_v = [Belt(0); DUV_LEN];
+    let poly_bpoly = POLY.as_slice();
+
+    bpegcd(
+        poly_bpoly,
+        poly_a,
+        poly_d.as_mut_slice(),
+        poly_u.as_mut_slice(),
+        poly_v.as_mut_slice(),
+    );
+
+    bpscal(poly_d[0].inv(), poly_v.as_slice(), &mut res.0);
+}
+
+pub fn finv_(a: &Felt) -> Felt {
+    let mut res: Felt = Felt::zero();
+    finv(a, &mut res);
+    res
+}
+
+#[inline(always)]
+pub fn fpow_(term: &Felt, exponent: u64) -> Felt {
+    let mut res: Felt = Felt::zero();
+    fpow(term, exponent, &mut res);
+    res
+}
+
+#[inline(always)]
+pub fn fpow(term: &Felt, exponent: u64, c: &mut Felt) {
+    let a: &mut Felt = &mut term.clone();
+    let mut b: u64 = exponent;
+    *c = Felt::from([Belt(1), Belt(0), Belt(0)]);
+    if b == 0 {
+        return;
+    }
+
+    while b > 1 {
+        let a_clone = *a;
+
+        if b & 1 == 0 {
+            fmul(&a_clone, &a_clone, a);
+            b /= 2;
+        } else {
+            fmul(&c.clone(), a, c);
+            fmul(&a_clone, &a_clone, a);
+            b = (b - 1) / 2;
+        }
+    }
+    fmul(&c.clone(), a, c);
+}
+
+#[inline(always)]
+pub fn fdiv(a: &Felt, b: &Felt, res: &mut Felt) {
+    let binv = &mut Felt::zero();
+    finv(b, binv);
+    fmul(a, binv, res);
+}
+
+#[inline(always)]
+pub fn fdiv_(a: &Felt, b: &Felt) -> Felt {
+    let mut res: Felt = Felt::zero();
+    fdiv(a, b, &mut res);
+    res
+}

+ 30 - 0
crates/zkvm-jetpack/src/form/math/mary.rs

@@ -0,0 +1,30 @@
+use crate::form::mary::{MarySlice, MarySliceMut};
+
+#[inline(always)]
+pub fn mary_weld(a: MarySlice, b: MarySlice, res: MarySliceMut) {
+    assert_eq!(a.step, b.step);
+    assert_eq!(res.len, a.len + b.len);
+    let a_len = a.len as usize;
+    let res_len = res.len as usize;
+    let step = res.step as usize;
+    res.dat[0..a_len * step].copy_from_slice(a.dat);
+    res.dat[a_len * step..res_len * step].copy_from_slice(b.dat);
+}
+
+#[inline(always)]
+pub fn mary_transpose(fpolys: MarySlice, offset: usize, res: &mut MarySliceMut) {
+    let step = fpolys.step as usize;
+    let len = fpolys.len as usize;
+
+    let num_cols = step / offset;
+    let num_rows = len;
+
+    for i in 0..num_cols {
+        for j in 0..num_rows {
+            for k in 0..offset {
+                res.dat[offset * (i * num_rows + j) + k] =
+                    fpolys.dat[offset * (j * num_cols + i) + k];
+            }
+        }
+    }
+}

+ 2 - 0
crates/zkvm-jetpack/src/form/math/mod.rs

@@ -1,5 +1,7 @@
 pub mod base;
 pub mod bpoly;
+pub mod fext;
+pub mod mary;
 pub mod tip5;
 
 pub use base::*;

+ 36 - 21
crates/zkvm-jetpack/src/form/math/tip5.rs

@@ -6,7 +6,7 @@ pub const NUM_SPLIT_AND_LOOKUP: usize = 4;
 pub const LOG2_STATE_SIZE: usize = 4;
 pub const CAPACITY: usize = 6;
 pub const RATE: usize = 10;
-pub const NUM_ROUNDS: usize = 5;
+pub const NUM_ROUNDS: usize = 7;
 const R: u128 = 18446744073709551616;
 
 const LOOKUP_TABLE: [u8; 256] = [
@@ -26,26 +26,41 @@ const LOOKUP_TABLE: [u8; 256] = [
 ];
 
 const ROUND_CONSTANTS: [u64; NUM_ROUNDS * STATE_SIZE] = [
-    13630775303355457758, 16896927574093233874, 10379449653650130495, 1965408364413093495,
-    15232538947090185111, 15892634398091747074, 3989134140024871768, 2851411912127730865,
-    8709136439293758776, 3694858669662939734, 12692440244315327141, 10722316166358076749,
-    12745429320441639448, 17932424223723990421, 7558102534867937463, 15551047435855531404,
-    17532528648579384106, 5216785850422679555, 15418071332095031847, 11921929762955146258,
-    9738718993677019874, 3464580399432997147, 13408434769117164050, 264428218649616431,
-    4436247869008081381, 4063129435850804221, 2865073155741120117, 5749834437609765994,
-    6804196764189408435, 17060469201292988508, 9475383556737206708, 12876344085611465020,
-    13835756199368269249, 1648753455944344172, 9836124473569258483, 12867641597107932229,
-    11254152636692960595, 16550832737139861108, 11861573970480733262, 1256660473588673495,
-    13879506000676455136, 10564103842682358721, 16142842524796397521, 3287098591948630584,
-    685911471061284805, 5285298776918878023, 18310953571768047354, 3142266350630002035,
-    549990724933663297, 4901984846118077401, 11458643033696775769, 8706785264119212710,
-    12521758138015724072, 11877914062416978196, 11333318251134523752, 3933899631278608623,
-    16635128972021157924, 10291337173108950450, 4142107155024199350, 16973934533787743537,
-    11068111539125175221, 17546769694830203606, 5315217744825068993, 4609594252909613081,
-    3350107164315270407, 17715942834299349177, 9600609149219873996, 12894357635820003949,
-    4597649658040514631, 7735563950920491847, 1663379455870887181, 13889298103638829706,
-    7375530351220884434, 3502022433285269151, 9231805330431056952, 9252272755288523725,
-    10014268662326746219, 15565031632950843234, 1209725273521819323, 6024642864597845108,
+    // 1st round constants
+    1332676891236936200, 16607633045354064669, 12746538998793080786, 15240351333789289931,
+    10333439796058208418, 986873372968378050, 153505017314310505, 703086547770691416,
+    8522628845961587962, 1727254290898686320, 199492491401196126, 2969174933639985366,
+    1607536590362293391, 16971515075282501568, 15401316942841283351, 14178982151025681389,
+    // 2nd round constants
+    2916963588744282587, 5474267501391258599, 5350367839445462659, 7436373192934779388,
+    12563531800071493891, 12265318129758141428, 6524649031155262053, 1388069597090660214,
+    3049665785814990091, 5225141380721656276, 10399487208361035835, 6576713996114457203,
+    12913805829885867278, 10299910245954679423, 12980779960345402499, 593670858850716490,
+    // 3rd round constants
+    12184128243723146967, 1315341360419235257, 9107195871057030023, 4354141752578294067,
+    8824457881527486794, 14811586928506712910, 7768837314956434138, 2807636171572954860,
+    9487703495117094125, 13452575580428891895, 14689488045617615844, 16144091782672017853,
+    15471922440568867245, 17295382518415944107, 15054306047726632486, 5708955503115886019,
+    // 4th round constants
+    9596017237020520842, 16520851172964236909, 8513472793890943175, 8503326067026609602,
+    9402483918549940854, 8614816312698982446, 7744830563717871780, 14419404818700162041,
+    8090742384565069824, 15547662568163517559, 17314710073626307254, 10008393716631058961,
+    14480243402290327574, 13569194973291808551, 10573516815088946209, 15120483436559336219,
+    // 5th round constants
+    3515151310595301563, 1095382462248757907, 5323307938514209350, 14204542692543834582,
+    12448773944668684656, 13967843398310696452, 14838288394107326806, 13718313940616442191,
+    15032565440414177483, 13769903572116157488, 17074377440395071208, 16931086385239297738,
+    8723550055169003617, 590842605971518043, 16642348030861036090, 10708719298241282592,
+    // 6th round constants
+    12766914315707517909, 11780889552403245587, 113183285481780712, 9019899125655375514,
+    3300264967390964820, 12802381622653377935, 891063765000023873, 15939045541699412539,
+    3240223189948727743, 4087221142360949772, 10980466041788253952, 18199914337033135244,
+    7168108392363190150, 16860278046098150740, 13088202265571714855, 4712275036097525581,
+    // 7th round constants
+    16338034078141228133, 1455012125527134274, 5024057780895012002, 9289161311673217186,
+    9401110072402537104, 11919498251456187748, 4173156070774045271, 15647643457869530627,
+    15642078237964257476, 1405048341078324037, 3059193199283698832, 1605012781983592984,
+    7134876918849821827, 5796994175286958720, 7251651436095127661, 4565856221886323991,
 ];
 
 const MDS_MATRIX_I64: [[i64; STATE_SIZE]; STATE_SIZE] = [

+ 2 - 0
crates/zkvm-jetpack/src/form/mod.rs

@@ -1,7 +1,9 @@
 pub mod belt;
 pub mod crypto;
+pub mod felt;
 pub mod mary;
 pub mod math;
 pub mod poly;
 
+pub use math::*;
 pub use poly::*;

+ 208 - 0
crates/zkvm-jetpack/src/hot.rs

@@ -3,19 +3,207 @@ use nockvm::jets::hot::{HotEntry, K_138};
 
 use crate::jets::base_jets::*;
 use crate::jets::bp_jets::*;
+use crate::jets::cheetah_jets::*;
 use crate::jets::crypto_jets::*;
+use crate::jets::fext_jets::*;
+use crate::jets::mary_jets::*;
 use crate::jets::tip5_jets::*;
+use crate::jets::verifier_jets::*;
 
 pub fn produce_prover_hot_state() -> Vec<HotEntry> {
     let mut jets: Vec<HotEntry> = Vec::new();
     jets.extend(BASE_FIELD_JETS);
     jets.extend(BASE_POLY_JETS);
+    jets.extend(CURVE_JETS);
     jets.extend(ZTD_JETS);
     jets.extend(KEYGEN_JETS);
+    jets.extend(XTRA_JETS);
+    jets.extend(EXTENSION_FIELD_JETS);
 
     jets
 }
 
+pub const XTRA_JETS: &[HotEntry] = &[
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ave"),
+            Left(b"weld"),
+        ],
+        1,
+        mary_weld_jet,
+    ),
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ave"),
+            Left(b"swag"),
+        ],
+        1,
+        mary_swag_jet,
+    ),
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ext-field"),
+            Left(b"misc-lib"),
+            Left(b"proof-lib"),
+            Left(b"utils"),
+            Left(b"fri"),
+            Left(b"table-lib"),
+            Left(b"stark-core"),
+            Left(b"fock-core"),
+            Left(b"pow"),
+            Left(b"stark-engine"),
+            Left(b"stark-verifier"),
+            Left(b"evaluate-deep"),
+        ],
+        1,
+        evaluate_deep_jet,
+    ),
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ave"),
+            Left(b"transpose"),
+        ],
+        1,
+        mary_transpose_jet,
+    ),
+];
+
+pub const EXTENSION_FIELD_JETS: &[HotEntry] = &[
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ext-field"),
+            Left(b"fadd"),
+        ],
+        1,
+        fadd_jet,
+    ),
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ext-field"),
+            Left(b"fsub"),
+        ],
+        1,
+        fsub_jet,
+    ),
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ext-field"),
+            Left(b"fneg"),
+        ],
+        1,
+        fneg_jet,
+    ),
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ext-field"),
+            Left(b"fmul"),
+        ],
+        1,
+        fmul_jet,
+    ),
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ext-field"),
+            Left(b"finv"),
+        ],
+        1,
+        finv_jet,
+    ),
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ext-field"),
+            Left(b"fdiv"),
+        ],
+        1,
+        fdiv_jet,
+    ),
+    (
+        &[
+            K_138,
+            Left(b"one"),
+            Left(b"two"),
+            Left(b"tri"),
+            Left(b"qua"),
+            Left(b"pen"),
+            Left(b"zeke"),
+            Left(b"ext-field"),
+            Left(b"fpow"),
+        ],
+        1,
+        fpow_jet,
+    ),
+];
+
 pub const BASE_FIELD_JETS: &[HotEntry] = &[
     (
         &[
@@ -248,3 +436,23 @@ pub const KEYGEN_JETS: &[HotEntry] = &[(
     1,
     argon2_jet,
 )];
+
+pub const CURVE_JETS: &[HotEntry] = &[(
+    &[
+        K_138,
+        Left(b"one"),
+        Left(b"two"),
+        Left(b"tri"),
+        Left(b"qua"),
+        Left(b"pen"),
+        Left(b"zeke"),
+        Left(b"ext-field"),
+        Left(b"misc-lib"),
+        Left(b"cheetah"),
+        Left(b"curve"),
+        Left(b"affine"),
+        Left(b"ch-scal"),
+    ],
+    1,
+    ch_scal_jet,
+)];

+ 5 - 6
crates/zkvm-jetpack/src/jets/bp_jets.rs

@@ -118,17 +118,16 @@ pub fn bpmul_jet(context: &mut Context, subject: Noun) -> Result {
         return jet_err();
     };
 
-    let res_len = bp_poly.len() + bq_poly.len() - 1;
-
-    //let (res_cell, mut res_poly): (Cell, BPolySliceMut) =
-    //    Cell::new_zeroed_handle_mut(&mut context.stack, Some(res_len as usize));
+    let res_len = if bp_poly.is_zero() | bq_poly.is_zero() {
+        1
+    } else {
+        bp_poly.len() + bq_poly.len() - 1
+    };
 
     let (res_atom, res_poly): (IndirectAtom, &mut [Belt]) =
         new_handle_mut_slice(&mut context.stack, Some(res_len));
 
-    //(c, BPolySliceMut::try_from(c).unwrap_or_else(|| panic!("Panicked at {}:{} (git sha: {:?})", file!(), line!(), option_env!("GIT_SHA"))))
     bpmul(bp_poly.0, bq_poly.0, res_poly);
-
     let res_cell = finalize_poly(&mut context.stack, Some(res_len), res_atom);
 
     Ok(res_cell)

+ 305 - 0
crates/zkvm-jetpack/src/jets/cheetah_jets.rs

@@ -0,0 +1,305 @@
+use ibig::UBig;
+use nockvm::interpreter::Context;
+use nockvm::jets::cold::{FromNounError, Nounable, NounableResult};
+use nockvm::jets::util::{slot, BAIL_FAIL};
+use nockvm::jets::JetErr;
+use nockvm::noun::{Atom, Noun, NounAllocator, NO, T, YES};
+
+use crate::form::math::base::bneg;
+use crate::form::math::bpoly::{bpegcd, bpscal};
+use crate::form::Belt;
+use crate::noun::noun_ext::AtomExt;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) struct CheetahPoint {
+    pub x: F6lt,
+    pub y: F6lt,
+    pub inf: bool,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) struct F6lt(pub [Belt; 6]);
+
+#[inline(always)]
+pub(crate) fn make_n_belt<A: NounAllocator>(stack: &mut A, arr: &[Belt]) -> Noun {
+    assert!(arr.len() > 0);
+    let n = arr.len();
+    let mut res_cell = Atom::new(stack, arr[n - 1].0).as_noun();
+    for i in (0..n - 1).rev() {
+        let b = Atom::new(stack, arr[i].0).as_noun();
+        res_cell = T(stack, &[b, res_cell]);
+    }
+    res_cell
+}
+
+impl Nounable for F6lt {
+    type Target = Self;
+    fn from_noun<A: NounAllocator>(_stack: &mut A, noun: &Noun) -> NounableResult<Self> {
+        let mut x = *noun;
+        // convert f6lts to vecs
+        let mut f6lt = [Belt(0); 6];
+        for i in 0..5 {
+            let cell = x.as_cell()?;
+            f6lt[i] = cell.head().as_atom()?.as_belt()?;
+            x = cell.tail();
+        }
+        f6lt[5] = x.as_atom()?.as_belt()?;
+
+        Ok(F6lt(f6lt))
+    }
+
+    fn into_noun<A: NounAllocator>(self, stack: &mut A) -> Noun {
+        make_n_belt(stack, &self.0)
+    }
+}
+
+impl Nounable for CheetahPoint {
+    type Target = Self;
+    fn from_noun<A: NounAllocator>(_stack: &mut A, noun: &Noun) -> NounableResult<Self> {
+        let x = slot(*noun, 2).map_err(|_| FromNounError::NotCell)?;
+        let y = slot(*noun, 6).map_err(|_| FromNounError::NotCell)?;
+        let inf = slot(*noun, 7).map_err(|_| FromNounError::NotCell)?;
+
+        // convert f6lts to vecs
+        let y_f6lt = F6lt::from_noun(_stack, &x)?;
+        let x_f6lt = F6lt::from_noun(_stack, &y)?;
+
+        Ok(CheetahPoint {
+            x: y_f6lt,
+            y: x_f6lt,
+            inf: inf.as_atom()?.as_bool()?,
+        })
+    }
+
+    fn into_noun<A: NounAllocator>(self, stack: &mut A) -> Noun {
+        let x_noun = make_n_belt(stack, &self.x.0);
+        let y_noun = make_n_belt(stack, &self.y.0);
+        let inf_noun = if self.inf { YES } else { NO };
+        T(stack, &[x_noun, y_noun, inf_noun])
+    }
+}
+
+#[inline(always)]
+pub fn ch_scal_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let sam = slot(subject, 6)?;
+    let n_atom = slot(sam, 2)?.as_atom()?;
+
+    let p = slot(sam, 3)?;
+    let a_pt = CheetahPoint::from_noun(&mut context.stack, &p)?;
+
+    let res = if let Ok(n) = n_atom.as_u64() {
+        ch_scal(n, &a_pt)?
+    } else {
+        let n_big = n_atom.as_ubig(&mut context.stack);
+        ch_scal_big(&n_big, &a_pt)?
+    };
+
+    let res_noun = CheetahPoint::into_noun(res, &mut context.stack);
+    Ok(res_noun)
+}
+
+#[inline(always)]
+pub(crate) fn f6_div(f1: &F6lt, f2: &F6lt) -> Result<F6lt, JetErr> {
+    let f2_inv = f6_inv(f2)?;
+    Ok(f6_mul(f1, &f2_inv))
+}
+
+#[inline(always)]
+fn karat3(a: &[Belt; 3], b: &[Belt; 3]) -> [Belt; 5] {
+    let m = [a[0] * b[0], a[1] * b[1], a[2] * b[2]];
+    [
+        m[0],
+        (a[0] + a[1]) * (b[0] + b[1]) - (m[0] + m[1]),
+        (a[0] + a[2]) * (b[0] + b[2]) - (m[0] + m[2]) + m[1],
+        (a[1] + a[2]) * (b[1] + b[2]) - (m[1] + m[2]),
+        m[2],
+    ]
+}
+
+#[inline(always)]
+fn f6_mul(f: &F6lt, g: &F6lt) -> F6lt {
+    let f0g0 = karat3(&[f.0[0], f.0[1], f.0[2]], &[g.0[0], g.0[1], g.0[2]]);
+    let f1g1 = karat3(&[f.0[3], f.0[4], f.0[5]], &[g.0[3], g.0[4], g.0[5]]);
+
+    let foil = karat3(
+        &[f.0[0] + f.0[3], f.0[1] + f.0[4], f.0[2] + f.0[5]],
+        &[g.0[0] + g.0[3], g.0[1] + g.0[4], g.0[2] + g.0[5]],
+    );
+
+    let cross = [
+        foil[0] - (f0g0[0] + f1g1[0]),
+        foil[1] - (f0g0[1] + f1g1[1]),
+        foil[2] - (f0g0[2] + f1g1[2]),
+        foil[3] - (f0g0[3] + f1g1[3]),
+        foil[4] - (f0g0[4] + f1g1[4]),
+    ];
+    F6lt([
+        f0g0[0] + Belt(7) * (cross[3] + f1g1[0]),
+        f0g0[1] + Belt(7) * (cross[4] + f1g1[1]),
+        f0g0[2] + Belt(7) * f1g1[2],
+        f0g0[3] + cross[0] + Belt(7) * f1g1[3],
+        f0g0[4] + cross[1] + Belt(7) * f1g1[4],
+        cross[2],
+    ])
+}
+
+#[inline(always)]
+fn f6_inv(f: &F6lt) -> Result<F6lt, JetErr> {
+    if f == &F6_ZERO {
+        return Err(BAIL_FAIL);
+    }
+    let mut res = [Belt(0); 6];
+    // length of d is at most min(6, 7) + 1
+    let mut d = [Belt(0); 7];
+    // length of u is at most deg(b) = 7
+    let mut u = [Belt(0); 7];
+    // length of u is at most deg(a) = 6
+    let mut v = [Belt(0); 6];
+    bpegcd(
+        &f.0,
+        &[Belt(bneg(7)), Belt(0), Belt(0), Belt(0), Belt(0), Belt(0), Belt(1)],
+        &mut d,
+        &mut u,
+        &mut v,
+    );
+    let inv = d[0].inv();
+    bpscal(inv, &u, &mut res);
+    Ok(F6lt(res))
+}
+
+#[inline(always)]
+fn f6_add(f1: &F6lt, f2: &F6lt) -> F6lt {
+    F6lt([
+        f1.0[0] + f2.0[0],
+        f1.0[1] + f2.0[1],
+        f1.0[2] + f2.0[2],
+        f1.0[3] + f2.0[3],
+        f1.0[4] + f2.0[4],
+        f1.0[5] + f2.0[5],
+    ])
+}
+
+fn f6_scal(s: Belt, f: &F6lt) -> F6lt {
+    F6lt([f.0[0] * s, f.0[1] * s, f.0[2] * s, f.0[3] * s, f.0[4] * s, f.0[5] * s])
+}
+
+// TODO: Try karat3-square if performance is an issue
+#[inline(always)]
+fn f6_square(f: &F6lt) -> F6lt {
+    f6_mul(f, f)
+}
+
+#[inline(always)]
+fn f6_neg(f: &F6lt) -> F6lt {
+    F6lt([-f.0[0], -f.0[1], -f.0[2], -f.0[3], -f.0[4], -f.0[5]])
+}
+
+#[inline(always)]
+fn f6_sub(f1: &F6lt, f2: &F6lt) -> F6lt {
+    f6_add(f1, &f6_neg(f2))
+}
+
+#[inline(always)]
+fn ch_double_unsafe(x: &F6lt, y: &F6lt) -> Result<CheetahPoint, JetErr> {
+    let slope = f6_div(
+        &f6_add(&f6_scal(Belt(3), &f6_square(x)), &F6_ONE),
+        &f6_scal(Belt(2), &y),
+    )?;
+    let x_out = f6_sub(&f6_square(&slope), &f6_scal(Belt(2), &x));
+    let y_out = f6_sub(&f6_mul(&slope, &f6_sub(x, &x_out)), y);
+    Ok(CheetahPoint {
+        x: x_out,
+        y: y_out,
+        inf: false,
+    })
+}
+
+pub(crate) const A_ID: CheetahPoint = CheetahPoint {
+    x: F6_ZERO,
+    y: F6_ONE,
+    inf: true,
+};
+pub(crate) const F6_ZERO: F6lt = F6lt([Belt(0); 6]);
+pub(crate) const F6_ONE: F6lt = F6lt([Belt(1), Belt(0), Belt(0), Belt(0), Belt(0), Belt(0)]);
+
+#[inline(always)]
+fn ch_double(p: CheetahPoint) -> Result<CheetahPoint, JetErr> {
+    if p.inf {
+        return Ok(A_ID);
+    }
+    if p.y == F6_ZERO {
+        return Ok(A_ID);
+    }
+    ch_double_unsafe(&p.x, &p.y)
+}
+
+#[inline(always)]
+fn ch_add_unsafe(p: CheetahPoint, q: CheetahPoint) -> Result<CheetahPoint, JetErr> {
+    let slope = f6_div(&f6_sub(&p.y, &q.y), &f6_sub(&p.x, &q.x))?;
+    let x_out = f6_sub(&f6_square(&slope), &f6_add(&p.x, &q.x));
+    let y_out = f6_sub(&f6_mul(&slope, &f6_sub(&p.x, &x_out)), &p.y);
+    Ok(CheetahPoint {
+        x: x_out,
+        y: y_out,
+        inf: false,
+    })
+}
+
+#[inline(always)]
+fn ch_neg(p: &CheetahPoint) -> CheetahPoint {
+    CheetahPoint {
+        x: p.x,
+        y: f6_neg(&p.y),
+        inf: p.inf,
+    }
+}
+
+#[inline(always)]
+fn ch_add(p: &CheetahPoint, q: &CheetahPoint) -> Result<CheetahPoint, JetErr> {
+    if p.inf {
+        return Ok(*q);
+    }
+    if q.inf {
+        return Ok(*p);
+    }
+    if *p == ch_neg(q) {
+        return Ok(A_ID);
+    }
+    if p == q {
+        return ch_double(*p);
+    }
+    ch_add_unsafe(*p, *q)
+}
+
+#[inline(always)]
+pub(crate) fn ch_scal(n: u64, p: &CheetahPoint) -> Result<CheetahPoint, JetErr> {
+    let mut n = n.clone();
+    let mut p_copy = *p;
+    let mut acc = A_ID;
+    while n > 0 {
+        if n & 1 == 1 {
+            acc = ch_add(&acc, &p_copy)?;
+        }
+        p_copy = ch_double(p_copy)?;
+        n >>= 1;
+    }
+    Ok(acc)
+}
+
+#[inline(always)]
+pub(crate) fn ch_scal_big(n: &UBig, p: &CheetahPoint) -> Result<CheetahPoint, JetErr> {
+    let mut n_copy = n.clone();
+    let zero = UBig::from(0u64);
+    let mut p_copy = *p;
+    let mut acc = A_ID;
+
+    while n_copy > zero {
+        // Check if least significant bit is set
+        if n_copy.bit(0) {
+            acc = ch_add(&acc, &p_copy)?;
+        }
+        p_copy = ch_double(p_copy)?;
+        n_copy >>= 1; // Right shift by 1 bit
+    }
+    Ok(acc)
+}

+ 121 - 0
crates/zkvm-jetpack/src/jets/fext_jets.rs

@@ -0,0 +1,121 @@
+use nockvm::interpreter::Context;
+use nockvm::jets::util::slot;
+use nockvm::jets::JetErr;
+use nockvm::noun::{IndirectAtom, Noun};
+use tracing::debug;
+
+use crate::form::fext::*;
+use crate::form::poly::*;
+use crate::hand::handle::new_handle_mut_felt;
+use crate::jets::utils::jet_err;
+use crate::noun::noun_ext::NounExt;
+use crate::utils::*;
+
+pub fn fadd_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let sam = slot(subject, 6)?;
+    let a = slot(sam, 2)?;
+    let b = slot(sam, 3)?;
+
+    let (Ok(a_felt), Ok(b_felt)) = (a.as_felt(), b.as_felt()) else {
+        debug!("a or b not a felt");
+        return jet_err();
+    };
+    let (res_atom, res_felt): (IndirectAtom, &mut Felt) = new_handle_mut_felt(&mut context.stack);
+    fadd(a_felt, b_felt, res_felt);
+
+    assert!(felt_atom_is_valid(res_atom));
+    Ok(res_atom.as_noun())
+}
+
+pub fn fsub_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let sam = slot(subject, 6)?;
+    let a = slot(sam, 2)?;
+    let b = slot(sam, 3)?;
+
+    let (Ok(a_felt), Ok(b_felt)) = (a.as_felt(), b.as_felt()) else {
+        debug!("a or b not a felt");
+        return jet_err();
+    };
+    let (res_atom, res_felt): (IndirectAtom, &mut Felt) = new_handle_mut_felt(&mut context.stack);
+    fsub(a_felt, b_felt, res_felt);
+
+    assert!(felt_atom_is_valid(res_atom));
+    Ok(res_atom.as_noun())
+}
+
+pub fn fneg_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let a = slot(subject, 6)?;
+
+    let Ok(a_felt) = a.as_felt() else {
+        debug!("a not a felt");
+        return jet_err();
+    };
+    let (res_atom, res_felt): (IndirectAtom, &mut Felt) = new_handle_mut_felt(&mut context.stack);
+    fneg(a_felt, res_felt);
+
+    assert!(felt_atom_is_valid(res_atom));
+    Ok(res_atom.as_noun())
+}
+
+pub fn fmul_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let sam = slot(subject, 6)?;
+    let a = slot(sam, 2)?;
+    let b = slot(sam, 3)?;
+
+    let (Ok(a_felt), Ok(b_felt)) = (a.as_felt(), b.as_felt()) else {
+        debug!("a or b not a felt");
+        return jet_err();
+    };
+    let (res_atom, res_felt): (IndirectAtom, &mut Felt) = new_handle_mut_felt(&mut context.stack);
+    fmul(a_felt, b_felt, res_felt);
+
+    assert!(felt_atom_is_valid(res_atom));
+    Ok(res_atom.as_noun())
+}
+
+pub fn finv_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let a = slot(subject, 6)?;
+
+    let Ok(a_felt) = a.as_felt() else {
+        debug!("a is not a felt");
+        return jet_err();
+    };
+    let (res_atom, res_felt): (IndirectAtom, &mut Felt) = new_handle_mut_felt(&mut context.stack);
+    finv(a_felt, res_felt);
+
+    assert!(felt_atom_is_valid(res_atom));
+    Ok(res_atom.as_noun())
+}
+
+pub fn fdiv_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let sam = slot(subject, 6)?;
+    let a = slot(sam, 2)?;
+    let b = slot(sam, 3)?;
+
+    let (Ok(a_felt), Ok(b_felt)) = (a.as_felt(), b.as_felt()) else {
+        debug!("a or b not felts");
+        return jet_err();
+    };
+    let (res_atom, res_felt): (IndirectAtom, &mut Felt) = new_handle_mut_felt(&mut context.stack);
+    fdiv(a_felt, b_felt, res_felt);
+
+    assert!(felt_atom_is_valid(res_atom));
+    Ok(res_atom.as_noun())
+}
+
+pub fn fpow_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let sam = slot(subject, 6)?;
+    let x = slot(sam, 2)?;
+    let n = slot(sam, 3)?;
+
+    let (Ok(x_felt), Ok(n_atom)) = (x.as_felt(), n.as_atom()) else {
+        debug!("x not a felt or n not an atom");
+        return jet_err();
+    };
+    let n_64 = n_atom.as_u64()?;
+    let (res_atom, res_felt): (IndirectAtom, &mut Felt) = new_handle_mut_felt(&mut context.stack);
+    fpow(x_felt, n_64, res_felt);
+
+    assert!(felt_atom_is_valid(res_atom));
+    Ok(res_atom.as_noun())
+}

+ 86 - 0
crates/zkvm-jetpack/src/jets/mary_jets.rs

@@ -0,0 +1,86 @@
+use nockvm::interpreter::Context;
+use nockvm::jets::util::slot;
+use nockvm::jets::JetErr;
+use nockvm::noun::{IndirectAtom, Noun};
+use tracing::debug;
+
+use crate::form::mary::*;
+use crate::form::math::mary::*;
+use crate::hand::handle::{finalize_mary, new_handle_mut_mary};
+use crate::jets::utils::jet_err;
+
+pub fn mary_swag_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let door = slot(subject, 7)?;
+    let ma = slot(door, 6)?;
+    let sam = slot(subject, 6)?;
+    let i = sam.as_cell()?.head().as_direct()?.data() as usize;
+    let j = sam.as_cell()?.tail().as_direct()?.data() as usize;
+
+    let Ok(mary) = MarySlice::try_from(ma) else {
+        debug!("cannot convert mary arg to mary");
+        return jet_err();
+    };
+
+    let (res, res_poly): (IndirectAtom, MarySliceMut) =
+        new_handle_mut_mary(&mut context.stack, mary.step as usize, j);
+    let step = mary.step as usize;
+
+    res_poly
+        .dat
+        .copy_from_slice(&mary.dat[(i * step)..(i + j) * step]);
+
+    let res_cell = finalize_mary(&mut context.stack, step, j, res);
+    Ok(res_cell)
+}
+
+pub fn mary_weld_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let door = slot(subject, 7)?;
+    let ma = slot(door, 6)?;
+    let ma2 = slot(subject, 6)?;
+
+    let step = ma.as_cell()?.head().as_direct()?.data() as u32;
+    let step2 = ma2.as_cell()?.head().as_direct()?.data() as u32;
+    if step != step2 {
+        debug!("can only weld marys of same step");
+        return jet_err();
+    }
+
+    let (Ok(mary1), Ok(mary2)) = (MarySlice::try_from(ma), MarySlice::try_from(ma2)) else {
+        debug!("mary1 or mary2 is not an fpoly");
+        return jet_err();
+    };
+    let res_len = mary1.len + mary2.len;
+    let (res, res_poly): (IndirectAtom, MarySliceMut) =
+        new_handle_mut_mary(&mut context.stack, step as usize, res_len as usize);
+
+    mary_weld(mary1, mary2, res_poly);
+    let res_cell = finalize_mary(&mut context.stack, step as usize, res_len as usize, res);
+    Ok(res_cell)
+}
+
+pub fn mary_transpose_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let door = slot(subject, 7)?;
+    let ma = slot(door, 6)?;
+    let offset = slot(subject, 6)?;
+
+    let (Ok(mary), Ok(offset)) = (MarySlice::try_from(ma), offset.as_atom()?.as_u64()) else {
+        debug!("fp is not an fpoly or n is not an atom");
+        return jet_err();
+    };
+
+    let offset = offset as usize;
+
+    let (res, mut res_poly): (IndirectAtom, MarySliceMut) = new_handle_mut_mary(
+        &mut context.stack,
+        mary.len as usize * offset,
+        mary.step as usize / offset,
+    );
+
+    mary_transpose(mary, offset, &mut res_poly);
+
+    let res_cell = finalize_mary(
+        &mut context.stack, res_poly.step as usize, res_poly.len as usize, res,
+    );
+
+    Ok(res_cell)
+}

+ 4 - 0
crates/zkvm-jetpack/src/jets/mod.rs

@@ -1,5 +1,9 @@
 pub mod base_jets;
 pub mod bp_jets;
+pub mod cheetah_jets;
 pub mod crypto_jets;
+pub mod fext_jets;
+pub mod mary_jets;
 pub mod tip5_jets;
 pub mod utils;
+pub mod verifier_jets;

+ 178 - 0
crates/zkvm-jetpack/src/jets/verifier_jets.rs

@@ -0,0 +1,178 @@
+use nockvm::interpreter::Context;
+use nockvm::jets::util::slot;
+use nockvm::jets::JetErr;
+use nockvm::noun::{Cell, IndirectAtom, Noun};
+use tracing::debug;
+
+use crate::form::math::fext::*;
+use crate::form::poly::Poly;
+use crate::form::{Belt, FPolySlice, Felt};
+use crate::hand::handle::new_handle_mut_felt;
+use crate::hand::structs::HoonList;
+use crate::jets::utils::jet_err;
+use crate::noun::noun_ext::NounExt;
+
+pub fn evaluate_deep_jet(context: &mut Context, subject: Noun) -> Result<Noun, JetErr> {
+    let sam = slot(subject, 6)?;
+    let mut sam_cur: Cell = sam.as_cell()?;
+
+    // Extract all parameters from the subject
+    let trace_evaluations = sam_cur.head();
+    sam_cur = sam_cur.tail().as_cell()?;
+    let comp_evaluations = sam_cur.head();
+    sam_cur = sam_cur.tail().as_cell()?;
+    let trace_elems = sam_cur.head();
+    sam_cur = sam_cur.tail().as_cell()?;
+    let comp_elems = sam_cur.head();
+    sam_cur = sam_cur.tail().as_cell()?;
+    let num_comp_pieces = sam_cur.head();
+    sam_cur = sam_cur.tail().as_cell()?;
+    let weights = sam_cur.head();
+    sam_cur = sam_cur.tail().as_cell()?;
+    let heights = sam_cur.head();
+    sam_cur = sam_cur.tail().as_cell()?;
+    let full_widths = sam_cur.head();
+    sam_cur = sam_cur.tail().as_cell()?;
+    let omega = sam_cur.head();
+    sam_cur = sam_cur.tail().as_cell()?;
+    let index = sam_cur.head();
+    sam_cur = sam_cur.tail().as_cell()?;
+    let deep_challenge = sam_cur.head();
+    let new_comp_eval = sam_cur.tail();
+
+    // Convert nouns to appropriate types
+    let Ok(trace_evaluations) = FPolySlice::try_from(trace_evaluations) else {
+        debug!("trace_evaluations is not a valid FPolySlice");
+        return jet_err();
+    };
+    let Ok(comp_evaluations) = FPolySlice::try_from(comp_evaluations) else {
+        debug!("comp_evaluations is not a valid FPolySlice");
+        return jet_err();
+    };
+    let trace_elems: Vec<Belt> = HoonList::try_from(trace_elems)?
+        .into_iter()
+        .map(|x| x.as_atom().unwrap().as_u64().unwrap())
+        .map(|x| Belt(x))
+        .collect();
+    let comp_elems: Vec<Belt> = HoonList::try_from(comp_elems)?
+        .into_iter()
+        .map(|x| x.as_atom().unwrap().as_u64().unwrap())
+        .map(|x| Belt(x))
+        .collect();
+    let num_comp_pieces = num_comp_pieces.as_atom()?.as_u64()?;
+    let Ok(weights) = FPolySlice::try_from(weights) else {
+        debug!("weights is not a valid FPolySlice");
+        return jet_err();
+    };
+    let heights: Vec<u64> = HoonList::try_from(heights)?
+        .into_iter()
+        .map(|x| x.as_atom().unwrap().as_u64().unwrap())
+        .collect();
+    let full_widths: Vec<u64> = HoonList::try_from(full_widths)?
+        .into_iter()
+        .map(|x| x.as_atom().unwrap().as_u64().unwrap())
+        .collect();
+    let omega = omega.as_felt()?;
+    let index = index.as_atom()?.as_u64()?;
+    let deep_challenge = deep_challenge.as_felt()?;
+    let new_comp_eval = new_comp_eval.as_felt()?;
+
+    //  TODO use g defined wherever it is
+    let g = Felt::lift(Belt(7));
+    let omega_pow = fmul_(&fpow_(&omega, index as u64), &g);
+
+    let mut acc = Felt::zero();
+    let mut num = 0usize;
+    let mut total_full_width = 0usize;
+
+    for (i, &height) in heights.iter().enumerate() {
+        let full_width = full_widths[i] as usize;
+        let omicron = Felt::lift(Belt(height).ordered_root()?);
+
+        let current_trace_elems = &trace_elems[total_full_width..(total_full_width + full_width)];
+
+        // Process first row trace columns
+        let denom = fsub_(&omega_pow, &deep_challenge);
+        (acc, num) = process_belt(
+            current_trace_elems, &trace_evaluations.0, &weights.0, full_width, num, &denom, &acc,
+        );
+
+        // Process second row trace columns (shifted by omicron)
+        let denom = fsub_(&omega_pow, &fmul_(&deep_challenge, &omicron));
+        (acc, num) = process_belt(
+            current_trace_elems, &trace_evaluations.0, &weights.0, full_width, num, &denom, &acc,
+        );
+
+        total_full_width += full_width;
+    }
+
+    total_full_width = 0;
+    for (i, &height) in heights.iter().enumerate() {
+        let full_width = full_widths[i] as usize;
+        let omicron = Felt::lift(Belt(height).ordered_root()?);
+
+        let current_trace_elems = &trace_elems[total_full_width..(total_full_width + full_width)];
+
+        // Process first row trace columns with new_comp_eval
+        let denom = fsub_(&omega_pow, &new_comp_eval);
+        (acc, num) = process_belt(
+            current_trace_elems, &trace_evaluations.0, &weights.0, full_width, num, &denom, &acc,
+        );
+
+        // Process second row trace columns with new_comp_eval (shifted by omicron)
+        let denom = fsub_(&omega_pow, &fmul_(&new_comp_eval, &omicron));
+        (acc, num) = process_belt(
+            current_trace_elems, &trace_evaluations.0, &weights.0, full_width, num, &denom, &acc,
+        );
+
+        total_full_width += full_width;
+    }
+
+    // Process composition elements
+    let denom = fsub_(&omega_pow, &fpow_(&deep_challenge, num_comp_pieces as u64));
+
+    (acc, _) = process_belt(
+        &comp_elems,
+        &comp_evaluations.0,
+        &weights.0[num..],
+        num_comp_pieces as usize,
+        0,
+        &denom,
+        &acc,
+    );
+
+    // Return the result as a Noun
+    let (res_atom, res_felt): (IndirectAtom, &mut Felt) = new_handle_mut_felt(&mut context.stack);
+    *res_felt = acc;
+
+    Ok(res_atom.as_noun())
+}
+
+// Helper function for processing belts
+fn process_belt(
+    elems: &[Belt],
+    evals: &[Felt],
+    weights: &[Felt],
+    width: usize,
+    start_num: usize,
+    denom: &Felt,
+    acc_start: &Felt,
+) -> (Felt, usize) {
+    let mut acc = *acc_start;
+    let mut num = start_num;
+
+    for i in 0..width {
+        let elem_val = Felt::lift(elems[i]);
+        let eval_val = evals[num];
+        let weight_val = weights[num];
+
+        // (elem_val - eval_val) / denom * weight_val + acc
+        let diff = fsub_(&elem_val, &eval_val);
+        let term = fmul_(&fdiv_(&diff, denom), &weight_val);
+        acc = fadd_(&acc, &term);
+
+        num += 1;
+    }
+
+    (acc, num)
+}

+ 1 - 0
crates/zkvm-jetpack/src/lib.rs

@@ -3,6 +3,7 @@ pub mod hand;
 pub mod hot;
 pub mod jets;
 pub mod noun;
+pub mod utils;
 
 #[macro_use]
 extern crate arrayref;

+ 11 - 0
crates/zkvm-jetpack/src/utils.rs

@@ -0,0 +1,11 @@
+// Utility functions and commonly used re-exports
+
+use nockvm::noun::IndirectAtom;
+pub use tracing::{debug, trace};
+
+// tests whether a felt atom has the leading 1. we cannot actually test
+// Felt, because it doesn't include the leading 1.
+pub fn felt_atom_is_valid(felt_atom: IndirectAtom) -> bool {
+    let dat_ptr = felt_atom.data_pointer();
+    unsafe { *(dat_ptr.add(3)) == 0x1 }
+}

+ 125 - 56
hoon/apps/dumbnet/inner.hoon

@@ -39,7 +39,7 @@
     ::
         [%blocks ~]
       ^-  (unit (unit (z-map block-id:t page:t)))
-      ``(~(run by blocks.c.k) to-page:local-page:t)
+      ``(~(run z-by blocks.c.k) to-page:local-page:t)
     ::
         [%transactions ~]
       ^-  (unit (unit (z-mip block-id:t tx-id:t tx:t)))
@@ -139,9 +139,13 @@
     |^
     =/  cause  ((soft cause:dk) dat)
     ?~  cause
-      ~>  %slog.[0 [%leaf "error: badly formatted cause, should never occur"]]
+      ~>  %slog.[0 [%leaf "error: badly formatted cause, should never occur."]]
       ~&  ;;([thing=@t ver=@ type=@t] [-.dat +<.dat +>-.dat])
-      `k
+      =/  peer-id  (get-peer-id wir)
+      ?~  peer-id
+        `k
+      ~>  %slog.[0 [leaf+"peer-id found in wire of badly formatted cause, emitting %liar-peer"]]
+      [[%liar-peer u.peer-id %invalid-fact]~ k]
     =/  cause  u.cause
     ::~&  "inner dumbnet cause: {<[-.cause -.+.cause]>}"
     =^  effs  k
@@ -162,7 +166,7 @@
     ::
     ::  +heard-genesis-block: check if block is a genesis block and decide whether to keep it
     ++  heard-genesis-block
-      |=  [wir=wire now=@da pag=page:t]
+      |=  [wir=wire now=@da eny=@ pag=page:t]
       ^-  [(list effect:dk) kernel-state:dk]
       ?:  (check-duplicate-block digest.pag)
         :: do nothing (idempotency), we already have block
@@ -178,7 +182,7 @@
         [[(liar-effect wir %not-a-genesis-block)]~ k]
       ::  heard valid genesis block
       ~>  %slog.[0 leaf+"validated genesis block!"]
-      (new-block now pag *tx-acc:t)
+      (new-block now eny pag *tx-acc:t)
     ::
     ++  heard-block
       |=  [wir=wire now=@da pag=page:t eny=@]
@@ -186,7 +190,7 @@
       ?:  =(*page-number:t height.pag)
         ::  heard genesis block
         ~>  %slog.[0 leaf+"heard genesis block"]
-        (heard-genesis-block wir now pag)
+        (heard-genesis-block wir now eny pag)
       ?~  heaviest-block.c.k
         =/  peer-id=(unit @)  (get-peer-id wir)
         ?~  peer-id
@@ -292,7 +296,7 @@
       ::
       ::  block has no missing transactions, so we check that its transactions
       ::  are valid
-      (process-block-with-txs now pag block-effs)
+      (process-block-with-txs now eny pag block-effs)
     ::
     ::  +heard-elders: handle response to parent hashes request
     ++  heard-elders
@@ -357,8 +361,9 @@
       ==
     ::
     ++  check-genesis
-     |=  [pag=page:t =btc-hash:t =genesis-seal:t]
+     |=  [pag=page:t btc-hash=(unit btc-hash:t) =genesis-seal:t]
      ^-  ?
+     =/  check-digest  (check-digest:page:t pag)
      =/  check-pow-hash=?
       ?.  check-pow-flag:t
          ::  this case only happens during testing
@@ -366,8 +371,13 @@
          %.y
        %-  check-target:mine
        :_  target.pag
-       (digest-to-atom:tip5:zeke (hash-proof:zeke (need pow.pag)))
+       (proof-to-pow:zeke (need pow.pag))
      =/  check-pow-valid=?  (check-pow pag)
+     ::
+     ::  check if timestamp is in base field, this will anchor subsequent timestamp checks
+     ::  since child block timestamps have to be within a certain range of the most recent
+     ::  N blocks.
+     =/  check-timestamp=?  (based:zeke timestamp.pag)
      =/  check-txs=?  =(tx-ids.pag *(z-set tx-id:t))
      =/  check-epoch=?  =(epoch-counter.pag *@)
      =/  check-target=?  =(target.pag genesis-target:t)
@@ -375,14 +385,30 @@
      =/  check-coinbase=?  =(coinbase.pag *(z-map lock:t @))
      =/  check-height=?  =(height.pag *page-number:t)
      =/  check-btc-hash=?
-       =(parent.pag (hash:btc-hash:t btc-hash))
+       ?~  btc-hash  ~>  %slog.[0 leaf+"Not checking btc hash when validating genesis block"]  %.y
+       =(parent.pag (hash:btc-hash:t u.btc-hash))
      ::
      ::  check that the message matches what's in the seal
      =/  check-msg=?
        ?~  genesis-seal  %.y
        =((hash:page-msg:t msg.pag) msg-hash.u.genesis-seal)
-     ?&  check-pow-hash
+     ~&  :*  check-digest+check-digest
+             check-pow-hash+check-pow-hash
+             check-pow-valid+check-pow-valid
+             check-timestamp+check-timestamp
+             check-txs+check-txs
+             check-epoch+check-epoch
+             check-target+check-target
+             check-work+check-work
+             check-coinbase+check-coinbase
+             check-height+check-height
+             check-msg+check-msg
+             check-btc-hash+check-btc-hash
+         ==
+     ?&  check-digest
+         check-pow-hash
          check-pow-valid
+         check-timestamp
          check-txs
          check-epoch
          check-target
@@ -423,20 +449,27 @@
       ==
     ::
     ++  heard-tx
-      |=  [wir=wire now=@da raw=raw-tx:t]
+      |=  [wir=wire now=@da raw=raw-tx:t eny=@]
       ^-  [(list effect:dk) kernel-state:dk]
+      ~>  %slog.[3 leaf+"heard-tx"]
       =/  id-b58  (to-b58:hash:t id.raw)
+      ~>  %slog.[3 leaf+(trip (cat 3 'raw-tx: ' id-b58))]
       ::
       ::  check tx-id. this is the fastest check to do, so we try it first before
       ::  calling validate:raw-tx (which also checks the id)
       ?.  =((compute-id:raw-tx:t raw) id.raw)
+        ~>  %slog.[3 leaf+"tx-id-invalid"]
         :_  k
         [(liar-effect wir %tx-id-invalid)]~
       ::
       ::  do we already have raw-tx?
       ?:  (~(has z-by raw-txs.p.k) id.raw)
         :: do nothing (idempotency), we already have it
+        ~>  %slog.[3 leaf+"tx-id-already-seen"]
         `k
+      ?:  (based:raw-tx:t raw)
+        :_  k
+        [(liar-effect wir %raw-tx-not-based)]~
       ::
       ::  check if raw-tx is part of a pending block
       ::
@@ -456,27 +489,32 @@
             |=  [id=block-id:t pend=_p.k]
             (remove-pending-block:pen id)
           ::
+          ~>  %slog.[3 leaf+"page-pending-raw-tx-invalid"]
           :_  k
           [(liar-effect wir %page-pending-raw-tx-invalid) ~]
         ::  add to raw-txs map, remove from tx-block jug, remove from
         ::  block-tx jug
         =.  p.k  (add-tx-in-pending-block:pen raw)
-        (process-ready-blocks now raw)
+        ~>  %slog.[3 leaf+"process-ready-blocks"]
+        (process-ready-blocks now eny raw)
       ::  no pending blocks waiting on tx
       ::
       ::  check if any inputs are absent in heaviest balance
       ?.  (inputs-in-heaviest-balance:con raw)
         ::  input(s) in tx not in balance, discard tx
+        ~>  %slog.[3 leaf+"inputs-in-heaviest-balance"]
         `k
       ::  all inputs in balance
       ::
       ::  check if any inputs are in spent-by
       ?:  (inputs-in-spent-by:pen raw)
         ::  inputs present in spent-by, discard tx
+        ~>  %slog.[3 leaf+"inputs-in-spent-by"]
         `k
       ::  inputs not present in spent-by
       ?.  (validate:raw-tx:t raw)
         ::  raw-tx doesn't validate.
+        ~>  %slog.[3 leaf+"raw-tx-invalid"]
         :_  k
         [(liar-effect wir %tx-inputs-not-in-spent-by-and-invalid)]~
       ::
@@ -487,12 +525,13 @@
       ::  determined that no pending blocks were waiting on this this,
       ::  so we just tell the miner.
       =.  m.k  (heard-new-tx:min raw)
-      :-  ~[[%seen %tx id.raw]]
+      ~>  %slog.[3 leaf+"heard-new-tx"]
+      :-  ~[[%seen %tx id.raw] [%gossip %0 %heard-tx raw]]
       k
     ::
     ::  +process-ready-blocks: process blocks no longer waitings on txs
     ++  process-ready-blocks
-      |=  [now=@da raw=raw-tx:t]
+      |=  [now=@da eny=@ raw=raw-tx:t]
       ^-  [(list effect:dk) kernel-state:dk]
       ::  .work contains block-ids for blocks that no longer have any
       ::  missing transactions
@@ -503,11 +542,13 @@
         ::  process the block, skipping the steps that we know its already
         ::  done by the fact that it was in pending-blocks.p.k
         =^  new-effs  k
-          %^  process-block-with-txs  now
+          %:  process-block-with-txs
+            now  eny
             (to-page:local-page:t (~(got z-by pending-blocks.p.k) bid))
-          :: if the block is bad, then tell the driver never to send it
-          :: to us again
-          ~[[%seen %block bid]]
+            :: if the block is bad, then tell the driver never to send it
+            :: to us again
+            ~[[%seen %block bid]]
+          ==
         ::  remove the block from pending blocks. at this point, its either
         ::  been discarded by the kernel or lives in the consensus state
         =.  p.k  (remove-pending-block:pen bid)
@@ -537,7 +578,7 @@
     ::    only if the block is bad. If the block is good then ++new-block
     ::    emits effects and bad-block-effs is ignored.
     ++  process-block-with-txs
-      |=  [now=@da pag=page:t bad-block-effs=(list effect:dk)]
+      |=  [now=@da eny=@ pag=page:t bad-block-effs=(list effect:dk)]
       ^-  [(list effect:dk) kernel-state:dk]
       =/  digest-b58  (to-b58:hash:t digest.pag)
       ::
@@ -547,7 +588,7 @@
         (validate-page-with-txs:con p.k pag)
       ?-    -.new-transfers
           %.y
-        (new-block now pag +.new-transfers)
+        (new-block now eny pag +.new-transfers)
         ::
           %.n
         ::  did not validate, so we throw the block out and stop
@@ -557,7 +598,7 @@
     ::
     ::  +new-block: update kernel state with new valid block.
     ++  new-block
-      |=  [now=@da pag=page:t acc=tx-acc:t]
+      |=  [now=@da eny=@ pag=page:t acc=tx-acc:t]
       ^-  [(list effect:dk) kernel-state:dk]
       ::
       ::  page is validated, update consensus and derived state
@@ -598,6 +639,10 @@
       ::
       ::  update derived state
       =.  d.k  (update:der c.k pag)
+      ?.  =(old-heavy heaviest-block.c.k)
+        =^  mining-effs  k  (do-mine (hash-noun-varlen:tip5:zeke [%nonce eny]))
+        =.  effs  (weld mining-effs effs)
+        effs^k
       ::
       effs^k
     ::
@@ -638,6 +683,7 @@
     ++  handle-command
       |=  [now=@da =command:dk]
       ^-  [(list effect:dk) kernel-state:dk]
+      ~>  %slog.[0 (cat 3 'command: ' -.command)]
       ::  ~&  "handling command: {<-.command>}"
       ?:  &(?=(init-command:dk -.command) !init.a.k)
         ::  kernel no longer in init phase, can't do init command
@@ -652,8 +698,8 @@
           %born
         do-born
       ::
-          %mine
-        do-mine
+          %pow
+        do-pow
       ::
           %set-mining-key
         do-set-mining-key
@@ -707,31 +753,32 @@
         :_  k
         [%request %block %by-height height]~
       ::
-      ++  do-mine
+      ++  do-pow
         ^-  [(list effect:dk) kernel-state:dk]
-        ?>  ?=([%mine *] command)
-        ?:  =(*(z-set lock:t) pubkeys.m.k)
-          ::~&  "cannot mine without first setting pubkey with %set-mining-key"
-          `k
-        ?.  mining.m.k
-          `k
+        ?>  ?=([%pow *] command)
         =/  commit=block-commitment:t
           (block-commitment:page:t candidate-block.m.k)
-        =/  [prf=proof:sp dig=tip5-hash-atom:zeke]
-          (prove-block:mine commit p.command)
-        ?:  %+  check-target:mine  dig
+        ?.  =(bc.command commit)
+          ~&  "mined for wrong (old) block commitment"  `k
+        ?.  =(nonce.command next-nonce.m.k)
+          ~&  "mined wrong (old) nonce"  `k
+        ?:  %+  check-target:mine  dig.command
             (~(got z-by targets.c.k) parent.candidate-block.m.k)
-          =.  m.k  (set-pow:min prf)
+          =.  m.k  (set-pow:min prf.command)
           =.  m.k  set-digest:min
-          ::
           (heard-block /poke/miner now candidate-block.m.k eny)
-        `k
+        :: mine the next nonce
+        (do-mine (atom-to-digest:tip5:zeke dig.command))
       ::
       ++  do-set-mining-key
         ^-  [(list effect:dk) kernel-state:dk]
         ?>  ?=([%set-mining-key *] command)
-        =/  pk=schnorr-pubkey:t  (from-b58:schnorr-pubkey:t p.command)
-        =/  =lock:t  (new:lock:t pk)
+        =/  pk=(unit schnorr-pubkey:t)
+          (mole |.((from-b58:schnorr-pubkey:t p.command)))
+        ?~  pk
+          ~>  %slog.[0 leaf+"invalid mining pubkey, exiting"]
+          [[%exit 1]~ k]
+        =/  =lock:t  (new:lock:t u.pk)
         =.  m.k  (set-pubkeys:min [lock]~)
         =.  m.k  (set-shares:min [lock 100]~)
         ::  ~&  >  "pubkeys.m set to {<pubkeys.m.k>}"
@@ -742,20 +789,26 @@
         ^-  [(list effect:dk) kernel-state:dk]
         ?>  ?=([%set-mining-key-advanced *] command)
         ?:  (gth (lent p.command) 2)
-        ~>  %slog.[0 [%leaf "coinbase split for more than two locks not yet supported, doing nothing"]]
-          `k
+        ~>  %slog.[0 [%leaf "coinbase split for more than two locks not yet supported, exiting"]]
+          [[%exit 1]~ k]
         ?~  p.command
-        ~>  %slog.[0 [%leaf "empty list of locks, doing nothing."]]
-          `k
+        ~>  %slog.[0 [%leaf "empty list of locks, exiting."]]
+          [[%exit 1]~ k]
         ::
-        =/  keys=(list lock:t)
-          %+  turn  p.command
-          |=  [s=@ m=@ ks=(list @t)]
-          (from-b58:lock:t m ks)
-        =/  shares=(list [lock:t @])
-          %+  turn  p.command
-          |=  [s=@ m=@ ks=(list @t)]
-          [(from-b58:lock:t m ks) s]
+        =/  [keys=(list lock:t) shares=(list [lock:t @]) crash=?]
+          %+  roll  `(list [@ @ (list @t)])`p.command
+          |=  $:  [s=@ m=@ ks=(list @t)]
+                  locks=(list lock:t)
+                  shares=(list [lock:t @])
+                  crash=_`?`%|
+              ==
+          =+  r=(mole |.((from-b58:lock:t m ks)))
+          ?~  r
+            [~ ~ %&]
+          [[u.r locks] [[u.r s] shares] crash]
+        ?:  crash
+          ~>  %slog.[0 leaf+"invalid public keys provided, exiting"]
+          [[%exit 1]~ k]
         =.  m.k  (set-pubkeys:min keys)
         =.  m.k  (set-shares:min shares)
         ::  ~&  >  "pubkeys.m set to {<pubkeys.m.k>}"
@@ -823,20 +876,20 @@
         =/  genesis-page=page:t
           (new-genesis:page:t genesis-template now)
         =.  candidate-block.m.k  genesis-page
-        =.  c.k  (add-btc-data:con btc-hash.p.command)
+        =.  c.k  (add-btc-data:con `btc-hash.p.command)
         `k
       ::
       ++  do-btc-data
         ^-  [(list effect:dk) kernel-state:dk]
         ?>  ?=([%btc-data *] command)
         =.  c.k  (add-btc-data:con p.command)
-        ~>  %slog.[0 leaf+"received btc block hash, waiting to hear nockchain genesis block!"]
         `k
       --::+handle-command
     ::
     ++  handle-fact
       |=  [wir=wire eny=@ our=@ux now=@da =fact:dk]
       ^-  [(list effect:dk) kernel-state:dk]
+      ~>  %slog.[0 (cat 3 'fact: ' +<.fact)]
       ?:  init.a.k
         ::  kernel in init phase, fact ignored
         `k
@@ -845,11 +898,27 @@
         (heard-block wir now p.data.fact eny)
       ::
           %heard-tx
-        (heard-tx wir now p.data.fact)
+        (heard-tx wir now p.data.fact eny)
       ::
           %heard-elders
         (heard-elders wir now p.data.fact)
       ==
-  --::  +poke
---::  +kernel
+      ::
+      ++  do-mine
+        |=  nonce=noun-digest:tip5:zeke
+        ^-  [(list effect:dk) kernel-state:dk]
+        ?.  mining.m.k
+          `k
+        ?:  =(*(z-set lock:t) pubkeys.m.k)
+          ::~&  "cannot mine without first setting pubkey with %set-mining-key"
+          `k
+        =/  commit=block-commitment:t
+          (block-commitment:page:t candidate-block.m.k)
+        =.  next-nonce.m.k  nonce
+        ~&  mining-on+nonce
+        :_  k
+        [%mine pow-len:zeke commit nonce]~
+    --::  +poke
+  --::  +kernel
 --
+:: churny churn 1

+ 26 - 13
hoon/apps/dumbnet/lib/consensus.hoon

@@ -18,8 +18,12 @@
   c(genesis-seal seal)
 ::
 ++  add-btc-data
-  |=  =btc-hash:t
+  |=  btc-hash=(unit btc-hash:t)
   ^-  consensus-state:dk
+  ?:  =(~ btc-hash)
+    ~>  %slog.[0 leaf+"Not checking btc hash for genesis block"]
+    c(btc-data `btc-hash)
+  ~>  %slog.[0 leaf+"received btc block hash, waiting to hear nockchain genesis block!"]
   c(btc-data `btc-hash)
 ::
 +|  %checks-and-computes
@@ -53,6 +57,7 @@
   ^-  (z-set nname:t)
   ~(key z-by get-cur-balance)
 ::
+::
 ::  +compute-target: find the new target
 ::
 ::    this is supposed to be mathematically identical to
@@ -93,9 +98,10 @@
     ?:  (gth next-target-atom max-target-atom:t)
       max-target:t
     (chunk:bignum:t next-target-atom)
-  ::~&  >>  "previous target: {(scow %ud prev-target-atom)}"
-  ::~&  >>  "epoch duration: {(scow %ud epoch-dur)}"
-  ::~&  >>  "new target: {(scow %ud next-target-atom)}"
+  ?:  =(prev-target-atom next-target-atom)
+    next-target-bn
+  ~>  %slog.[0 (cat 3 'previous target: ' (scot %ud prev-target-atom))]
+  ~>  %slog.[0 (cat 3 'new target: ' (scot %ud next-target-atom))]
   next-target-bn
 ::
 ::  +compute-epoch-duration: computes the duration of an epoch in seconds
@@ -229,7 +235,7 @@
       %.y
     %-  check-target:mine
     :_  target.pag
-    (digest-to-atom:tip5:t (hash-proof:t (need pow.pag)))
+    (proof-to-pow:t (need pow.pag))
   ?.  check-pow-hash
     [%.n %pow-target-check-failed]
   ::
@@ -259,14 +265,18 @@
   ?.  check-heaviness
     [%.n %page-heaviness-invalid]
   ::
-  =/  check-split-length=?
-    (lte (lent ~(key z-by coinbase.pag)) max-coinbase-split:t)
-  ?.  check-split-length
-    [%.n %split-too-large]
+  =/  check-coinbase-split=?
+    (based:coinbase-split:t coinbase.pag)
+  ?.  check-coinbase-split
+    [%.n %coinbase-split-not-based]
   =/  check-msg-length=?
     (lth (lent msg.pag) 20)
   ?.  check-msg-length
     [%.n %msg-too-large]
+  =/  check-msg-valid=?
+    (validate:page-msg:t msg.pag)
+  ?.  check-msg-valid
+    [%.n %msg-not-valid]
   ::
   [%.y ~]
 ::
@@ -284,14 +294,16 @@
   ?.  (check-size p pag)
     ::~&  >>>  "block {digest-b58} is too large"
     [%.n %block-too-large]
-  =/  raw-tx-set=(set raw-tx:t)
-    (~(run z-in tx-ids.pag) |=(=tx-id:t (~(got z-by raw-txs.p) tx-id)))
-  =/  raw-tx-list=(list raw-tx:t)  ~(tap z-in raw-tx-set)
+  =/  raw-tx-set=(set (unit raw-tx:t))
+    (~(run z-in tx-ids.pag) |=(=tx-id:t (~(get z-by raw-txs.p) tx-id)))
+  =/  raw-tx-list=(list (unit raw-tx:t))  ~(tap z-in raw-tx-set)
   =|  tx-list=(list tx:t)
   =.  tx-list
     |-
     ?~  raw-tx-list  tx-list
-    =/  utx=(unit tx:t)  (mole |.((new:tx:t i.raw-tx-list height.pag)))
+    ?~  i.raw-tx-list
+      ~  :: exit early b/c raw-tx was not present in raw-tx-set
+    =/  utx=(unit tx:t)  (mole |.((new:tx:t u.i.raw-tx-list height.pag)))
     ?~  utx  :: exit early b/c raw-tx failed to convert
       ~
     %=  $
@@ -304,6 +316,7 @@
   :: initialize balance transfer accumulator with parent block's balance
   =/  acc=tx-acc:t
     (new:tx-acc:t (~(get z-by balance.c) parent.pag))
+  ::
   ::  test to see that the input notes for all transactions
   ::  exist in the parent block's balance, that they are not
   ::  over- or underspent, and that the resulting

+ 8 - 7
hoon/apps/dumbnet/lib/miner.hoon

@@ -22,12 +22,13 @@
     (~(gas z-in *(z-set lock:t)) pks)
   m
 ::
-::  +set-shares set .shares
+::  +set-shares validate and set .shares
 ++  set-shares
   |=  shr=(list [lock:t @])
-  =.  shares.m
-    (~(gas z-by *(z-map lock:t @)) shr)
-  m
+  =/  s=shares:t  (~(gas z-by *(z-map lock:t @)) shr)
+  ?.  (validate:shares:t s)
+    ~|('invalid shares' !!)
+  m(shares s)
 ::
 +|  %candidate-block
 ++  set-pow
@@ -66,7 +67,8 @@
 ++  heard-new-tx
   |=  raw=raw-tx:t
   ^-  mining-state:dk
-  ?.  mining.m  m  :: if we're not mining, do nothing
+  ~>  %slog.[3 'miner: heard-new-tx']
+  ~>  %slog.[3 (cat 3 'miner: heard-new-tx: raw-tx: ' (to-b58:hash:t id.raw))]
   ::
   ::  if the mining pubkey is not set, do nothing
   ?:  =(*(z-set lock:t) pubkeys.m)  m
@@ -120,7 +122,6 @@
 ++  heard-new-block
   |=  [c=consensus-state:dk p=pending-state:dk now=@da]
   ^-  mining-state:dk
-  ?.  mining.m  m  ::  if we're not mining, do nothing
   ::
   ::  do a sanity check that we have a heaviest block, and that the heaviest block
   ::  is not the parent of our current candidate block
@@ -143,7 +144,7 @@
     (to-b58:hash:t u.heaviest-block.c)
   ~>  %slog.[0 [%leaf print-var]]
   =.  candidate-block.m
-    %-  new:page:t
+    %-  new-candidate:page:t
     :*  (to-page:local-page:t (~(got z-by blocks.c) u.heaviest-block.c))
         now
         (~(got z-by targets.c) u.heaviest-block.c)

+ 10 - 4
hoon/apps/dumbnet/lib/types.hoon

@@ -1,6 +1,9 @@
 /=  *   /common/zoon
+/=  zeke  /common/zeke
 /=  w   /common/wrapper
 /=  dt  /common/tx-engine
+/=  sp  /common/stark/prover
+/=  miner-kernel  /apps/dumbnet/miner
 |%
 +|  %state
 +$  kernel-state
@@ -34,7 +37,7 @@
     ::
     ::  Bitcoin block hash for genesis block
     ::>)  TODO: change face to btc-hash?
-      btc-data=(unit btc-hash:dt)
+      btc-data=(unit (unit btc-hash:dt))
       =genesis-seal:dt  ::  desired seal for genesis block
   ==
 ::
@@ -73,6 +76,7 @@
       shares=(z-map lock:dt @)         ::  shares of coinbase+fees among locks
       candidate-block=page:dt            ::  the next block we will attempt to mine.
       candidate-acc=tx-acc:dt           ::  accumulator for txs in candidate block
+      next-nonce=noun-digest:tip5:zeke  :: nonce being mined
   ==
 ::
 +$  init-phase  $~(%.y ?)
@@ -87,7 +91,7 @@
 ::
 +$  command
   $+  command
-  $%  [%mine p=[@ @ @ @ @]]  ::  mine at nonce .p
+  $%  [%pow prf=proof:sp dig=tip5-hash-atom:zeke bc=digest:tip5:zeke nonce=noun-digest:tip5:zeke] :: check if a proof of work is good for the next block, issue a block if so
       [%set-mining-key p=@t]  ::  set $lock for coinbase in mined blocks
       [%set-mining-key-advanced p=(list [share=@ m=@ keys=(list @t)])]  :: multisig and/or split coinbases
       [%enable-mining p=?]  ::  switch for generating candidate blocks for mining
@@ -96,7 +100,7 @@
       [%genesis p=[=btc-hash:dt block-height=@ message=cord]]  ::  emit genesis block with this template
       :: set expected btc height and msg hash of genesis block
       [%set-genesis-seal p=[height=page-number:dt msg-hash=@t]]
-      [%btc-data p=btc-hash:dt]  ::  data from BTC RPC node
+      [%btc-data p=(unit btc-hash:dt)]  ::  data from BTC RPC node
       test-command
   ==
 ::
@@ -115,7 +119,7 @@
       %born
   ==
 ::  commands that can only be performed if init-phase is %.n
-+$  non-init-command  ?(%timer)
++$  non-init-command  ?(%timer %pow)
 ::
 +$  fact
   $+  fact
@@ -133,8 +137,10 @@
       [%request p=request]  :: request specific tx or block
       [%track p=track]  :: runtime tracking of blocks for %liar-block-id effect
       [%seen p=seen]    ::  seen so don't reprocess
+      [%mine length=@ block-commitment=noun-digest:tip5:zeke nonce=noun-digest:tip5:zeke]
       lie
       span-effect
+      [%exit code=@]
   ==
 ::
 +$  seen

+ 40 - 0
hoon/apps/dumbnet/miner.hoon

@@ -0,0 +1,40 @@
+/=  mine  /common/pow
+/=  sp  /common/stark/prover
+/=  *  /common/zoon
+/=  *  /common/zeke
+/=  *  /common/wrapper
+=<  ((moat |) inner)  :: wrapped kernel
+=>
+  |%
+  +$  effect  [%command %pow prf=proof:sp dig=tip5-hash-atom block-commitment=noun-digest:tip5 nonce=noun-digest:tip5]
+  +$  kernel-state  [%state version=%1]
+  +$  cause  [length=@ block-commitment=noun-digest:tip5 nonce=noun-digest:tip5]
+  --
+|%
+++  moat  (keep kernel-state) :: no state
+++  inner
+  |_  k=kernel-state
+  ::  do-nothing load
+  ++  load
+    |=  =kernel-state  kernel-state
+  ::  crash-only peek
+  ++  peek
+    |=  arg=*
+    =/  pax  ((soft path) arg)
+    ?~  pax  ~|(not-a-path+arg !!)
+    ~|(invalid-peek+pax !!)
+  ::  poke: try to prove a block
+  ++  poke
+    |=  [wir=wire eny=@ our=@ux now=@da dat=*]
+    ^-  [(list effect) k=kernel-state]
+    =/  cause  ((soft cause) dat)
+    ?~  cause
+      ~>  %slog.[0 [%leaf "error: bad cause"]]
+      `k
+    =/  cause  u.cause
+    :: XX TODO set up stark config, construct effect
+    =/  [prf=proof:sp dig=tip5-hash-atom]  (prove-block-inner:mine cause)
+    :_  k
+    [%command %pow prf dig block-commitment.cause nonce.cause]~
+  --
+--

+ 1 - 0
hoon/apps/dumbnet/outer.hoon

@@ -1,2 +1,3 @@
 /=  dumb  /apps/dumbnet/inner
 ((moat:dumb |) inner:dumb)
+:: churning the file on purpose 20

+ 5 - 1
hoon/common/nock-prover.hoon

@@ -1,13 +1,17 @@
 /=  *  /common/zeke
 /=  stark-prover  /common/stark/prover
 /=  common  /common/nock-common
-/#  sc=stark-config
+/#  softed-constraints
 ::
 |%
 ::
 ++  prover
   =|  in=stark-input
   ::  +<+< = stark-engine door sample wrt stark-verifier core
+  =/  sc=stark-config
+    %*  .  *stark-config
+      prep  softed-constraints
+    ==
   %_    stark-prover
       +<+<
     %_  in

+ 5 - 1
hoon/common/nock-verifier.hoon

@@ -1,13 +1,17 @@
 /=  *  /common/zeke
 /=  stark-verifier  /common/stark/verifier
 /=  common  /common/nock-common
-/#  sc=stark-config
+/#  softed-constraints
 ::
 |%
 ::
 ++  verifier
   =|  in=stark-input
   ::  +<+< = stark-engine door sample wrt stark-verifier core
+  =/  sc=stark-config
+    %*  .  *stark-config
+      prep  softed-constraints
+    ==
   %_    stark-verifier
       +<+<
     %_  in

+ 1 - 1
hoon/common/pow.hoon

@@ -19,6 +19,6 @@
     (prove:np block-commitment nonce length ~)
   ?>  ?=(%& -.prove-result)
   =/  =proof:sp  p.prove-result
-  =/  proof-hash=tip5-hash-atom  (digest-to-atom:tip5 (hash-proof proof))
+  =/  proof-hash=tip5-hash-atom  (proof-to-pow proof)
   [proof proof-hash]
 --

+ 117 - 36
hoon/common/stark/prover.hoon

@@ -38,6 +38,8 @@
 ::
 :: generate-proof is the main body of the prover.
 ++  generate-proof
+  :: Disabled jet hint for now, under development.
+  :: ~/  %generate-proof
   |=  $:  header=noun-digest:tip5
           nonce=noun-digest:tip5
           pow-len=@
@@ -75,9 +77,6 @@
   =.  proof  (~(push proof-stream proof) [%heights heights])
   =/  pre=preprocess-0  prep.stark-config
   ::
-  ::  remove preprocess data for unused tables
-  =.  pre
-    (remove-unused-constraints:nock-common pre table-names override)
   =/  clc  ~(. calc heights cd.pre)
   =*  num-colinearity-tests=@  num-colinearity-tests:fri:clc
   =*  fri-domain-len=@  init-domain-len:fri:clc
@@ -120,10 +119,10 @@
   ::  check that the tables have correct num of ext cols. Comment this out for production.
   ::
   ::?:  %+  levy  (zip-up tables table-exts)
-  ::    |=  [table=table-dat ext=table-mary]
-  ::    !=(step.p.ext ext-width.p.table)
-  ::  ::~&  %widths-mismatch
-  ::  ~|("prove: mismatch between table ext widths and actual ext widths" !!)
+      ::|=  [table=table-dat ext=table-mary]
+      ::!=(step.p.ext ext-width.p.table)
+    ::~&  %widths-mismatch
+    ::~|("prove: mismatch between table ext widths and actual ext widths" !!)
   ::~&  %ext-cols
   ::
   ::  convert the ext columns to marys
@@ -149,11 +148,8 @@
     (make-challenge-map:chal challenges s f)
   ::
   ::  build mega-extension columns
-  =/  table-mega-exts
-    %+  turn  tables
-    |=  t=table-dat
-    ^-  table-mary
-    (mega-extend:q.t p.t (weld chals-rd1 chals-rd2) return)
+  =/  table-mega-exts=(list table-mary) 
+    (build-mega-extend tables challenges return)
   ::~&  %tables-built
   =.  tables
     %+  turn  (zip-up tables table-mega-exts)
@@ -163,12 +159,12 @@
     (weld-exts:tlib p.t mega-ext)
   ::
   ::  check that the tables have correct num of ext cols. Comment this out for production.
-  ::  ::~&  >>  %check-mega-ext-cols
+  ::~&  >>  %check-mega-ext-cols
   ::?:  %+  levy  (zip-up tables table-mega-exts)
-  ::    |=  [table=table-dat mext=table-mary]
-  ::    !=(step.p.mext mega-ext-width.p.table)
-  ::  ::~&  %widths-mismatch
-  ::  ~|("prove: mismatch between table ext widths and actual ext widths" !!)
+      ::|=  [table=table-dat mext=table-mary]
+      ::!=(step.p.mext mega-ext-width.p.table)
+    ::~&  %widths-mismatch
+    ::~|("prove: mismatch between table ext widths and actual ext widths" !!)
   ::
   ::  convert the mega-ext columns to marys
   ::
@@ -180,7 +176,6 @@
     [p.t (add width mega-ext-width.t)]
   =/  mega-ext=codeword-commitments
     (compute-codeword-commitments mega-ext-marys fri-domain-len width)
-  =.  proof  (~(push proof-stream proof) [%m-root h.q.merk-heap.mega-ext])
   ::
   ::  get terminal values for use in permutation/evaluation arguments
   =/  dyn-map=(map @ bpoly)
@@ -194,9 +189,8 @@
     %+  roll  (range (lent tables))
     |=  [i=@ acc=bpoly]
     (~(weld bop acc) (~(got by dyn-map) i))
-  ::
+  ::  send terminals to verifier
   =.  proof  (~(push proof-stream proof) terms+terminals)
-  ::
   ::  reseed the rng
   =.  rng  ~(prover-fiat-shamir proof-stream proof)
   ::
@@ -213,7 +207,7 @@
   ::    [challenges (~(got by dyn-map) i) r.t]
   ::~&  %passed-tests
   ::
-  =/  num-constraints=@
+  =/  num-extra-constraints=@
     %+  roll  (range num-tables)
     |=  [i=@ acc=@]
     =/  cs  (~(got by count-map.pre) i)
@@ -223,13 +217,9 @@
       row.cs
       transition.cs
       terminal.cs
+      extra.cs
     ==
   ::
-  ::  compute weights used in linear combination of composition polynomial
-  =^  comp-weights=bpoly  rng
-    =^  belt-list  rng  (belts:rng (mul 2 num-constraints))
-    [(init-bpoly belt-list) rng]
-  ::
   =/  total-cols=@
     %+  roll  tables
     |=  [[p=table-mary *] sum=@]
@@ -258,6 +248,7 @@
     |=  [bm=mary em=mary mem=mary]
     ^-  mary
     (~(weld ave bm) (~(weld ave em) mem))
+  ::
   =/  second-row-trace-polys=(list mary)
     %+  turn  transposed-tables
     |=  polys=mary
@@ -294,10 +285,93 @@
     |=  [i=@ polys=mary]
     (precompute-ntts polys max-height ntt-len)
   ::
+  ::
+  ::  compute extra composition poly
+  =/  omicrons-belt
+    %+  turn  tables
+    |=  [t=table-mary *]
+    ~(omicron quot t)
+  =/  omicrons-bpoly=bpoly  (init-bpoly omicrons-belt)
+  =/  omicrons-fpoly=fpoly
+    (init-fpoly (turn omicrons-belt lift))
+  =^  extra-comp-weights=bpoly  rng
+    =^  belt-list  rng  (belts:rng (mul 2 num-extra-constraints))
+    [(init-bpoly belt-list) rng]
+  =/  extra-composition-chals=(map @ bpoly)
+    %-  ~(gas by *(map @ bpoly))
+    =-  -<
+    %+  roll  (range num-tables)
+    |=  [i=@ acc=(list [@ bpoly]) num=@]
+    =/  cs  (~(got by count-map.pre) i)
+    =/  num-extra-constraints=@
+      ;:  add
+          boundary.cs
+          row.cs
+          transition.cs
+          terminal.cs
+          extra.cs
+      ==
+    :_  (add num (mul 2 num-extra-constraints))
+    [[i (~(swag bop extra-comp-weights) num (mul 2 num-extra-constraints))] acc]
+  ::~&  %computing-extra-composition-poly
+  =/  extra-composition-poly=bpoly
+    %-  compute-composition-poly
+    :*  omicrons-bpoly
+        heights
+        tworow-trace-polys-eval
+        constraint-map.pre
+        count-map.pre
+        extra-composition-chals
+        chal-map
+        dyn-map
+        %.y
+    ==
+  =.  proof
+    (~(push proof-stream proof) [%poly extra-composition-poly])
+  =.  rng  ~(prover-fiat-shamir proof-stream proof)
+  =^  extra-comp-eval-point  rng  $:felt:rng
+  ::
+  ::  compute extra trace evals
+  ::~&  %evaluating-trace-at-new-comp-eval-point
+  =/  extra-trace-evaluations=fpoly
+    %-  init-fpoly
+    %-  zing
+    %+  turn  tworow-trace-polys
+    |=  polys=mary
+    %+  turn  (range len.array.polys)
+    |=  i=@
+    =/  b=bpoly  (~(snag-as-bpoly ave polys) i)
+    (bpeval-lift b extra-comp-eval-point)
+  ::
+  =.  proof
+    (~(push proof-stream proof) [%evals extra-trace-evaluations])
+  ::
+  ::  send mega extension columns to verifier
+  =.  proof  (~(push proof-stream proof) [%m-root h.q.merk-heap.mega-ext])
+  ::  reseed the rng
+  =/  rng  ~(prover-fiat-shamir proof-stream proof)
+  ::
   ::  compute the Composition Polynomial
   ::  This polynomial composes the trace polynomials with the constraints, takes quotients
   ::  over the rows where the constraint should be zero, adjusts the degree so they all
   ::  have the same maximal degree, and combines them into one big random linear combination.
+  ::
+  ::  compute weights used in linear combination of composition polynomial
+  =/  num-constraints=@
+    %+  roll  (range num-tables)
+    |=  [i=@ acc=@]
+    =/  cs  (~(got by count-map.pre) i)
+    ;:  add
+      acc
+      boundary.cs
+      row.cs
+      transition.cs
+      terminal.cs
+    ==
+  =^  comp-weights=bpoly  rng
+    =^  belt-list  rng  (belts:rng (mul 2 num-constraints))
+    [(init-bpoly belt-list) rng]
+  ::
   =/  composition-chals=(map @ bpoly)
     %-  ~(gas by *(map @ bpoly))
     =-  -<
@@ -314,13 +388,6 @@
     :_  (add num (mul 2 num-constraints))
     [[i (~(swag bop comp-weights) num (mul 2 num-constraints))] acc]
   ::
-  =/  omicrons-belt
-    %+  turn  tables
-    |=  [t=table-mary *]
-    ~(omicron quot t)
-  =/  omicrons-bpoly=bpoly  (init-bpoly omicrons-belt)
-  =/  omicrons-fpoly=fpoly
-    (init-fpoly (turn omicrons-belt lift))
   ::~&  %computing-composition-poly
   =/  composition-poly=bpoly
     %-  compute-composition-poly
@@ -332,6 +399,7 @@
         composition-chals
         chal-map
         dyn-map
+        %.n
     ==
   ::
   ::  decompose composition polynomial into one polynomial for each degree of the
@@ -357,6 +425,9 @@
   =.  proof
     (~(push proof-stream proof) [%comp-m h.q.composition-merk num-composition-pieces])
   ::
+  ::
+  ::
+  ::
   ::  reseed the rng
   =.  rng  ~(prover-fiat-shamir proof-stream proof)
   ::
@@ -406,18 +477,20 @@
   =^  deep-weights=fpoly  rng
     =^  felt-list  rng
       %-  felts:rng
-      (add (mul 2 total-cols) max-constraint-degree)
+      (add (mul 4 total-cols) max-constraint-degree)
     [(init-fpoly felt-list) rng]
+  =/  all-evals  (~(weld fop trace-evaluations) extra-trace-evaluations)
   ::~&  %computing-deep-poly
   =/  deep-poly=fpoly
     %-  compute-deep
     :*  trace-polys
-        trace-evaluations
+        all-evals
         composition-pieces-fpoly
         composition-piece-evaluations
         deep-weights
         omicrons-fpoly
         deep-challenge
+        extra-comp-eval-point
     ==
   ::
   ::  create DEEP codeword and push to proof
@@ -473,7 +546,7 @@
     m-pathbf+[(tail elem) path.opening]
   ::
   ::~&  %finished-proof
-  [%& objects.proof ~ 0]
+  [%& %0 objects.proof ~ 0]
 ::
 ::
 ++  build-table-dats
@@ -504,4 +577,12 @@
       :-  name:static:common:memory-table
       funcs:memory-table
   ==
+++  build-mega-extend
+  ~/  %build-mega-extend
+  |=  [tables=(list table-dat) chals=(list belt) return=fock-return]
+  ^-  (list table-mary)
+  %+  turn  tables
+  |=  t=table-dat
+  ^-  table-mary
+  (mega-extend:q.t p.t chals return)
 --

+ 202 - 74
hoon/common/stark/verifier.hoon

@@ -64,7 +64,7 @@
     ::
     ::  number of static items in proof-data
     ::
-      10
+      12
     ::
     ::  number of items written by FRI
     ::
@@ -101,12 +101,12 @@
   =/  chal-map=(map term belt)
       (bp-zip-chals-list:chal chal-names-basic:chal challenges)
   ::
-  =/  [alf=pelt a=pelt b=pelt c=pelt d=pelt z=pelt]
+  =/  [alf=pelt j=pelt k=pelt l=pelt m=pelt z=pelt]
     :*  (got-pelt chal-map %alf)
-        (got-pelt chal-map %a)
-        (got-pelt chal-map %b)
-        (got-pelt chal-map %c)
-        (got-pelt chal-map %d)
+        (got-pelt chal-map %j)
+        (got-pelt chal-map %k)
+        (got-pelt chal-map %l)
+        (got-pelt chal-map %m)
         (got-pelt chal-map %z)
     ==
   ::
@@ -117,16 +117,12 @@
   =/  prod-data
     (build-tree-data:fock p.puzzle alf)
   ::
-  ::  get merkle root of mega-extension tables
-  =^  mega-ext-root   proof
-    =^(m proof ~(pull proof-stream proof) ?>(?=(%m-root -.m) p.m^proof))
-  ::
-  =.  rng  ~(verifier-fiat-shamir proof-stream proof)
-  ::
   ::  get terminals
   =^  terminals  proof
     =^(t proof ~(pull proof-stream proof) ?>(?=(%terms -.t) p.t^proof))
   ::
+  =.  rng  ~(verifier-fiat-shamir proof-stream proof)
+  ::
   ::  verify that len.terminals is as expected
   ?.  .=  len.terminals
       %-  lent
@@ -142,7 +138,6 @@
     ~&  "len.terminals is not equal to the data buffer"
     !!
   ::
-  =.  rng  ~(verifier-fiat-shamir proof-stream proof)
   ::
   =/  [terminal-map=(map term belt) dyn-map=(map @ bpoly)]
     =-  [term-map dyn-map]
@@ -157,10 +152,89 @@
     %-  ~(put by dyn-map)
     [table-num (~(swag bop terminals) idx (lent terms))]
   ::
-  ?.  (linking-checks subj-data form-data prod-data a b c d z terminal-map)
+  ?.  (linking-checks subj-data form-data prod-data j k l m z terminal-map)
     ~&  "failed input linking checks"  !!
   ::
   ::
+  ::  evaluate the second composition poly
+  =/  total-extra-constraints=@
+    %+  roll  (range num-tables)
+    |=  [i=@ acc=@]
+    =/  cs  (~(got by count-map.pre) i)
+    ;:  add
+        acc
+        boundary.cs
+        row.cs
+        transition.cs
+        terminal.cs
+        extra.cs
+    ==
+  =^  extra-comp-weights=bpoly  rng
+    =^  belts  rng  (belts:rng (mul 2 total-extra-constraints))
+    [(init-bpoly belts) rng]
+  =/  extra-composition-chals=(map @ bpoly)
+    %-  ~(gas by *(map @ bpoly))
+    =-  -<
+    %+  roll  (range num-tables)
+    |=  [i=@ acc=(list [@ bpoly]) num=@]
+    =/  cs  (~(got by count-map.pre) i)
+    =/  num-constraints=@
+      ;:  add
+          boundary.cs
+          row.cs
+          transition.cs
+          terminal.cs
+          extra.cs
+      ==
+    :_  (add num (mul 2 num-constraints))
+    [[i (~(scag bop (~(slag bop extra-comp-weights) num)) (mul 2 num-constraints))] acc]
+  ::
+  =^  extra-comp-bpoly  proof
+    =^(c proof ~(pull proof-stream proof) ?>(?=(%poly -.c) p.c^proof))
+  ::
+  =.  rng  ~(verifier-fiat-shamir proof-stream proof)
+  ::
+  =^  extra-comp-eval-point  rng  $:felt:rng
+  ::
+  =^  extra-trace-evaluations=fpoly  proof
+    =^(t proof ~(pull proof-stream proof) ?>(?=(%evals -.t) p.t^proof))
+  ::
+  ::  check that the size of the evaluations is exactly twice the total number of
+  ::  columns across all tables
+  =/  total-cols=@
+    %+  roll  table-full-widths
+    |=  [w=@ acc=@]
+    (add w acc)
+  ?>  =(len.extra-trace-evaluations (mul 2 total-cols))
+  ?>  ~(chck fop extra-trace-evaluations)
+  ::
+  =/  extra-composition-eval=felt
+    %-  eval-composition-poly
+    :*  extra-trace-evaluations
+        heights
+        constraint-map.pre
+        count-map.pre
+        dyn-map
+        extra-composition-chals
+        challenges
+        max-degree:clc
+        extra-comp-eval-point
+        table-full-widths
+        s
+        f
+        %.y
+    ==
+  ::
+  =/  extra-comp-bpoly-eval  (bpeval-lift extra-comp-bpoly extra-comp-eval-point)
+  ::
+  ::  check that the extra composition eval equals the eval pt
+  ?>  =(extra-composition-eval extra-comp-bpoly-eval)
+  ::
+  ::  get merkle root of mega-extension tables
+  =^  mega-ext-root   proof
+    =^(m proof ~(pull proof-stream proof) ?>(?=(%m-root -.m) p.m^proof))
+  ::
+  =.  rng  ~(verifier-fiat-shamir proof-stream proof)
   ::
   ::  We now use the randomness to compute the expected fingerprints of the compute stack and product stack based on the given [s f] and product, respectively.
   ::  We then dynamically generate constraints that force the cs and ps to be equivalent to the expected fingerprints.
@@ -168,7 +242,7 @@
   ::  The boundary constraint then ensures that the computation in cleartext is linked to the computation in the trace.
   ::
   ::  generate scalars for the random linear combination of the composition polynomial
-  =/  num-constraints=@
+  =/  total-constraints=@
     %+  roll  (range num-tables)
     |=  [i=@ acc=@]
     =/  cs  (~(got by count-map.pre) i)
@@ -180,7 +254,7 @@
         terminal.cs
     ==
   =^  comp-weights=bpoly  rng
-    =^  belts  rng  (belts:rng (mul 2 num-constraints))
+    =^  belts  rng  (belts:rng (mul 2 total-constraints))
     [(init-bpoly belts) rng]
   =/  composition-chals=(map @ bpoly)
     %-  ~(gas by *(map @ bpoly))
@@ -203,6 +277,7 @@
   =^  comp-root  proof
     =^(c proof ~(pull proof-stream proof) ?>(?=(%comp-m -.c) [p.c num.c]^proof))
   ::
+  ::
   =.  rng  ~(verifier-fiat-shamir proof-stream proof)
   ::
   ::  generate the DEEP challenge
@@ -222,12 +297,9 @@
   =^  trace-evaluations=fpoly  proof
     =^(t proof ~(pull proof-stream proof) ?>(?=(%evals -.t) p.t^proof))
   ::
+  ::
   ::  check that the size of the evaluations is exactly twice the total number of
   ::  columns across all tables
-  =/  total-cols=@
-    %+  roll  table-full-widths
-    |=  [w=@ acc=@]
-    (add w acc)
   ?>  =(len.trace-evaluations (mul 2 total-cols))
   ?>  ~(chck fop trace-evaluations)
   ::
@@ -241,8 +313,8 @@
       ==
       ~&  >>  %num-composition-piece-evals-wrong
       !!
-  =.  rng  ~(verifier-fiat-shamir proof-stream proof)
   ::
+  =.  rng  ~(verifier-fiat-shamir proof-stream proof)
   :: verify the composition polynomial equals the composition pieces by evaluating each side
   :: at the DEEP challenge point
   =/  composition-eval=felt
@@ -259,6 +331,7 @@
         table-full-widths
         s
         f
+        %.n
     ==
   ::
   =/  decomposition-eval=felt
@@ -273,11 +346,12 @@
     ~&  %invalid-proof  !!
   ::~&  %composition-eval-passed
   ::
+  ::
   ::  generate random weights for DEEP composition polynomial
   =^  deep-weights=fpoly  rng
     =^  felt-list  rng
       %-  felts:rng
-      (add len.trace-evaluations len.composition-piece-evaluations)
+      :(add len.trace-evaluations len.extra-trace-evaluations len.composition-piece-evaluations)
     [(init-fpoly felt-list) rng]
   ::
   ::  read the merkle root of the DEEP composition polynomial
@@ -378,13 +452,14 @@
   ::
   :: evaluate DEEP polynomial at the indices
   =/  omega=felt  (lift omega:clc)
+  =/  all-evals  (~(weld fop trace-evaluations) extra-trace-evaluations)
   =/  eval-res=?
     %+  roll  elems
     |=  [[idx=@ trace-elems=(list belt) comp-elems=(list belt) deep-elem=felt] acc=?]
     ^-  ?
     =/  deep-eval
       %-  evaluate-deep
-      :*  trace-evaluations
+      :*  all-evals
           composition-piece-evaluations
           trace-elems
           comp-elems
@@ -395,6 +470,7 @@
           omega
           idx
           deep-challenge
+          extra-comp-eval-point
       ==
     ~|  "DEEP codeword doesn't match evaluation"
     ?>  =(deep-eval deep-elem)
@@ -424,12 +500,12 @@
 ++  linking-checks
   ~/  %linking-checks
   |=  $:  s=tree-data  f=tree-data  p=tree-data
-          a=pelt  b=pelt  c=pelt  d=pelt  z=pelt
+          j=pelt  k=pelt  l=pelt  m=pelt  z=pelt
           mp=(map term belt)
       ==
   ^-  ?
-  =/  ifp-f  (compress-pelt ~[a b c] ~[size dyck leaf]:f)
-  =/  ifp-s  (compress-pelt ~[a b c] ~[size dyck leaf]:s)
+  =/  ifp-f  (compress-pelt ~[j k l] ~[size dyck leaf]:f)
+  =/  ifp-s  (compress-pelt ~[j k l] ~[size dyck leaf]:s)
   ?&
       =;  bool
         ?:  bool  bool
@@ -443,10 +519,10 @@
         ?:  bool  bool
         ~&("memory table kvs input check failed" bool)
       .=  ?@  n.s
-            (pmul z (padd ifp-f (pscal 0 d)))
+            (pmul z (padd ifp-f (pscal 0 m)))
           %+  padd
-            (pmul z (padd ifp-s (pscal 1 d)))
-          :(pmul z z (padd ifp-f (pscal 0 d)))
+            (pmul z (padd ifp-s (pscal 1 m)))
+          :(pmul z z (padd ifp-f (pscal 0 m)))
       (got-pelt mp %memory-kvs)
     ::
       =;  bool
@@ -520,12 +596,13 @@
           table-full-widths=(list @)
           s=*
           f=*
+          is-extra=?
       ==
   ^-  felt
   =/  max-height=@
     %-  bex  %-  xeb  %-  dec
     (roll heights max)
-  =/  dp  (degree-processing heights constraint-map)
+  =/  dp  (degree-processing heights constraint-map is-extra)
   =/  boundary-zerofier=felt
     (finv (fsub deep-challenge (lift 1)))
   =/  chal-map=(map @ belt)
@@ -593,6 +670,20 @@
         dyns
         chal-map
         current-evals
+      ::
+        %+  ~(swag bop chals)
+          (mul 2 :(add boundary.counts row.counts transition.counts))
+        (mul 2 terminal.counts)
+      ::
+    ==
+  ::
+    ?.  is-extra  (lift 0)
+    %+  fmul  row-zerofier
+    %-  evaluate-constraints
+    :*  extra.constraints
+        dyns
+        chal-map
+        current-evals
       ::
         %-  ~(slag bop chals)
         %+  mul  2
@@ -600,6 +691,7 @@
           boundary.counts
           row.counts
           transition.counts
+          terminal.counts
         ==
       ::
     ==
@@ -655,54 +747,90 @@
           omega=felt
           index=@
           deep-challenge=felt
+          new-comp-eval=felt
       ==
   ^-  felt
   =/  omega-pow  (fmul (lift g) (fpow omega index))
   |^
-  =;  dat=[acc=felt num=@ @]
-    =-  -<
-    =/  denom  (fsub omega-pow (fpow deep-challenge num-comp-pieces))
-    %-  process-belt
-    :*  comp-elems
-        comp-evaluations
-        (~(slag fop weights) num.dat)
-        num-comp-pieces
-        0
-        denom
-        acc.dat
-    ==
-  %^  zip-roll  (range (lent heights))  heights
-  |=  [[i=@ height=@] acc=_(lift 0) num=@ total-full-width=@]
-  =/  full-width  (snag i full-widths)
-  =/  omicron  (lift (ordered-root height))
-  =/  current-trace-elems  (swag [total-full-width full-width] trace-elems)
-  =/  dat=[acc=felt num=@]  [acc num]
-  ::  first row trace columns
-  =/  denom  (fsub omega-pow deep-challenge)
-  =.  dat
-    %-  process-belt
-    :*  current-trace-elems
-        trace-evaluations
-        weights
-        full-width
-        num.dat
-        denom
-        acc.dat
-    ==
-  ::  second row trace columns obtained by shifting by omicron
-  =.  denom  (fsub omega-pow (fmul deep-challenge omicron))
-  =/  acc  acc.dat
-  =.  dat
-    %-  process-belt
-    :*  current-trace-elems
-        trace-evaluations
-        weights
-        full-width
-        num.dat
-        denom
-        acc.dat
-    ==
-  [acc.dat num.dat (add total-full-width full-width)]
+  =/  [acc=felt num=@ @]
+    %^  zip-roll  (range (lent heights))  heights
+    |=  [[i=@ height=@] acc=_(lift 0) num=@ total-full-width=@]
+    =/  full-width  (snag i full-widths)
+    =/  omicron  (lift (ordered-root height))
+    =/  current-trace-elems  (swag [total-full-width full-width] trace-elems)
+    =/  dat=[acc=felt num=@]  [acc num]
+    ::  first row trace columns
+    =/  denom  (fsub omega-pow deep-challenge)
+    =.  dat
+      %-  process-belt
+      :*  current-trace-elems
+          trace-evaluations
+          weights
+          full-width
+          num.dat
+          denom
+          acc.dat
+      ==
+    ::  second row trace columns obtained by shifting by omicron
+    =.  denom  (fsub omega-pow (fmul deep-challenge omicron))
+    =.  dat
+      %-  process-belt
+      :*  current-trace-elems
+          trace-evaluations
+          weights
+          full-width
+          num.dat
+          denom
+          acc.dat
+      ==
+    [acc.dat num.dat (add total-full-width full-width)]
+  ::
+  ::
+  =/  [acc=felt num=@ @]
+    %^  zip-roll  (range (lent heights))  heights
+    |=  [[i=@ height=@] acc=_acc num=_num total-full-width=@]
+    =/  full-width  (snag i full-widths)
+    =/  omicron  (lift (ordered-root height))
+    =/  current-trace-elems  (swag [total-full-width full-width] trace-elems)
+    =/  dat=[acc=felt num=@]  [acc num]
+    ::  first row trace columns
+    ::  evaluate new evals
+    =/  denom  (fsub omega-pow new-comp-eval)
+    =.  dat
+      %-  process-belt
+      :*  current-trace-elems
+          trace-evaluations
+          weights
+          full-width
+          num.dat
+          denom
+          acc.dat
+      ==
+    ::  second row trace columns obtained by shifting by omicron
+    =.  denom  (fsub omega-pow (fmul new-comp-eval omicron))
+    =.  dat
+      %-  process-belt
+      :*  current-trace-elems
+          trace-evaluations
+          weights
+          full-width
+          num.dat
+          denom
+          acc.dat
+      ==
+    [acc.dat num.dat (add total-full-width full-width)]
+  ::
+  =/  denom  (fsub omega-pow (fpow deep-challenge num-comp-pieces))
+  =-  -<
+  %-  process-belt
+  :*  comp-elems
+      comp-evaluations
+      (~(slag fop weights) num)
+      num-comp-pieces
+      0
+      denom
+      acc
+  ==
   ::
   ++  process-belt
     |=  $:  elems=(list belt)

+ 1 - 0
hoon/common/table/memory.hoon

@@ -49,6 +49,7 @@
     %+  pelt-col  %kvsf  ::  key-value flags
     %+  pelt-col  %decode-mset
     %+  pelt-col  %op0-mset
+    %+  pelt-col  %data-k
     ~
   ++  variables
     ^-  (map col-name mp-mega)

+ 20 - 28
hoon/common/table/prover/compute.hoon

@@ -185,39 +185,31 @@
   %+  print-pelt  dyck.n
   tail
 ::
-++  write-compressed-noun
-  ~/  %write-compressed-noun
-  |=  chals=ext-chals:chal
-  |=  [[s=tree-data f=tree-data e=tree-data] tail=(list belt)]
-  ^-  (list belt)
-  %+  print-pelt  ((compress-nouns chals) s f e)
-  tail
-::
 ++  compress-nouns
   ~/  %compress-nouns
-  |=  chals=ext-chals:chal
+  |=  chals=mega-ext-chals:chal
   |=  [s=tree-data f=tree-data e=tree-data]
   ^-  pelt
   ;:  padd
-    %+  pmul  d.chals
+    %+  pmul  m.chals
     ;:  padd
-      (pmul a.chals size.s)
-      (pmul b.chals dyck.s)
-      (pmul c.chals leaf.s)
+      (pmul j.chals size.s)
+      (pmul k.chals dyck.s)
+      (pmul l.chals leaf.s)
     ==
   ::
-    %+  pmul  e.chals
+    %+  pmul  n.chals
     ;:  padd
-      (pmul a.chals size.f)
-      (pmul b.chals dyck.f)
-      (pmul c.chals leaf.f)
+      (pmul j.chals size.f)
+      (pmul k.chals dyck.f)
+      (pmul l.chals leaf.f)
     ==
   ::
-    %+  pmul  f.chals
+    %+  pmul  o.chals
     ;:  padd
-      (pmul a.chals size.e)
-      (pmul b.chals dyck.e)
-      (pmul c.chals leaf.e)
+      (pmul j.chals size.e)
+      (pmul k.chals dyck.e)
+      (pmul l.chals leaf.e)
     ==
   ==
 ::
@@ -232,12 +224,12 @@
       (pmul c.chals leaf.s)
     ==
   =/  maxis=pelt
-    (pmul d.chals leaf.axis)
+    (pmul m.chals leaf.axis)
   =/  mval=pelt
     ;:  padd
-      (pmul e.chals size.e)
-      (pmul f.chals dyck.e)
-      (pmul g.chals leaf.e)
+      (pmul j.chals size.e)
+      (pmul k.chals dyck.e)
+      (pmul l.chals leaf.e)
     ==
   =/  mvar=pelt
     :(padd mroot maxis mval)
@@ -267,7 +259,7 @@
   ~/  %update-stack
   |=  [state=state-data row=row-data chals=mega-ext-chals:chal]
   ^-  pelt
-  =/  c  (compress-nouns (to-ext-chals chals))
+  =/  c  (compress-nouns chals)
   =/  program  (c s.row f.row e.row)
   =/  sp1  (c sf1-s.row sf1-f.row sf1-e.row)
   =/  sp2  (c sf2-s.row sf2-f.row sf2-e.row)
@@ -343,7 +335,6 @@
   ^-  bpoly
   =/  p  print-pelt
   =/  n  (write-noun alf.chals)
-  =/  c  (write-compressed-noun chals)
   %-  init-bpoly
   %+  n  s.row
   %+  n  f.row
@@ -479,7 +470,7 @@
   =/  z=pelt-chal:constraint-util  z.chals
   =/  z2=pelt-chal:constraint-util  (pmul z z)
   =/  z3=pelt-chal:constraint-util  (pmul z2 z)
-  =/  compress  (compress-nouns (to-ext-chals chals))
+  =/  compress  (compress-nouns chals)
   =/  stack=(list tree-data)
     (build-compute-queue:fock queue.fock-meta alf.chals)
   =|  rows=(list bpoly)
@@ -958,5 +949,6 @@
   ++  row-constraints         row-constraints:funcs:engine:verifier-compute
   ++  transition-constraints  transition-constraints:funcs:engine:verifier-compute
   ++  terminal-constraints    terminal-constraints:funcs:engine:verifier-compute
+  ++  extra-constraints       extra-constraints:funcs:engine:verifier-compute
   --
 --

+ 42 - 23
hoon/common/table/prover/memory.hoon

@@ -51,6 +51,7 @@
   ++  kvsf-idx           59
   ++  decode-mset-idx    62
   ++  op0-mset-idx       65
+  ++  data-k-idx         68
   --
 ++  test-nocks
   ^-  (list ^)
@@ -511,13 +512,14 @@
     =/  [first-row=row second-row=row]
       :-  (~(snag-as-bpoly ave p.table) 0)
       (~(snag-as-bpoly ave p.table) 1)
+    =/  input  (grab-pelt input-idx:ids first-row)
     =/  first-row-ax  (grab axis-idx:ids first-row)
     =/  first-row-fp
       %-  ifp-compress
       :*  :+  (grab-pelt parent-size-idx:ids first-row)
             (grab-pelt parent-dyck-idx:ids first-row)
           (grab-pelt parent-leaf-idx:ids first-row)
-          a.chals  b.chals  c.chals
+          j.chals  k.chals  l.chals
       ==
     =/  second-row-ax  (grab axis-idx:ids second-row)
     =/  second-row-fp
@@ -525,7 +527,7 @@
       :*  :+  (grab-pelt parent-size-idx:ids second-row)
             (grab-pelt parent-dyck-idx:ids second-row)
           (grab-pelt parent-leaf-idx:ids second-row)
-          a.chals  b.chals  c.chals
+          j.chals  k.chals  l.chals
       ==
     =/  subj-info=[ax=belt fp=pelt]
       ?@  s.return  [0 pzero]
@@ -535,8 +537,8 @@
         [first-row-ax first-row-fp]
       [second-row-ax second-row-fp]
     =/  [input-subj-fp=pelt input-form-fp=pelt]
-      :-  (padd fp.subj-info (pscal ax.subj-info d.chals))
-      (padd fp.form-info (pscal ax.form-info d.chals))
+      :-  (padd fp.subj-info (pscal ax.subj-info m.chals))
+      (padd fp.form-info (pscal ax.form-info m.chals))
     %-  head
     %^  spin  (range len.array.p.table)
       :*  z.chals
@@ -569,18 +571,15 @@
     =/  left-is-atom  ?:(=((grab op-l-idx:ids row) 0) %.y %.n)
     =/  right-is-atom  ?:(=((grab op-r-idx:ids row) 0) %.y %.n)
     =/  ax  (grab axis-idx:ids row)
-    =/  [pc1=pelt wt-pax=pelt pc2=pelt]
-      :+  (ifp-compress parent [a b c]:chals)
-        (pscal ax d.chals)
-      (ifp-compress parent [e f g]:chals)
-    =/  [lc1=pelt wt-lax=pelt lc2=pelt]
-      :+  (ifp-compress left [a b c]:chals)
-        (pscal (go-left ax) d.chals)
-      (ifp-compress left [e f g]:chals)
-    =/  [rc1=pelt wt-rax=pelt rc2=pelt]
-      :+  (ifp-compress right [a b c]:chals)
-        (pscal (go-right ax) d.chals)
-      (ifp-compress right [e f g]:chals)
+    =/  [par=pelt wt-pax=pelt]
+      :-  (ifp-compress parent [j k l]:chals)
+      (pscal ax m.chals)
+    =/  [lc=pelt wt-lax=pelt]
+      :-  (ifp-compress left [j k l]:chals)
+      (pscal (go-left ax) m.chals)
+    =/  [rc=pelt wt-rax=pelt]
+      :-  (ifp-compress right [j k l]:chals)
+      (pscal (go-right ax) m.chals)
     =/  new-line-ct  (pmul line-ct z.chals)
     =/  new-node-ct
       ?:  left-is-atom
@@ -600,7 +599,7 @@
           ;:  pmul
             z.chals
             node-ct
-            (padd lc1 wt-lax)
+            (padd lc wt-lax)
           ==
         ::
           ?.  ?&(left-is-atom !right-is-atom)
@@ -608,7 +607,7 @@
           ;:  pmul
             z.chals
             node-ct
-            (padd rc1 wt-rax)
+            (padd rc wt-rax)
           ==
         ::
           ?.  ?&(!left-is-atom !right-is-atom)
@@ -616,10 +615,10 @@
           ;:  pmul
             z.chals  z.chals
             node-ct
-            (padd rc1 wt-rax)
+            (padd rc wt-rax)
           ==
         ==
-      (pmul (padd pc1 wt-pax) line-ct)
+      (pmul (padd par wt-pax) line-ct)
     =/  new-decode-mset
       %-  rear
       %-  ~(add-all ld-pelt:constraint-util decode-mset)
@@ -640,14 +639,32 @@
       %-  rear
       %-  ~(add-all ld-pelt:constraint-util op0-mset)
       ;:  weld
-        ~[[:(padd fp.subj-info wt-pax pc2) (grab mult-idx:ids row)]]
+        ~[[:(padd input wt-pax par) (grab mult-idx:ids row)]]
       ::
         ?.  left-is-atom  ~
-        ~[[:(padd fp.subj-info wt-lax lc2) (grab mult-lc-idx:ids row)]]
+        ~[[:(padd input wt-lax lc) (grab mult-lc-idx:ids row)]]
       ::
         ?.  right-is-atom  ~
-        ~[[:(padd fp.subj-info wt-rax rc2) (grab mult-rc-idx:ids row)]]
+        ~[[:(padd input wt-rax rc) (grab mult-rc-idx:ids row)]]
       ==
+    ::
+    =/  data-k=pelt
+      =/  p1=pelt
+        ;:  padd
+          (pmul j.chals line-ct)
+          (pmul k.chals node-ct)
+          (pmul l.chals kvs)
+          (pmul m.chals (pioz kvs))
+        ==
+      =/  p2=pelt
+        ;:  padd
+          (pmul n.chals line-ct)
+          (pmul o.chals node-ct)
+          (pmul w.chals kvs)
+          (pmul x.chals (pioz kvs))
+        ==
+      :(pmul p1 p2 (padd p1 p2) (pioz kvs))
+    ::
     :_  [new-line-ct new-node-ct new-decode-mset new-op0-mset new-kvs]
     %-  init-bpoly
     %+  pr  line-ct
@@ -657,6 +674,7 @@
     %+  pr  (pmul kvs (pioz kvs))  ::  %kvsf
     %+  pr  dat.decode-mset
     %+  pr  dat.op0-mset
+    %+  pr  data-k
     ~
   ::
   ++  terminal
@@ -677,5 +695,6 @@
   ++  row-constraints         row-constraints:funcs:engine:verifier-memory
   ++  transition-constraints  transition-constraints:funcs:engine:verifier-memory
   ++  terminal-constraints    terminal-constraints:funcs:engine:verifier-memory
+  ++  extra-constraints       extra-constraints:funcs:engine:verifier-memory
   --
 --

+ 74 - 64
hoon/common/table/verifier/compute.hoon

@@ -148,6 +148,12 @@
       =/  d-chal=mp-pelt    (r %d)
       =/  e=mp-pelt    (r %e)
       =/  f=mp-pelt    (r %f)
+      =/  j=mp-pelt    (r %j)
+      =/  k=mp-pelt    (r %k)
+      =/  l=mp-pelt    (r %l)
+      =/  m=mp-pelt    (r %m)
+      =/  n=mp-pelt    (r %n)
+      =/  o=mp-pelt    (r %o)
       ::
       %-  ~(gas by *(map col-name mp-ultra))
       ::
@@ -170,25 +176,25 @@
         %+  mpsub-pelt  (w %stack-kv)
         %+  mpmul-pelt  z
         ;:  mpadd-pelt
-          %+  mpmul-pelt  d-chal
+          %+  mpmul-pelt  m
           ;:  mpadd-pelt
-            (mpmul-pelt a (w %s-size))
-            (mpmul-pelt b (w %s-dyck))
-            (mpmul-pelt c (w %s-leaf))
+            (mpmul-pelt j (w %s-size))
+            (mpmul-pelt k (w %s-dyck))
+            (mpmul-pelt l (w %s-leaf))
           ==
         ::
-          %+  mpmul-pelt  e
+          %+  mpmul-pelt  n
           ;:  mpadd-pelt
-            (mpmul-pelt a (w %f-size))
-            (mpmul-pelt b (w %f-dyck))
-            (mpmul-pelt c (w %f-leaf))
+            (mpmul-pelt j (w %f-size))
+            (mpmul-pelt k (w %f-dyck))
+            (mpmul-pelt l (w %f-leaf))
           ==
         ::
-          %+  mpmul-pelt  f
+          %+  mpmul-pelt  o
           ;:  mpadd-pelt
-            (mpmul-pelt a (w %e-size))
-            (mpmul-pelt b (w %e-dyck))
-            (mpmul-pelt c (w %e-leaf))
+            (mpmul-pelt j (w %e-size))
+            (mpmul-pelt k (w %e-dyck))
+            (mpmul-pelt l (w %e-leaf))
           ==
         ==
       ::
@@ -1065,97 +1071,97 @@
       :-  :-  %stack-update
         =/  program
           ;:  mpadd-pelt
-            %+  mpmul-pelt  d
+            %+  mpmul-pelt  m
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %s-size))
-              (mpmul-pelt b (w %s-dyck))
-              (mpmul-pelt c (w %s-leaf))
+              (mpmul-pelt j (w %s-size))
+              (mpmul-pelt k (w %s-dyck))
+              (mpmul-pelt l (w %s-leaf))
             ==
           ::
-            %+  mpmul-pelt  e
+            %+  mpmul-pelt  n
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %f-size))
-              (mpmul-pelt b (w %f-dyck))
-              (mpmul-pelt c (w %f-leaf))
+              (mpmul-pelt j (w %f-size))
+              (mpmul-pelt k (w %f-dyck))
+              (mpmul-pelt l (w %f-leaf))
             ==
           ::
-            %+  mpmul-pelt  f
+            %+  mpmul-pelt  o
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %e-size))
-              (mpmul-pelt b (w %e-dyck))
-              (mpmul-pelt c (w %e-leaf))
+              (mpmul-pelt j (w %e-size))
+              (mpmul-pelt k (w %e-dyck))
+              (mpmul-pelt l (w %e-leaf))
             ==
           ==
         ::
         =/  sp1
           ;:  mpadd-pelt
-            %+  mpmul-pelt  d
+            %+  mpmul-pelt  m
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %sf1-s-size))
-              (mpmul-pelt b (w %sf1-s-dyck))
-              (mpmul-pelt c (w %sf1-s-leaf))
+              (mpmul-pelt j (w %sf1-s-size))
+              (mpmul-pelt k (w %sf1-s-dyck))
+              (mpmul-pelt l (w %sf1-s-leaf))
             ==
           ::
-            %+  mpmul-pelt  e
+            %+  mpmul-pelt  n
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %sf1-f-size))
-              (mpmul-pelt b (w %sf1-f-dyck))
-              (mpmul-pelt c (w %sf1-f-leaf))
+              (mpmul-pelt j (w %sf1-f-size))
+              (mpmul-pelt k (w %sf1-f-dyck))
+              (mpmul-pelt l (w %sf1-f-leaf))
             ==
           ::
-            %+  mpmul-pelt  f
+            %+  mpmul-pelt  o
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %sf1-e-size))
-              (mpmul-pelt b (w %sf1-e-dyck))
-              (mpmul-pelt c (w %sf1-e-leaf))
+              (mpmul-pelt j (w %sf1-e-size))
+              (mpmul-pelt k (w %sf1-e-dyck))
+              (mpmul-pelt l (w %sf1-e-leaf))
             ==
           ==
         ::
         =/  sp2
           ;:  mpadd-pelt
-            %+  mpmul-pelt  d
+            %+  mpmul-pelt  m
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %sf2-s-size))
-              (mpmul-pelt b (w %sf2-s-dyck))
-              (mpmul-pelt c (w %sf2-s-leaf))
+              (mpmul-pelt j (w %sf2-s-size))
+              (mpmul-pelt k (w %sf2-s-dyck))
+              (mpmul-pelt l (w %sf2-s-leaf))
             ==
           ::
-            %+  mpmul-pelt  e
+            %+  mpmul-pelt  n
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %sf2-f-size))
-              (mpmul-pelt b (w %sf2-f-dyck))
-              (mpmul-pelt c (w %sf2-f-leaf))
+              (mpmul-pelt j (w %sf2-f-size))
+              (mpmul-pelt k (w %sf2-f-dyck))
+              (mpmul-pelt l (w %sf2-f-leaf))
             ==
           ::
-            %+  mpmul-pelt  f
+            %+  mpmul-pelt  o
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %sf2-e-size))
-              (mpmul-pelt b (w %sf2-e-dyck))
-              (mpmul-pelt c (w %sf2-e-leaf))
+              (mpmul-pelt j (w %sf2-e-size))
+              (mpmul-pelt k (w %sf2-e-dyck))
+              (mpmul-pelt l (w %sf2-e-leaf))
             ==
           ==
         ::
         =/  sp3
           ;:  mpadd-pelt
-            %+  mpmul-pelt  d
+            %+  mpmul-pelt  m
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %sf3-s-size))
-              (mpmul-pelt b (w %sf3-s-dyck))
-              (mpmul-pelt c (w %sf3-s-leaf))
+              (mpmul-pelt j (w %sf3-s-size))
+              (mpmul-pelt k (w %sf3-s-dyck))
+              (mpmul-pelt l (w %sf3-s-leaf))
             ==
           ::
-            %+  mpmul-pelt  e
+            %+  mpmul-pelt  n
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %sf3-f-size))
-              (mpmul-pelt b (w %sf3-f-dyck))
-              (mpmul-pelt c (w %sf3-f-leaf))
+              (mpmul-pelt j (w %sf3-f-size))
+              (mpmul-pelt k (w %sf3-f-dyck))
+              (mpmul-pelt l (w %sf3-f-leaf))
             ==
           ::
-            %+  mpmul-pelt  f
+            %+  mpmul-pelt  o
             ;:  mpadd-pelt
-              (mpmul-pelt a (w %sf3-e-size))
-              (mpmul-pelt b (w %sf3-e-dyck))
-              (mpmul-pelt c (w %sf3-e-leaf))
+              (mpmul-pelt j (w %sf3-e-size))
+              (mpmul-pelt k (w %sf3-e-dyck))
+              (mpmul-pelt l (w %sf3-e-leaf))
             ==
           ==
         ::
@@ -1266,12 +1272,12 @@
             (mpmul-pelt c (w %s-leaf))
           ==
         =/  maxis
-          (mpmul-pelt d (w %f-t-leaf))
+          (mpmul-pelt m (w %f-t-leaf))
         =/  mval
           ;:  mpadd-pelt
-            (mpmul-pelt e (w %e-size))
-            (mpmul-pelt f (w %e-dyck))
-            (mpmul-pelt g (w %e-leaf))
+            (mpmul-pelt j (w %e-size))
+            (mpmul-pelt k (w %e-dyck))
+            (mpmul-pelt l (w %e-leaf))
           ==
         =/  mvar
           :(mpadd-pelt mroot maxis mval)
@@ -1313,6 +1319,10 @@
         %+  mpscal-pelt  (v %pad)
         (mpsub-pelt (w %op0-mset) (w-n %op0-mset))
       ~
+    ::
+    ++  extra-constraints
+      ^-  (map term mp-ultra)
+      ~
     --
   --
 --

+ 43 - 7
hoon/common/table/verifier/memory.hoon

@@ -329,7 +329,7 @@
         %+  mpsub-pelt
           %+  mpadd-pelt  (w-n %kvs)
           %+  mpmul-pelt  (w %ln)
-          %-  ~(compress poly-tupler-pelt ~[a b c d])
+          %-  ~(compress poly-tupler-pelt ~[j k l m])
           :~  (w %parent-size)
               (w %parent-dyck)
               (w %parent-leaf)
@@ -342,7 +342,7 @@
           ;:  mpmul-pelt
             z
             (w %nc)
-            %-  ~(compress poly-tupler-pelt ~[a b c d])
+            %-  ~(compress poly-tupler-pelt ~[j k l m])
             :~  (w %lc-size)
                 (w %lc-dyck)
                 (w %lc-leaf)
@@ -354,7 +354,7 @@
           ;:  mpmul-pelt
             z
             (w %nc)
-            %-  ~(compress poly-tupler-pelt ~[a b c d])
+            %-  ~(compress poly-tupler-pelt ~[j k l m])
             :~  (w %rc-size)
                 (w %rc-dyck)
                 (w %rc-leaf)
@@ -366,7 +366,7 @@
           ;:  mpmul-pelt
             z  z
             (w %nc)
-            %-  ~(compress poly-tupler-pelt ~[a b c d])
+            %-  ~(compress poly-tupler-pelt ~[j k l m])
             :~  (w %rc-size)
                 (w %rc-dyck)
                 (w %rc-leaf)
@@ -405,7 +405,7 @@
         =/  mvar
           %+  mpadd-pelt
             input
-          %-  ~(compress poly-tupler-pelt ~[d e f g])
+          %-  ~(compress poly-tupler-pelt ~[m j k l])
           :~  [(v %axis) (mp-c 0) (mp-c 0)]
               (w %parent-size)
               (w %parent-dyck)
@@ -414,7 +414,7 @@
         =/  mvar-lc
           %+  mpadd-pelt
             input
-          %-  ~(compress poly-tupler-pelt ~[d e f g])
+          %-  ~(compress poly-tupler-pelt ~[m j k l])
           :~  [l-axis (mp-c 0) (mp-c 0)]
               (w %lc-size)
               (w %lc-dyck)
@@ -423,7 +423,7 @@
         =/  mvar-rc
           %+  mpadd-pelt
             input
-          %-  ~(compress poly-tupler-pelt ~[d e f g])
+          %-  ~(compress poly-tupler-pelt ~[m j k l])
           :~  [r-axis (mp-c 0) (mp-c 0)]
               (w %rc-size)
               (w %rc-dyck)
@@ -453,6 +453,42 @@
           (mpmul-pelt (mpsub-pelt bet mvar) (mpsub-pelt bet mvar-lc))
         ==
       ~
+    ::
+    ++  extra-constraints
+      ^-  (map term mp-ultra)
+      ~+
+      ~&  %processing-extra-constraints
+      =,  constraint-util
+      =/  r   ~(r rnd:chal (make-chal-mps:chal chal-names-all:chal))
+      =/  [j=mp-pelt k=mp-pelt l=mp-pelt m=mp-pelt n=mp-pelt o=mp-pelt ww=mp-pelt x=mp-pelt y=mp-pelt]
+        [(r %j) (r %k) (r %l) (r %m) (r %n) (r %o) (r %w) (r %x) (r %y)]
+      =/  [alf=mp-pelt bet=mp-pelt gam=mp-pelt z=mp-pelt]
+        [(r %alf) (r %bet) (r %gam) (r %z)]
+      %-  ~(gas by *(map term mp-ultra))
+      ::
+      %^  tag-mp-pelt  %data-constraint-1
+        =/  p1
+          %-  ~(compress poly-tupler-pelt ~[j k l m])
+          :~  (w %ln)
+              (w %nc)
+              (w %kvs)
+              (w %kvs-ioz)
+          ==
+        =/  p2
+          %-  ~(compress poly-tupler-pelt ~[n o ww x])
+          :~  (w %ln)
+              (w %nc)
+              (w %kvs)
+              (w %kvs-ioz)
+          ==
+        %+  mpsub-pelt  (w %data-k)
+        ;:  mpmul-pelt
+          p1
+          p2
+          (mpadd-pelt p1 p2)
+          (w %kvs-ioz)
+        ==
+      ~
     --
   --
 --

+ 374 - 116
hoon/common/tx-engine.hoon

@@ -1,14 +1,12 @@
-/=  sp  /common/stark/prover
 /=  emission  /common/schedule
 /=  mine  /common/pow
 /=  *  /common/zeke
 /=  *  /common/zoon
 ::    tx-engine: this contains all transaction types and logic related to dumbnet.
 ::
-::  the most notable thing about how this library is written are the types.
-::  we are experimenting with a namespacing scheme for functions that are
-::  primarily about particular types inside of the namespace for that type,
-::  as suggested by Ted in urbit/#6881. that is
+::  the most notable thing about how this library is written are the types. we use
+::  a namespacing scheme for functions that are primarily about particular types inside
+::  of the namespace for that type, as suggested by Ted in urbit/#6881. that is:
 ::
 ::  ++  list
 ::    =<  form
@@ -19,17 +17,17 @@
 ::    ...
 ::    --
 ::
-::  this file is an experiment to maximize this style for a single module, and then
-::  see how well it interfaces with hoon written in more familiar styles.
+::  we refer to these types as 'B-types'
+::
 =>
 ~%  %dumb-transact  ..stark-engine-jet-hook  ~
 |%
 +|  %misc-types
 ::
-::  size in bytes. this is not a blockchain constant, its just an alias
+::  size in bits. this is not a blockchain constant, its just an alias
 ::  to make it clear what the atom represents and theres not a better spot
 ::  for it.
-+$  size  @bytes
++$  size  @bits
 ::
 ::   $blockchain-constants
 ::
@@ -37,8 +35,8 @@
 ::  when using non-default constants.
 +$  blockchain-constants
   $+  blockchain-constants
-  $~  :*  ::  max block size in bytes
-          max-block-size=`size``@`1.000.000
+  $~  :*  ::  max block size in bits
+          max-block-size=`@`8.000.000
           :: actual number of blocks, not 2017 by counting from 0
           blocks-per-epoch=2.016
           ::  14 days measured in seconds, 1.209.600
@@ -51,7 +49,7 @@
           ::  which a new block's timestamp must be after to be considered valid
           min-past-blocks=11
           ::TODO determine appropriate genesis target
-          genesis-target-atom=^~((div max-tip5-atom:tip5 (bex 2)))
+          genesis-target-atom=^~((div max-tip5-atom:tip5 (bex 14)))
           ::TODO determine a real max-target-atom. BTC uses 32 leading zeroes
           max-target-atom=max-tip5-atom:tip5
           ::  whether or not to check the pow of blocks
@@ -62,6 +60,8 @@
           pow-len=pow-len
           ::  how many ways the coinbase may be split
           max-coinbase-split=2
+          ::  first month block height. enforces lock on first month coins.
+          first-month-coinbase-min=4.383
       ==
   $:  max-block-size=size
       blocks-per-epoch=@
@@ -75,6 +75,7 @@
       coinbase-timelock-min=@
       pow-len=@
       max-coinbase-split=@
+      first-month-coinbase-min=@
   ==
 --
 ::
@@ -124,6 +125,8 @@
   |%
   ++  form  hash
   ++  to-list  to-list:hash
+  ++  based    based:hash
+  ++  max-size  max-size:hash
   --
 ::  $hash: output of tip:zoon arm
 ++  hash
@@ -135,6 +138,25 @@
   ::
   ++  to-b58  |=(has=form `cord`(crip (en-base58 (digest-to-atom:tip5 has))))
   ++  from-b58  |=(=cord `form`(atom-to-digest:tip5 (de-base58 (trip cord))))
+  ::
+  ::  +max-size:  max size of hash in bits, obtained by running:
+  ::    (compute-size-jam [(dec p) (sub p 2) (sub p 3) (sub p 4) (sub p 5)])
+  ++  max-size
+    ^-  size
+    `size``@`403
+  ::
+  ::  +validate: checks if elements of hash are in base field.
+  ++  based
+    |=  has=form
+    ^-  ?
+    =+  [a=@ b=@ c=@ d=@ e=@]=has
+    ?&  (^based a)
+        (^based b)
+        (^based c)
+        (^based d)
+        (^based e)
+    ==
+  ::
   ++  to-list
     |=  bid=form
     ^-  (list @)
@@ -142,11 +164,34 @@
     [a b c d e ~]
   --
 ::
+++  proof
+  =<  form
+  |%
+  +$  form  ^proof
+  ::
+  ::  +max-size:  max size of proof in bits. We upper bound it to
+  ::    125kb or 125000 bytes or 1000000 bits
+  ++  max-size
+    ^-  size
+    `size``@`1.000.000
+  ::
+  ++  hash  |=(=form (hash-proof form))
+  --
+::
 ++  schnorr-pubkey
   =<  form
   |%
   +$  form  a-pt:curve:cheetah
   ::
+  ++  based
+    |=  =form
+    ^-  ?
+    (a-pt-based:curve:cheetah form)
+  ::
+  ++  validate
+    |=  =form
+    (in-g:affine:curve:cheetah form)
+  ::
   ++  to-b58  |=(sop=form `cord`(a-pt-to-base58:cheetah sop))
   ++  from-b58  |=(=cord `form`(base58-to-a-pt:cheetah cord))
   ++  to-lock  |=(sop=form (new:lock sop))
@@ -172,6 +217,12 @@
   +$  form
     [chal=chal:belt-schnorr:cheetah sig=sig:belt-schnorr:cheetah]
   ::
+  ++  based
+    |=  =form
+    ?&  (based:belt-schnorr:cheetah chal.form)
+        (based:belt-schnorr:cheetah sig.form)
+    ==
+  ::
   ++  hashable  |=(=form leaf+form)
   ++  hash  |=(=form (hash-hashable:tip5 (hashable form)))
   --
@@ -181,6 +232,14 @@
   |%
   +$  form  (z-map schnorr-pubkey schnorr-signature)
   ::
+  ++  based
+    |=  =form
+    ^-  ?
+    %+  levy  ~(tap z-by form)
+    |=  [pubkey=schnorr-pubkey sig=schnorr-signature]
+    ?&  (based:schnorr-pubkey pubkey)
+        (based:schnorr-signature sig)
+    ==
   ++  hashable
     |=  =form
     ^-  hashable:tip5
@@ -284,6 +343,12 @@
     :-  (to-b58:^hash -.nom)
     (to-b58:^hash +<.nom)
   ::
+  ++  based
+    |=  =form
+    ?&  (based:^hash -.form)
+        (based:^hash -.+.form)
+    ==
+  ::
   ++  hashable
     |=  =form
     ^-  hashable:tip5
@@ -325,7 +390,7 @@
     $+  page
     $:  digest=block-id
         :: everything below this is what is hashed for the digest: +.page
-        pow=$+(pow (unit proof:sp))
+        pow=$+(pow (unit proof))
         :: everything below this is what is hashed for the block commitment: +>.page
         parent=block-id   ::TODO sam's comment on why this is here
         tx-ids=(z-set tx-id)
@@ -348,11 +413,15 @@
   ::
   ::    while we store target and accumulated-work as bignums, we
   ::    do not yet employ bignum arithmetic
-  ++  new
+  ::
+  ::    We assume that par is a valid block that was obtained from
+  ::    the node's blockmap. The other arguments in the sample are
+  ::    also validated in the context in +new is called, so we skip
+  ::    validation. Notably, shares is obtained from the mining state,
+  ::    where it was validated in +set-shares.
+  ++  new-candidate
     |=  [par=form now=@da target-bn=bignum:bn shares=(z-map lock @)]
     ^-  form
-    ::  at launch, we do not allow coinbases to be split more than two ways
-    ?>  (lte (lent ~(key z-by shares)) max-coinbase-split)
     =/  accumulated-work=bignum:bn
       %-  chunk:bn
       (add (merge:bn (compute-work target-bn)) (merge:bn accumulated-work.par))
@@ -381,20 +450,18 @@
     ^-  form
     ::  explicitly writing out the bunts is unnecessary, but we want to make it clear
     ::  that each of these choices was deliberate rather than unfinished
-    =/  pag=form
-      %*  .  *form
-        pow                    *(unit proof:sp)
-        tx-ids                 *(z-set tx-id)
-        timestamp              (time-in-secs timestamp)
-        epoch-counter          *@
-        target                 genesis-target
-        accumulated-work       (compute-work genesis-target)
-        coinbase               *(z-map lock @)  :: ensure coinbase is unspendable
-        height                 *page-number
-        parent                 (hash:btc-hash btc-hash.tem)
-        msg                    message.tem
-      ==
-    pag(digest (compute-digest pag))
+    %*  .  *form
+      pow                    *(unit proof)         ::  pow is uninitialized because it needs to be mined
+      tx-ids                 *(z-set tx-id)
+      timestamp              (time-in-secs timestamp)
+      epoch-counter          *@
+      target                 genesis-target
+      accumulated-work       (compute-work genesis-target)
+      coinbase               *(z-map lock coins)  :: ensure coinbase is unspendable
+      height                 *page-number
+      parent                 (hash:btc-hash btc-hash.tem)
+      msg                    message.tem
+    ==
   ::
   ::
   ::  +block-commitment: hash commitment of block contents for miner
@@ -431,7 +498,9 @@
   ++  check-digest
     |=  pag=form
     ^-  ?
-    =(digest.pag (compute-digest pag))
+    ?&  (based:block-id digest.pag)
+        =(digest.pag (compute-digest pag))
+    ==
   ::
   ::  Hash pow with hash-proof and hash the rest of the page.
   ++  compute-digest
@@ -463,19 +532,27 @@
   ++  compute-size
     |=  [pag=form raw-txs=(z-map tx-id raw-tx)]
     ^-  size
-    %+  add
-      :: size of page in number of bytes. note that we do not include the digest
-      :: or powork.
-      (div (compute-size-belt-noun `*`+>.pag) 8)
-    %+  roll  ~(tap z-in tx-ids.pag)
-    |=  [id=tx-id sum-sizes=size]
-    %+  add  sum-sizes
-    (compute-size:raw-tx (~(got z-by raw-txs) id))
+    ;:  add
+        ::  max size of digest in bits, we need to check against upper bound because
+        ::  a digest cannot be calculated without pow
+        max-size:block-id
+        ::  max size of proof is 90 kb or 90000 bytes or 720000 bits
+        max-size:proof
+        :: size of page in number of bits. note that we do not include the digest
+        :: or powork.
+        (compute-size-jam `*`+>.pag)
+        ::
+        %+  roll
+          ~(tap z-in tx-ids.pag)
+        |=  [id=tx-id sum-sizes=size]
+        %+  add  sum-sizes
+        (compute-size:raw-tx (~(got z-by raw-txs) id))
+    ==
   ::
   ++  to-local-page
     |=  pag=form
     ^-  local-page
-    pag(pow (bind pow.pag |=(p=proof:sp (jam p))))
+    pag(pow (bind pow.pag |=(p=proof (jam p))))
   ::
   ::  +compute-work: how much heaviness a block contribute to .accumulated-work
   ::
@@ -535,19 +612,20 @@
   ++  to-page
     |=  lp=form
     ^-  page
-    lp(pow (biff pow.lp |=(j=@ ((soft proof:sp) (cue j)))))
+    lp(pow (biff pow.lp |=(j=@ ((soft proof) (cue j)))))
   --
 ::
 ::  +page-msg: (list belt) that enforces that each elt is a belt
 ++  page-msg
   =<  form
   |%
-  +$  form
-    $|  (list belt)
-    |=  tap=(list belt)
-    (levy tap |=(t=@ (based t)))
+  +$  form  (list belt)
   ::
   ++  new  |=(msg=cord (form (rip-correct 5 msg)))
+  ++  validate
+    |=  tap=(list belt)
+    ^-  ?
+    (levy tap |=(t=@ (based t)))
   ::
   ++  hash
     |=  =form
@@ -579,7 +657,7 @@
   ++  new
     |=  [height=page-number msg-hash=@t]
     ^-  form
-    (some [height (from-b58:hash msg-hash)])
+    `[height (from-b58:hash msg-hash)]
   --
 ::
 ::  $genesis-template:
@@ -668,6 +746,18 @@
         =(name name.note.inp)
     ==
   ::
+  ++  based
+    ~/  %based
+    |=  ips=form
+    ^-  ?
+    ?:  =(ips *form)
+      %&
+    %+  levy  ~(tap z-by ips)
+    |=  [name=nname inp=input]
+    ?&  (based:input inp)
+        (based:nname name)
+    ==
+  ::
   ++  hashable
     |=  =form
     ^-  hashable:tip5
@@ -858,6 +948,16 @@
       (hashable:timelock-range timelock-range.raw)
     leaf+total-fees.raw
   ::
+  ++  based
+    ~/  %based
+    |=  raw=form
+    ^-  ?
+    ?&  (based:hash id.raw)
+        (based:inputs inputs.raw)
+        (based:timelock-range timelock-range.raw)
+        (^based total-fees.raw)
+    ==
+  ::
   ++  validate
     ~/  %validate
     |=  raw=form
@@ -865,17 +965,20 @@
     =/  check-inputs  (validate:inputs inputs.raw)
     =/  check-fees  =(total-fees.raw (roll-fees:inputs inputs.raw))
     =/  check-timelock  =(timelock-range.raw (roll-timelocks:inputs inputs.raw))
+    =/  check-field  (based:hash id.raw)
     =/  check-id  =(id.raw (compute-id raw))
     :: %-  %-  slog
     ::     :~  leaf+"validate-raw-tx"
     ::         leaf+"inputs: {<check-inputs>}"
     ::         leaf+"fees: {<check-fees>}"
     ::         leaf+"timelock: {<check-timelock>}"
+    ::         leaf+"field: {<check-field>}"
     ::         leaf+"id: {<check-id>}"
     ::     ==
     ?&  check-inputs
         check-fees
         check-timelock
+        check-field
         check-id
     ==
   ::
@@ -884,11 +987,11 @@
     ^-  (z-set nname)
     (names:inputs inputs.raw)
   ::
-  ::  +compute-size: returns size in number of bytes
+  ::  +compute-size: returns size in number of bits
   ++  compute-size
     |=  raw=form
     ^-  size
-    (div (compute-size-belt-noun `*`raw) 8)
+    (compute-size-jam `*`raw)
   --
 ::
 ::  $tx: once a raw-tx is being included in a block, it becomes a tx
@@ -953,6 +1056,15 @@
         relative=timelock-range
     ==
   ::
+  ++  based
+    ~/  %based
+    |=  =form
+    ?~  form
+      %&
+    ?&  (based:timelock-range absolute.u.form)
+        (based:timelock-range relative.u.form)
+    ==
+  ::
   ++  hashable
     ~/  %hashable
     |=  =form
@@ -981,16 +1093,16 @@
   ::
   ::  +fix-absolute: produce absolute timelock from relative timelock and page number
   ++  fix-absolute
-    |=  [til=form page=page-number]
+    |=  [til=form page-num=page-number]
     ^-  timelock-range
     ?~  til  *timelock-range
-    =/  add-page  |=(a=page-number (add a page))
+    =/  make-absolute  |=(relative=page-number (add relative page-num))
     =/  absolutification=timelock-range
       ?:  =(*timelock-range relative.u.til)  *timelock-range
       =/  min=(unit page-number)
-        (bind min.relative.u.til add-page)
+        (bind min.relative.u.til make-absolute)
       =/  max=(unit page-number)
-        (bind max.relative.u.til add-page)
+        (bind max.relative.u.til make-absolute)
       (new:timelock-range [min max])
     (merge:timelock-range absolutification absolute.u.til)
   ::
@@ -1021,9 +1133,6 @@
     |=  [tir=form new-page-number=page-number]
     ^-  ?
     ?:  =(tir *form)  %.y
-    :: TODO normalize timelock range
-    ::=/  min  (fall min 0)
-    ::=/  max  (fall max 0)
     =/  min-ok=?
       ?~  min.tir  %.y
       (gte new-page-number u.min.tir)
@@ -1048,7 +1157,7 @@
         min.b
       ?~  min.b
         min.a
-      (some (max u.min.a u.min.b))
+      `(max u.min.a u.min.b)
     =/  max=(unit page-number)
       ?~  max.a
         ?~  max.b
@@ -1056,9 +1165,16 @@
         max.b
       ?~  max.b
         max.a
-      (some (^min u.max.a u.max.b))
+      `(^min u.max.a u.max.b)
     (new [min max])
   ::
+  ++  based
+    |=  =form
+    ^-  ?
+    ?&  ?~(min.form %& (^based u.min.form))
+        ?~(max.form %& (^based u.max.form))
+    ==
+  ::
   ++  hashable
     |=  =form
     ^-  hashable:tip5
@@ -1075,6 +1191,8 @@
 ::    a lock may only be "unlocked" if m =<n, we do permit constructing m>n
 ::    with an issued warning, since this may happen when constructing a
 ::    transaction piece-by-piece.
+::
+::    TODO: disambiguate intermediate locks and final locks for validation.
 ++  lock
   =<  form
   ~%  %lock  ..lock  ~
@@ -1083,19 +1201,71 @@
     $~  [m=1 pubkeys=*(z-set schnorr-pubkey)]
     [m=@udD pubkeys=(z-set schnorr-pubkey)]
   ::
+  ++  validate
+    |=  =form
+    ^-  ?
+    ?&  (validate-intermediate form)
+        (lte m.form ~(wyt z-in pubkeys.form))
+    ==
+  ::
+  ++  validate-intermediate
+    |=  form
+    ^-  ?
+    =/  num-keys=@  ~(wyt z-in pubkeys)
+    ::
+    ::  validate:schnorr-pubkey is computationally intensive,
+    ::  do not call this in performance sensistive code
+    =/  check-a-pt=?
+      %+  levy  ~(tap z-in pubkeys)
+      validate:schnorr-pubkey
+    ::  we do not check m <= num-keys here because we allow constructing a tx
+    ::  piece by piece
+    ?&  check-a-pt
+        (lte m 255)
+        !=(m 0)
+        (lte num-keys 255)
+        !=(num-keys 0)
+    ==
+  ::
+  ++  check
+    |=  =form
+    ^-  ^form
+    ?.  (validate-intermediate form)
+      !!
+    form
+  ::
+  ::  +based: checks if all components of lock struct are in based field. called
+  ::    on the block receiver side. we skip validating that the pubkeys are valid
+  ::    curve points because it is costly. in cases where checking validity is just
+  ::    as fast as checking for field membership, we check for validity.
+  ++  based
+    |=  =form
+    ?.  (lte m.form 255)
+      %|
+    ?.  (lte ~(wyt z-in pubkeys.form) 255)
+      %|
+    %+  levy  ~(tap z-in pubkeys.form)
+    |=  pt=schnorr-pubkey
+    ?&  (a-pt-based:curve:cheetah pt)
+        ::  this extra validity check costs nothing, so we throw it in
+        ::  even though both %.n and %.y are belts.
+        =(%.n inf.pt)
+    ==
+  ::
   ++  new
     =<  default
     |%
     ++  default
       |=  key=schnorr-pubkey
-      %*  .  *form
-        m  1
-        pubkeys  (~(put z-in *(z-set schnorr-pubkey)) key)
+      %-  check
+      :*  m=1
+          pubkeys=(~(put z-in *(z-set schnorr-pubkey)) key)
       ==
     ::
     ::  +m-of-n: m signers required of n=#keys.
     ++  m-of-n
       |=  [m=@ud keys=(z-set schnorr-pubkey)]
+      %-  check
       =/  n=@  ~(wyt z-in keys)
       ?>  ?&  (lte m 255)
               (lte n 255)
@@ -1107,16 +1277,14 @@
           warning: lock requires more signatures {(scow %ud m)} than there
           are in .pubkeys: {(scow %ud n)}
           """
-      %*  .  *form
-        m  m
-        pubkeys  keys
-      ==
+      [m=m pubkeys=keys]
     --
   ::
   ::  +join: union of several $locks
   ++  join
     |=  [m=@udD locks=(list form)]
     ^-  form
+    %-  check
     =/  new-keys=(z-set schnorr-pubkey)
       %+  roll  locks
       |=  [loc=form keys=(z-set schnorr-pubkey)]
@@ -1126,6 +1294,7 @@
   ++  set-m
     |=  [lock=form new-m=@ud]
     ^+  lock
+    %-  check
     =/  n=@  ~(wyt z-in pubkeys.lock)
     ?>  ?&  (lte new-m 255)
             !=(new-m 0)
@@ -1145,6 +1314,7 @@
       ++  default
         |=  [lock=form new-key=schnorr-pubkey]
         ^+  lock
+        %-  check
         ?:  (~(has z-in pubkeys.lock) new-key)
           ~&  >>>  "signer {<new-key>} already exists in lock"
           lock
@@ -1157,6 +1327,7 @@
       ++  multi
         |=  [lock=form new-keys=(z-set schnorr-pubkey)]
         ^+  lock
+        %-  check
         %-  ~(rep z-in new-keys)
         |=  [new-key=schnorr-pubkey new-lock=_lock]
         (default new-lock new-key)
@@ -1168,6 +1339,7 @@
       ++  default
         |=  [lock=form no-key=schnorr-pubkey]
         ^+  lock
+        %-  check
         ?.  (~(has z-in pubkeys.lock) no-key)
           ~&  >>>  "key {<no-key>} does not exist in lock"
           lock
@@ -1184,25 +1356,17 @@
       ++  multi
         |=  [lock=form no-keys=(z-set schnorr-pubkey)]
         ^+  lock
+        %-  check
         %-  ~(rep z-in no-keys)
         |=  [no-key=schnorr-pubkey new-lock=_lock]
         (default new-lock no-key)
       --
     --
   ::
-  ++  validate
-    |=  lock=form
-    ^-  ?
-    =/  num-keys=@  ~(wyt z-in pubkeys.lock)
-    ?&  (lte m.lock 255)
-        !=(m.lock 0)
-        (lte num-keys 255)
-        !=(num-keys 0)
-    ==
-  ::
   ++  from-b58
     |=  [m=@ pks=(list @t)]
     ^-  form
+    %-  check
     %+  m-of-n:new  m
     %-  ~(gas z-in *(z-set schnorr-pubkey))
     %+  turn  pks
@@ -1254,6 +1418,15 @@
       assets=coins
     ==
   ::
+  ++  based
+    ~/  %based
+    |=  =form
+    ?&  (based:nname name.form)
+        (based:lock lock.form)
+        (based:^hash p.source.form)
+        (^based assets.form)
+    ==
+  ::
   ++  hashable
     ~/  %hashable
     |=  =form
@@ -1279,45 +1452,61 @@
   =<  form
   |%
   ++  form
-    $:  $~  %*  .  *nnote
-              timelock            coinbase-timelock
-              is-coinbase.source  %.y
-            ==
-        $|  nnote
-        |=  note=nnote
-        ::  mining reward may only be spent 100 blocks after confirmation
-        ?&  =(coinbase-timelock timelock.note)
-            is-coinbase.source.note
+    $~  %*  .  *nnote
+          timelock            coinbase-timelock
+          is-coinbase.source  %.y
         ==
-        ::  these aren't the only conditions needed for a coinbase. we also
-        ::  need that p.source is the hash of the previous block.
-    ==
+    nnote
+  ::
+  ++  validate
+    |=  [pag=page =form]
+    ^-  ?
+    ?.  ?&  is-coinbase.source.form
+            =(p.source.form parent.pag)
+        ==
+      %.n
+    ?:  (lth height.pag first-month-coinbase-min)
+      =(first-month-coinbase-timelock timelock.form)
+    =(coinbase-timelock timelock.form)
   ::
   ::  +new: make coinbase for page. not for genesis.
   ++  new
-    |=  [pag=page =lock]
-    =/  reward=coins  (~(got z-by coinbase.pag) lock)
+    |=  [pag=page lok=lock]
+    =/  reward=coins  (~(got z-by coinbase.pag) lok)
     ^-  form
-    %*  .  *nnote
-      assets       reward
-      lock         lock
-      timelock     coinbase-timelock
-      origin-page  height.pag
-      name         (name-from-parent-hash lock parent.pag)
-    ::
-      ::  this uses the ID of the parent block to avoid a hashloop in airwalk
-      source       [parent.pag %.y]
-    ==
+    =/  =timelock
+      ?:  (lth height.pag first-month-coinbase-min)
+        first-month-coinbase-timelock
+      coinbase-timelock
+    =/  note=nnote
+      %*  .  *nnote
+        assets       reward
+        lock         lok
+        timelock     timelock
+        origin-page  height.pag
+        name         (name-from-parent-hash lok parent.pag height.pag timelock)
+      ::
+        ::  this uses the ID of the parent block to avoid a hashloop in airwalk
+        source       [parent.pag %.y]
+      ==
+    ?.  (validate pag note)
+      ~|  %invalid-coinbase
+      !!
+    note
   ::
   ::  +name-from-parent-hash: the name of a coinbase with given owner and parent block.
   ++  name-from-parent-hash
-    |=  [owners=lock parent-hash=hash]
+    |=  [owners=lock parent-hash=hash height-page=@ =timelock]
     ^-  nname
-    (new:nname owners [parent-hash %.y] coinbase-timelock)
+    (new:nname owners [parent-hash %.y] timelock)
   ::
   ++  coinbase-timelock
     ^-  timelock
-    (some [*timelock-range (new:timelock-range [(some coinbase-timelock-min) ~])])
+    `[*timelock-range (new:timelock-range [`coinbase-timelock-min ~])]
+  ::
+  ++  first-month-coinbase-timelock
+    ^-  timelock
+    `[*timelock-range (new:timelock-range [`4.383 ~])]
   ::
   ++  emission-calc
     |=  =page-number
@@ -1325,6 +1514,23 @@
     (schedule:emission `@`page-number)
   --
 ::
+++  shares
+  =<  form
+  |%
+  +$  form  $+(shares (z-map lock @))
+  ::
+  ++  validate
+    |=  =form
+    ?&  (lte ~(wyt z-by form) max-coinbase-split)
+    ::
+        %+  levy  ~(tap z-by form)
+        |=  [=lock s=@]
+        ?&  !=(s 0)
+            (validate:^lock lock)
+        ==
+    ==
+  --
+::
 ::  $coinbase-split: total number of nicks split between mining pubkeys
 ::
 ::    despite also being a (z-map lock @), this is not the same thing as .shares
@@ -1336,6 +1542,8 @@
   |%
   +$  form  (z-map lock coins)
   ::
+  ::  +new: construct a coinbase with assets and shares
+  ::    we assume $shares are already validated inside of the mining state
   ++  new
     |=  [assets=coins shares=(z-map lock @)]
     ^-  form
@@ -1395,6 +1603,25 @@
       recursion-depth  +(recursion-depth)
     ==
   ::
+  ::  +based: checks that coinbase split is in base field.
+  ::    Called by the receiver of a block. In cases where checking for validity
+  ::    costs the same amount as checking if in field, we check for validity.
+  ++  based
+    |=  =form
+    ?.  (lte ~(wyt z-by form) max-coinbase-split)
+      %|
+    %+  levy  ~(tap z-by form)
+    |=  [=lock =coins]
+    ~&  :*  %based-split-check
+            coins-not-zero+!=(0 coins)
+            coins-based+(^based coins)
+            lock-based+(based:^lock lock)
+        ==
+    ?&  !=(0 coins)
+        (^based coins)
+        (based:^lock lock)
+    ==
+  ::
   ++  hashable
     |=  =form
     ^-  hashable:tip5
@@ -1474,6 +1701,20 @@
       --
     --
   ::
+  ++  based
+    |=  =form
+    ^-  ?
+    =/  based-output-source
+      ?~  output-source.form
+        %&
+      (based:^hash p.u.output-source.form)
+    ?&  based-output-source
+        (based:lock recipient.form)
+        (based:timelock-intent timelock-intent.form)
+        (^based gift.form)
+        (based:^hash parent-hash.form)
+    ==
+  ::
   ::  +hashable: we don't include output-source since it could create a hash loop
   ++  hashable
     |=  sed=form
@@ -1537,6 +1778,13 @@
       --
     --
   ::
+  ++  based
+    |=  =form
+    ^-  ?
+    %+  levy  ~(tap z-in form)
+    |=  s=seed
+    (based:seed s)
+  ::
   ++  hashable
     |=  =form
     ^-  hashable:tip5
@@ -1631,10 +1879,10 @@
       (leaf-sequence:shape (sig-hash sen))
     ?:  =(~ signature.sen)
       %_  sen
-        signature  (some (~(put z-by *signature) pk sig))
+        signature  `(~(put z-by *signature) pk sig)
       ==
     %_  sen
-      signature  (some (~(put z-by (need signature.sen)) pk sig))
+      signature  `(~(put z-by (need signature.sen)) pk sig)
     ==
   ::
   ::  +verify: verify the .signature and each seed has correct parent-hash
@@ -1679,6 +1927,17 @@
         (~(got z-by u.signature.sen) pk)
     ==
   ::
+  ++  based
+    |=  sen=form
+    ^-  ?
+    =/  check-sig=?
+      ?~(signature.sen %& (based:signature u.signature.sen))
+    ?.  check-sig
+      %|
+    ?&  (^based fee.sen)
+        (based:seeds seeds.sen)
+    ==
+  ::
   ++  hashable
     |=  sen=form
     ^-  hashable:tip5
@@ -1792,6 +2051,11 @@
     ::  ==
     ?&(check-spend check-gifts-and-fee)
   ::
+  ++  based
+    ~/  %based
+    |=  inp=form
+    &((based:nnote note.inp) (based:spend spend.inp))
+  ::
   ++  hashable
     |=  inp=form
     ^-  hashable:tip5
@@ -1873,21 +2137,15 @@
     ^-  (unit form)
     %-  mole
     |.
-    ~|  "tx invalid"
-    ?>  (validate:^tx tx new-page-number)
-    ::
-    ::  check that adding size of tx will not cause block size to be exceeded
-    =/  new-size=size  (add size.tx-acc total-size.tx)
-    ?.  (lte new-size max-block-size)
-      ~|  "adding new transaction {<id.tx>} would exceed max block size"
-      !!
+    ?.  (validate:^tx tx new-page-number)
+      ~>  %slog.[0 leaf+"tx invalid"]  !!
     ::
     ::  process outputs
     =.  balance.tx-acc
       %+  roll  ~(val z-by outputs.tx)
       |=  [op=output bal=_balance.tx-acc]
       ?:  (~(has z-by bal) name.note.op)
-        ~|  "tx output already exists in balance"
+        ~>  %slog.[0 leaf+"tx output already exists in balance"]
         !!
       (~(put z-by bal) name.note.op note.op)
     ::
@@ -1895,8 +2153,9 @@
     =/  [tic=timelock-range tac=form]
       %+  roll  ~(val z-by inputs.tx)
       |=  [ip=input tic=timelock-range tac=_tx-acc]
-      ?.  =((some note.ip) (~(get z-by balance.tx-acc) name.note.ip))
-        ~|  "tx input does not exist in balance"  !!
+      ?.  =(`note.ip (~(get z-by balance.tx-acc) name.note.ip))
+        ~>  %slog.[0 leaf+"tx input does not exist in balance"]
+        !!
       =.  balance.tac  (~(del z-by balance.tac) name.note.ip)
       =.  fees.tac  (add fees.tac fee.spend.ip)
       =.  tic
@@ -1907,10 +2166,9 @@
       [tic tac]
     ::
     ?.  (check:timelock-range tic new-page-number)
-      ~|  "failed timelock check"  !!
+      ~>  %slog.[0 leaf+"failed timelock check"]  !!
     ::
     %_  tac
-      size  new-size
       txs   (~(put z-in txs.tac) tx)
     ==
   --

+ 1 - 1
hoon/common/zeke.hoon

@@ -1,2 +1,2 @@
 /=  ztd-eight  /common/ztd/eight
-ztd-eight
+ztd-eight

+ 137 - 45
hoon/common/ztd/eight.hoon

@@ -5,7 +5,7 @@
 |%
 +|  %types
 ::  $zerofier-cache: cache from table height -> zerofier
-+$  constraint-degrees  [boundary=@ row=@ transition=@ terminal=@]
++$  constraint-degrees  [boundary=@ row=@ transition=@ terminal=@ extra=@]
 ::  $table-to-constraint-degree: a map from table number to maximum constraint degree for that table
 +$  table-to-constraint-degree  (map @ constraint-degrees)
 ::  mp-ultra constraint along with corresponding degrees of the constraints inside
@@ -16,6 +16,7 @@
       row=(list constraint-data)
       transition=(list constraint-data)
       terminal=(list constraint-data)
+      extra=(list constraint-data)
   ==
 ::  constraint types
 +$  constraint-type    ?(%boundary %row %transition %terminal)
@@ -25,6 +26,7 @@
       row=@
       transition=@
       terminal=@
+      extra=@
   ==
 ::
 ::  $preprocess-0: preprocess with a version tag attached
@@ -37,7 +39,7 @@
 ::
 ::  $stark-config: prover+verifier parameters unrelated to a particular computation
 +$  stark-config
-  $:  conf=[log-expand-factor=_6 security-level=_100]
+  $:  conf=[log-expand-factor=_6 security-level=_50]
       prep=preprocess-0
   ==
 ::TODO this type could potentially be improved
@@ -99,6 +101,7 @@
    ==
 ::
 ++  compute-codeword-commitments
+  ~/  %compute-codeword-commitments
   |=  $:  table-marys=(list mary)
           fri-domain-len=@
           total-cols=@
@@ -167,67 +170,95 @@
       row=(list [(list @) mp-ultra])
       transition=(list [(list @) mp-ultra])
       terminal=(list [(list @) mp-ultra])
+      extra=(list [(list @) mp-ultra])
   ==
 ::
 ::  fri-deg-bound is D-1, where D is the next power of 2 greater than
 ::  the degree bounds of all composition codewords
 ++  degree-processing
-  |=  [heights=(list @) constraint-map=(map @ constraints)]
+  |=  [heights=(list @) constraint-map=(map @ constraints) is-extra=?]
   ^-  [fri-deg-bound=@ constraint-w-deg-map=(map @ constraints-w-deg)]
   =-  [(dec (bex (xeb (dec d)))) m]
   %+  roll  (range (lent heights))
   |=  [i=@ d=@ m=(map @ constraints-w-deg)]
   =/  height=@  (snag i heights)
   =/  constraints  (~(got by constraint-map) i)
-  =-  :-  :(max d d.bnd d.row d.trn d.trm)
-      (~(put by m) i [c.bnd c.row c.trn c.trm])
+  =-  :-  :(max d d.bnd d.row d.trn d.trm d.xta)
+      (~(put by m) i [c.bnd c.row c.trn c.trm c.xta])
   ::  attach composition degree to each mp & keep a running max of degrees
   ::  divided by boundary, row, transition, terminal
   :*
     ^=  bnd=[c d]
-    %^  spin  boundary.constraints  0
-    |=  [cd=constraint-data d=@]
-    =;  degrees=(list @)
-      :-  [degrees cs.cd]
-      (roll `(list @)`[d degrees] max)
-    %+  turn  degs.cd
-    |=  deg=@
-    ?:  =(height 1)  0
-    (dec (mul deg (dec height)))
+      %^  spin  boundary.constraints  0
+      |=  [cd=constraint-data d=@]
+      =;  degrees=(list @)
+        :-  [degrees cs.cd]
+        (roll `(list @)`[d degrees] max)
+      %+  turn  degs.cd
+      |=  deg=@
+      ?:  =(height 1)  0
+      (dec (mul deg (dec height)))
   ::
     ^=  row=[c d]
-    %^  spin  row.constraints  0
-    |=  [cd=constraint-data d=@]
-    =;  degrees=(list @)
-      :-  [degrees cs.cd]
-      (roll `(list @)`[d degrees] max)
-    %+  turn  degs.cd
-    |=  deg=@
-    ?:  ?|(=(height 1) =(deg 1))  0
-    (sub (mul deg (dec height)) height)
+      %^  spin  row.constraints  0
+      |=  [cd=constraint-data d=@]
+      =;  degrees=(list @)
+        :-  [degrees cs.cd]
+        (roll `(list @)`[d degrees] max)
+      %+  turn  degs.cd
+      |=  deg=@
+      ?:  ?|(=(height 1) =(deg 1))  0
+      (sub (mul deg (dec height)) height)
   ::
     ^=  trn=[c d]
-    %^  spin  transition.constraints  0
-    |=  [cd=constraint-data d=@]
-    =;  degrees=(list @)
-      :-  [degrees cs.cd]
-      (roll `(list @)`[d degrees] max)
-    %+  turn  degs.cd
-    |=(@ (mul (dec +<) (dec height)))
+      %^  spin  transition.constraints  0
+      |=  [cd=constraint-data d=@]
+      =;  degrees=(list @)
+        :-  [degrees cs.cd]
+        (roll `(list @)`[d degrees] max)
+      %+  turn  degs.cd
+      |=(@ (mul (dec +<) (dec height)))
   ::
     ^=  trm=[c d]
-    %^  spin  terminal.constraints  0
-    |=  [cd=constraint-data d=@]
-    =;  degrees=(list @)
-      :-  [degrees cs.cd]
-      (roll `(list @)`[d degrees] max)
-    %+  turn  degs.cd
-    |=  deg=@
-    ?:  =(height 1)  0
-    (dec (mul deg (dec height)))
+      %^  spin  terminal.constraints  0
+      |=  [cd=constraint-data d=@]
+      =;  degrees=(list @)
+        :-  [degrees cs.cd]
+        (roll `(list @)`[d degrees] max)
+      %+  turn  degs.cd
+      |=  deg=@
+      ?:  =(height 1)  0
+      (dec (mul deg (dec height)))
+  ::
+    ^=  xta=[c d]
+      ?.  is-extra  [~ 0]
+      %^  spin  extra.constraints  0
+      |=  [cd=constraint-data d=@]
+      =;  degrees=(list @)
+        :-  [degrees cs.cd]
+        (roll `(list @)`[d degrees] max)
+      %+  turn  degs.cd
+      |=  deg=@
+      ?:  ?|(=(height 1) =(deg 1))  0
+      (sub (mul deg (dec height)) height)
   ==
 ::
 ++  compute-composition-poly
+  ~/  %compute-composition-poly-hoon
+  |=  $:  omicrons=bpoly
+          heights=(list @)
+          tworow-trace-polys=(list bpoly)
+          constraint-map=(map @ constraints)
+          constraint-counts=(map @ constraint-counts)
+          composition-chals=(map @ bpoly)
+          chal-map=(map @ belt)
+          dyn-map=(map @ bpoly)
+          is-extra=?
+      ==
+  ^-  bpoly
+  (do-compute-composition-poly +<)
+::
+++  do-compute-composition-poly
   ~/  %compute-composition-poly
   |=  $:  omicrons=bpoly
           heights=(list @)
@@ -237,12 +268,13 @@
           composition-chals=(map @ bpoly)
           chal-map=(map @ belt)
           dyn-map=(map @ bpoly)
+          is-extra=?
       ==
   ^-  bpoly
   =/  max-height=@
     %-  bex  %-  xeb  %-  dec
     (roll heights max)
-  =/  dp  (degree-processing heights constraint-map)
+  =/  dp  (degree-processing heights constraint-map is-extra)
   |^
   =/  boundary-zerofier  (init-bpoly ~[(bneg 1) 1])          ::  f(X)=X-1
   ::
@@ -306,6 +338,20 @@
     %-  process-composition-constraints
     :*  terminal.constraints
         trace
+      ::
+        %+  ~(swag bop chals)
+          (mul 2 :(add boundary.counts row.counts transition.counts))
+        (mul 2 terminal.counts)
+      ::
+        dyns
+    ==
+  ::
+    ?.  is-extra  zero-bpoly
+    %-  bpdiv
+    :_  row-zerofier
+    %-  process-composition-constraints
+    :*  extra.constraints
+        trace
       ::
         %-  ~(slag bop chals)
         %+  mul  2
@@ -313,14 +359,15 @@
           boundary.counts
           row.counts
           transition.counts
+          terminal.counts
         ==
       ::
         dyns
     ==
+  ::
   ==
   ::
   ++  process-composition-constraints
-    ~/  %process-composition-constraints
     |=  $:  constraints=(list [(list @) mp-ultra])
             trace=bpoly
             weights=bpoly
@@ -371,6 +418,7 @@
           weights=fpoly
           omicrons=fpoly
           deep-challenge=felt
+          comp-eval-point=felt
       ==
   |^  ^-  fpoly
   =/  [acc=fpoly num=@]
@@ -382,7 +430,13 @@
       (bpoly-to-fpoly (~(snag-as-bpoly ave p) i))
     =/  omicron  (~(snag fop omicrons) i)
     =/  [first-row=fpoly num=@]    :: first row:  f(x)-f(Z)/x-Z
-      (weighted-linear-combo lis trace-openings num (fp-c deep-challenge) weights)
+      %-  weighted-linear-combo
+      :*  lis
+          trace-openings
+          num
+          (fp-c deep-challenge)
+          weights
+      ==
     =/  [second-row=fpoly num=@]   :: second row:  f(x)-f(gZ)/x-gZ
       %-  weighted-linear-combo
       :*  lis
@@ -394,6 +448,36 @@
     :_  num
     :(fpadd acc first-row second-row)
   ::
+  ::  do the same thing for the second composition poly evals
+  =/  [acc=fpoly num=@]
+    %^  zip-roll  (range (lent trace-polys))  trace-polys
+    |=  [[i=@ p=mary] acc=_acc num=_num]
+    =/  lis=(list fpoly)
+      %+  turn  (range len.array.p)
+      |=  i=@
+      (bpoly-to-fpoly (~(snag-as-bpoly ave p) i))
+    =/  omicron  (~(snag fop omicrons) i)
+    ::  add new composition poly
+    =/  [new-first-row=fpoly num=@]    :: first row:  f(x)-f(Z)/x-Z
+      %-  weighted-linear-combo
+      :*  lis
+          trace-openings
+          num
+          (fp-c comp-eval-point)
+          weights
+      ==
+    ::  second row
+    =/  [new-second-row=fpoly num=@]   :: second row:  f(x)-f(gZ)/x-gZ
+      %-  weighted-linear-combo
+      :*  lis
+          trace-openings
+          num
+          (fp-c (fmul omicron comp-eval-point))
+          weights
+      ==
+    :_  num
+    :(fpadd acc new-first-row new-second-row)
+  ::
   =/  [pieces=fpoly @]
     %-  weighted-linear-combo
     :*  composition-pieces
@@ -802,8 +886,7 @@
 --
 ~%  %pow  ..fock  ~
 |%
-++  pow-len  `@`128
-+$  tip5-hash-atom  @
+++  pow-len  `@`64
 ::
 ::  +puzzle-nock: powork puzzle
 ::
@@ -849,6 +932,14 @@
 ~%  %stark-engine  ..puzzle-nock  ~
 ::    stark-engine
 |%
+::
+::  This is a dummy arm which is only here so lib/stark/prover.hoon can use it as its parent core.
+::  Without it, jets won't work in that file.
+::
+::  !!!!!!!
+::  DONT RELEASE THIS IN THE OPEN SOURCE VERSION
+::  IT IS ALPHA FOR HOW TO GET JETS TO WORK
+::  !!!!!!!
 ++  stark-engine-jet-hook
   ~/  %stark-engine-jet-hook
   |=  n=@
@@ -915,12 +1006,13 @@
     |=  [funcs=verifier-funcs maybe-jutes=(unit jute-map)]
     ^-  constraint-degrees
     =/  jutes=jute-map  ?^(maybe-jutes (need maybe-jutes) *jute-map)
-    =-  [(snag 0 -) (snag 1 -) (snag 2 -) (snag 3 -)]
+    =-  [(snag 0 -) (snag 1 -) (snag 2 -) (snag 3 -) (snag 4 -)]
     %+  turn
       :~  (unlabel-constraints:util boundary-constraints:funcs)
           (unlabel-constraints:util row-constraints:funcs)
           (unlabel-constraints:util transition-constraints:funcs)
           (unlabel-constraints:util terminal-constraints:funcs)
+          (unlabel-constraints:util extra-constraints:funcs)
       ==
     |=  l=(list mp-ultra)
     %+  roll

+ 4 - 2
hoon/common/ztd/five.hoon

@@ -10,7 +10,8 @@
     ~/  %push
     |=  dat=proof-data
     ^-  proof
-    :+  (snoc objects dat)
+    :^    %0
+        (snoc objects dat)
       (snoc hashes (hash-hashable:tip5 (hashable-proof-data dat)))
     read-index
   ::
@@ -19,7 +20,8 @@
     ?>  (lth read-index (lent objects))
     =/  dat  (snag read-index objects)
     :-  dat
-    :+  objects
+    :^     %0
+         objects
       (snoc hashes (hash-hashable:tip5 (hashable-proof-data dat)))
     +(read-index)
   ::

+ 21 - 1
hoon/common/ztd/four.hoon

@@ -19,16 +19,35 @@
       [%comp-m p=noun-digest:tip5 num=@]  ::  composition-merk
       [%evals p=fpoly]  ::  evaluations
       [%heights p=(list @)]  ::  n, where 2^n is the number of rows
+      [%poly p=bpoly]
   ==
 ::
 +$  proof-objects  (list proof-data)
 ::
 +$  proof
-  $:  objects=proof-objects
+  $:  version=%0
+      objects=proof-objects
       hashes=(list noun-digest:tip5)
       read-index=@
   ==
 ::
++$  tip5-hash-atom  @ux
+::
+::  number of items in proof used for pow
+++  pow-items  7
+::  extract pow from proof
+++  get-pow
+  ~/  %get-pow
+  |=  p=proof
+  ^-  proof
+  p(objects (scag pow-items objects.p))
+::
+++  proof-to-pow
+  ~/  %proof-to-pow
+  |=  =proof
+  ^-  tip5-hash-atom
+  (digest-to-atom:tip5 (hash-proof (get-pow proof)))
+::
 ++  hashable-proof-objects
   ~/  %hashable-proof-objects
   |=  ps=proof-objects
@@ -80,6 +99,7 @@
     %codeword  [leaf+%codeword (hashable-fpoly:tip5 p.pd)]
     %evals     [leaf+%evals (hashable-fpoly:tip5 p.pd)]
     %terms     [leaf+%terms (hashable-bpoly:tip5 p.pd)]
+    %poly      [leaf+%poly (hashable-bpoly:tip5 p.pd)]
   ::
       %m-pathbf
     :-  leaf+%m-pathbf

+ 21 - 6
hoon/common/ztd/one.hoon

@@ -399,6 +399,14 @@
     ~/  %snoc
     |=  j=elt
     ^-  mary
+    ::  fix bunt
+    ::
+    ::  *bpoly should be [1 0x1] not [1 0x0] so it has a high bit with no data. We can't
+    ::  change the bunt right now, so this is a workaround just for snoc. Without it,
+    ::  snoccing onto *bpoly fails.
+    =.  ma
+      ?.  =(ma [1 *bpoly])  ma
+      [1 0 0x1]
     ?>  (~(fet mary-utils step.ma) j)
     :-  step.ma
     :-  +(len.array.ma)
@@ -480,11 +488,13 @@
   --  :: ave
 ::
 ++  transpose-fpolys
+  ~/  %transpose-fpolys
   |=  fpolys=mary
   ^-  mary
   (~(transpose ave fpolys) 3)
 ::
 ++  transpose-bpolys
+  ~/  %transpose-bpolys
   |=  bpolys=mary
   ^-  mary
   (~(transpose ave bpolys) 1)
@@ -962,18 +972,23 @@
     ==
   --
 ::
-::  +compute-size: computes the size in bits of a jammed noun consisting of belts
+::  +compute-size: computes the size in bits of a jammed noun
+::
+++  compute-size-jam
+  |=  n=*
+  ^-  @
+  (met 0 (jam n))
+::
+::  +compute-size-noun: computes the size in bits of a noun in unrolled form in the memory arena
 ::
-::    this is a shortcut to actually jamming a noun and counting the bits.
-::TODO can this be made tail call optimized?
-++  compute-size-belt-noun
+++  compute-size-noun
   |=  n=*
   ^-  @
   |-
   ?@  n
     ?>  (based n)  :: check that atom is a belt
-    p:(mat n)
-  (add 2 (add $(n -.n) $(n +.n)))  :: 2 bits per cell
+    64
+  (add 64 (add $(n -.n) $(n +.n)))
 ::
 +|  %bpoly
 ::  +bcan: gives the canonical leading-zero-stripped representation of p(x)

+ 6 - 0
hoon/common/ztd/seven.hoon

@@ -174,6 +174,7 @@
   ++  row-constraints         row-constraints:*verifier-funcs
   ++  transition-constraints  transition-constraints:*verifier-funcs
   ++  terminal-constraints    terminal-constraints:*verifier-funcs
+  ++  extra-constraints       extra-constraints:*verifier-funcs
   --
 ::
 ++  verifier-funcs
@@ -197,6 +198,10 @@
   ::  apply to the final row only
   ++  terminal-constraints
     *(map term mp-ultra)
+  ::
+  ::  apply to extra composition poly only
+  ++  extra-constraints
+    *(map term mp-ultra)
   --
 ::
 +|  %cores
@@ -614,6 +619,7 @@
     ++  row-constraints         row-constraints:fs
     ++  transition-constraints  transition-constraints:fs
     ++  terminal-constraints    terminal-constraints:fs
+    ++  extra-constraints       extra-constraints:fs
     --
   ::
   ::  (representing the state of the computation at each step), but rather

+ 173 - 47
hoon/common/ztd/three.hoon

@@ -109,9 +109,13 @@
   ::  mirrors bignum from flib
   ::  32 bits = 2^5 bits => bloq size of 5
   +$  bignum
-    ::  LSB order (based on result of rip)
-    ::  empty array is 0
-    [%bn p=(list u32)]
+  ::  LSB order (based on result of rip)
+  ::  empty array is 0
+  [%bn p=(list u32)]
+  ::
+  ++  validate
+    |=  bn=bignum
+    (levy p.bn is-u32:l32)
   ::
   ::  +p: Goldilocks prime, written in bignum form
   ::
@@ -322,7 +326,7 @@
 ::
 ++  tip5  ::  lib/tip5
   ~%  %tip5-lib  ..tip5  ~
-  |%
+  |_  num-rounds=_7
   +|  %user-types
   +$  noun-digest           [belt belt belt belt belt]
   +$  ten-cell              [noun-digest noun-digest]
@@ -398,6 +402,11 @@
     ^-  hashable
     mary+`mary`[%1 bp]
   ::
+  ++  hashable-felt
+    |=  f=felt
+    ^-  hashable
+    (hashable-bpoly [3 f])
+  ::
   ++  hashable-fpoly
     |=  fp=fpoly
     ^-  hashable
@@ -538,7 +547,6 @@
   ++  num-split-and-lookup  4
   ++  capacity              6
   ++  rate                  10
-  ++  num-rounds            5
   ++  max-tip5-atom  (digest-to-atom [(dec p) (dec p) (dec p) (dec p) (dec p)])
   ::
   ++  state-dyck-word
@@ -571,55 +579,124 @@
   ::  +round-constants: 5 length=16 vectors added to the state in the final layer each round
   ++  round-constants
     ::  notice melt and montify: these are in Montgomery representation
-    ^~  ^-  (list melt)
+    ^-  (list melt)
     %-  turn  :_  montify
-    ::  length = num-rounds * state-size = 80
+    ?>  ?|(=(num-rounds 5) =(num-rounds 7))
+    ?:  =(num-rounds 5)
+      ::  length = 5 * state-size = 80
+      :~
+      ::  1st round constants
+        13.630.775.303.355.457.758  16.896.927.574.093.233.874
+        10.379.449.653.650.130.495  1.965.408.364.413.093.495
+        15.232.538.947.090.185.111  15.892.634.398.091.747.074
+        3.989.134.140.024.871.768   2.851.411.912.127.730.865
+        8.709.136.439.293.758.776   3.694.858.669.662.939.734
+        12.692.440.244.315.327.141  10.722.316.166.358.076.749
+        12.745.429.320.441.639.448  17.932.424.223.723.990.421
+        7.558.102.534.867.937.463   15.551.047.435.855.531.404
+      ::  2nd round constants
+        17.532.528.648.579.384.106  5.216.785.850.422.679.555
+        15.418.071.332.095.031.847  11.921.929.762.955.146.258
+        9.738.718.993.677.019.874   3.464.580.399.432.997.147
+        13.408.434.769.117.164.050  264.428.218.649.616.431
+        4.436.247.869.008.081.381   4.063.129.435.850.804.221
+        2.865.073.155.741.120.117   5.749.834.437.609.765.994
+        6.804.196.764.189.408.435   17.060.469.201.292.988.508
+        9.475.383.556.737.206.708   12.876.344.085.611.465.020
+      ::  3rd round constants
+        13.835.756.199.368.269.249  1.648.753.455.944.344.172
+        9.836.124.473.569.258.483   12.867.641.597.107.932.229
+        11.254.152.636.692.960.595  16.550.832.737.139.861.108
+        11.861.573.970.480.733.262  1.256.660.473.588.673.495
+        13.879.506.000.676.455.136  10.564.103.842.682.358.721
+        16.142.842.524.796.397.521  3.287.098.591.948.630.584
+        685.911.471.061.284.805     5.285.298.776.918.878.023
+        18.310.953.571.768.047.354  3.142.266.350.630.002.035
+      ::  4th round constants
+        549.990.724.933.663.297     4.901.984.846.118.077.401
+        11.458.643.033.696.775.769  8.706.785.264.119.212.710
+        12.521.758.138.015.724.072  11.877.914.062.416.978.196
+        11.333.318.251.134.523.752  3.933.899.631.278.608.623
+        16.635.128.972.021.157.924  10.291.337.173.108.950.450
+        4.142.107.155.024.199.350   16.973.934.533.787.743.537
+        11.068.111.539.125.175.221  17.546.769.694.830.203.606
+        5.315.217.744.825.068.993   4.609.594.252.909.613.081
+      ::  5th round constants
+        3.350.107.164.315.270.407   17.715.942.834.299.349.177
+        9.600.609.149.219.873.996   12.894.357.635.820.003.949
+        4.597.649.658.040.514.631   7.735.563.950.920.491.847
+        1.663.379.455.870.887.181   13.889.298.103.638.829.706
+        7.375.530.351.220.884.434   3.502.022.433.285.269.151
+        9.231.805.330.431.056.952   9.252.272.755.288.523.725
+        10.014.268.662.326.746.219  15.565.031.632.950.843.234
+        1.209.725.273.521.819.323   6.024.642.864.597.845.108
+      ==
+    ::
+    ::  length = 7 * state-size = 112
     :~
     ::  1st round constants
-      13.630.775.303.355.457.758  16.896.927.574.093.233.874
-      10.379.449.653.650.130.495  1.965.408.364.413.093.495
-      15.232.538.947.090.185.111  15.892.634.398.091.747.074
-      3.989.134.140.024.871.768   2.851.411.912.127.730.865
-      8.709.136.439.293.758.776   3.694.858.669.662.939.734
-      12.692.440.244.315.327.141  10.722.316.166.358.076.749
-      12.745.429.320.441.639.448  17.932.424.223.723.990.421
-      7.558.102.534.867.937.463   15.551.047.435.855.531.404
+      1.332.676.891.236.936.200   16.607.633.045.354.064.669
+      12.746.538.998.793.080.786  15.240.351.333.789.289.931
+      10.333.439.796.058.208.418  986.873.372.968.378.050
+      153.505.017.314.310.505     703.086.547.770.691.416
+      8.522.628.845.961.587.962   1.727.254.290.898.686.320
+      199.492.491.401.196.126     2.969.174.933.639.985.366
+      1.607.536.590.362.293.391   16.971.515.075.282.501.568
+      15.401.316.942.841.283.351  14.178.982.151.025.681.389
     ::  2nd round constants
-      17.532.528.648.579.384.106  5.216.785.850.422.679.555
-      15.418.071.332.095.031.847  11.921.929.762.955.146.258
-      9.738.718.993.677.019.874   3.464.580.399.432.997.147
-      13.408.434.769.117.164.050  264.428.218.649.616.431
-      4.436.247.869.008.081.381   4.063.129.435.850.804.221
-      2.865.073.155.741.120.117   5.749.834.437.609.765.994
-      6.804.196.764.189.408.435   17.060.469.201.292.988.508
-      9.475.383.556.737.206.708   12.876.344.085.611.465.020
+      2.916.963.588.744.282.587   5.474.267.501.391.258.599
+      5.350.367.839.445.462.659   7.436.373.192.934.779.388
+      12.563.531.800.071.493.891  12.265.318.129.758.141.428
+      6.524.649.031.155.262.053   1.388.069.597.090.660.214
+      3.049.665.785.814.990.091   5.225.141.380.721.656.276
+      10.399.487.208.361.035.835  6.576.713.996.114.457.203
+      12.913.805.829.885.867.278  10.299.910.245.954.679.423
+      12.980.779.960.345.402.499  593.670.858.850.716.490
     ::  3rd round constants
-      13.835.756.199.368.269.249  1.648.753.455.944.344.172
-      9.836.124.473.569.258.483   12.867.641.597.107.932.229
-      11.254.152.636.692.960.595  16.550.832.737.139.861.108
-      11.861.573.970.480.733.262  1.256.660.473.588.673.495
-      13.879.506.000.676.455.136  10.564.103.842.682.358.721
-      16.142.842.524.796.397.521  3.287.098.591.948.630.584
-      685.911.471.061.284.805     5.285.298.776.918.878.023
-      18.310.953.571.768.047.354  3.142.266.350.630.002.035
+      12.184.128.243.723.146.967  1.315.341.360.419.235.257
+      9.107.195.871.057.030.023   4.354.141.752.578.294.067
+      8.824.457.881.527.486.794   14.811.586.928.506.712.910
+      7.768.837.314.956.434.138   2.807.636.171.572.954.860
+      9.487.703.495.117.094.125   13.452.575.580.428.891.895
+      14.689.488.045.617.615.844  16.144.091.782.672.017.853
+      15.471.922.440.568.867.245  17.295.382.518.415.944.107
+      15.054.306.047.726.632.486  5.708.955.503.115.886.019
     ::  4th round constants
-      549.990.724.933.663.297     4.901.984.846.118.077.401
-      11.458.643.033.696.775.769  8.706.785.264.119.212.710
-      12.521.758.138.015.724.072  11.877.914.062.416.978.196
-      11.333.318.251.134.523.752  3.933.899.631.278.608.623
-      16.635.128.972.021.157.924  10.291.337.173.108.950.450
-      4.142.107.155.024.199.350   16.973.934.533.787.743.537
-      11.068.111.539.125.175.221  17.546.769.694.830.203.606
-      5.315.217.744.825.068.993   4.609.594.252.909.613.081
+      9.596.017.237.020.520.842   16.520.851.172.964.236.909
+      8.513.472.793.890.943.175   8.503.326.067.026.609.602
+      9.402.483.918.549.940.854   8.614.816.312.698.982.446
+      7.744.830.563.717.871.780   14.419.404.818.700.162.041
+      8.090.742.384.565.069.824   15.547.662.568.163.517.559
+      17.314.710.073.626.307.254  10.008.393.716.631.058.961
+      14.480.243.402.290.327.574  13.569.194.973.291.808.551
+      10.573.516.815.088.946.209  15.120.483.436.559.336.219
     ::  5th round constants
-      3.350.107.164.315.270.407   17.715.942.834.299.349.177
-      9.600.609.149.219.873.996   12.894.357.635.820.003.949
-      4.597.649.658.040.514.631   7.735.563.950.920.491.847
-      1.663.379.455.870.887.181   13.889.298.103.638.829.706
-      7.375.530.351.220.884.434   3.502.022.433.285.269.151
-      9.231.805.330.431.056.952   9.252.272.755.288.523.725
-      10.014.268.662.326.746.219  15.565.031.632.950.843.234
-      1.209.725.273.521.819.323   6.024.642.864.597.845.108
+      3.515.151.310.595.301.563   1.095.382.462.248.757.907
+      5.323.307.938.514.209.350   14.204.542.692.543.834.582
+      12.448.773.944.668.684.656  13.967.843.398.310.696.452
+      14.838.288.394.107.326.806  13.718.313.940.616.442.191
+      15.032.565.440.414.177.483  13.769.903.572.116.157.488
+      17.074.377.440.395.071.208  16.931.086.385.239.297.738
+      8.723.550.055.169.003.617   590.842.605.971.518.043
+      16.642.348.030.861.036.090  10.708.719.298.241.282.592
+    ::  6th round constants
+      12.766.914.315.707.517.909  11.780.889.552.403.245.587
+      113.183.285.481.780.712     9.019.899.125.655.375.514
+      3.300.264.967.390.964.820   12.802.381.622.653.377.935
+      891.063.765.000.023.873     15.939.045.541.699.412.539
+      3.240.223.189.948.727.743   4.087.221.142.360.949.772
+      10.980.466.041.788.253.952  18.199.914.337.033.135.244
+      7.168.108.392.363.190.150   16.860.278.046.098.150.740
+      13.088.202.265.571.714.855  4.712.275.036.097.525.581
+    ::  7th round constants
+      16.338.034.078.141.228.133  1.455.012.125.527.134.274
+      5.024.057.780.895.012.002   9.289.161.311.673.217.186
+      9.401.110.072.402.537.104   11.919.498.251.456.187.748
+      4.173.156.070.774.045.271   15.647.643.457.869.530.627
+      15.642.078.237.964.257.476  1.405.048.341.078.324.037
+      3.059.193.199.283.698.832   1.605.012.781.983.592.984
+      7.134.876.918.849.821.827   5.796.994.175.286.958.720
+      7.251.651.436.095.127.661   4.565.856.221.886.323.991
     ==
   ::
   ::  +mds-matrix-first-column: the mds matrix is determined by any column
@@ -1198,6 +1275,17 @@
   |%
   ::  f6lt: element of F_p[x]/(x^6 - 7)
   +$  f6lt     [a0=belt a1=belt a2=belt a3=belt a4=belt a5=belt]
+  ++  f6lt-based
+    |=  f=f6lt
+    =+  [a=belt b=belt c=belt d=belt e=belt f=belt]=f
+    ?&  (based a)
+        (based b)
+        (based c)
+        (based d)
+        (based e)
+        (based f)
+    ==
+
   ++  f6lt-dyck-word
     ^-  (list @)
     ~[0 1 0 1 0 1 0 1 0 1]
@@ -1360,6 +1448,8 @@
   ++  f6-inv
     |=  f=f6lt
     ^-  f6lt
+    ?:  =(f f6-zero)
+      ~|('+f6-inv: zero point has no inverse' !!)
     =/  eucl
       %+  bpegcd
         (init-bpoly (f6lt-to-list f))
@@ -1370,6 +1460,7 @@
     (weld - (reap (sub 6 (lent -)) 0))
   ::
   ++  f6-div
+    ~/  %f6-div
     |=  [f1=f6lt f2=f6lt]
     ^-  f6lt
     (f6-mul f1 (f6-inv f2))
@@ -1413,6 +1504,14 @@
     ::    projective space. By the projective equivalence relation, this
     ::    representation is not unique.
     +$  a-pt  [x=f6lt y=f6lt inf=?]
+    ::
+    ::  +a-pt-based: checks if elements in a-pt are in base field.
+    ++  a-pt-based
+      |=  a-pt
+      ?&  (f6lt-based x)
+          (f6lt-based y)
+      ==
+    ::
     ++  a-pt-dyck-word
       ^~  ^-  (list @)
       (snoc (weld [0 f6lt-dyck-word] [1 0 f6lt-dyck-word]) 1)
@@ -1506,6 +1605,7 @@
       ::
       ::    This is the action of Z on cheetah as an abelian group.
       ++  ch-scal
+        ~/  %ch-scal
         |=  [n=@ p=a-pt]
         ^-  a-pt
         =/  acc  a-id
@@ -1660,6 +1760,21 @@
     +$  sig  t8
     +$  chal  t8
     ::
+    ++  based
+      |=  =t8
+      ^-  ?
+      =+  [a=@ux b=@ux c=@ux d=@ux e=@ux f=@ux g=@ux h=@ux]=t8
+      ?&  (^based a)
+          (^based b)
+          (^based c)
+          (^based d)
+          (^based e)
+          (^based f)
+          (^based g)
+          (^based h)
+      ==
+    ::
+    ::
     ++  atom-to-t8
       |=  a=@ux
       ^-  t8
@@ -1744,6 +1859,11 @@
     [%tree (hash-ten-cell:tip5 h.l h.r) l r]
   ::
   ++  build-merk-heap
+    ~/  %build-merk-heap-hoon
+    |=  m=mary
+    (do-build-merk-heap +<)
+  ::
+  ++  do-build-merk-heap
     ~/  %build-merk-heap
     |=  m=mary
     ^-  [depth=@ heap=merk-heap]
@@ -1785,6 +1905,12 @@
     --
   ::
   ++  bp-build-merk-heap
+    ~/  %bp-build-merk-heap-hoon
+    |=  m=mary
+    ^-  (pair @ merk-heap)
+    (do-bp-build-merk-heap +<)
+  ::
+  ++  do-bp-build-merk-heap
     ~/  %bp-build-merk-heap
     |=  m=mary
     ^-  (pair @ merk-heap)

BIN
hoon/constraints/constraints-0.jam


+ 5 - 2
hoon/dat/constraints.hoon

@@ -28,12 +28,13 @@
   ++  compute-constraint-degree
     |=  funcs=verifier-funcs:z
     ^-  constraint-degrees:z
-    =-  [(snag 0 -) (snag 1 -) (snag 2 -) (snag 3 -)]
+    =-  [(snag 0 -) (snag 1 -) (snag 2 -) (snag 3 -) (snag 4 -)]
     %+  turn
       :~  (unlabel-constraints:constraint-util:z boundary-constraints:funcs)
           (unlabel-constraints:constraint-util:z row-constraints:funcs)
           (unlabel-constraints:constraint-util:z transition-constraints:funcs)
           (unlabel-constraints:constraint-util:z terminal-constraints:funcs)
+          (unlabel-constraints:constraint-util:z extra-constraints:funcs)
       ==
     |=  l=(list mp-ultra:z)
     %+  roll
@@ -55,6 +56,7 @@
         (build-constraint-data row-constraints:funcs)
         (build-constraint-data transition-constraints:funcs)
         (build-constraint-data terminal-constraints:funcs)
+        (build-constraint-data extra-constraints:funcs)
     ==
     ::
     ++  build-constraint-data
@@ -78,6 +80,7 @@
         (count (unlabel-constraints:constraint-util:z row-constraints:funcs))
         (count (unlabel-constraints:constraint-util:z transition-constraints:funcs))
         (count (unlabel-constraints:constraint-util:z terminal-constraints:funcs))
+        (count (unlabel-constraints:constraint-util:z extra-constraints:funcs))
     ==
     ::
     ++  count
@@ -92,4 +95,4 @@
       ==
     --
   --  :: |^
---  :: %pre
+--

+ 8 - 0
hoon/dat/softed-constraints.hoon

@@ -0,0 +1,8 @@
+/=  *  /common/zeke
+/*  constraints-0  %jam  /constraints/constraints-0/jam
+^-  preprocess-0
+=/  cue=*  (cue q.constraints-0)
+=/  soft  ((soft preprocess-0) cue)
+?~  soft
+  ~&  "fatal: failed to soft constraints!"  !!
+u.soft