Преглед изворни кода

drk is back in the menu boys

skoupidi пре 2 година
родитељ
комит
3062597fca

+ 3 - 0
.gitignore

@@ -16,6 +16,9 @@
 /bin/darkfi-mmproxy/darkfi-mmproxy
 /darkfi-mmproxy
 
+/bin/drk/drk
+/drk
+
 /bin/darkirc/darkirc
 /darkirc
 

+ 392 - 5
Cargo.lock

@@ -131,6 +131,28 @@ version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
 
+[[package]]
+name = "alsa"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2562ad8dcf0f789f65c6fdaad8a8a9708ed6b488e649da28c01656ad66b8b47"
+dependencies = [
+ "alsa-sys",
+ "bitflags 1.3.2",
+ "libc",
+ "nix 0.24.3",
+]
+
+[[package]]
+name = "alsa-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
 [[package]]
 name = "amplify"
 version = "4.5.0"
@@ -823,6 +845,26 @@ dependencies = [
  "which",
 ]
 
+[[package]]
+name = "bindgen"
+version = "0.69.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4c69fae65a523209d34240b60abe0c42d33d1045d445c0839d8a4894a736e2d"
+dependencies = [
+ "bitflags 2.4.1",
+ "cexpr",
+ "clang-sys",
+ "lazy_static",
+ "lazycell",
+ "peeking_take_while",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn 2.0.48",
+]
+
 [[package]]
 name = "bit-set"
 version = "0.5.3"
@@ -1082,6 +1124,12 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
 [[package]]
 name = "cexpr"
 version = "0.6.0"
@@ -1261,6 +1309,16 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 
+[[package]]
+name = "combine"
+version = "4.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
+dependencies = [
+ "bytes 1.5.0",
+ "memchr",
+]
+
 [[package]]
 name = "concurrent-queue"
 version = "2.4.0"
@@ -1389,6 +1447,26 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "coreaudio-rs"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation-sys",
+ "coreaudio-sys",
+]
+
+[[package]]
+name = "coreaudio-sys"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f01585027057ff5f0a5bf276174ae4c1594a2c5bde93d5f46a016d76270f5a9"
+dependencies = [
+ "bindgen 0.69.2",
+]
+
 [[package]]
 name = "corosensei"
 version = "0.1.4"
@@ -1402,6 +1480,31 @@ dependencies = [
  "windows-sys 0.33.0",
 ]
 
+[[package]]
+name = "cpal"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c"
+dependencies = [
+ "alsa",
+ "core-foundation-sys",
+ "coreaudio-rs",
+ "dasp_sample",
+ "jni 0.19.0",
+ "js-sys",
+ "libc",
+ "mach2",
+ "ndk",
+ "ndk-context",
+ "oboe",
+ "once_cell",
+ "parking_lot 0.12.1",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "windows",
+]
+
 [[package]]
 name = "cpufeatures"
 version = "0.2.12"
@@ -1699,7 +1802,7 @@ version = "3.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b"
 dependencies = [
- "nix",
+ "nix 0.27.1",
  "windows-sys 0.52.0",
 ]
 
@@ -2260,6 +2363,12 @@ dependencies = [
  "num-order",
 ]
 
+[[package]]
+name = "dasp_sample"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
+
 [[package]]
 name = "data-encoding"
 version = "2.5.0"
@@ -2558,6 +2667,33 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
 
+[[package]]
+name = "drk"
+version = "0.4.1"
+dependencies = [
+ "blake3 1.5.0",
+ "bs58",
+ "darkfi",
+ "darkfi-sdk",
+ "darkfi-serial",
+ "darkfi_dao_contract",
+ "darkfi_money_contract",
+ "easy-parallel",
+ "log",
+ "prettytable-rs",
+ "rand 0.8.5",
+ "rodio",
+ "rusqlite",
+ "serde",
+ "signal-hook",
+ "signal-hook-async-std",
+ "simplelog",
+ "smol",
+ "structopt",
+ "structopt-toml",
+ "url",
+]
+
 [[package]]
 name = "dwrote"
 version = "0.11.0"
@@ -3811,6 +3947,40 @@ version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
 
+[[package]]
+name = "jni"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
+dependencies = [
+ "cesu8",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "jni"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c"
+dependencies = [
+ "cesu8",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
 [[package]]
 name = "jobserver"
 version = "0.1.27"
@@ -4023,6 +4193,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "mach2"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "matchers"
 version = "0.1.0"
@@ -4118,6 +4297,26 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
+[[package]]
+name = "minimp3-sys"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e21c73734c69dc95696c9ed8926a2b393171d98b3f5f5935686a26a487ab9b90"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "minimp3_fixed"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42b0f14e7e75da97ae396c2656b10262a3d4afa2ec98f35795630eff0c8b951b"
+dependencies = [
+ "minimp3-sys",
+ "slice-ring-buffer",
+ "thiserror",
+]
+
 [[package]]
 name = "miniz_oxide"
 version = "0.7.1"
@@ -4149,6 +4348,46 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389"
 
+[[package]]
+name = "ndk"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"
+dependencies = [
+ "bitflags 1.3.2",
+ "jni-sys",
+ "ndk-sys",
+ "num_enum 0.5.11",
+ "raw-window-handle",
+ "thiserror",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.4.1+23.1.7779620"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "nix"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if 1.0.0",
+ "libc",
+]
+
 [[package]]
 name = "nix"
 version = "0.27.1"
@@ -4208,6 +4447,17 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "num-derive"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "num-integer"
 version = "0.1.45"
@@ -4265,13 +4515,34 @@ dependencies = [
  "libm",
 ]
 
+[[package]]
+name = "num_enum"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
+dependencies = [
+ "num_enum_derive 0.5.11",
+]
+
 [[package]]
 name = "num_enum"
 version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
 dependencies = [
- "num_enum_derive",
+ "num_enum_derive 0.7.2",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
+dependencies = [
+ "proc-macro-crate 1.3.1",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -4304,6 +4575,29 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "oboe"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8868cc237ee02e2d9618539a23a8d228b9bb3fc2e7a5b11eed3831de77c395d0"
+dependencies = [
+ "jni 0.20.0",
+ "ndk",
+ "ndk-context",
+ "num-derive",
+ "num-traits",
+ "oboe-sys",
+]
+
+[[package]]
+name = "oboe-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f44155e7fb718d3cfddcf70690b2b51ac4412f347cd9e4fbe511abe9cd7b5f2"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "oid-registry"
 version = "0.6.1"
@@ -5161,10 +5455,16 @@ name = "randomx"
 version = "1.1.11"
 source = "git+https://github.com/darkrenaissance/RandomX#65c0b322edf5ab66fad53bd0d163b7a2e9c63125"
 dependencies = [
- "bindgen",
+ "bindgen 0.66.1",
  "bitflags 1.3.2",
 ]
 
+[[package]]
+name = "raw-window-handle"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
+
 [[package]]
 name = "rayon"
 version = "1.8.0"
@@ -5378,6 +5678,16 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "rodio"
+version = "0.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b1bb7b48ee48471f55da122c0044fcc7600cfcc85db88240b89cb832935e611"
+dependencies = [
+ "cpal",
+ "minimp3_fixed",
+]
+
 [[package]]
 name = "routefinder"
 version = "0.4.0"
@@ -6098,6 +6408,17 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
 
+[[package]]
+name = "slice-ring-buffer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7081c7e452cb62f5f0c32edd4e265391bdbb23e90905df8bb88a23d3b5166b77"
+dependencies = [
+ "libc",
+ "mach2",
+ "winapi",
+]
+
 [[package]]
 name = "slotmap"
 version = "1.0.7"
@@ -7223,7 +7544,7 @@ dependencies = [
  "humantime",
  "humantime-serde",
  "itertools 0.12.0",
- "num_enum",
+ "num_enum 0.7.2",
  "pin-project",
  "postage",
  "rand 0.8.5",
@@ -7473,7 +7794,7 @@ dependencies = [
  "hex",
  "humantime",
  "itertools 0.12.0",
- "num_enum",
+ "num_enum 0.7.2",
  "rand 0.8.5",
  "serde",
  "signature 1.6.4",
@@ -8393,6 +8714,15 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
 [[package]]
 name = "windows-core"
 version = "0.52.0"
@@ -8433,6 +8763,21 @@ dependencies = [
  "windows-targets 0.52.0",
 ]
 
+[[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"
@@ -8463,6 +8808,12 @@ dependencies = [
  "windows_x86_64_msvc 0.52.0",
 ]
 
+[[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"
@@ -8481,6 +8832,12 @@ version = "0.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807"
 
+[[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"
@@ -8499,6 +8856,12 @@ version = "0.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e"
 
+[[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"
@@ -8517,6 +8880,12 @@ version = "0.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0"
 
+[[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"
@@ -8535,6 +8904,12 @@ version = "0.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784"
 
+[[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"
@@ -8547,6 +8922,12 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
 
+[[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"
@@ -8565,6 +8946,12 @@ version = "0.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa"
 
+[[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"

+ 1 - 2
Cargo.toml

@@ -23,8 +23,7 @@ members = [
     #"bin/darkfid",
     "bin/darkfid2",
     "bin/darkfi-mmproxy",
-    #"bin/drk",
-    #"bin/drk2",
+    "bin/drk",
     #"bin/faucetd",
     #"bin/fud/fu",
     #"bin/fud/fud",

+ 7 - 0
Makefile

@@ -60,6 +60,13 @@ darkfi-mmproxy:
 		RUST_TARGET="$(RUST_TARGET)" \
 		RUSTFLAGS="$(RUSTFLAGS)"
 
+drk: contracts
+	$(MAKE) -C bin/$@ \
+		PREFIX="$(PREFIX)" \
+		CARGO="$(CARGO)" \
+		RUST_TARGET="$(RUST_TARGET)" \
+		RUSTFLAGS="$(RUSTFLAGS)"
+
 darkirc: zkas
 	$(MAKE) -C bin/$@ \
 		PREFIX="$(PREFIX)" \

+ 22 - 14
bin/drk/Cargo.toml

@@ -9,23 +9,31 @@ license = "AGPL-3.0-only"
 edition = "2021"
 
 [dependencies]
-anyhow = "1.0.79"
-async-std = {version = "1.12.0", features = ["attributes"]}
+# Darkfi
+darkfi = {path = "../../", features = ["async-daemonize", "bs58", "rpc"]}
+darkfi_money_contract = {path = "../../src/contract/money", features = ["no-entrypoint", "client"]}
+darkfi_dao_contract = {path = "../../src/contract/dao", features = ["no-entrypoint", "client"]}
+darkfi-sdk = {path = "../../src/sdk", features = ["async"]}
+darkfi-serial = {path = "../../src/serial"}
+
+# Misc
 blake3 = "1.5.0"
 bs58 = "0.5.0"
-clap = {version = "4.4.14", features = ["derive"]}
-clap_complete = "4.4.6"
-darkfi = {path = "../../", features = ["blockchain", "rpc", "util", "wallet"]}
-darkfi-sdk = {path = "../../src/sdk"}
-darkfi-serial = {path = "../../src/serial", features = ["derive", "crypto"]}
-darkfi-money-contract = {path = "../../src/contract/money", features = ["no-entrypoint", "client"]}
-darkfi-dao-contract = {path = "../../src/contract/dao", features = ["no-entrypoint", "client"]}
+log = "0.4.20"
 prettytable-rs = "0.10.0"
 rand = "0.8.5"
-serde_json = "1.0.111"
-smol = "1.3.0"
-simplelog = "0.12.1"
+rodio = {version = "0.17.3", default-features = false, features = ["minimp3"]}
+rusqlite = {version = "0.30.0", features = ["sqlcipher"]}
+url = "2.5.0"
+
+# Daemon
+easy-parallel = "3.3.1"
 signal-hook-async-std = "0.2.2"
 signal-hook = "0.3.17"
-url = "2.5.0"
-rodio = {version = "0.17.3", default-features = false, features = ["minimp3"]}
+simplelog = "0.12.1"
+smol = "1.3.0"
+
+# Argument parsing
+serde = {version = "1.0.195", features = ["derive"]}
+structopt = "0.3.26"
+structopt-toml = "0.5.1"

+ 0 - 0
bin/drk2/Makefile → bin/drk/Makefile


+ 0 - 4
bin/drk/TODO

@@ -1,4 +0,0 @@
-* Spent coins can be tracked through nullifiers
-  * So in the wallet, once we broadcast a tx, we can spend a coin
-  * Also extend the wallet.sql schema to support both confirmed and
-    unconfirmed coins (use tx hash for that like DAO does)

+ 0 - 0
bin/drk2/drk_config.toml → bin/drk/drk_config.toml


+ 392 - 10
bin/drk/src/cli_util.rs

@@ -15,17 +15,17 @@
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-use std::{io::Cursor, process::exit};
+use std::{io::Cursor, process::exit, str::FromStr};
 
-use darkfi::{
-    util::{async_util::sleep, parse::decode_base10},
-    Result,
-};
-use darkfi_sdk::crypto::TokenId;
 use rodio::{source::Source, Decoder, OutputStream};
+use structopt_toml::clap::{App, Arg, Shell, SubCommand};
+
+use darkfi::{cli_desc, system::sleep, util::parse::decode_base10, Error, Result};
+use darkfi_sdk::crypto::TokenId;
 
-use super::Drk;
+use crate::{money::BALANCE_BASE10_DECIMALS, Drk};
 
+/// Auxiliary function to parse provided string into a values pair.
 pub fn parse_value_pair(s: &str) -> Result<(u64, u64)> {
     let v: Vec<&str> = s.split(':').collect();
     if v.len() != 2 {
@@ -33,9 +33,8 @@ pub fn parse_value_pair(s: &str) -> Result<(u64, u64)> {
         exit(1);
     }
 
-    // TODO: We shouldn't be hardcoding everything to 8 decimals.
-    let val0 = decode_base10(v[0], 8, true);
-    let val1 = decode_base10(v[1], 8, true);
+    let val0 = decode_base10(v[0], BALANCE_BASE10_DECIMALS, true);
+    let val1 = decode_base10(v[1], BALANCE_BASE10_DECIMALS, true);
 
     if val0.is_err() || val1.is_err() {
         eprintln!("Invalid value pair. Use a pair such as 13.37:11.0");
@@ -45,6 +44,7 @@ pub fn parse_value_pair(s: &str) -> Result<(u64, u64)> {
     Ok((val0.unwrap(), val1.unwrap()))
 }
 
+/// Auxiliary function to parse provided string into a tokens pair.
 pub async fn parse_token_pair(drk: &Drk, s: &str) -> Result<(TokenId, TokenId)> {
     let v: Vec<&str> = s.split(':').collect();
     if v.len() != 2 {
@@ -85,3 +85,385 @@ pub async fn kaching() {
 
     sleep(2).await;
 }
+
+/// Auxiliary function to generate provided shell completions.
+pub fn generate_completions(shell: &str) -> Result<()> {
+    // Sub-commands
+
+    // Kaching
+    let kaching = SubCommand::with_name("kaching").about("Fun");
+
+    // Ping
+    let ping =
+        SubCommand::with_name("ping").about("Send a ping request to the darkfid RPC endpoint");
+
+    // Completions
+    let shell_arg = Arg::with_name("shell").help("The Shell you want to generate script for");
+
+    let completions = SubCommand::with_name("completions")
+        .about("Generate a SHELL completion script and print to stdout")
+        .arg(shell_arg);
+
+    // Wallet
+    let initialize =
+        Arg::with_name("initialize").long("initialize").help("Initialize wallet database");
+
+    let keygen =
+        Arg::with_name("keygen").long("keygen").help("Generate a new keypair in the wallet");
+
+    let balance =
+        Arg::with_name("balance").long("balance").help("Query the wallet for known balances");
+
+    let address =
+        Arg::with_name("address").long("address").help("Get the default address in the wallet");
+
+    let addresses =
+        Arg::with_name("addresses").long("addresses").help("Print all the addresses in the wallet");
+
+    let default_address = Arg::with_name("default-address")
+        .long("default-address")
+        .takes_value(true)
+        .help("Set the default address in the wallet");
+
+    let secrets =
+        Arg::with_name("secrets").long("secrets").help("Print all the secret keys from the wallet");
+
+    let import_secrets = Arg::with_name("import-secrets")
+        .long("import-secrets")
+        .help("Import secret keys from stdin into the wallet, separated by newlines");
+
+    let tree = Arg::with_name("tree").long("tree").help("Print the Merkle tree in the wallet");
+
+    let coins = Arg::with_name("coins").long("coins").help("Print all the coins in the wallet");
+
+    let wallet = SubCommand::with_name("wallet").about("Wallet operations").args(&vec![
+        initialize,
+        keygen,
+        balance,
+        address,
+        addresses,
+        default_address,
+        secrets,
+        import_secrets,
+        tree,
+        coins,
+    ]);
+
+    // Unspend
+    let coin = Arg::with_name("coin").help("base58-encoded coin to mark as unspent");
+
+    let unspend = SubCommand::with_name("unspend").about("Unspend a coin").arg(coin);
+
+    // Transfer
+    let amount = Arg::with_name("amount").help("Amount to send");
+
+    let token = Arg::with_name("token").help("Token ID to send");
+
+    let recipient = Arg::with_name("recipient").help("Recipient address");
+
+    let transfer = SubCommand::with_name("transfer")
+        .about("Create a payment transaction")
+        .args(&vec![amount, token, recipient]);
+
+    // Otc
+    let value_pair = Arg::with_name("value-pair")
+        .short("v")
+        .long("value-pair")
+        .takes_value(true)
+        .help("Value pair to send:recv (11.55:99.42)");
+
+    let token_pair = Arg::with_name("token-pair")
+        .short("t")
+        .long("token-pair")
+        .takes_value(true)
+        .help("Token pair to send:recv (f00:b4r)");
+
+    let init = SubCommand::with_name("init")
+        .about("Initialize the first half of the atomic swap")
+        .args(&vec![value_pair, token_pair]);
+
+    let join =
+        SubCommand::with_name("join").about("Build entire swap tx given the first half from stdin");
+
+    let inspect = SubCommand::with_name("inspect")
+        .about("Inspect a swap half or the full swap tx from stdin");
+
+    let sign = SubCommand::with_name("sign")
+        .about("Sign a transaction given from stdin as the first-half");
+
+    let otc = SubCommand::with_name("otc")
+        .about("OTC atomic swap")
+        .subcommands(vec![init, join, inspect, sign]);
+
+    // Inspect
+    let inspect = SubCommand::with_name("inspect").about("Inspect a transaction from stdin");
+
+    // Broadcast
+    let broadcast =
+        SubCommand::with_name("broadcast").about("Read a transaction from stdin and broadcast it");
+
+    // Subscribe
+    let subscribe = SubCommand::with_name("subscribe").about(
+        "This subscription will listen for incoming blocks from darkfid and look \
+                    through their transactions to see if there's any that interest us. \
+                    With `drk` we look at transactions calling the money contract so we can \
+                    find coins sent to us and fill our wallet with the necessary metadata.",
+    );
+
+    // DAO
+    let proposer_limit = Arg::with_name("proposer-limit")
+        .help("The minimum amount of governance tokens needed to open a proposal for this DAO");
+
+    let quorum = Arg::with_name("quorum")
+        .help("Minimal threshold of participating total tokens needed for a proposal to pass");
+
+    let approval_ratio = Arg::with_name("approval-ratio")
+        .help("The ratio of winning votes/total votes needed for a proposal to pass (2 decimals)");
+
+    let gov_token_id = Arg::with_name("gov-token-id").help("DAO's governance token ID");
+
+    let create = SubCommand::with_name("create").about("Create DAO parameters").args(&vec![
+        proposer_limit,
+        quorum,
+        approval_ratio,
+        gov_token_id,
+    ]);
+
+    let view = SubCommand::with_name("view").about("View DAO data from stdin");
+
+    let dao_name = Arg::with_name("dao-name").help("Named identifier for the DAO");
+
+    let import =
+        SubCommand::with_name("import").about("Import DAO data from stdin").args(&vec![dao_name]);
+
+    let dao_alias = Arg::with_name("dao-alias").help("Numeric identifier for the DAO (optional)");
+
+    let list = SubCommand::with_name("list")
+        .about("List imported DAOs (or info about a specific one)")
+        .args(&vec![dao_alias]);
+
+    let dao_alias = Arg::with_name("dao-alias").help("Name or numeric identifier for the DAO");
+
+    let balance = SubCommand::with_name("balance")
+        .about("Show the balance of a DAO")
+        .args(&vec![dao_alias.clone()]);
+
+    let mint = SubCommand::with_name("mint")
+        .about("Mint an imported DAO on-chain")
+        .args(&vec![dao_alias.clone()]);
+
+    let recipient =
+        Arg::with_name("recipient").help("Pubkey to send tokens to with proposal success");
+
+    let amount = Arg::with_name("amount").help("Amount to send from DAO with proposal success");
+
+    let token = Arg::with_name("token").help("Token ID to send from DAO with proposal success");
+
+    let propose = SubCommand::with_name("propose")
+        .about("Create a proposal for a DAO")
+        .args(&vec![dao_alias.clone(), recipient, amount, token]);
+
+    let proposals = SubCommand::with_name("proposals")
+        .about("List DAO proposals")
+        .args(&vec![dao_alias.clone()]);
+
+    let proposal_id = Arg::with_name("proposal-id").help("Numeric identifier for the proposal");
+
+    let proposal = SubCommand::with_name("proposal")
+        .about("View a DAO proposal data")
+        .args(&vec![dao_alias.clone(), proposal_id.clone()]);
+
+    let vote = Arg::with_name("vote").help("Vote (0 for NO, 1 for YES)");
+
+    let vote_weight =
+        Arg::with_name("vote-weight").help("Vote weight (amount of governance tokens)");
+
+    let vote = SubCommand::with_name("vote").about("Vote on a given proposal").args(&vec![
+        dao_alias.clone(),
+        proposal_id.clone(),
+        vote,
+        vote_weight,
+    ]);
+
+    let exec = SubCommand::with_name("exec")
+        .about("Execute a DAO proposal")
+        .args(&vec![dao_alias, proposal_id]);
+
+    let dao = SubCommand::with_name("dao").about("DAO functionalities").subcommands(vec![
+        create, view, import, list, balance, mint, propose, proposals, proposal, vote, exec,
+    ]);
+
+    // Scan
+    let reset = Arg::with_name("reset")
+        .long("reset")
+        .help("Reset Merkle tree and start scanning from first block");
+
+    let list = Arg::with_name("list").long("list").help("List all available checkpoints");
+
+    let checkpoint = Arg::with_name("checkpoint")
+        .long("checkpoint")
+        .takes_value(true)
+        .help("Reset Merkle tree to checkpoint index and start scanning");
+
+    let scan = SubCommand::with_name("scan")
+        .about("Scan the blockchain and parse relevant transactions")
+        .args(&vec![reset, list, checkpoint]);
+
+    // Explorer
+    let tx_hash = Arg::with_name("tx-hash").help("Transaction hash");
+
+    let full = Arg::with_name("full").long("full").help("Print the full transaction information");
+
+    let encode = Arg::with_name("encode").long("encode").help("Encode transaction to base58");
+
+    let fetch_tx = SubCommand::with_name("fetch-tx")
+        .about("Fetch a blockchain transaction by hash")
+        .args(&vec![tx_hash, full, encode]);
+
+    let simulate_tx =
+        SubCommand::with_name("simulate-tx").about("Read a transaction from stdin and simulate it");
+
+    let tx_hash = Arg::with_name("tx-hash").help("Transaction hash");
+
+    let encode = Arg::with_name("encode")
+        .long("encode")
+        .help("Encode specific history record transaction to base58");
+
+    let txs_history = SubCommand::with_name("txs-history")
+        .about("Fetch broadcasted transactions history")
+        .args(&vec![tx_hash, encode]);
+
+    let explorer = SubCommand::with_name("explorer")
+        .about("Explorer related subcommands")
+        .subcommands(vec![fetch_tx, simulate_tx, txs_history]);
+
+    // Alias
+    let alias = Arg::with_name("alias").help("Token alias");
+
+    let token = Arg::with_name("token").help("Token to create alias for");
+
+    let add = SubCommand::with_name("add").about("Create a Token alias").args(&vec![alias, token]);
+
+    let alias = Arg::with_name("alias")
+        .short("a")
+        .long("alias")
+        .takes_value(true)
+        .help("Token alias to search for");
+
+    let token = Arg::with_name("token")
+        .short("t")
+        .long("token")
+        .takes_value(true)
+        .help("Token to search alias for");
+
+    let show = SubCommand::with_name("show")
+        .about(
+            "Print alias info of optional arguments. \
+                    If no argument is provided, list all the aliases in the wallet.",
+        )
+        .args(&vec![alias, token]);
+
+    let alias = Arg::with_name("alias").help("Token alias to remove");
+
+    let remove = SubCommand::with_name("remove").about("Remove a Token alias").arg(alias);
+
+    let alias = SubCommand::with_name("alias")
+        .about("Manage Token aliases")
+        .subcommands(vec![add, show, remove]);
+
+    // Token
+    let import = SubCommand::with_name("import").about("Import a mint authority secret from stdin");
+
+    let generate_mint =
+        SubCommand::with_name("generate-mint").about("Generate a new mint authority");
+
+    let list =
+        SubCommand::with_name("list").about("List token IDs with available mint authorities");
+
+    let token = Arg::with_name("token").help("Token ID to mint");
+
+    let amount = Arg::with_name("amount").help("Amount to mint");
+
+    let recipient = Arg::with_name("recipient").help("Recipient of the minted tokens");
+
+    let mint =
+        SubCommand::with_name("mint").about("Mint tokens").args(&vec![token, amount, recipient]);
+
+    let token = Arg::with_name("token").help("Token ID to freeze");
+
+    let freeze = SubCommand::with_name("freeze").about("Freeze a token mint").arg(token);
+
+    let token = SubCommand::with_name("token").about("Token functionalities").subcommands(vec![
+        import,
+        generate_mint,
+        list,
+        mint,
+        freeze,
+    ]);
+
+    // Main arguments
+    let config = Arg::with_name("config")
+        .short("c")
+        .long("config")
+        .takes_value(true)
+        .help("Configuration file to use");
+
+    let wallet_path = Arg::with_name("wallet_path")
+        .long("wallet-path")
+        .takes_value(true)
+        .help("Path to wallet database");
+
+    let wallet_pass = Arg::with_name("wallet_pass")
+        .long("wallet-pass")
+        .takes_value(true)
+        .help("Password for the wallet database");
+
+    let endpoint = Arg::with_name("endpoint")
+        .short("e")
+        .long("endpoint")
+        .takes_value(true)
+        .help("darkfid JSON-RPC endpoint");
+
+    let command = vec![
+        kaching,
+        ping,
+        completions,
+        wallet,
+        unspend,
+        transfer,
+        otc,
+        inspect,
+        broadcast,
+        subscribe,
+        dao,
+        scan,
+        explorer,
+        alias,
+        token,
+    ];
+
+    let log = Arg::with_name("log")
+        .short("l")
+        .long("log")
+        .takes_value(true)
+        .help("Set log file to ouput into");
+
+    let verbose = Arg::with_name("verbose")
+        .short("v")
+        .multiple(true)
+        .help("Increase verbosity (-vvv supported)");
+
+    let mut app = App::new("drk")
+        .about(cli_desc!())
+        .args(&vec![config, wallet_path, wallet_pass, endpoint, log, verbose])
+        .subcommands(command);
+
+    let shell = match Shell::from_str(shell) {
+        Ok(s) => s,
+        Err(e) => return Err(Error::Custom(e)),
+    };
+
+    app.gen_completions_to("./drk", shell, &mut std::io::stdout());
+
+    Ok(())
+}

+ 0 - 0
bin/drk2/src/dao.rs → bin/drk/src/dao.rs


+ 0 - 181
bin/drk/src/deploy_contract.rs

@@ -1,181 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{
-    env::set_current_dir,
-    fs::{read, read_dir, read_to_string, File},
-    io::{ErrorKind, Write},
-    path::{Path, PathBuf},
-    str::FromStr,
-};
-
-use rand::{rngs::OsRng, RngCore};
-
-use darkfi::{
-    crypto::keypair::SecretKey,
-    node::{MemoryState, State},
-    runtime::vm_runtime::Runtime,
-    util::cli::{fg_green, fg_red},
-    zkas::ZkBinary,
-    Error, Result,
-};
-
-const CIRCUIT_DIR_NAME: &str = "proof";
-const CONTRACT_FILE_NAME: &str = "contract.wasm";
-const DEPLOY_KEY_NAME: &str = "deploy.key";
-
-/// Creates a new deploy key used for deploying private smart contracts.
-/// This key allows to update the wasm code and the zk circuits on chain
-/// by creating a signature. When deployed, the contract can be accessed
-/// by requesting the public counterpart of this secret key.
-pub fn create_deploy_key(mut rng: impl RngCore, path: &Path) -> Result<SecretKey> {
-    let secret = SecretKey::random(&mut rng);
-    let mut file = File::create(path)?;
-    file.write_all(bs58::encode(&secret.to_bytes()).into_string().as_bytes())?;
-    Ok(secret)
-}
-
-/// Reads a deploy key from a file on the filesystem and returns it.
-fn read_deploy_key(s: &Path) -> core::result::Result<SecretKey, std::io::Error> {
-    eprintln!("Trying to read deploy key from file: {:?}", s);
-    let contents = read_to_string(s)?;
-    let secret = SecretKey::from_str(&contents).unwrap();
-    Ok(secret)
-}
-
-/// Creates necessary data to deploy a given smart contract on the network.
-/// For consistency, we point this function to a directory where our smart
-/// contract and the compiled circuits are contained. This is going to give
-/// us a uniform approach to scm and gives a generic layout of the source:
-/// ```text
-/// smart-contract
-/// ├── Cargo.toml
-/// ├── deploy.key
-/// ├── Makefile
-/// ├── proof
-/// │   ├── circuit0.zk
-/// │   ├── circuit0.zk.bin
-/// │   ├── circuit1.zk
-/// │   └── circuit1.zk.bin
-/// ├── contract.wasm
-/// ├── src
-/// │   └── lib.rs
-/// └── tests
-/// ```
-//pub fn create_deploy_data(path: &Path) -> Result<ContractDeploy> {
-pub fn create_deploy_data(path: &Path) -> Result<()> {
-    // Try to chdir into the contract directory
-    if let Err(e) = set_current_dir(path) {
-        eprintln!("Failed to chdir into {:?}", path);
-        return Err(e.into())
-    }
-
-    let deploy_key: SecretKey;
-
-    let deploy_key = match read_deploy_key(&PathBuf::from(DEPLOY_KEY_NAME)) {
-        Ok(v) => deploy_key = v,
-        Err(e) => {
-            if e.kind() == ErrorKind::NotFound {
-                // We didn't find a deploy key, generate a new one.
-                eprintln!("Did not find an existing key, creating a new one.");
-                match create_deploy_key(&mut OsRng, &PathBuf::from(DEPLOY_KEY_NAME)) {
-                    Ok(v) => {
-                        eprintln!("Created new deploy key in \"{}\".", DEPLOY_KEY_NAME);
-                        deploy_key = v;
-                    }
-                    Err(e) => {
-                        eprintln!("Failed to create new deploy key");
-                        return Err(e)
-                    }
-                }
-            }
-            eprintln!("Failed to read deploy key");
-            return Err(e.into())
-        }
-    };
-
-    // Search for ZK circuits in the directory. If none are found, we'll bail.
-    // The logic searches for `.zk.bin` files created by zkas.
-    eprintln!("Searching for compiled ZK circuits in \"{}\" ...", CIRCUIT_DIR_NAME);
-    let mut circuits = vec![];
-    for i in read_dir(CIRCUIT_DIR_NAME)? {
-        if let Err(e) = i {
-            eprintln!("Error iterating over \"{}\" directory", CIRCUIT_DIR_NAME);
-            return Err(e.into())
-        }
-
-        let f = i.unwrap();
-        let fname = f.file_name();
-        let fname = fname.to_str().unwrap();
-
-        if fname.ends_with(".zk.bin") {
-            // Validate that the files can be properly decoded
-            eprintln!("{} {}", fg_green("Found:"), f.path().display());
-            let buf = read(f.path())?;
-            if let Err(e) = ZkBinary::decode(&buf) {
-                eprintln!("{} Failed to decode zkas bincode in {:?}", fg_red("Error:"), f.path());
-                return Err(e)
-            }
-
-            circuits.push(buf.clone());
-        }
-    }
-
-    if circuits.is_empty() {
-        return Err(Error::Custom("Found no valid ZK circuits".to_string()))
-    }
-
-    /* FIXME
-    // Validate wasm binary. We inspect the bincode and try to load it into
-    // the wasm runtime. If loaded, we then look for the `ENTRYPOINT` function
-    // which we hardcode into our sdk and runtime and is the canonical way to
-    // run wasm binaries on chain.
-    eprintln!("Inspecting wasm binary in \"{}\"", CONTRACT_FILE_NAME);
-    let wasm_bytes = read(CONTRACT_FILE_NAME)?;
-    eprintln!("Initializing moch wasm runtime to check validity");
-    let runtime = match Runtime::new(&wasm_bytes, MemoryState::new(State::dummy()?)) {
-        Ok(v) => {
-            eprintln!("Found {} wasm binary", fg_green("valid"));
-            v
-        }
-        Err(e) => {
-            eprintln!("Failed to initialize wasm runtime");
-            return Err(e)
-        }
-    };
-
-    eprintln!("Looking for entrypoint function inside the wasm");
-    let cs = ContractSection::Exec;
-    if let Err(e) = runtime.instance.exports.get_function(cs.name()) {
-        eprintln!("{} Could not find entrypoint function", fg_red("Error:"));
-        return Err(e.into())
-    }
-
-    // TODO: Create a ZK proof enforcing the deploy key relations with their public
-    // counterparts (public key and contract address)
-    let mut total_bytes = 0;
-    total_bytes += wasm_bytes.len();
-    for circuit in circuits {
-        total_bytes += circuit.len();
-    }
-    */
-
-    // TODO: Return the data back to the main function, and work further in creating
-    // a transaction and broadcasting it.
-    Ok(())
-}

+ 0 - 0
bin/drk2/src/error.rs → bin/drk/src/error.rs


Разлика између датотеке није приказан због своје велике величине
+ 358 - 331
bin/drk/src/main.rs


+ 0 - 0
bin/drk2/src/money.rs → bin/drk/src/money.rs


+ 0 - 0
bin/drk2/src/rpc.rs → bin/drk/src/rpc.rs


+ 0 - 72
bin/drk/src/rpc_airdrop.rs

@@ -1,72 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use anyhow::{anyhow, Result};
-use darkfi::rpc::{client::RpcClient, jsonrpc::JsonRequest};
-use darkfi_sdk::{
-    crypto::{mimc_vdf, PublicKey},
-    num_bigint::BigUint,
-    num_traits::Num,
-};
-use serde_json::json;
-use url::Url;
-
-use super::Drk;
-
-impl Drk {
-    /// Request an airdrop of `amount` `token_id` tokens from a faucet.
-    /// Returns a transaction ID on success.
-    pub async fn request_airdrop(
-        &self,
-        faucet_endpoint: Url,
-        amount: f64,
-        address: PublicKey,
-    ) -> Result<String> {
-        let rpc_client = RpcClient::new(faucet_endpoint, None).await?;
-
-        // First we request a VDF challenge from the faucet
-        let params = json!([format!("{}", address)]);
-        let req = JsonRequest::new("challenge", params);
-        let rep = rpc_client.request(req).await?;
-
-        let Some(rep) = rep.as_array() else {
-            return Err(anyhow!("Invalid challenge response from faucet: {:?}", rep))
-        };
-        if rep.len() != 2 || !rep[0].is_string() || !rep[1].is_u64() {
-            return Err(anyhow!("Invalid challenge response from faucet: {:?}", rep))
-        }
-
-        // Retrieve VDF challenge
-        let challenge = BigUint::from_str_radix(rep[0].as_str().unwrap(), 16)?;
-        let n_steps = rep[1].as_u64().unwrap();
-
-        // Then evaluate the VDF
-        eprintln!("Evaluating VDF with n_steps={} ... (this could take about a minute)", n_steps);
-        let witness = mimc_vdf::eval(&challenge, n_steps);
-        eprintln!("Done! Sending airdrop request...");
-
-        // And finally request airdrop with the VDF evaluation witness
-        let params = json!([format!("{}", address), amount, witness.to_str_radix(16)]);
-        let req = JsonRequest::new("airdrop", params);
-        let rep = rpc_client.oneshot_request(req).await?;
-
-        let txid = serde_json::from_value(rep)?;
-
-        Ok(txid)
-    }
-}

+ 0 - 355
bin/drk/src/rpc_blockchain.rs

@@ -1,355 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use anyhow::{anyhow, Result};
-use async_std::{stream::StreamExt, task};
-use darkfi::{
-    consensus::BlockInfo,
-    rpc::{
-        client::RpcClient,
-        jsonrpc::{JsonRequest, JsonResult},
-    },
-    system::Subscriber,
-    tx::Transaction,
-    wallet::walletdb::QueryType,
-};
-use darkfi_money_contract::client::{MONEY_INFO_COL_LAST_SCANNED_SLOT, MONEY_INFO_TABLE};
-use darkfi_sdk::crypto::ContractId;
-use darkfi_serial::{deserialize, serialize};
-use serde_json::json;
-use signal_hook::consts::{SIGINT, SIGQUIT, SIGTERM};
-use signal_hook_async_std::Signals;
-use url::Url;
-
-use super::Drk;
-
-impl Drk {
-    /// Subscribes to darkfid's JSON-RPC notification endpoint that serves
-    /// new finalized blocks. Upon receiving them, all the transactions are
-    /// scanned and we check if any of them call the money contract, and if
-    /// the payments are intended for us. If so, we decrypt them and append
-    /// the metadata to our wallet.
-    pub async fn subscribe_blocks(&self, endpoint: Url) -> Result<()> {
-        let req = JsonRequest::new("blockchain.last_known_slot", json!([]));
-        let rep = self.rpc_client.request(req).await?;
-        let last_known: u64 = serde_json::from_value(rep)?;
-        let last_scanned = self.last_scanned_slot().await?;
-
-        if last_known != last_scanned {
-            eprintln!("Warning: Last scanned slot is not the last known slot.");
-            eprintln!("You should first fully scan the blockchain, and then subscribe");
-            return Err(anyhow!("Blockchain not fully scanned"))
-        }
-
-        eprintln!("Subscribing to receive notifications of incoming blocks");
-        let subscriber = Subscriber::new();
-        let subscription = subscriber.clone().subscribe().await;
-
-        let rpc_client = RpcClient::new(endpoint, None).await?;
-
-        let req = JsonRequest::new("blockchain.subscribe_blocks", json!([]));
-        task::spawn(async move { rpc_client.subscribe(req, subscriber).await.unwrap() });
-        eprintln!("Detached subscription to background");
-        eprintln!("All is good. Waiting for block notifications...");
-
-        let e = loop {
-            match subscription.receive().await {
-                JsonResult::Notification(n) => {
-                    eprintln!("Got Block notification from darkfid subscription");
-                    if n.method != "blockchain.subscribe_blocks" {
-                        break anyhow!("Got foreign notification from darkfid: {}", n.method)
-                    }
-
-                    let Some(params) = n.params.as_array() else {
-                        break anyhow!("Received notification params are not an array")
-                    };
-
-                    if params.len() != 1 {
-                        break anyhow!("Notification parameters are not len 1")
-                    }
-
-                    let params = n.params.as_array().unwrap()[0].as_str().unwrap();
-                    let bytes = bs58::decode(params).into_vec()?;
-
-                    let block_data: BlockInfo = deserialize(&bytes)?;
-                    eprintln!("=======================================");
-                    eprintln!("Block header:\n{:#?}", block_data.header);
-                    eprintln!("=======================================");
-
-                    eprintln!("Deserialized successfully. Scanning block...");
-                    self.scan_block_money(&block_data).await?;
-                    self.scan_block_dao(&block_data).await?;
-                    self.update_tx_history_records_status(&block_data.txs, "Finalized").await?;
-                }
-
-                JsonResult::Error(e) => {
-                    // Some error happened in the transmission
-                    break anyhow!("Got error from JSON-RPC: {:?}", e)
-                }
-
-                x => {
-                    // And this is weird
-                    break anyhow!("Got unexpected data from JSON-RPC: {:?}", x)
-                }
-            }
-        };
-
-        Err(e)
-    }
-
-    /// `scan_block_dao` will go over transactions in a block and fetch the ones dealing
-    /// with the dao contract. Then over all of them, try to see if any are related
-    /// to us. If any are found, the metadata is extracted and placed into the wallet
-    /// for future use.
-    async fn scan_block_dao(&self, block: &BlockInfo) -> Result<()> {
-        eprintln!("[DAO] Iterating over {} transactions", block.txs.len());
-        for tx in block.txs.iter() {
-            self.apply_tx_dao_data(tx, true).await?;
-        }
-
-        Ok(())
-    }
-
-    /// `scan_block_money` will go over transactions in a block and fetch the ones dealing
-    /// with the money contract. Then over all of them, try to see if any are related
-    /// to us. If any are found, the metadata is extracted and placed into the wallet
-    /// for future use.
-    async fn scan_block_money(&self, block: &BlockInfo) -> Result<()> {
-        eprintln!("[Money] Iterating over {} transactions", block.txs.len());
-
-        for tx in block.txs.iter() {
-            self.apply_tx_money_data(tx, true).await?;
-        }
-
-        // Write this slot into `last_scanned_slot`
-        let query =
-            format!("UPDATE {} SET {} = ?1;", MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT);
-        let params = json!([query, QueryType::Integer as u8, block.header.slot]);
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    /// Try to fetch zkas bincodes for the given `ContractId`.
-    pub async fn lookup_zkas(&self, contract_id: &ContractId) -> Result<Vec<(String, Vec<u8>)>> {
-        eprintln!("Querying zkas bincode for {}", contract_id);
-
-        let params = json!([format!("{}", contract_id)]);
-        let req = JsonRequest::new("blockchain.lookup_zkas", params);
-
-        let rep = self.rpc_client.request(req).await?;
-
-        let ret = serde_json::from_value(rep)?;
-        Ok(ret)
-    }
-
-    /// Broadcast a given transaction to darkfid and forward onto the network.
-    /// Returns the transaction ID upon success
-    pub async fn broadcast_tx(&self, tx: &Transaction) -> Result<String> {
-        eprintln!("Broadcasting transaction...");
-
-        let params = json!([bs58::encode(&serialize(tx)).into_string()]);
-        let req = JsonRequest::new("tx.broadcast", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let txid = serde_json::from_value(rep)?;
-
-        // Store transactions history record
-        self.insert_tx_history_record(tx).await?;
-
-        Ok(txid)
-    }
-
-    /// Simulate the transaction with the state machine
-    pub async fn simulate_tx(&self, tx: &Transaction) -> Result<bool> {
-        let params = json!([bs58::encode(&serialize(tx)).into_string()]);
-        let req = JsonRequest::new("tx.simulate", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let is_valid = serde_json::from_value(rep)?;
-        Ok(is_valid)
-    }
-
-    /// Queries darkfid for a block with given slot
-    async fn get_block_by_slot(&self, slot: u64) -> Result<Option<BlockInfo>> {
-        let req = JsonRequest::new("blockchain.get_slot", json!([slot]));
-
-        // This API is weird, we need some way of telling it's an empty slot and
-        // not an error
-        match self.rpc_client.request(req).await {
-            Ok(v) => {
-                let block_bytes: Vec<u8> = serde_json::from_value(v)?;
-                let block = deserialize(&block_bytes)?;
-                Ok(Some(block))
-            }
-
-            Err(_) => Ok(None),
-        }
-    }
-
-    /// Queries darkfid for a tx with given hash
-    pub async fn get_tx(&self, tx_hash: &blake3::Hash) -> Result<Option<Transaction>> {
-        let tx_hash_str: &str = &tx_hash.to_hex();
-        let req = JsonRequest::new("blockchain.get_tx", json!([tx_hash_str]));
-
-        match self.rpc_client.request(req).await {
-            Ok(v) => {
-                let tx_bytes: Vec<u8> = serde_json::from_value(v)?;
-                let tx = deserialize(&tx_bytes)?;
-                Ok(Some(tx))
-            }
-
-            Err(_) => Ok(None),
-        }
-    }
-
-    /// Scans the blockchain starting from the last scanned slot, for relevant
-    /// money transfer transactions. If reset flag is provided, Merkle tree state
-    /// and coins are reset, and start scanning from beginning. Alternatively,
-    /// it looks for a checkpoint in the wallet to reset and start scanning from.
-    pub async fn scan_blocks(&self, reset: bool) -> Result<()> {
-        let mut sl = if reset {
-            self.reset_money_tree().await?;
-            self.reset_money_coins().await?;
-            self.reset_dao_trees().await?;
-            self.reset_daos().await?;
-            self.reset_dao_proposals().await?;
-            self.reset_dao_votes().await?;
-            self.update_all_tx_history_records_status("Rejected").await?;
-            0
-        } else {
-            self.last_scanned_slot().await?
-        };
-
-        let req = JsonRequest::new("blockchain.last_known_slot", json!([]));
-        let rep = self.rpc_client.request(req).await?;
-        let last: u64 = serde_json::from_value(rep)?;
-
-        eprintln!("Requested to scan from slot number: {}", sl);
-        eprintln!("Last known slot number reported by darkfid: {}", last);
-
-        // Already scanned last known slot
-        if sl == last {
-            return Ok(())
-        }
-
-        // We set this up to handle an interrupt
-        let mut signals = Signals::new([SIGTERM, SIGINT, SIGQUIT])?;
-        let handle = signals.handle();
-        let (term_tx, _term_rx) = smol::channel::bounded::<()>(1);
-
-        let term_tx_ = term_tx.clone();
-        let signals_task = task::spawn(async move {
-            while let Some(signal) = signals.next().await {
-                match signal {
-                    SIGTERM | SIGINT | SIGQUIT => term_tx_.close(),
-                    _ => unreachable!(),
-                };
-            }
-        });
-
-        while !term_tx.is_closed() {
-            sl += 1;
-
-            if sl > last {
-                term_tx.close();
-                break
-            }
-
-            eprint!("Requesting slot {}... ", sl);
-            if let Some(block) = self.get_block_by_slot(sl).await? {
-                eprintln!("Found");
-                self.scan_block_money(&block).await?;
-                self.scan_block_dao(&block).await?;
-                self.update_tx_history_records_status(&block.txs, "Finalized").await?;
-            } else {
-                eprintln!("Not found");
-                // Write down the slot number into back to the wallet
-                // This might be a bit intense, but we accept it for now.
-                let query = format!(
-                    "UPDATE {} SET {} = ?1;",
-                    MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT
-                );
-                let params = json!([query, QueryType::Integer as u8, sl]);
-                let req = JsonRequest::new("wallet.exec_sql", params);
-                let _ = self.rpc_client.request(req).await?;
-            }
-        }
-
-        handle.close();
-        signals_task.await;
-
-        Ok(())
-    }
-
-    /// Subscribes to darkfid's JSON-RPC notification endpoint that serves
-    /// erroneous transactions rejections.
-    pub async fn subscribe_err_txs(&self, endpoint: Url) -> Result<()> {
-        eprintln!("Subscribing to receive notifications of erroneous transactions");
-        let subscriber = Subscriber::new();
-        let subscription = subscriber.clone().subscribe().await;
-
-        let rpc_client = RpcClient::new(endpoint, None).await?;
-
-        let req = JsonRequest::new("blockchain.subscribe_err_txs", json!([]));
-        task::spawn(async move { rpc_client.subscribe(req, subscriber).await.unwrap() });
-        eprintln!("Detached subscription to background");
-        eprintln!("All is good. Waiting for erroneous transactions notifications...");
-
-        let e = loop {
-            match subscription.receive().await {
-                JsonResult::Notification(n) => {
-                    eprintln!("Got erroneous transaction notification from darkfid subscription");
-                    if n.method != "blockchain.subscribe_err_txs" {
-                        break anyhow!("Got foreign notification from darkfid: {}", n.method)
-                    }
-
-                    let Some(params) = n.params.as_array() else {
-                        break anyhow!("Received notification params are not an array")
-                    };
-
-                    if params.len() != 1 {
-                        break anyhow!("Notification parameters are not len 1")
-                    }
-
-                    let params = n.params.as_array().unwrap()[0].as_str().unwrap();
-                    let bytes = bs58::decode(params).into_vec()?;
-
-                    let tx_hash: String = deserialize(&bytes)?;
-                    eprintln!("===================================");
-                    eprintln!("Erroneous transaction: {}", tx_hash);
-                    eprintln!("===================================");
-                    self.update_tx_history_record_status(&tx_hash, "Rejected").await?;
-                }
-
-                JsonResult::Error(e) => {
-                    // Some error happened in the transmission
-                    break anyhow!("Got error from JSON-RPC: {:?}", e)
-                }
-
-                x => {
-                    // And this is weird
-                    break anyhow!("Got unexpected data from JSON-RPC: {:?}", x)
-                }
-            }
-        };
-
-        Err(e)
-    }
-}

+ 0 - 553
bin/drk/src/rpc_dao.rs

@@ -1,553 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use anyhow::{anyhow, Result};
-use darkfi::{
-    tx::Transaction,
-    zk::{empty_witnesses, halo2::Field, ProvingKey, ZkCircuit},
-    zkas::ZkBinary,
-};
-use darkfi_dao_contract::{
-    client as dao_client,
-    client::{DaoInfo, DaoProposalInfo, DaoVoteCall, DaoVoteInput},
-    model::DaoBlindAggregateVote,
-    DaoFunction, DAO_CONTRACT_ZKAS_DAO_EXEC_NS, DAO_CONTRACT_ZKAS_DAO_MINT_NS,
-    DAO_CONTRACT_ZKAS_DAO_PROPOSE_BURN_NS, DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS,
-    DAO_CONTRACT_ZKAS_DAO_VOTE_BURN_NS, DAO_CONTRACT_ZKAS_DAO_VOTE_MAIN_NS,
-};
-use darkfi_money_contract::{
-    client::{transfer_v1::TransferCallBuilder, OwnCoin},
-    MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
-};
-use darkfi_sdk::{
-    crypto::{
-        pedersen_commitment_u64, Keypair, PublicKey, SecretKey, TokenId, DAO_CONTRACT_ID,
-        MONEY_CONTRACT_ID,
-    },
-    pasta::pallas,
-    ContractCall,
-};
-use darkfi_serial::Encodable;
-use rand::rngs::OsRng;
-
-use super::Drk;
-use crate::wallet_dao::{Dao, DaoProposal};
-
-impl Drk {
-    /// Mint a DAO on-chain
-    pub async fn dao_mint(&self, dao_id: u64) -> Result<Transaction> {
-        let dao = self.get_dao_by_id(dao_id).await?;
-
-        if dao.tx_hash.is_some() {
-            return Err(anyhow!("This DAO seems to have already been minted on-chain"))
-        }
-
-        let dao_info = DaoInfo {
-            proposer_limit: dao.proposer_limit,
-            quorum: dao.quorum,
-            approval_ratio_base: dao.approval_ratio_base,
-            approval_ratio_quot: dao.approval_ratio_quot,
-            gov_token_id: dao.gov_token_id,
-            public_key: PublicKey::from_secret(dao.secret_key),
-            bulla_blind: dao.bulla_blind,
-        };
-
-        let zkas_bins = self.lookup_zkas(&DAO_CONTRACT_ID).await?;
-        let Some(dao_mint_zkbin) = zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_MINT_NS)
-        else {
-            return Err(anyhow!("DAO Mint circuit not found"))
-        };
-
-        let dao_mint_zkbin = ZkBinary::decode(&dao_mint_zkbin.1)?;
-        let dao_mint_circuit = ZkCircuit::new(empty_witnesses(&dao_mint_zkbin)?, &dao_mint_zkbin);
-        eprintln!("Creating DAO Mint proving key");
-        let dao_mint_pk = ProvingKey::build(dao_mint_zkbin.k, &dao_mint_circuit);
-
-        let (params, proofs) =
-            dao_client::make_mint_call(&dao_info, &dao.secret_key, &dao_mint_zkbin, &dao_mint_pk)?;
-
-        let mut data = vec![DaoFunction::Mint as u8];
-        params.encode(&mut data)?;
-        let calls = vec![ContractCall { contract_id: *DAO_CONTRACT_ID, data }];
-        let proofs = vec![proofs];
-        let mut tx = Transaction { calls, proofs, signatures: vec![] };
-        let sigs = tx.create_sigs(&mut OsRng, &[dao.secret_key])?;
-        tx.signatures = vec![sigs];
-
-        Ok(tx)
-    }
-
-    /// Create a DAO proposal
-    pub async fn dao_propose(
-        &self,
-        dao_id: u64,
-        recipient: PublicKey,
-        amount: u64,
-        token_id: TokenId,
-    ) -> Result<Transaction> {
-        let Ok(dao) = self.get_dao_by_id(dao_id).await else {
-            return Err(anyhow!("DAO not found in wallet"))
-        };
-
-        if dao.leaf_position.is_none() || dao.tx_hash.is_none() {
-            return Err(anyhow!("DAO seems to not have been deployed yet"))
-        }
-
-        let bulla = dao.bulla();
-        let owncoins = self.get_coins(false).await?;
-
-        let mut dao_owncoins: Vec<OwnCoin> = owncoins.iter().map(|x| x.0.clone()).collect();
-        dao_owncoins.retain(|x| {
-            x.note.token_id == token_id &&
-                x.note.spend_hook == DAO_CONTRACT_ID.inner() &&
-                x.note.user_data == bulla.inner()
-        });
-
-        let mut gov_owncoins: Vec<OwnCoin> = owncoins.iter().map(|x| x.0.clone()).collect();
-        gov_owncoins.retain(|x| x.note.token_id == dao.gov_token_id);
-
-        if dao_owncoins.is_empty() {
-            return Err(anyhow!("Did not find any {} coins owned by this DAO", token_id))
-        }
-
-        if gov_owncoins.is_empty() {
-            return Err(anyhow!("Did not find any governance {} coins in wallet", dao.gov_token_id))
-        }
-
-        if dao_owncoins.iter().map(|x| x.note.value).sum::<u64>() < amount {
-            return Err(anyhow!("Not enough DAO balance for token ID: {}", token_id))
-        }
-
-        if gov_owncoins.iter().map(|x| x.note.value).sum::<u64>() < dao.proposer_limit {
-            return Err(anyhow!("Not enough gov token {} balance to propose", dao.gov_token_id))
-        }
-
-        // FIXME: Here we're looking for a coin == proposer_limit but this shouldn't have to
-        // be the case {
-        let Some(gov_coin) = gov_owncoins.iter().find(|x| x.note.value == dao.proposer_limit)
-        else {
-            return Err(anyhow!("Did not find a single gov coin of value {}", dao.proposer_limit))
-        };
-        // }
-
-        // Lookup the zkas bins
-        let zkas_bins = self.lookup_zkas(&DAO_CONTRACT_ID).await?;
-        let Some(propose_burn_zkbin) =
-            zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_PROPOSE_BURN_NS)
-        else {
-            return Err(anyhow!("Propose Burn circuit not found"))
-        };
-
-        let Some(propose_main_zkbin) =
-            zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS)
-        else {
-            return Err(anyhow!("Propose Main circuit not found"))
-        };
-
-        let propose_burn_zkbin = ZkBinary::decode(&propose_burn_zkbin.1)?;
-        let propose_main_zkbin = ZkBinary::decode(&propose_main_zkbin.1)?;
-
-        let propose_burn_circuit =
-            ZkCircuit::new(empty_witnesses(&propose_burn_zkbin)?, &propose_burn_zkbin);
-        let propose_main_circuit =
-            ZkCircuit::new(empty_witnesses(&propose_main_zkbin)?, &propose_main_zkbin);
-
-        eprintln!("Creating Propose Burn circuit proving key");
-        let propose_burn_pk = ProvingKey::build(propose_burn_zkbin.k, &propose_burn_circuit);
-        eprintln!("Creating Propose Main circuit proving key");
-        let propose_main_pk = ProvingKey::build(propose_main_zkbin.k, &propose_main_circuit);
-
-        // Now create the parameters for the proposal tx
-        let signature_secret = SecretKey::random(&mut OsRng);
-
-        // Get the Merkle path for the gov coin in the money tree
-        let money_merkle_tree = self.get_money_tree().await?;
-        let gov_coin_merkle_path = money_merkle_tree.witness(gov_coin.leaf_position, 0).unwrap();
-
-        // Fetch the daos Merkle tree
-        let (daos_tree, _) = self.get_dao_trees().await?;
-
-        let input = dao_client::DaoProposeStakeInput {
-            secret: gov_coin.secret, // <-- TODO: Is this correct?
-            note: gov_coin.note.clone(),
-            leaf_position: gov_coin.leaf_position,
-            merkle_path: gov_coin_merkle_path,
-            signature_secret,
-        };
-
-        let (dao_merkle_path, dao_merkle_root) = {
-            let root = daos_tree.root(0).unwrap();
-            let leaf_pos = dao.leaf_position.unwrap();
-            let dao_merkle_path = daos_tree.witness(leaf_pos, 0).unwrap();
-            (dao_merkle_path, root)
-        };
-
-        let proposal_blind = pallas::Base::random(&mut OsRng);
-        let proposal = dao_client::DaoProposalInfo {
-            dest: recipient,
-            amount,
-            token_id,
-            blind: proposal_blind,
-        };
-
-        let daoinfo = DaoInfo {
-            proposer_limit: dao.proposer_limit,
-            quorum: dao.quorum,
-            approval_ratio_quot: dao.approval_ratio_quot,
-            approval_ratio_base: dao.approval_ratio_base,
-            gov_token_id: dao.gov_token_id,
-            public_key: PublicKey::from_secret(dao.secret_key),
-            bulla_blind: dao.bulla_blind,
-        };
-
-        let call = dao_client::DaoProposeCall {
-            inputs: vec![input],
-            proposal,
-            dao: daoinfo,
-            dao_leaf_position: dao.leaf_position.unwrap(),
-            dao_merkle_path,
-            dao_merkle_root,
-        };
-
-        eprintln!("Creating ZK proofs...");
-        let (params, proofs) = call.make(
-            &propose_burn_zkbin,
-            &propose_burn_pk,
-            &propose_main_zkbin,
-            &propose_main_pk,
-        )?;
-
-        let mut data = vec![DaoFunction::Propose as u8];
-        params.encode(&mut data)?;
-        let calls = vec![ContractCall { contract_id: *DAO_CONTRACT_ID, data }];
-        let proofs = vec![proofs];
-        let mut tx = Transaction { calls, proofs, signatures: vec![] };
-        let sigs = tx.create_sigs(&mut OsRng, &[signature_secret])?;
-        tx.signatures = vec![sigs];
-
-        Ok(tx)
-    }
-
-    /// Vote on a DAO proposal
-    pub async fn dao_vote(
-        &self,
-        dao_id: u64,
-        proposal_id: u64,
-        vote_option: bool,
-        weight: u64,
-    ) -> Result<Transaction> {
-        let dao = self.get_dao_by_id(dao_id).await?;
-        let proposals = self.get_dao_proposals(dao_id).await?;
-        let Some(proposal) = proposals.iter().find(|x| x.id == proposal_id) else {
-            return Err(anyhow!("Proposal ID not found"))
-        };
-
-        let money_tree = proposal.money_snapshot_tree.clone().unwrap();
-
-        let mut coins: Vec<OwnCoin> =
-            self.get_coins(false).await?.iter().map(|x| x.0.clone()).collect();
-
-        coins.retain(|x| x.note.token_id == dao.gov_token_id);
-        coins.retain(|x| x.note.spend_hook == pallas::Base::zero());
-
-        if coins.iter().map(|x| x.note.value).sum::<u64>() < weight {
-            return Err(anyhow!("Not enough balance for vote weight"))
-        }
-
-        // TODO: The spent coins need to either be marked as spent here, and/or on scan
-        let mut spent_value = 0;
-        let mut spent_coins = vec![];
-        let mut inputs = vec![];
-        let mut input_secrets = vec![];
-
-        // FIXME: We don't take back any change so it's possible to vote with > requested weight.
-        for coin in coins {
-            if spent_value >= weight {
-                break
-            }
-
-            spent_value += coin.note.value;
-            spent_coins.push(coin.clone());
-
-            let signature_secret = SecretKey::random(&mut OsRng);
-            input_secrets.push(signature_secret);
-
-            let leaf_position = coin.leaf_position;
-            let merkle_path = money_tree.witness(coin.leaf_position, 0).unwrap();
-
-            let input = DaoVoteInput {
-                secret: coin.secret,
-                note: coin.note.clone(),
-                leaf_position,
-                merkle_path,
-                signature_secret,
-            };
-
-            inputs.push(input);
-        }
-
-        // We use the DAO secret to encrypt the vote.
-        let vote_keypair = Keypair::new(dao.secret_key);
-
-        let proposal_info = DaoProposalInfo {
-            dest: proposal.recipient,
-            amount: proposal.amount,
-            token_id: proposal.token_id,
-            blind: proposal.bulla_blind,
-        };
-
-        let dao_info = DaoInfo {
-            proposer_limit: dao.proposer_limit,
-            quorum: dao.quorum,
-            approval_ratio_quot: dao.approval_ratio_quot,
-            approval_ratio_base: dao.approval_ratio_base,
-            gov_token_id: dao.gov_token_id,
-            public_key: PublicKey::from_secret(dao.secret_key),
-            bulla_blind: dao.bulla_blind,
-        };
-
-        let call = DaoVoteCall {
-            inputs,
-            vote_option,
-            yes_vote_blind: pallas::Scalar::random(&mut OsRng),
-            vote_keypair,
-            proposal: proposal_info,
-            dao: dao_info,
-        };
-
-        let zkas_bins = self.lookup_zkas(&DAO_CONTRACT_ID).await?;
-        let Some(dao_vote_burn_zkbin) =
-            zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_VOTE_BURN_NS)
-        else {
-            return Err(anyhow!("DAO Vote Burn circuit not found"))
-        };
-
-        let Some(dao_vote_main_zkbin) =
-            zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_VOTE_MAIN_NS)
-        else {
-            return Err(anyhow!("DAO Vote Main circuit not found"))
-        };
-
-        let dao_vote_burn_zkbin = ZkBinary::decode(&dao_vote_burn_zkbin.1)?;
-        let dao_vote_main_zkbin = ZkBinary::decode(&dao_vote_main_zkbin.1)?;
-
-        let dao_vote_burn_circuit =
-            ZkCircuit::new(empty_witnesses(&dao_vote_burn_zkbin)?, &dao_vote_burn_zkbin);
-        let dao_vote_main_circuit =
-            ZkCircuit::new(empty_witnesses(&dao_vote_main_zkbin)?, &dao_vote_main_zkbin);
-
-        eprintln!("Creating DAO Vote Burn proving key");
-        let dao_vote_burn_pk = ProvingKey::build(dao_vote_burn_zkbin.k, &dao_vote_burn_circuit);
-        eprintln!("Creating DAO Vote Main proving key");
-        let dao_vote_main_pk = ProvingKey::build(dao_vote_main_zkbin.k, &dao_vote_main_circuit);
-
-        let (params, proofs) = call.make(
-            &dao_vote_burn_zkbin,
-            &dao_vote_burn_pk,
-            &dao_vote_main_zkbin,
-            &dao_vote_main_pk,
-        )?;
-
-        let mut data = vec![DaoFunction::Vote as u8];
-        params.encode(&mut data)?;
-        let calls = vec![ContractCall { contract_id: *DAO_CONTRACT_ID, data }];
-        let proofs = vec![proofs];
-        let mut tx = Transaction { calls, proofs, signatures: vec![] };
-        let sigs = tx.create_sigs(&mut OsRng, &input_secrets)?;
-        tx.signatures = vec![sigs];
-
-        Ok(tx)
-    }
-
-    /// Import given DAO votes into the wallet
-    /// This function is really bad but I'm also really tired and annoyed.
-    pub async fn dao_exec(&self, dao: Dao, proposal: DaoProposal) -> Result<Transaction> {
-        let dao_bulla = dao.bulla();
-        eprintln!("Fetching proposal's votes");
-        let votes = self.get_dao_proposal_votes(proposal.id).await?;
-
-        // Find the treasury coins that can be used for this proposal
-        let mut coins: Vec<OwnCoin> =
-            self.get_coins(false).await?.iter().map(|x| x.0.clone()).collect();
-        coins.retain(|x| x.note.spend_hook == DAO_CONTRACT_ID.inner());
-        coins.retain(|x| x.note.user_data == dao_bulla.inner());
-        coins.retain(|x| x.note.token_id == proposal.token_id);
-
-        if coins.iter().map(|x| x.note.value).sum::<u64>() < proposal.amount {
-            return Err(anyhow!("Not enough balance in DAO treasury to execute proposal"))
-        }
-
-        // FIXME: This assumes we aren't sending to a protocol.
-        let rcpt_spend_hook = pallas::Base::ZERO;
-        let rcpt_user_data = pallas::Base::ZERO;
-        let rcpt_user_data_blind = pallas::Base::random(&mut OsRng);
-
-        let change_spend_hook = DAO_CONTRACT_ID.inner();
-        let change_user_data = dao_bulla.inner();
-        let change_user_data_blind = pallas::Base::random(&mut OsRng);
-
-        let money_merkle_tree = self.get_money_tree().await?;
-
-        let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
-        let Some(mint_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_MINT_NS_V1)
-        else {
-            return Err(anyhow!("Money Mint circuit not found"))
-        };
-        let Some(burn_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_BURN_NS_V1)
-        else {
-            return Err(anyhow!("Money Burn circuit not found"))
-        };
-        let mint_zkbin = ZkBinary::decode(&mint_zkbin.1)?;
-        let burn_zkbin = ZkBinary::decode(&burn_zkbin.1)?;
-        let mint_circuit = ZkCircuit::new(empty_witnesses(&mint_zkbin)?, &mint_zkbin);
-        let burn_circuit = ZkCircuit::new(empty_witnesses(&burn_zkbin)?, &burn_zkbin);
-        eprintln!("Creating Money Mint circuit proving key");
-        let mint_pk = ProvingKey::build(mint_zkbin.k, &mint_circuit);
-        eprintln!("Creating Money Burn circuit proving key");
-        let burn_pk = ProvingKey::build(burn_zkbin.k, &burn_circuit);
-
-        let xfer_builder = TransferCallBuilder {
-            keypair: dao.keypair(),
-            recipient: proposal.recipient,
-            value: proposal.amount,
-            token_id: proposal.token_id,
-            rcpt_spend_hook,
-            rcpt_user_data,
-            rcpt_user_data_blind,
-            change_spend_hook,
-            change_user_data,
-            change_user_data_blind,
-            coins,
-            tree: money_merkle_tree,
-            mint_zkbin: mint_zkbin.clone(),
-            mint_pk: mint_pk.clone(),
-            burn_zkbin: burn_zkbin.clone(),
-            burn_pk: burn_pk.clone(),
-            clear_input: false,
-        };
-
-        let xfer_debris = xfer_builder.build()?;
-
-        let mut data = vec![MoneyFunction::TransferV1 as u8];
-        xfer_debris.params.encode(&mut data)?;
-        let xfer_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
-
-        let zkas_bins = self.lookup_zkas(&DAO_CONTRACT_ID).await?;
-        let Some(exec_zkbin) = zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_EXEC_NS)
-        else {
-            return Err(anyhow!("DAO Exec circuit not found"))
-        };
-        let exec_zkbin = ZkBinary::decode(&exec_zkbin.1)?;
-        let exec_circuit = ZkCircuit::new(empty_witnesses(&exec_zkbin)?, &exec_zkbin);
-        eprintln!("Creating DAO Exec circuit proving key");
-        let exec_pk = ProvingKey::build(exec_zkbin.k, &exec_circuit);
-
-        // Count votes
-        let mut total_yes_vote_value = 0;
-        let mut total_all_vote_value = 0;
-        let mut blind_total_vote = DaoBlindAggregateVote::default();
-        let mut total_yes_vote_blind = pallas::Scalar::zero();
-        let mut total_all_vote_blind = pallas::Scalar::zero();
-
-        for (_, vote) in votes.iter().enumerate() {
-            total_yes_vote_blind += vote.yes_vote_blind;
-            total_all_vote_blind += vote.all_vote_blind;
-
-            let yes_vote_value = vote.vote_option as u64 * vote.all_vote_value;
-            eprintln!("yes_vote = {}", yes_vote_value);
-            total_yes_vote_value += yes_vote_value;
-            total_all_vote_value += vote.all_vote_value;
-
-            let yes_vote_commit = pedersen_commitment_u64(yes_vote_value, vote.yes_vote_blind);
-            let all_vote_commit = pedersen_commitment_u64(vote.all_vote_value, vote.all_vote_blind);
-
-            let blind_vote = DaoBlindAggregateVote { yes_vote_commit, all_vote_commit };
-            blind_total_vote.aggregate(blind_vote);
-        }
-
-        eprintln!("yes = {}, all = {}", total_yes_vote_value, total_all_vote_value);
-
-        let prop_t = DaoProposalInfo {
-            dest: proposal.recipient,
-            amount: proposal.amount,
-            token_id: proposal.token_id,
-            blind: proposal.bulla_blind, // <-- FIXME: wtf
-        };
-
-        // TODO: allvote/yesvote is 11 weirdly
-
-        let dao_t = DaoInfo {
-            proposer_limit: dao.proposer_limit,
-            quorum: dao.quorum,
-            approval_ratio_quot: dao.approval_ratio_quot,
-            approval_ratio_base: dao.approval_ratio_base,
-            gov_token_id: dao.gov_token_id,
-            public_key: PublicKey::from_secret(dao.secret_key),
-            bulla_blind: dao.bulla_blind,
-        };
-
-        // We need to extract stuff from the inputs and outputs that we'll also
-        // use in the DAO::Exec call. This DAO API needs to be better.
-        let mut input_value = 0;
-        let mut input_value_blind = pallas::Scalar::ZERO;
-        for (input, blind) in xfer_debris.spent_coins.iter().zip(xfer_debris.input_value_blinds) {
-            input_value += input.note.value;
-            input_value_blind += blind;
-        }
-
-        // First output is change, second output is recipient.
-        let dao_serial = xfer_debris.minted_coins[0].note.serial;
-        let user_serial = xfer_debris.minted_coins[1].note.serial;
-
-        // TODO: FIXME: This is not checked anywhere!
-        let exec_signature_secret = SecretKey::random(&mut OsRng);
-
-        let dao_exec_call = dao_client::DaoExecCall {
-            proposal: prop_t,
-            dao: dao_t,
-            yes_vote_value: total_yes_vote_value,
-            all_vote_value: total_all_vote_value,
-            yes_vote_blind: total_yes_vote_blind,
-            all_vote_blind: total_all_vote_blind,
-            user_serial,
-            dao_serial,
-            input_value,
-            input_value_blind,
-            hook_dao_exec: DAO_CONTRACT_ID.inner(),
-            signature_secret: exec_signature_secret,
-        };
-
-        let (exec_params, exec_proofs) = dao_exec_call.make(&exec_zkbin, &exec_pk)?;
-
-        let mut data = vec![DaoFunction::Exec as u8];
-        exec_params.encode(&mut data)?;
-        let exec_call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
-
-        let mut tx = Transaction {
-            calls: vec![xfer_call, exec_call],
-            proofs: vec![xfer_debris.proofs, exec_proofs],
-            signatures: vec![],
-        };
-
-        let xfer_sigs = tx.create_sigs(&mut OsRng, &xfer_debris.signature_secrets)?;
-        let exec_sigs = tx.create_sigs(&mut OsRng, &[exec_signature_secret])?;
-        tx.signatures = vec![xfer_sigs, exec_sigs];
-
-        Ok(tx)
-    }
-}

+ 0 - 462
bin/drk/src/rpc_swap.rs

@@ -1,462 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-use std::fmt;
-
-use anyhow::{anyhow, Result};
-use darkfi::{
-    tx::Transaction,
-    util::parse::encode_base10,
-    zk::{halo2::Field, proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses, Proof},
-    zkas::ZkBinary,
-};
-use darkfi_money_contract::{
-    client::{swap_v1::SwapCallBuilder, MoneyNote},
-    model::{Coin, MoneyTransferParamsV1},
-    MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
-};
-use darkfi_sdk::{
-    crypto::{
-        contract_id::MONEY_CONTRACT_ID, pedersen::pedersen_commitment_u64, poseidon_hash,
-        PublicKey, SecretKey, TokenId,
-    },
-    pasta::pallas,
-    tx::ContractCall,
-};
-use darkfi_serial::{deserialize, Encodable, SerialDecodable, SerialEncodable};
-use rand::rngs::OsRng;
-
-use super::Drk;
-
-#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
-/// Half of the swap data, includes the coin that is supposed to be sent,
-/// and the coin that is supposed to be received.
-pub struct PartialSwapData {
-    params: MoneyTransferParamsV1,
-    proofs: Vec<Proof>,
-    value_pair: (u64, u64),
-    token_pair: (TokenId, TokenId),
-    value_blinds: Vec<pallas::Scalar>,
-    token_blinds: Vec<pallas::Base>,
-}
-
-impl fmt::Display for PartialSwapData {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let s =
-            format!(
-            "{:#?}\nValue pair: {}:{}\nToken pair: {}:{}\nValue blinds: {:?}\nToken blinds: {:?}\n",
-            self.params, self.value_pair.0, self.value_pair.1, self.token_pair.0, self.token_pair.1,
-            self.value_blinds, self.token_blinds,
-        );
-
-        write!(f, "{}", s)
-    }
-}
-
-impl Drk {
-    /// Initialize the first half of an atomic swap
-    pub async fn init_swap(
-        &self,
-        value_send: u64,
-        token_send: TokenId,
-        value_recv: u64,
-        token_recv: TokenId,
-    ) -> Result<PartialSwapData> {
-        // First we'll fetch all of our unspent coins from the wallet.
-        let mut owncoins = self.get_coins(false).await?;
-        // Then we see if we have one that we can send.
-        owncoins.retain(|x| {
-            x.0.note.value == value_send &&
-                x.0.note.token_id == token_send &&
-                x.0.note.spend_hook == pallas::Base::zero()
-        });
-
-        if owncoins.is_empty() {
-            return Err(anyhow!(
-                "Did not find any unspent coins of value {} and token_id {}",
-                value_send,
-                token_send
-            ))
-        }
-
-        // If there are any, we'll just spend the first one we see.
-        let burn_coin = owncoins[0].0.clone();
-
-        // Fetch our default address
-        let address = self.wallet_address(1).await?;
-
-        // We'll also need our Merkle tree
-        let tree = self.get_money_tree().await?;
-
-        let contract_id = *MONEY_CONTRACT_ID;
-
-        // Now we need to do a lookup for the zkas proof bincodes, and create
-        // the circuit objects and proving keys so we can build the transaction.
-        // We also do this through the RPC.
-        let zkas_bins = self.lookup_zkas(&contract_id).await?;
-
-        let Some(mint_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_MINT_NS_V1)
-        else {
-            return Err(anyhow!("Mint circuit not found"))
-        };
-
-        let Some(burn_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_BURN_NS_V1)
-        else {
-            return Err(anyhow!("Burn circuit not found"))
-        };
-
-        let mint_zkbin = ZkBinary::decode(&mint_zkbin.1)?;
-        let burn_zkbin = ZkBinary::decode(&burn_zkbin.1)?;
-
-        let mint_circuit = ZkCircuit::new(empty_witnesses(&mint_zkbin)?, &mint_zkbin);
-        let burn_circuit = ZkCircuit::new(empty_witnesses(&burn_zkbin)?, &burn_zkbin);
-
-        // Since we're creating the first half, we generate the blinds.
-        let value_blinds = [pallas::Scalar::random(&mut OsRng), pallas::Scalar::random(&mut OsRng)];
-        let token_blinds = [pallas::Base::random(&mut OsRng), pallas::Base::random(&mut OsRng)];
-
-        // Now we should have everything we need to build the swap half
-        eprintln!("Creating Mint and Burn circuit proving keys");
-        let mint_pk = ProvingKey::build(mint_zkbin.k, &mint_circuit);
-        let burn_pk = ProvingKey::build(burn_zkbin.k, &burn_circuit);
-        let builder = SwapCallBuilder {
-            pubkey: address,
-            value_send,
-            token_id_send: token_send,
-            value_recv,
-            token_id_recv: token_recv,
-            user_data_blind_send: pallas::Base::random(&mut OsRng), // <-- FIXME: Perhaps should be passed in
-            spend_hook_recv: pallas::Base::zero(), // <-- FIXME: Should be passed in
-            user_data_recv: pallas::Base::zero(),  // <-- FIXME: Should be passed in
-            value_blinds,
-            token_blinds,
-            coin: burn_coin,
-            tree,
-            mint_zkbin,
-            mint_pk,
-            burn_zkbin,
-            burn_pk,
-        };
-
-        eprintln!("Building first half of the swap transaction");
-        let debris = builder.build()?;
-
-        // Now we have the half, so we can build `PartialSwapData` and return it.
-        let ret = PartialSwapData {
-            params: debris.params,
-            proofs: debris.proofs,
-            value_pair: (value_send, value_recv),
-            token_pair: (token_send, token_recv),
-            value_blinds: value_blinds.to_vec(),
-            token_blinds: token_blinds.to_vec(),
-        };
-
-        Ok(ret)
-    }
-
-    /// Create a full transaction by inspecting and verifying given partial swap data,
-    /// making the other half, and joining all this into a `Transaction` object.
-    pub async fn join_swap(&self, partial: PartialSwapData) -> Result<Transaction> {
-        // Our side of the tx in the pairs is the second half, so we try to find
-        // an unspent coin like that in our wallet.
-        let mut owncoins = self.get_coins(false).await?;
-        owncoins.retain(|x| {
-            x.0.note.value == partial.value_pair.1 && x.0.note.token_id == partial.token_pair.1
-        });
-
-        if owncoins.is_empty() {
-            return Err(anyhow!(
-                "Did not find any unspent coins of value {} and token_id {}",
-                partial.value_pair.1,
-                partial.token_pair.1
-            ))
-        }
-
-        // If there are any, we'll just spend the first one we see.
-        let burn_coin = owncoins[0].0.clone();
-
-        // Fetch our default address // FIXME: Should actually be getting is_default
-        let address = self.wallet_address(1).await?;
-
-        // We'll also need our Merkle tree
-        let tree = self.get_money_tree().await?;
-
-        let contract_id = *MONEY_CONTRACT_ID;
-
-        // Now we need to do a lookup for the zkas proof bincodes, and create
-        // the circuit objects and proving keys so we can build the transaction.
-        // We also do this through the RPC.
-        let zkas_bins = self.lookup_zkas(&contract_id).await?;
-
-        let Some(mint_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_MINT_NS_V1)
-        else {
-            return Err(anyhow!("Mint circuit not found"))
-        };
-
-        let Some(burn_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_BURN_NS_V1)
-        else {
-            return Err(anyhow!("Burn circuit not found"))
-        };
-
-        let mint_zkbin = ZkBinary::decode(&mint_zkbin.1)?;
-        let burn_zkbin = ZkBinary::decode(&burn_zkbin.1)?;
-
-        let mint_circuit = ZkCircuit::new(empty_witnesses(&mint_zkbin)?, &mint_zkbin);
-        let burn_circuit = ZkCircuit::new(empty_witnesses(&burn_zkbin)?, &burn_zkbin);
-
-        // TODO: Maybe some kind of verification at this point
-
-        // Now we should have everything we need to build the swap half
-        eprintln!("Creating Mint and Burn circuit proving keys");
-        let mint_pk = ProvingKey::build(mint_zkbin.k, &mint_circuit);
-        let burn_pk = ProvingKey::build(burn_zkbin.k, &burn_circuit);
-        let builder = SwapCallBuilder {
-            pubkey: address,
-            value_send: partial.value_pair.1,
-            token_id_send: partial.token_pair.1,
-            value_recv: partial.value_pair.0,
-            token_id_recv: partial.token_pair.0,
-            user_data_blind_send: pallas::Base::random(&mut OsRng), // <-- FIXME: Perhaps should be passed in
-            spend_hook_recv: pallas::Base::zero(), // <-- FIXME: Should be passed in
-            user_data_recv: pallas::Base::zero(),  // <-- FIXME: Should be passed in
-            value_blinds: [partial.value_blinds[1], partial.value_blinds[0]],
-            token_blinds: [partial.token_blinds[1], partial.token_blinds[0]],
-            coin: burn_coin,
-            tree,
-            mint_zkbin,
-            mint_pk,
-            burn_zkbin,
-            burn_pk,
-        };
-
-        eprintln!("Building second half of the swap transaction");
-        let debris = builder.build()?;
-
-        let full_params = MoneyTransferParamsV1 {
-            clear_inputs: vec![],
-            inputs: vec![partial.params.inputs[0].clone(), debris.params.inputs[0].clone()],
-            outputs: vec![partial.params.outputs[0].clone(), debris.params.outputs[0].clone()],
-        };
-
-        let full_proofs = vec![
-            partial.proofs[0].clone(),
-            debris.proofs[0].clone(),
-            partial.proofs[1].clone(),
-            debris.proofs[1].clone(),
-        ];
-
-        let mut data = vec![MoneyFunction::OtcSwapV1 as u8];
-        full_params.encode(&mut data)?;
-        let mut tx = Transaction {
-            calls: vec![ContractCall { contract_id, data }],
-            proofs: vec![full_proofs],
-            signatures: vec![],
-        };
-        eprintln!("Signing swap transaction");
-        let sigs = tx.create_sigs(&mut OsRng, &[debris.signature_secret])?;
-        tx.signatures = vec![sigs];
-
-        Ok(tx)
-    }
-
-    /// Inspect and verify a given swap (half or full) transaction
-    pub async fn inspect_swap(&self, bytes: Vec<u8>) -> Result<()> {
-        let mut full: Option<Transaction> = None;
-        let mut half: Option<PartialSwapData> = None;
-
-        if let Ok(v) = deserialize(&bytes) {
-            full = Some(v)
-        };
-
-        match deserialize(&bytes) {
-            Ok(v) => half = Some(v),
-            Err(_) => {
-                if full.is_none() {
-                    return Err(anyhow!("Failed to deserialize to Transaction or PartialSwapData"))
-                }
-            }
-        }
-
-        if let Some(tx) = full {
-            // We're inspecting a full transaction
-            if tx.calls.len() != 1 {
-                eprintln!(
-                    "Found {} contract calls in the transaction, there should be 1",
-                    tx.calls.len()
-                );
-                return Err(anyhow!("Inspection failed"))
-            }
-
-            let params: MoneyTransferParamsV1 = deserialize(&tx.calls[0].data[1..])?;
-            eprintln!("Parameters:\n{:#?}", params);
-
-            if params.inputs.len() != 2 {
-                eprintln!("Found {} inputs, there should be 2", params.inputs.len());
-                return Err(anyhow!("Inspection failed"))
-            }
-
-            if params.outputs.len() != 2 {
-                eprintln!("Found {} outputs, there should be 2", params.outputs.len());
-                return Err(anyhow!("Inspection failed"))
-            }
-
-            // Try to decrypt one of the outputs.
-            let secret_keys = self.get_money_secrets().await?;
-            let mut skey: Option<SecretKey> = None;
-            let mut note: Option<MoneyNote> = None;
-            let mut output_idx = 0;
-
-            for output in &params.outputs {
-                eprintln!("Trying to decrypt note in output {}", output_idx);
-
-                for secret in &secret_keys {
-                    if let Ok(d_note) = output.note.decrypt::<MoneyNote>(secret) {
-                        let s: SecretKey = deserialize(&d_note.memo)?;
-                        skey = Some(s);
-                        note = Some(d_note);
-                        eprintln!("Successfully decrypted and found an ephemeral secret");
-                        break
-                    }
-                }
-
-                if note.is_some() {
-                    break
-                }
-
-                output_idx += 1;
-            }
-
-            let Some(note) = note else {
-                eprintln!("Error: Could not decrypt notes of either output");
-                return Err(anyhow!("Inspection failed"))
-            };
-
-            eprintln!(
-                "Output[{}] value: {} ({})",
-                output_idx,
-                note.value,
-                encode_base10(note.value, 8)
-            );
-            eprintln!("Output[{}] token ID: {}", output_idx, note.token_id);
-
-            let skey = skey.unwrap();
-            let (pub_x, pub_y) = PublicKey::from_secret(skey).xy();
-            let coin = Coin::from(poseidon_hash([
-                pub_x,
-                pub_y,
-                pallas::Base::from(note.value),
-                note.token_id.inner(),
-                note.serial,
-            ]));
-
-            if coin == params.outputs[output_idx].coin {
-                eprintln!("Output[{}] coin matches decrypted note metadata", output_idx);
-            } else {
-                eprintln!("Error: Output[{}] coin does not match note metadata", output_idx);
-                return Err(anyhow!("Inspection failed"))
-            }
-
-            let valcom = pedersen_commitment_u64(note.value, note.value_blind);
-            let tokcom = poseidon_hash([note.token_id.inner(), note.token_blind]);
-
-            if valcom != params.outputs[output_idx].value_commit {
-                eprintln!(
-                    "Error: Output[{}] value commitment does not match note metadata",
-                    output_idx
-                );
-                return Err(anyhow!("Inspection failed"))
-            }
-
-            if tokcom != params.outputs[output_idx].token_commit {
-                eprintln!(
-                    "Error: Output[{}] token commitment does not match note metadata",
-                    output_idx
-                );
-                return Err(anyhow!("Inspection failed"))
-            }
-
-            eprintln!("Value and token commitments match decrypted note metadata");
-
-            // Verify that the output commitments match the other input commitments
-            match output_idx {
-                0 => {
-                    if valcom != params.inputs[1].value_commit ||
-                        tokcom != params.inputs[1].token_commit
-                    {
-                        eprintln!("Error: Value/Token commits of output[0] do not match input[1]");
-                        return Err(anyhow!("Inspection failed"))
-                    }
-                }
-                1 => {
-                    if valcom != params.inputs[0].value_commit ||
-                        tokcom != params.inputs[0].token_commit
-                    {
-                        eprintln!("Error: Value/Token commits of output[1] do not match input[0]");
-                        return Err(anyhow!("Inspection failed"))
-                    }
-                }
-                _ => unreachable!(),
-            }
-
-            eprintln!("Found matching pedersen commitments for outputs and inputs");
-
-            // TODO: Verify signature
-            // TODO: Verify ZK proofs
-            return Ok(())
-        }
-
-        // Inspect PartialSwapData
-        let partial = half.unwrap();
-        eprintln!("{}", partial);
-
-        Ok(())
-    }
-
-    /// Sign a given transaction by retrieving the secret key from the encrypted
-    /// note and prepending it to the transaction's signatures.
-    pub async fn sign_swap(&self, tx: &mut Transaction) -> Result<()> {
-        // We need our secret keys to try and decrypt the note
-        let secret_keys = self.get_money_secrets().await?;
-        let params: MoneyTransferParamsV1 = deserialize(&tx.calls[0].data[1..])?;
-
-        // Our output should be outputs[0] so we try to decrypt that.
-        let encrypted_note = &params.outputs[0].note;
-
-        eprintln!("Trying to decrypt note in outputs[0]");
-        let mut skey = None;
-
-        for secret in &secret_keys {
-            if let Ok(note) = encrypted_note.decrypt::<MoneyNote>(secret) {
-                let s: SecretKey = deserialize(&note.memo)?;
-                eprintln!("Successfully decrypted and found an ephemeral secret");
-                skey = Some(s);
-                break
-            }
-        }
-
-        let Some(skey) = skey else {
-            eprintln!("Error: Failed to decrypt note with any of our secret keys");
-            return Err(anyhow!("Failed to decrypt note with any of our secret keys"))
-        };
-
-        eprintln!("Signing swap transaction");
-        let sigs = tx.create_sigs(&mut OsRng, &[skey])?;
-        tx.signatures[0].insert(0, sigs[0]);
-
-        Ok(())
-    }
-}

+ 0 - 153
bin/drk/src/rpc_token.rs

@@ -1,153 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use anyhow::{anyhow, Result};
-use darkfi::{
-    tx::Transaction,
-    util::parse::decode_base10,
-    zk::{proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses},
-    zkas::ZkBinary,
-};
-use darkfi_money_contract::{
-    client::{token_freeze_v1::TokenFreezeCallBuilder, token_mint_v1::TokenMintCallBuilder},
-    MoneyFunction, MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
-};
-use darkfi_sdk::{
-    crypto::{contract_id::MONEY_CONTRACT_ID, Keypair, PublicKey, TokenId},
-    pasta::pallas,
-    tx::ContractCall,
-};
-use darkfi_serial::Encodable;
-use rand::rngs::OsRng;
-
-use super::Drk;
-
-impl Drk {
-    /// Create a token mint transaction. Returns the transaction object on success.
-    pub async fn mint_token(
-        &self,
-        amount: &str,
-        recipient: PublicKey,
-        token_id: TokenId,
-    ) -> Result<Transaction> {
-        // TODO: Mint directly into DAO treasury
-        let spend_hook = pallas::Base::zero();
-        let user_data = pallas::Base::zero();
-
-        let amount = decode_base10(amount, 8, false)?;
-
-        let mut tokens = self.list_tokens().await?;
-        tokens.retain(|x| x.0 == token_id);
-        if tokens.is_empty() {
-            return Err(anyhow!("Did not find mint authority for token ID {}", token_id))
-        }
-        assert!(tokens.len() == 1);
-
-        let mint_authority = Keypair::new(tokens[0].1);
-
-        if tokens[0].2 {
-            return Err(anyhow!("This token mint is marked as frozen in the wallet"))
-        }
-
-        // Now we need to do a lookup for the zkas proof bincodes, and create
-        // the circuit objects and proving keys so we can build the transaction.
-        // We also do this through the RPC.
-        let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
-        let zkas_ns = MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1;
-
-        let Some(token_mint_zkbin) = zkas_bins.iter().find(|x| x.0 == zkas_ns) else {
-            return Err(anyhow!("Token mint circuit not found"))
-        };
-
-        let token_mint_zkbin = ZkBinary::decode(&token_mint_zkbin.1)?;
-        let token_mint_circuit =
-            ZkCircuit::new(empty_witnesses(&token_mint_zkbin)?, &token_mint_zkbin);
-
-        eprintln!("Creating token mint circuit proving keys");
-        let token_mint_pk = ProvingKey::build(token_mint_zkbin.k, &token_mint_circuit);
-        let mint_builder = TokenMintCallBuilder {
-            mint_authority,
-            recipient,
-            amount,
-            spend_hook,
-            user_data,
-            token_mint_zkbin,
-            token_mint_pk,
-        };
-
-        eprintln!("Building transaction parameters");
-        let debris = mint_builder.build()?;
-
-        // Encode and sign the transaction
-        let mut data = vec![MoneyFunction::TokenMintV1 as u8];
-        debris.params.encode(&mut data)?;
-        let calls = vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }];
-        let proofs = vec![debris.proofs];
-        let mut tx = Transaction { calls, proofs, signatures: vec![] };
-        let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?;
-        tx.signatures = vec![sigs];
-
-        Ok(tx)
-    }
-
-    /// Create a token freeze transaction. Returns the transaction object on success.
-    pub async fn freeze_token(&self, token_id: TokenId) -> Result<Transaction> {
-        let mut tokens = self.list_tokens().await?;
-        tokens.retain(|x| x.0 == token_id);
-        if tokens.is_empty() {
-            return Err(anyhow!("Did not find mint authority for token ID {}", token_id))
-        }
-        assert!(tokens.len() == 1);
-
-        let mint_authority = Keypair::new(tokens[0].1);
-
-        if tokens[0].2 {
-            return Err(anyhow!("This token is already marked as frozen in the wallet"))
-        }
-
-        let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
-        let zkas_ns = MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1;
-
-        let Some(token_freeze_zkbin) = zkas_bins.iter().find(|x| x.0 == zkas_ns) else {
-            return Err(anyhow!("Token freeze circuit not found"))
-        };
-
-        let token_freeze_zkbin = ZkBinary::decode(&token_freeze_zkbin.1)?;
-        let token_freeze_circuit =
-            ZkCircuit::new(empty_witnesses(&token_freeze_zkbin)?, &token_freeze_zkbin);
-
-        eprintln!("Creating token freeze circuit proving keys");
-        let token_freeze_pk = ProvingKey::build(token_freeze_zkbin.k, &token_freeze_circuit);
-        let freeze_builder =
-            TokenFreezeCallBuilder { mint_authority, token_freeze_zkbin, token_freeze_pk };
-
-        eprintln!("Building transaction parameters");
-        let debris = freeze_builder.build()?;
-
-        // Encode and sign the transaction
-        let mut data = vec![MoneyFunction::TokenFreezeV1 as u8];
-        debris.params.encode(&mut data)?;
-        let calls = vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }];
-        let proofs = vec![debris.proofs];
-        let mut tx = Transaction { calls, proofs, signatures: vec![] };
-        let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?;
-        tx.signatures = vec![sigs];
-
-        Ok(tx)
-    }
-}

+ 0 - 171
bin/drk/src/rpc_transfer.rs

@@ -1,171 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::str::FromStr;
-
-use anyhow::{anyhow, Result};
-use darkfi::{
-    tx::Transaction,
-    util::parse::{decode_base10, encode_base10},
-    zk::{halo2::Field, proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses},
-    zkas::ZkBinary,
-};
-use darkfi_dao_contract::model::DaoBulla;
-use darkfi_money_contract::{
-    client::{transfer_v1::TransferCallBuilder, OwnCoin},
-    MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
-};
-use darkfi_sdk::{
-    crypto::{
-        contract_id::{DAO_CONTRACT_ID, MONEY_CONTRACT_ID},
-        Keypair, PublicKey, TokenId,
-    },
-    pasta::pallas,
-    tx::ContractCall,
-};
-use darkfi_serial::Encodable;
-use rand::rngs::OsRng;
-
-use super::Drk;
-
-impl Drk {
-    /// Create a payment transaction. Returns the transaction object on success.
-    pub async fn transfer(
-        &self,
-        amount: &str,
-        token_id: TokenId,
-        recipient: PublicKey,
-        dao: bool,
-        dao_bulla: Option<String>,
-    ) -> Result<Transaction> {
-        let dao_bulla: Option<DaoBulla> = if dao {
-            let Some(dao_bulla) = dao_bulla else {
-                return Err(anyhow!("Missing DAO bulla in parameters"))
-            };
-
-            Some(DaoBulla::from_str(dao_bulla.as_str())?)
-        } else {
-            None
-        };
-
-        let (spend_hook, user_data, user_data_blind) = if dao {
-            (DAO_CONTRACT_ID.inner(), dao_bulla.unwrap().inner(), pallas::Base::random(&mut OsRng))
-        } else {
-            (pallas::Base::zero(), pallas::Base::zero(), pallas::Base::random(&mut OsRng))
-        };
-
-        // First get all unspent OwnCoins to see what our balance is.
-        eprintln!("Fetching OwnCoins");
-        let owncoins = self.get_coins(false).await?;
-        let mut owncoins: Vec<OwnCoin> = owncoins.iter().map(|x| x.0.clone()).collect();
-        // We're only interested in the ones for the token_id we're sending
-        // And the ones not owned by some protocol (meaning spend-hook should be 0)
-        owncoins.retain(|x| x.note.token_id == token_id);
-        owncoins.retain(|x| x.note.spend_hook == pallas::Base::zero());
-        if owncoins.is_empty() {
-            return Err(anyhow!("Did not find any coins with token ID: {}", token_id))
-        }
-
-        // FIXME: Do not hardcode 8 decimals
-        let amount = decode_base10(amount, 8, false)?;
-        let mut balance = 0;
-        for coin in owncoins.iter() {
-            balance += coin.note.value;
-        }
-
-        if balance < amount {
-            return Err(anyhow!(
-                "Not enough balance for token ID: {}, found: {}",
-                token_id,
-                encode_base10(balance, 8)
-            ))
-        }
-
-        // We'll also need our Merkle tree
-        let tree = self.get_money_tree().await?;
-
-        // TODO: Which keypair to actually use?
-        let secrets = self.get_money_secrets().await?;
-        let keypair = Keypair::new(secrets[0]);
-
-        let contract_id = *MONEY_CONTRACT_ID;
-
-        // Now we need to do a lookup for the zkas proof bincodes, and create
-        // the circuit objects and proving keys so we can build the transaction.
-        // We also do this through the RPC.
-        let zkas_bins = self.lookup_zkas(&contract_id).await?;
-
-        let Some(mint_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_MINT_NS_V1)
-        else {
-            return Err(anyhow!("Mint circuit not found"))
-        };
-
-        let Some(burn_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_BURN_NS_V1)
-        else {
-            return Err(anyhow!("Burn circuit not found"))
-        };
-
-        let mint_zkbin = ZkBinary::decode(&mint_zkbin.1)?;
-        let burn_zkbin = ZkBinary::decode(&burn_zkbin.1)?;
-
-        let mint_circuit = ZkCircuit::new(empty_witnesses(&mint_zkbin)?, &mint_zkbin);
-        let burn_circuit = ZkCircuit::new(empty_witnesses(&burn_zkbin)?, &burn_zkbin);
-
-        eprintln!("Creating Mint and Burn circuit proving keys");
-        let mint_pk = ProvingKey::build(mint_zkbin.k, &mint_circuit);
-        let burn_pk = ProvingKey::build(burn_zkbin.k, &burn_circuit);
-        let transfer_builder = TransferCallBuilder {
-            keypair,
-            recipient,
-            value: amount,
-            token_id,
-            rcpt_spend_hook: spend_hook,
-            rcpt_user_data: user_data,
-            rcpt_user_data_blind: user_data_blind,
-            change_spend_hook: pallas::Base::zero(),
-            change_user_data: pallas::Base::zero(),
-            change_user_data_blind: user_data_blind, // FIXME: I'm reusing this blind but dunno why
-            coins: owncoins,
-            tree,
-            mint_zkbin,
-            mint_pk,
-            burn_zkbin,
-            burn_pk,
-            clear_input: false,
-        };
-
-        eprintln!("Building transaction parameters");
-        let debris = transfer_builder.build()?;
-
-        // Encode and sign the transaction
-        let mut data = vec![MoneyFunction::TransferV1 as u8];
-        debris.params.encode(&mut data)?;
-        let calls = vec![ContractCall { contract_id, data }];
-        let proofs = vec![debris.proofs];
-        let mut tx = Transaction { calls, proofs, signatures: vec![] };
-        let sigs = tx.create_sigs(&mut OsRng, &debris.signature_secrets)?;
-        tx.signatures = vec![sigs];
-
-        // We need to mark the coins we've spent in our wallet
-        for spent_coin in debris.spent_coins {
-            self.mark_spent_coin(&spent_coin.coin).await?;
-        }
-
-        Ok(tx)
-    }
-}

+ 0 - 0
bin/drk2/src/swap.rs → bin/drk/src/swap.rs


+ 0 - 0
bin/drk2/src/token.rs → bin/drk/src/token.rs


+ 0 - 0
bin/drk2/src/transfer.rs → bin/drk/src/transfer.rs


+ 0 - 0
bin/drk2/src/txs_history.rs → bin/drk/src/txs_history.rs


+ 0 - 43
bin/drk/src/wallet.rs

@@ -1,43 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use anyhow::Result;
-use darkfi::rpc::jsonrpc::JsonRequest;
-use serde_json::json;
-
-use super::Drk;
-
-impl Drk {
-    /// Initialize wallet with tables for drk
-    pub async fn initialize_wallet(&self) -> Result<()> {
-        let wallet_schema = include_str!("../wallet.sql");
-
-        // We perform a request to darkfid with the schema to initialize
-        // the necessary tables in the wallet.
-        let req = JsonRequest::new("wallet.exec_sql", json!([wallet_schema]));
-        let rep = self.rpc_client.request(req).await?;
-
-        if rep == true {
-            eprintln!("Successfully initialized wallet schema for drk");
-        } else {
-            eprintln!("[initialize_wallet] Got unexpected reply from darkfid: {}", rep);
-        }
-
-        Ok(())
-    }
-}

+ 0 - 1322
bin/drk/src/wallet_dao.rs

@@ -1,1322 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{collections::HashMap, fmt};
-
-use anyhow::{anyhow, Result};
-use darkfi::{
-    rpc::jsonrpc::JsonRequest, tx::Transaction, util::parse::encode_base10,
-    wallet::walletdb::QueryType,
-};
-use darkfi_dao_contract::{
-    client::{
-        DaoProposeNote, DaoVoteNote, DAO_DAOS_COL_APPROVAL_RATIO_BASE,
-        DAO_DAOS_COL_APPROVAL_RATIO_QUOT, DAO_DAOS_COL_BULLA_BLIND, DAO_DAOS_COL_CALL_INDEX,
-        DAO_DAOS_COL_DAO_ID, DAO_DAOS_COL_GOV_TOKEN_ID, DAO_DAOS_COL_LEAF_POSITION,
-        DAO_DAOS_COL_NAME, DAO_DAOS_COL_PROPOSER_LIMIT, DAO_DAOS_COL_QUORUM, DAO_DAOS_COL_SECRET,
-        DAO_DAOS_COL_TX_HASH, DAO_DAOS_TABLE, DAO_PROPOSALS_COL_AMOUNT,
-        DAO_PROPOSALS_COL_BULLA_BLIND, DAO_PROPOSALS_COL_CALL_INDEX, DAO_PROPOSALS_COL_DAO_ID,
-        DAO_PROPOSALS_COL_LEAF_POSITION, DAO_PROPOSALS_COL_MONEY_SNAPSHOT_TREE,
-        DAO_PROPOSALS_COL_OUR_VOTE_ID, DAO_PROPOSALS_COL_PROPOSAL_ID,
-        DAO_PROPOSALS_COL_RECV_PUBLIC, DAO_PROPOSALS_COL_SENDCOIN_TOKEN_ID,
-        DAO_PROPOSALS_COL_TX_HASH, DAO_PROPOSALS_TABLE, DAO_TREES_COL_DAOS_TREE,
-        DAO_TREES_COL_PROPOSALS_TREE, DAO_TREES_TABLE, DAO_VOTES_COL_ALL_VOTE_BLIND,
-        DAO_VOTES_COL_ALL_VOTE_VALUE, DAO_VOTES_COL_CALL_INDEX, DAO_VOTES_COL_PROPOSAL_ID,
-        DAO_VOTES_COL_TX_HASH, DAO_VOTES_COL_VOTE_ID, DAO_VOTES_COL_VOTE_OPTION,
-        DAO_VOTES_COL_YES_VOTE_BLIND, DAO_VOTES_TABLE,
-    },
-    model::{DaoBulla, DaoMintParams, DaoProposeParams, DaoVoteParams},
-    DaoFunction,
-};
-use darkfi_sdk::{
-    bridgetree,
-    crypto::{
-        poseidon_hash, Keypair, MerkleNode, MerkleTree, PublicKey, SecretKey, TokenId,
-        DAO_CONTRACT_ID,
-    },
-    pasta::pallas,
-};
-use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
-use serde_json::json;
-
-use super::Drk;
-
-#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
-/// Parameters representing a DAO to be initialized
-pub struct DaoParams {
-    /// The minimum amount of governance tokens needed to open a proposal
-    pub proposer_limit: u64,
-    /// Minimal threshold of participating total tokens needed for a proposal to pass
-    pub quorum: u64,
-    /// The ratio of winning/total votes needed for a proposal to pass
-    pub approval_ratio_base: u64,
-    pub approval_ratio_quot: u64,
-    /// DAO's governance token ID
-    pub gov_token_id: TokenId,
-    /// Secret key for the DAO
-    pub secret_key: SecretKey,
-    /// DAO bulla blind
-    pub bulla_blind: pallas::Base,
-}
-
-impl fmt::Display for DaoParams {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let s = format!(
-            "{}\n{}\n{}: {} ({})\n{}: {} ({})\n{}: {}\n{}: {}\n{}: {}\n{}: {}\n{}: {:?}",
-            "DAO Parameters",
-            "==============",
-            "Proposer limit",
-            encode_base10(self.proposer_limit, 8),
-            self.proposer_limit,
-            "Quorum",
-            encode_base10(self.quorum, 8),
-            self.quorum,
-            "Approval ratio",
-            self.approval_ratio_quot as f64 / self.approval_ratio_base as f64,
-            "Governance Token ID",
-            self.gov_token_id,
-            "Public key",
-            PublicKey::from_secret(self.secret_key),
-            "Secret key",
-            self.secret_key,
-            "Bulla blind",
-            self.bulla_blind,
-        );
-
-        write!(f, "{}", s)
-    }
-}
-
-#[derive(Debug, Clone)]
-/// Parameters representing an intialized DAO, optionally deployed on-chain
-pub struct Dao {
-    /// Numeric identifier for the DAO
-    pub id: u64,
-    /// Named identifier for the DAO
-    pub name: String,
-    /// The minimum amount of governance tokens needed to open a proposal
-    pub proposer_limit: u64,
-    /// Minimal threshold of participating total tokens needed for a proposal to pass
-    pub quorum: u64,
-    /// The ratio of winning/total votes needed for a proposal to pass
-    pub approval_ratio_base: u64,
-    pub approval_ratio_quot: u64,
-    /// DAO's governance token ID
-    pub gov_token_id: TokenId,
-    /// Secret key for the DAO
-    pub secret_key: SecretKey,
-    /// DAO bulla blind
-    pub bulla_blind: pallas::Base,
-    /// Leaf position of the DAO in the Merkle tree of DAOs
-    pub leaf_position: Option<bridgetree::Position>,
-    /// The transaction hash where the DAO was deployed
-    pub tx_hash: Option<blake3::Hash>,
-    /// The call index in the transaction where the DAO was deployed
-    pub call_index: Option<u32>,
-}
-
-impl Dao {
-    pub fn bulla(&self) -> DaoBulla {
-        let (x, y) = PublicKey::from_secret(self.secret_key).xy();
-
-        DaoBulla::from(poseidon_hash([
-            pallas::Base::from(self.proposer_limit),
-            pallas::Base::from(self.quorum),
-            pallas::Base::from(self.approval_ratio_quot),
-            pallas::Base::from(self.approval_ratio_base),
-            self.gov_token_id.inner(),
-            x,
-            y,
-            self.bulla_blind,
-        ]))
-    }
-
-    pub fn keypair(&self) -> Keypair {
-        let public = PublicKey::from_secret(self.secret_key);
-        Keypair { public, secret: self.secret_key }
-    }
-}
-
-impl fmt::Display for Dao {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let s = format!(
-            "{}\n{}\n{}: {}\n{}: {}\n{}: {} ({})\n{}: {} ({})\n{}: {}\n{}: {}\n{}: {}\n{}: {}\n{}: {:?}\n{}: {:?}\n{}: {:?}\n{}: {:?}",
-            "DAO Parameters",
-            "==============",
-            "Name",
-            self.name,
-            "Bulla",
-            self.bulla(),
-            "Proposer limit",
-            encode_base10(self.proposer_limit, 8),
-            self.proposer_limit,
-            "Quorum",
-            encode_base10(self.quorum, 8),
-            self.quorum,
-            "Approval ratio",
-            self.approval_ratio_quot as f64 / self.approval_ratio_base as f64,
-            "Governance Token ID",
-            self.gov_token_id,
-            "Public key",
-            PublicKey::from_secret(self.secret_key),
-            "Secret key",
-            self.secret_key,
-            "Bulla blind",
-            self.bulla_blind,
-            "Leaf position",
-            self.leaf_position,
-            "Tx hash",
-            self.tx_hash,
-            "Call idx",
-            self.call_index,
-        );
-
-        write!(f, "{}", s)
-    }
-}
-
-#[derive(Debug, Clone)]
-/// Parameters representing an initialized DAO proposal, optionally deployed on-chain
-pub struct DaoProposal {
-    /// Numeric identifier for the proposal
-    pub id: u64,
-    /// The DAO bulla related to this proposal
-    pub dao_bulla: DaoBulla,
-    /// Recipient of this proposal's funds
-    pub recipient: PublicKey,
-    /// Amount of this proposal
-    pub amount: u64,
-    /// Token ID to be sent
-    pub token_id: TokenId,
-    /// Proposal's bulla blind
-    pub bulla_blind: pallas::Base,
-    /// Leaf position of this proposal in the Merkle tree of proposals
-    pub leaf_position: Option<bridgetree::Position>,
-    /// Snapshotted Money Merkle tree
-    pub money_snapshot_tree: Option<MerkleTree>,
-    /// Transaction hash where this proposal was proposed
-    pub tx_hash: Option<blake3::Hash>,
-    /// call index in the transaction where this proposal was proposed
-    pub call_index: Option<u32>,
-    /// The vote ID we've voted on this proposal
-    pub vote_id: Option<pallas::Base>,
-}
-
-impl DaoProposal {
-    pub fn bulla(&self) -> pallas::Base {
-        let (dest_x, dest_y) = self.recipient.xy();
-
-        poseidon_hash([
-            dest_x,
-            dest_y,
-            pallas::Base::from(self.amount),
-            self.token_id.inner(),
-            self.dao_bulla.inner(),
-            self.bulla_blind,
-        ])
-    }
-}
-
-impl fmt::Display for DaoProposal {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let s = format!(
-            concat!(
-                "Proposal parameters\n",
-                "===================\n",
-                "DAO Bulla: {}\n",
-                "Recipient: {}\n",
-                "Proposal amount: {} ({})\n",
-                "Proposal Token ID: {:?}\n",
-                "Proposal bulla blind: {:?}\n",
-                "Proposal leaf position: {:?}\n",
-                "Proposal tx hash: {:?}\n",
-                "Proposal call index: {:?}\n",
-                "Proposal vote ID: {:?}",
-            ),
-            self.dao_bulla,
-            self.recipient,
-            encode_base10(self.amount, 8),
-            self.amount,
-            self.token_id,
-            self.bulla_blind,
-            self.leaf_position,
-            self.tx_hash,
-            self.call_index,
-            self.vote_id,
-        );
-
-        write!(f, "{}", s)
-    }
-}
-
-#[derive(Debug, Clone)]
-/// Parameters representing a vote we've made on a DAO proposal
-pub struct DaoVote {
-    /// Numeric identifier for the vote
-    pub id: u64,
-    /// Numeric identifier for the proposal related to this vote
-    pub proposal_id: u64,
-    /// The vote
-    pub vote_option: bool,
-    /// Blinding factor for the yes vote
-    pub yes_vote_blind: pallas::Scalar,
-    /// Value of all votes
-    pub all_vote_value: u64,
-    /// Blinding facfor of all votes
-    pub all_vote_blind: pallas::Scalar,
-    /// Transaction hash where this vote was casted
-    pub tx_hash: Option<blake3::Hash>,
-    /// call index in the transaction where this vote was casted
-    pub call_index: Option<u32>,
-}
-
-impl Drk {
-    /// Initialize wallet with tables for the DAO contract
-    pub async fn initialize_dao(&self) -> Result<()> {
-        let wallet_schema = include_str!("../../../src/contract/dao/wallet.sql");
-
-        // We perform a request to darkfid with the schema to initialize
-        // the necessary tables in the wallet.
-        let req = JsonRequest::new("wallet.exec_sql", json!([wallet_schema]));
-        let rep = self.rpc_client.request(req).await?;
-
-        if rep == true {
-            eprintln!("Successfully initialized wallet schema for the DAO contract");
-        } else {
-            eprintln!("[initialize_dao] Got unexpected reply from darkfid: {}", rep);
-        }
-
-        // Check if we have to initialize the Merkle trees.
-        // We check if one exists, but we actually create two. This should be written
-        // a bit better and safer.
-        let mut tree_needs_init = false;
-        let query = format!("SELECT {} FROM {}", DAO_TREES_COL_DAOS_TREE, DAO_TREES_TABLE);
-        let params = json!([query, QueryType::Blob as u8, DAO_TREES_COL_DAOS_TREE]);
-        let req = JsonRequest::new("wallet.query_row_single", params);
-
-        // For now, on success, we don't care what's returned, but in the future
-        // we should actually check it.
-        // TODO: The RPC needs a better variant for errors so detailed inspection
-        //       can be done with error codes and all that.
-        if (self.rpc_client.request(req).await).is_err() {
-            tree_needs_init = true;
-        }
-
-        if tree_needs_init {
-            eprintln!("Initializing DAO Merkle trees");
-            let tree = MerkleTree::new(100);
-            self.put_dao_trees(&tree, &tree).await?;
-            eprintln!("Successfully initialized Merkle trees for the DAO contract");
-        }
-
-        Ok(())
-    }
-
-    /// Fetch all DAO secret keys from the wallet
-    pub async fn get_dao_secrets(&self) -> Result<Vec<SecretKey>> {
-        let daos = self.get_daos().await?;
-        let mut ret = Vec::with_capacity(daos.len());
-        for dao in daos {
-            ret.push(dao.secret_key);
-        }
-
-        Ok(ret)
-    }
-
-    /// Replace the DAO Merkle trees in the wallet.
-    pub async fn put_dao_trees(
-        &self,
-        daos_tree: &MerkleTree,
-        proposals_tree: &MerkleTree,
-    ) -> Result<()> {
-        let query = format!(
-            "DELETE FROM {}; INSERT INTO {} ({}, {}) VALUES (?1, ?2);",
-            DAO_TREES_TABLE, DAO_TREES_TABLE, DAO_TREES_COL_DAOS_TREE, DAO_TREES_COL_PROPOSALS_TREE,
-        );
-
-        let params = json!([
-            query,
-            QueryType::Blob as u8,
-            serialize(daos_tree),
-            QueryType::Blob as u8,
-            serialize(proposals_tree),
-        ]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    /// Fetch DAO Merkle trees from the wallet
-    pub async fn get_dao_trees(&self) -> Result<(MerkleTree, MerkleTree)> {
-        let query = format!("SELECT * FROM {}", DAO_TREES_TABLE);
-
-        let params = json!([
-            query,
-            QueryType::Blob as u8,
-            DAO_TREES_COL_DAOS_TREE,
-            QueryType::Blob as u8,
-            DAO_TREES_COL_PROPOSALS_TREE,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_single", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let daos_tree_bytes: Vec<u8> = serde_json::from_value(rep[0].clone())?;
-        let daos_tree = deserialize(&daos_tree_bytes)?;
-
-        let proposals_tree_bytes: Vec<u8> = serde_json::from_value(rep[1].clone())?;
-        let proposals_tree = deserialize(&proposals_tree_bytes)?;
-
-        Ok((daos_tree, proposals_tree))
-    }
-
-    /// Reset the DAO Merkle trees in the wallet
-    pub async fn reset_dao_trees(&self) -> Result<()> {
-        eprintln!("Resetting DAO Merkle trees");
-        let tree = MerkleTree::new(100);
-        self.put_dao_trees(&tree, &tree).await?;
-        eprintln!("Successfully reset DAO Merkle trees");
-
-        Ok(())
-    }
-
-    /// Reset confirmed DAOs in the wallet
-    pub async fn reset_daos(&self) -> Result<()> {
-        eprintln!("Resetting DAO confirmations");
-        let daos = self.get_daos().await?;
-        self.unconfirm_daos(&daos).await?;
-        eprintln!("Successfully unconfirmed DAOs");
-
-        Ok(())
-    }
-
-    pub async fn reset_dao_proposals(&self) -> Result<()> {
-        eprintln!("Resetting DAO proposals");
-        let query = format!("DELETE FROM {};", DAO_PROPOSALS_TABLE);
-
-        let params = json!([query]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    pub async fn reset_dao_votes(&self) -> Result<()> {
-        eprintln!("Resetting DAO votes");
-        let query = format!("DELETE FROM {};", DAO_VOTES_TABLE);
-
-        let params = json!([query]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    pub async fn get_dao_id_by_alias(&self, alias_filter: &str) -> Result<u64> {
-        let query = format!(
-            "SELECT {}, {} FROM {}",
-            DAO_DAOS_COL_DAO_ID, DAO_DAOS_COL_NAME, DAO_DAOS_TABLE,
-        );
-        let params = json!([
-            query,
-            QueryType::Integer as u8,
-            DAO_DAOS_COL_DAO_ID,
-            QueryType::Text as u8,
-            DAO_DAOS_COL_NAME,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_multi", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        // The returned thing should be an array of found rows.
-        let Some(rows) = rep.as_array() else {
-            return Err(anyhow!("[get_dao_id_by_alias] Unexpected response from darkfid: {}", rep))
-        };
-
-        for row in rows {
-            let Some(row) = row.as_array() else {
-                return Err(anyhow!(
-                    "[get_dao_id_by_alias] Unexpected response from darkfid: {}",
-                    rep
-                ))
-            };
-
-            let alias: String = serde_json::from_value(row[1].clone())?;
-            if alias != alias_filter {
-                continue
-            }
-
-            let dao_id: u64 = serde_json::from_value(row[0].clone())?;
-            return Ok(dao_id)
-        }
-
-        Err(anyhow!("[get_dao_id_by_alias] DAO not found"))
-    }
-
-    /// Convenience function. Interprets the alias either as the DAO alias or its ID
-    pub async fn get_dao_id(&self, alias: &str) -> Result<u64> {
-        if let Ok(id) = self.get_dao_id_by_alias(alias).await {
-            return Ok(id)
-        }
-        Ok(alias.parse()?)
-    }
-
-    /// Import given DAO params into the wallet with a given name.
-    pub async fn import_dao(&self, dao_name: String, dao_params: DaoParams) -> Result<()> {
-        // First let's check if we've imported this DAO with the given name before.
-        let daos = self.get_daos().await?;
-        if daos.iter().any(|x| x.name == dao_name) {
-            return Err(anyhow!("This DAO has already been imported"))
-        }
-
-        eprintln!("Importing \"{}\" DAO into the wallet", dao_name);
-
-        let query = format!(
-            "INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8);",
-            DAO_DAOS_TABLE,
-            DAO_DAOS_COL_NAME,
-            DAO_DAOS_COL_PROPOSER_LIMIT,
-            DAO_DAOS_COL_QUORUM,
-            DAO_DAOS_COL_APPROVAL_RATIO_BASE,
-            DAO_DAOS_COL_APPROVAL_RATIO_QUOT,
-            DAO_DAOS_COL_GOV_TOKEN_ID,
-            DAO_DAOS_COL_SECRET,
-            DAO_DAOS_COL_BULLA_BLIND,
-        );
-
-        let params = json!([
-            query,
-            QueryType::Text as u8,
-            dao_name,
-            QueryType::Blob as u8,
-            serialize(&dao_params.proposer_limit),
-            QueryType::Blob as u8,
-            serialize(&dao_params.quorum),
-            QueryType::Integer as u8,
-            dao_params.approval_ratio_base,
-            QueryType::Integer as u8,
-            dao_params.approval_ratio_quot,
-            QueryType::Blob as u8,
-            serialize(&dao_params.gov_token_id),
-            QueryType::Blob as u8,
-            serialize(&dao_params.secret_key),
-            QueryType::Blob as u8,
-            serialize(&dao_params.bulla_blind),
-        ]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-        eprintln!("DAO imported successfully");
-
-        Ok(())
-    }
-
-    /// List DAO(s) imported in the wallet. If an ID is given, just print the
-    /// metadata for that specific one, if found.
-    pub async fn dao_list(&self, dao_id: Option<u64>) -> Result<()> {
-        if let Some(dao_id) = dao_id {
-            return self.dao_list_single(dao_id).await
-        }
-
-        let daos = self.get_daos().await?;
-        for dao in daos {
-            println!("[{}] {}", dao.id, dao.name);
-        }
-
-        Ok(())
-    }
-
-    async fn dao_list_single(&self, dao_id: u64) -> Result<()> {
-        let dao = self.get_dao_by_id(dao_id).await?;
-
-        println!("{}", dao);
-
-        Ok(())
-    }
-
-    /// Fetch a DAO given a numeric ID
-    pub async fn get_dao_by_id(&self, dao_id: u64) -> Result<Dao> {
-        let daos = self.get_daos().await?;
-
-        let Some(dao) = daos.iter().find(|x| x.id == dao_id) else {
-            return Err(anyhow!("DAO not found in wallet"))
-        };
-
-        Ok(dao.clone())
-    }
-
-    /// Fetch all known DAOs from the wallet.
-    pub async fn get_daos(&self) -> Result<Vec<Dao>> {
-        let query = format!("SELECT * FROM {}", DAO_DAOS_TABLE);
-
-        let params = json!([
-            query,
-            QueryType::Integer as u8,
-            DAO_DAOS_COL_DAO_ID,
-            QueryType::Text as u8,
-            DAO_DAOS_COL_NAME,
-            QueryType::Blob as u8,
-            DAO_DAOS_COL_PROPOSER_LIMIT,
-            QueryType::Blob as u8,
-            DAO_DAOS_COL_QUORUM,
-            QueryType::Integer as u8,
-            DAO_DAOS_COL_APPROVAL_RATIO_BASE,
-            QueryType::Integer as u8,
-            DAO_DAOS_COL_APPROVAL_RATIO_QUOT,
-            QueryType::Blob as u8,
-            DAO_DAOS_COL_GOV_TOKEN_ID,
-            QueryType::Blob as u8,
-            DAO_DAOS_COL_SECRET,
-            QueryType::Blob as u8,
-            DAO_DAOS_COL_BULLA_BLIND,
-            QueryType::OptionBlob as u8,
-            DAO_DAOS_COL_LEAF_POSITION,
-            QueryType::OptionBlob as u8,
-            DAO_DAOS_COL_TX_HASH,
-            QueryType::OptionInteger as u8,
-            DAO_DAOS_COL_CALL_INDEX,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_multi", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let Some(rows) = rep.as_array() else {
-            return Err(anyhow!("[get_daos] Unexpected response from darkfid: {}", rep))
-        };
-
-        let mut daos = Vec::with_capacity(rows.len());
-
-        for row in rows {
-            let Some(row) = row.as_array() else {
-                return Err(anyhow!("[get_daos] Unexpected response from darkfid: {}", rep))
-            };
-
-            let id: u64 = serde_json::from_value(row[0].clone())?;
-
-            let name: String = serde_json::from_value(row[1].clone())?;
-
-            let proposer_limit_bytes: Vec<u8> = serde_json::from_value(row[2].clone())?;
-            let proposer_limit = deserialize(&proposer_limit_bytes)?;
-
-            let quorum_bytes: Vec<u8> = serde_json::from_value(row[3].clone())?;
-            let quorum = deserialize(&quorum_bytes)?;
-
-            let approval_ratio_base = serde_json::from_value(row[4].clone())?;
-            let approval_ratio_quot = serde_json::from_value(row[5].clone())?;
-
-            let gov_token_bytes: Vec<u8> = serde_json::from_value(row[6].clone())?;
-            let gov_token_id = deserialize(&gov_token_bytes)?;
-
-            let secret_bytes: Vec<u8> = serde_json::from_value(row[7].clone())?;
-            let secret_key = deserialize(&secret_bytes)?;
-
-            let bulla_blind_bytes: Vec<u8> = serde_json::from_value(row[8].clone())?;
-            let bulla_blind = deserialize(&bulla_blind_bytes)?;
-
-            let leaf_position_bytes: Vec<u8> = serde_json::from_value(row[9].clone())?;
-            let tx_hash_bytes: Vec<u8> = serde_json::from_value(row[10].clone())?;
-            let call_index = serde_json::from_value(row[11].clone())?;
-
-            let leaf_position = if leaf_position_bytes.is_empty() {
-                None
-            } else {
-                Some(deserialize(&leaf_position_bytes)?)
-            };
-
-            let tx_hash =
-                if tx_hash_bytes.is_empty() { None } else { Some(deserialize(&tx_hash_bytes)?) };
-
-            let dao = Dao {
-                id,
-                name,
-                proposer_limit,
-                quorum,
-                approval_ratio_base,
-                approval_ratio_quot,
-                gov_token_id,
-                secret_key,
-                bulla_blind,
-                leaf_position,
-                tx_hash,
-                call_index,
-            };
-
-            daos.push(dao);
-        }
-
-        // Here we sort the vec by ID. The SQL SELECT statement does not guarantee
-        // this, so just do it here.
-        daos.sort_by(|a, b| a.id.cmp(&b.id));
-        Ok(daos)
-    }
-
-    /// Fetch known unspent balances from the wallet for the given DAO ID
-    pub async fn dao_balance(&self, dao_id: u64) -> Result<HashMap<String, u64>> {
-        let daos = self.get_daos().await?;
-        let Some(dao) = daos.get(dao_id as usize - 1) else {
-            return Err(anyhow!("DAO with ID {} not found in wallet", dao_id))
-        };
-
-        let mut coins = self.get_coins(false).await?;
-        coins.retain(|x| x.0.note.spend_hook == DAO_CONTRACT_ID.inner());
-        coins.retain(|x| x.0.note.user_data == dao.bulla().inner());
-
-        // Fill this map with balances
-        let mut balmap: HashMap<String, u64> = HashMap::new();
-
-        for coin in coins {
-            let mut value = coin.0.note.value;
-
-            if let Some(prev) = balmap.get(&coin.0.note.token_id.to_string()) {
-                value += prev;
-            }
-
-            balmap.insert(coin.0.note.token_id.to_string(), value);
-        }
-
-        Ok(balmap)
-    }
-
-    /// Fetch all known DAO proposals from the wallet given a DAO ID
-    pub async fn get_dao_proposals(&self, dao_id: u64) -> Result<Vec<DaoProposal>> {
-        let daos = self.get_daos().await?;
-        let Some(dao) = daos.get(dao_id as usize - 1) else {
-            return Err(anyhow!("DAO with ID {} not found in wallet", dao_id))
-        };
-
-        let query = format!(
-            "SELECT * FROM {} WHERE {} = {}",
-            DAO_PROPOSALS_TABLE, DAO_PROPOSALS_COL_DAO_ID, dao_id
-        );
-
-        let params = json!([
-            query,
-            QueryType::Integer as u8,
-            DAO_PROPOSALS_COL_PROPOSAL_ID,
-            QueryType::Integer as u8,
-            DAO_PROPOSALS_COL_DAO_ID,
-            QueryType::Blob as u8,
-            DAO_PROPOSALS_COL_RECV_PUBLIC,
-            QueryType::Blob as u8,
-            DAO_PROPOSALS_COL_AMOUNT,
-            QueryType::Blob as u8,
-            DAO_PROPOSALS_COL_SENDCOIN_TOKEN_ID,
-            QueryType::Blob as u8,
-            DAO_PROPOSALS_COL_BULLA_BLIND,
-            QueryType::OptionBlob as u8,
-            DAO_PROPOSALS_COL_LEAF_POSITION,
-            QueryType::OptionBlob as u8,
-            DAO_PROPOSALS_COL_MONEY_SNAPSHOT_TREE,
-            QueryType::OptionBlob as u8,
-            DAO_PROPOSALS_COL_TX_HASH,
-            QueryType::OptionInteger as u8,
-            DAO_PROPOSALS_COL_CALL_INDEX,
-            QueryType::OptionBlob as u8,
-            DAO_PROPOSALS_COL_OUR_VOTE_ID,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_multi", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let Some(rows) = rep.as_array() else {
-            return Err(anyhow!("[get_proposals] Unexpected response from darkfid: {}", rep))
-        };
-
-        let mut proposals = Vec::with_capacity(rows.len());
-
-        for row in rows {
-            let Some(row) = row.as_array() else {
-                return Err(anyhow!("[get_proposals] Unexpected response from darkfid: {}", rep))
-            };
-
-            let id: u64 = serde_json::from_value(row[0].clone())?;
-
-            let dao_id: u64 = serde_json::from_value(row[1].clone())?;
-            assert!(dao_id == dao.id);
-            let dao_bulla = dao.bulla();
-
-            let recipient_bytes: Vec<u8> = serde_json::from_value(row[2].clone())?;
-            let recipient = deserialize(&recipient_bytes)?;
-
-            let amount_bytes: Vec<u8> = serde_json::from_value(row[3].clone())?;
-            let amount = deserialize(&amount_bytes)?;
-
-            let token_id_bytes: Vec<u8> = serde_json::from_value(row[4].clone())?;
-            let token_id = deserialize(&token_id_bytes)?;
-
-            let bulla_blind_bytes: Vec<u8> = serde_json::from_value(row[5].clone())?;
-            let bulla_blind = deserialize(&bulla_blind_bytes)?;
-
-            let leaf_position_bytes: Vec<u8> = serde_json::from_value(row[6].clone())?;
-
-            let money_snapshot_tree_bytes: Vec<u8> = serde_json::from_value(row[7].clone())?;
-
-            let tx_hash_bytes: Vec<u8> = serde_json::from_value(row[8].clone())?;
-
-            let call_index = serde_json::from_value(row[9].clone())?;
-
-            let vote_id_bytes: Vec<u8> = serde_json::from_value(row[10].clone())?;
-
-            let leaf_position = if leaf_position_bytes.is_empty() {
-                None
-            } else {
-                Some(deserialize(&leaf_position_bytes)?)
-            };
-
-            let money_snapshot_tree = if money_snapshot_tree_bytes.is_empty() {
-                None
-            } else {
-                Some(deserialize(&money_snapshot_tree_bytes)?)
-            };
-
-            let tx_hash =
-                if tx_hash_bytes.is_empty() { None } else { Some(deserialize(&tx_hash_bytes)?) };
-
-            let vote_id =
-                if vote_id_bytes.is_empty() { None } else { Some(deserialize(&vote_id_bytes)?) };
-
-            let proposal = DaoProposal {
-                id,
-                dao_bulla,
-                recipient,
-                amount,
-                token_id,
-                bulla_blind,
-                leaf_position,
-                money_snapshot_tree,
-                tx_hash,
-                call_index,
-                vote_id,
-            };
-
-            proposals.push(proposal);
-        }
-
-        // Here we sort the vec by ID. The SQL SELECT statement does not guarantee
-        // this, so just do it here.
-        proposals.sort_by(|a, b| a.id.cmp(&b.id));
-        Ok(proposals)
-    }
-
-    /// Fetch a DAO proposal by its ID
-    pub async fn get_dao_proposal_by_id(&self, proposal_id: u64) -> Result<DaoProposal> {
-        let query = format!(
-            "SELECT * FROM {} WHERE {} = {}",
-            DAO_PROPOSALS_TABLE, DAO_PROPOSALS_COL_PROPOSAL_ID, proposal_id
-        );
-
-        let params = json!([
-            query,
-            QueryType::Integer as u8,
-            DAO_PROPOSALS_COL_PROPOSAL_ID,
-            QueryType::Integer as u8,
-            DAO_PROPOSALS_COL_DAO_ID,
-            QueryType::Blob as u8,
-            DAO_PROPOSALS_COL_RECV_PUBLIC,
-            QueryType::Blob as u8,
-            DAO_PROPOSALS_COL_AMOUNT,
-            QueryType::Blob as u8,
-            DAO_PROPOSALS_COL_SENDCOIN_TOKEN_ID,
-            QueryType::Blob as u8,
-            DAO_PROPOSALS_COL_BULLA_BLIND,
-            QueryType::OptionBlob as u8,
-            DAO_PROPOSALS_COL_LEAF_POSITION,
-            QueryType::OptionBlob as u8,
-            DAO_PROPOSALS_COL_MONEY_SNAPSHOT_TREE,
-            QueryType::OptionBlob as u8,
-            DAO_PROPOSALS_COL_TX_HASH,
-            QueryType::OptionInteger as u8,
-            DAO_PROPOSALS_COL_CALL_INDEX,
-            QueryType::OptionBlob as u8,
-            DAO_PROPOSALS_COL_OUR_VOTE_ID,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_single", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let Some(row) = rep.as_array() else {
-            return Err(anyhow!("[get_proposal_by_id] Unexpected response from darkfid: {}", rep))
-        };
-
-        let id: u64 = serde_json::from_value(row[0].clone())?;
-        let dao_id: u64 = serde_json::from_value(row[1].clone())?;
-
-        let recipient_bytes: Vec<u8> = serde_json::from_value(row[2].clone())?;
-        let recipient = deserialize(&recipient_bytes)?;
-
-        let amount_bytes: Vec<u8> = serde_json::from_value(row[3].clone())?;
-        let amount = deserialize(&amount_bytes)?;
-
-        let token_id_bytes: Vec<u8> = serde_json::from_value(row[4].clone())?;
-        let token_id = deserialize(&token_id_bytes)?;
-
-        let bulla_blind_bytes: Vec<u8> = serde_json::from_value(row[5].clone())?;
-        let bulla_blind = deserialize(&bulla_blind_bytes)?;
-
-        let leaf_position_bytes: Vec<u8> = serde_json::from_value(row[6].clone())?;
-
-        let money_snapshot_tree_bytes: Vec<u8> = serde_json::from_value(row[7].clone())?;
-
-        let tx_hash_bytes: Vec<u8> = serde_json::from_value(row[8].clone())?;
-
-        let call_index = serde_json::from_value(row[9].clone())?;
-
-        let vote_id_bytes: Vec<u8> = serde_json::from_value(row[10].clone())?;
-
-        let leaf_position = if leaf_position_bytes.is_empty() {
-            None
-        } else {
-            Some(deserialize(&leaf_position_bytes)?)
-        };
-
-        let tx_hash =
-            if tx_hash_bytes.is_empty() { None } else { Some(deserialize(&tx_hash_bytes)?) };
-
-        let money_snapshot_tree = if money_snapshot_tree_bytes.is_empty() {
-            None
-        } else {
-            Some(deserialize(&money_snapshot_tree_bytes)?)
-        };
-
-        let vote_id =
-            if vote_id_bytes.is_empty() { None } else { Some(deserialize(&vote_id_bytes)?) };
-
-        let dao = self.get_dao_by_id(dao_id).await?;
-
-        let proposal = DaoProposal {
-            id,
-            dao_bulla: dao.bulla(),
-            recipient,
-            amount,
-            token_id,
-            bulla_blind,
-            leaf_position,
-            money_snapshot_tree,
-            tx_hash,
-            call_index,
-            vote_id,
-        };
-
-        Ok(proposal)
-    }
-
-    // Fetch all known DAO proposal votes from the wallet given a proposal ID
-    pub async fn get_dao_proposal_votes(&self, proposal_id: u64) -> Result<Vec<DaoVote>> {
-        let query = format!(
-            "SELECT * FROM {} WHERE {} = {}",
-            DAO_VOTES_TABLE, DAO_VOTES_COL_PROPOSAL_ID, proposal_id
-        );
-
-        let params = json!([
-            query,
-            QueryType::Integer as u8,
-            DAO_VOTES_COL_VOTE_ID,
-            QueryType::Integer as u8,
-            DAO_VOTES_COL_PROPOSAL_ID,
-            QueryType::Integer as u8,
-            DAO_VOTES_COL_VOTE_OPTION,
-            QueryType::Blob as u8,
-            DAO_VOTES_COL_YES_VOTE_BLIND,
-            QueryType::Blob as u8,
-            DAO_VOTES_COL_ALL_VOTE_VALUE,
-            QueryType::Blob as u8,
-            DAO_VOTES_COL_ALL_VOTE_BLIND,
-            QueryType::OptionBlob as u8,
-            DAO_VOTES_COL_TX_HASH,
-            QueryType::OptionInteger as u8,
-            DAO_VOTES_COL_CALL_INDEX,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_multi", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let Some(rows) = rep.as_array() else {
-            return Err(anyhow!(
-                "[get_dao_proposal_votes] Unexpected response from darkfid: {}",
-                rep
-            ))
-        };
-
-        let mut votes = Vec::with_capacity(rows.len());
-
-        for row in rows {
-            let Some(row) = row.as_array() else {
-                return Err(anyhow!(
-                    "[get_dao_proposal_votes] Unexpected response from darkfid: {}",
-                    rep
-                ))
-            };
-
-            let id: u64 = serde_json::from_value(row[0].clone())?;
-            let proposal_id: u64 = serde_json::from_value(row[1].clone())?;
-            let vote_option: u32 = serde_json::from_value(row[2].clone())?;
-            let vote_option = vote_option != 0;
-
-            let yes_vote_blind_bytes: Vec<u8> = serde_json::from_value(row[3].clone())?;
-            let yes_vote_blind = deserialize(&yes_vote_blind_bytes)?;
-
-            let all_vote_value_bytes: Vec<u8> = serde_json::from_value(row[4].clone())?;
-            let all_vote_value = deserialize(&all_vote_value_bytes)?;
-
-            let all_vote_blind_bytes: Vec<u8> = serde_json::from_value(row[5].clone())?;
-            let all_vote_blind = deserialize(&all_vote_blind_bytes)?;
-
-            let tx_hash_bytes: Vec<u8> = serde_json::from_value(row[6].clone())?;
-
-            let call_index = serde_json::from_value(row[7].clone())?;
-
-            let tx_hash =
-                if tx_hash_bytes.is_empty() { None } else { Some(deserialize(&tx_hash_bytes)?) };
-
-            let vote = DaoVote {
-                id,
-                proposal_id,
-                vote_option,
-                yes_vote_blind,
-                all_vote_value,
-                all_vote_blind,
-                tx_hash,
-                call_index,
-            };
-
-            votes.push(vote);
-        }
-
-        Ok(votes)
-    }
-
-    /// Append data related to DAO contract transactions into the wallet database.
-    /// Optionally, if `confirm` is true, also append the data in the Merkle trees, etc.
-    pub async fn apply_tx_dao_data(&self, tx: &Transaction, confirm: bool) -> Result<()> {
-        let cid = *DAO_CONTRACT_ID;
-        let mut daos = self.get_daos().await?;
-        let mut daos_to_confirm = vec![];
-        let (mut daos_tree, mut proposals_tree) = self.get_dao_trees().await?;
-
-        // DAOs that have been minted
-        let mut new_dao_bullas: Vec<(DaoBulla, Option<blake3::Hash>, u32)> = vec![];
-        // DAO proposals that have been minted
-        let mut new_dao_proposals: Vec<(
-            DaoProposeParams,
-            Option<MerkleTree>,
-            Option<blake3::Hash>,
-            u32,
-        )> = vec![];
-        let mut our_proposals: Vec<DaoProposal> = vec![];
-        // DAO votes that have been seen
-        let mut new_dao_votes: Vec<(DaoVoteParams, Option<blake3::Hash>, u32)> = vec![];
-        let mut dao_votes: Vec<DaoVote> = vec![];
-
-        // Run through the transaction and see what we got:
-        for (i, call) in tx.calls.iter().enumerate() {
-            if call.contract_id == cid && call.data[0] == DaoFunction::Mint as u8 {
-                eprintln!("Found Dao::Mint in call {}", i);
-                let params: DaoMintParams = deserialize(&call.data[1..])?;
-                let tx_hash = if confirm { Some(blake3::hash(&serialize(tx))) } else { None };
-                new_dao_bullas.push((params.dao_bulla, tx_hash, i as u32));
-                continue
-            }
-
-            if call.contract_id == cid && call.data[0] == DaoFunction::Propose as u8 {
-                eprintln!("Found Dao::Propose in call {}", i);
-                let params: DaoProposeParams = deserialize(&call.data[1..])?;
-                let tx_hash = if confirm { Some(blake3::hash(&serialize(tx))) } else { None };
-                // We need to clone the tree here for reproducing the snapshot Merkle root
-                let money_tree = if confirm { Some(self.get_money_tree().await?) } else { None };
-                new_dao_proposals.push((params, money_tree, tx_hash, i as u32));
-                continue
-            }
-
-            if call.contract_id == cid && call.data[0] == DaoFunction::Vote as u8 {
-                eprintln!("Found Dao::Vote in call {}", i);
-                let params: DaoVoteParams = deserialize(&call.data[1..])?;
-                let tx_hash = if confirm { Some(blake3::hash(&serialize(tx))) } else { None };
-                new_dao_votes.push((params, tx_hash, i as u32));
-                continue
-            }
-
-            if call.contract_id == cid && call.data[0] == DaoFunction::Exec as u8 {
-                // This seems to not need any special action
-                eprintln!("Found Dao::Exec in call {}", i);
-                continue
-            }
-        }
-
-        // This code should only be executed when finalized blocks are being scanned.
-        // Here we write the tx metadata, and actually do Merkle tree appends so we
-        // have to make sure it's the same for everyone.
-        if confirm {
-            for new_bulla in new_dao_bullas {
-                daos_tree.append(MerkleNode::from(new_bulla.0.inner()));
-                for dao in daos.iter_mut() {
-                    if dao.bulla() == new_bulla.0 {
-                        eprintln!(
-                            "Found minted DAO {}, noting down for wallet update",
-                            new_bulla.0
-                        );
-                        // We have this DAO imported in our wallet. Add the metadata:
-                        dao.leaf_position = daos_tree.mark();
-                        dao.tx_hash = new_bulla.1;
-                        dao.call_index = Some(new_bulla.2);
-                        daos_to_confirm.push(dao.clone());
-                    }
-                }
-            }
-
-            for proposal in new_dao_proposals {
-                proposals_tree.append(MerkleNode::from(proposal.0.proposal_bulla.inner()));
-
-                // If we're able to decrypt this note, that's the way to link it
-                // to a specific DAO.
-                for dao in &daos {
-                    if let Ok(note) = proposal.0.note.decrypt::<DaoProposeNote>(&dao.secret_key) {
-                        // We managed to decrypt it. Let's place this in a proper
-                        // DaoProposal object. We assume we can just increment the
-                        // ID by looking at how many proposals we already have.
-                        // We also assume we don't mantain duplicate DAOs in the
-                        // wallet.
-                        eprintln!("Managed to decrypt DAO proposal note");
-                        let daos_proposals = self.get_dao_proposals(dao.id).await?;
-                        let our_prop = DaoProposal {
-                            // This ID stuff is flaky.
-                            id: daos_proposals.len() as u64 + our_proposals.len() as u64 + 1,
-                            dao_bulla: dao.bulla(),
-                            recipient: note.proposal.dest,
-                            amount: note.proposal.amount,
-                            token_id: note.proposal.token_id,
-                            bulla_blind: note.proposal.blind,
-                            leaf_position: proposals_tree.mark(),
-                            money_snapshot_tree: proposal.1,
-                            tx_hash: proposal.2,
-                            call_index: Some(proposal.3),
-                            vote_id: None,
-                        };
-
-                        our_proposals.push(our_prop);
-                        break
-                    }
-                }
-            }
-
-            for vote in new_dao_votes {
-                for dao in &daos {
-                    if let Ok(note) = vote.0.note.decrypt::<DaoVoteNote>(&dao.secret_key) {
-                        eprintln!("Managed to decrypt DAO proposal vote note");
-                        let daos_proposals = self.get_dao_proposals(dao.id).await?;
-                        let mut proposal_id = None;
-
-                        for i in daos_proposals {
-                            if i.bulla() == vote.0.proposal_bulla.inner() {
-                                proposal_id = Some(i.id);
-                                break
-                            }
-                        }
-
-                        if proposal_id.is_none() {
-                            eprintln!("Warning: Decrypted DaoVoteNote but did not find proposal");
-                            break
-                        }
-
-                        let v = DaoVote {
-                            id: 0,
-                            proposal_id: proposal_id.unwrap(),
-                            vote_option: note.vote_option,
-                            yes_vote_blind: note.yes_vote_blind,
-                            all_vote_value: note.all_vote_value,
-                            all_vote_blind: note.all_vote_blind,
-                            tx_hash: vote.1,
-                            call_index: Some(vote.2),
-                        };
-
-                        dao_votes.push(v);
-                    }
-                }
-            }
-        }
-
-        if confirm {
-            self.put_dao_trees(&daos_tree, &proposals_tree).await?;
-            self.confirm_daos(&daos_to_confirm).await?;
-            self.put_dao_proposals(&our_proposals).await?;
-            self.put_dao_votes(&dao_votes).await?;
-        }
-
-        Ok(())
-    }
-
-    /// Confirm already imported DAO metadata into the wallet.
-    /// Here we just write the leaf position, tx hash, and call index.
-    /// Panics if the fields are None.
-    pub async fn confirm_daos(&self, daos: &[Dao]) -> Result<()> {
-        for dao in daos {
-            let query = format!(
-                "UPDATE {} SET {} = ?1, {} = ?2, {} = ?3 WHERE {} = {};",
-                DAO_DAOS_TABLE,
-                DAO_DAOS_COL_LEAF_POSITION,
-                DAO_DAOS_COL_TX_HASH,
-                DAO_DAOS_COL_CALL_INDEX,
-                DAO_DAOS_COL_DAO_ID,
-                dao.id,
-            );
-
-            let params = json!([
-                query,
-                QueryType::Blob as u8,
-                serialize(&dao.leaf_position.unwrap()),
-                QueryType::Blob as u8,
-                serialize(&dao.tx_hash.unwrap()),
-                QueryType::Integer as u8,
-                dao.call_index.unwrap(),
-            ]);
-
-            let req = JsonRequest::new("wallet.exec_sql", params);
-            let _ = self.rpc_client.request(req).await?;
-        }
-
-        Ok(())
-    }
-
-    /// Unconfirm imported DAOs by removing the leaf position, txid, and call index.
-    pub async fn unconfirm_daos(&self, daos: &[Dao]) -> Result<()> {
-        for dao in daos {
-            let query = format!(
-                "UPDATE {} SET {} = ?1, {} = ?2, {} = ?3 WHERE {} = {};",
-                DAO_DAOS_TABLE,
-                DAO_DAOS_COL_LEAF_POSITION,
-                DAO_DAOS_COL_TX_HASH,
-                DAO_DAOS_COL_CALL_INDEX,
-                DAO_DAOS_COL_DAO_ID,
-                dao.id,
-            );
-
-            let params = json!([
-                query,
-                QueryType::OptionBlob as u8,
-                None::<Vec<u8>>,
-                QueryType::OptionBlob as u8,
-                None::<Vec<u8>>,
-                QueryType::OptionInteger as u8,
-                None::<u64>,
-            ]);
-
-            let req = JsonRequest::new("wallet.exec_sql", params);
-            let _ = self.rpc_client.request(req).await?;
-        }
-
-        Ok(())
-    }
-
-    /// Import given DAO proposals into the wallet
-    pub async fn put_dao_proposals(&self, proposals: &[DaoProposal]) -> Result<()> {
-        let daos = self.get_daos().await?;
-
-        for proposal in proposals {
-            let Some(dao) = daos.iter().find(|x| x.bulla() == proposal.dao_bulla) else {
-                return Err(anyhow!("[put_dao_proposals] Couldn't find respective DAO"))
-            };
-
-            let query = format!(
-                "INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9);",
-                DAO_PROPOSALS_TABLE,
-                DAO_PROPOSALS_COL_DAO_ID,
-                DAO_PROPOSALS_COL_RECV_PUBLIC,
-                DAO_PROPOSALS_COL_AMOUNT,
-                DAO_PROPOSALS_COL_SENDCOIN_TOKEN_ID,
-                DAO_PROPOSALS_COL_BULLA_BLIND,
-                DAO_PROPOSALS_COL_LEAF_POSITION,
-                DAO_PROPOSALS_COL_MONEY_SNAPSHOT_TREE,
-                DAO_PROPOSALS_COL_TX_HASH,
-                DAO_PROPOSALS_COL_CALL_INDEX,
-            );
-
-            let params = json!([
-                query,
-                QueryType::Integer as u8,
-                dao.id,
-                QueryType::Blob as u8,
-                serialize(&proposal.recipient),
-                QueryType::Blob as u8,
-                serialize(&proposal.amount),
-                QueryType::Blob as u8,
-                serialize(&proposal.token_id),
-                QueryType::Blob as u8,
-                serialize(&proposal.bulla_blind),
-                QueryType::Blob as u8,
-                serialize(&proposal.leaf_position.unwrap()),
-                QueryType::Blob as u8,
-                serialize(&proposal.money_snapshot_tree.clone().unwrap()),
-                QueryType::Blob as u8,
-                serialize(&proposal.tx_hash.unwrap()),
-                QueryType::Integer as u8,
-                proposal.call_index,
-            ]);
-
-            let req = JsonRequest::new("wallet.exec_sql", params);
-            let _ = self.rpc_client.request(req).await?;
-        }
-
-        Ok(())
-    }
-
-    /// Import given DAO votes into the wallet
-    pub async fn put_dao_votes(&self, votes: &[DaoVote]) -> Result<()> {
-        for vote in votes {
-            eprintln!("Importing DAO vote into wallet");
-
-            let query = format!(
-                "INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);",
-                DAO_VOTES_TABLE,
-                DAO_VOTES_COL_PROPOSAL_ID,
-                DAO_VOTES_COL_VOTE_OPTION,
-                DAO_VOTES_COL_YES_VOTE_BLIND,
-                DAO_VOTES_COL_ALL_VOTE_VALUE,
-                DAO_VOTES_COL_ALL_VOTE_BLIND,
-                DAO_VOTES_COL_TX_HASH,
-                DAO_VOTES_COL_CALL_INDEX,
-            );
-
-            let params = json!([
-                query,
-                QueryType::Integer as u8,
-                vote.proposal_id,
-                QueryType::Integer as u8,
-                vote.vote_option as u64,
-                QueryType::Blob as u8,
-                serialize(&vote.yes_vote_blind),
-                QueryType::Blob as u8,
-                serialize(&vote.all_vote_value),
-                QueryType::Blob as u8,
-                serialize(&vote.all_vote_blind),
-                QueryType::Blob as u8,
-                serialize(&vote.tx_hash.unwrap()),
-                QueryType::Integer as u8,
-                vote.call_index.unwrap(),
-            ]);
-
-            let req = JsonRequest::new("wallet.exec_sql", params);
-            let _ = self.rpc_client.request(req).await?;
-            eprintln!("DAO vote added to wallet");
-        }
-
-        Ok(())
-    }
-}

+ 0 - 788
bin/drk/src/wallet_money.rs

@@ -1,788 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{collections::HashMap, str::FromStr};
-
-use anyhow::{anyhow, Result};
-use darkfi::{rpc::jsonrpc::JsonRequest, tx::Transaction, wallet::walletdb::QueryType};
-use darkfi_money_contract::{
-    client::{
-        MoneyNote, OwnCoin, MONEY_ALIASES_COL_ALIAS, MONEY_ALIASES_COL_TOKEN_ID,
-        MONEY_ALIASES_TABLE, MONEY_COINS_COL_COIN, MONEY_COINS_COL_IS_SPENT,
-        MONEY_COINS_COL_LEAF_POSITION, MONEY_COINS_COL_MEMO, MONEY_COINS_COL_NULLIFIER,
-        MONEY_COINS_COL_SECRET, MONEY_COINS_COL_SERIAL, MONEY_COINS_COL_SPEND_HOOK,
-        MONEY_COINS_COL_TOKEN_BLIND, MONEY_COINS_COL_TOKEN_ID, MONEY_COINS_COL_USER_DATA,
-        MONEY_COINS_COL_VALUE, MONEY_COINS_COL_VALUE_BLIND, MONEY_COINS_TABLE,
-        MONEY_INFO_COL_LAST_SCANNED_SLOT, MONEY_INFO_TABLE, MONEY_KEYS_COL_IS_DEFAULT,
-        MONEY_KEYS_COL_KEY_ID, MONEY_KEYS_COL_PUBLIC, MONEY_KEYS_COL_SECRET, MONEY_KEYS_TABLE,
-        MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_TOKEN_ID, MONEY_TOKENS_TABLE,
-        MONEY_TREE_COL_TREE, MONEY_TREE_TABLE,
-    },
-    model::{
-        Coin, MoneyTokenFreezeParamsV1, MoneyTokenMintParamsV1, MoneyTransferParamsV1, Output,
-    },
-    MoneyFunction,
-};
-use darkfi_sdk::{
-    bridgetree,
-    crypto::{
-        pasta_prelude::Field, poseidon_hash, Keypair, MerkleNode, MerkleTree, Nullifier, PublicKey,
-        SecretKey, TokenId, MONEY_CONTRACT_ID,
-    },
-    pasta::pallas,
-};
-use darkfi_serial::{deserialize, serialize};
-use rand::rngs::OsRng;
-use serde_json::json;
-
-use super::Drk;
-use crate::cli_util::kaching;
-
-impl Drk {
-    /// Initialize wallet with tables for the Money contract
-    pub async fn initialize_money(&self) -> Result<()> {
-        let wallet_schema = include_str!("../../../src/contract/money/wallet.sql");
-
-        // We perform a request to darkfid with the schema to initialize
-        // the necessary tables in the wallet.
-        let req = JsonRequest::new("wallet.exec_sql", json!([wallet_schema]));
-        let rep = self.rpc_client.request(req).await?;
-
-        if rep == true {
-            eprintln!("Successfully initialized wallet schema for the Money contract");
-        } else {
-            eprintln!("[initialize_money] Got unexpected reply from darkfid: {}", rep);
-        }
-
-        // Check if we have to initialize the Merkle tree.
-        // We check if we find a row in the tree table, and if not, we create a
-        // new tree and push it into the table.
-        let mut tree_needs_init = false;
-        let query = format!("SELECT {} FROM {}", MONEY_TREE_COL_TREE, MONEY_TREE_TABLE);
-        let params = json!([query, QueryType::Blob as u8, MONEY_TREE_COL_TREE]);
-        let req = JsonRequest::new("wallet.query_row_single", params);
-
-        // For now, on success, we don't care what's returned, but in the future
-        // we should actually check it.
-        // TODO: The RPC needs a better variant for errors so detailed inspection
-        //       can be done with error codes and all that.
-        if (self.rpc_client.request(req).await).is_err() {
-            tree_needs_init = true;
-        }
-
-        if tree_needs_init {
-            eprintln!("Initializing Money Merkle tree");
-            let mut tree = MerkleTree::new(100);
-            tree.append(MerkleNode::from(pallas::Base::ZERO));
-            let _ = tree.mark().unwrap();
-            self.put_money_tree(&tree).await?;
-            eprintln!("Successfully initialized Merkle tree for the Money contract");
-        }
-
-        // We maintain the last scanned slot as part of the Money contract,
-        // but at this moment it is also somewhat applicable to DAO scans.
-        if (self.last_scanned_slot().await).is_err() {
-            let query = format!(
-                "INSERT INTO {} ({}) VALUES (?1);",
-                MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT
-            );
-
-            let params = json!([query, QueryType::Integer as u8, 0]);
-            let req = JsonRequest::new("wallet.exec_sql", params);
-            let _ = self.rpc_client.request(req).await?;
-        }
-
-        Ok(())
-    }
-
-    /// Generate a new keypair and place it into the wallet.
-    pub async fn money_keygen(&self) -> Result<()> {
-        eprintln!("Generating a new keypair");
-        // TODO: We might want to have hierarchical deterministic key derivation.
-        let keypair = Keypair::random(&mut OsRng);
-        let is_default = 0;
-
-        let query = format!(
-            "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
-            MONEY_KEYS_TABLE,
-            MONEY_KEYS_COL_IS_DEFAULT,
-            MONEY_KEYS_COL_PUBLIC,
-            MONEY_KEYS_COL_SECRET,
-        );
-
-        let params = json!([
-            query,
-            QueryType::Integer as u8,
-            is_default,
-            QueryType::Blob as u8,
-            serialize(&keypair.public),
-            QueryType::Blob as u8,
-            serialize(&keypair.secret),
-        ]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        if rep == true {
-            eprintln!("Successfully added new keypair to wallet");
-        } else {
-            eprintln!("[money_keygen] Got unexpected reply from darkfid: {}", rep);
-        }
-
-        eprintln!("New address:");
-        println!("{}", keypair.public);
-
-        Ok(())
-    }
-
-    /// Fetch all secret keys from the wallet
-    pub async fn get_money_secrets(&self) -> Result<Vec<SecretKey>> {
-        let query = format!("SELECT {} FROM {};", MONEY_KEYS_COL_SECRET, MONEY_KEYS_TABLE);
-        let params = json!([query, QueryType::Blob as u8, MONEY_KEYS_COL_SECRET]);
-        let req = JsonRequest::new("wallet.query_row_multi", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        // The returned thing should be an array of found rows.
-        let Some(rows) = rep.as_array() else {
-            return Err(anyhow!("[get_money_secrets] Unexpected response from darkfid: {}", rep))
-        };
-
-        let mut secrets = Vec::with_capacity(rows.len());
-
-        // Let's scan through the rows and see if we got anything.
-        for row in rows {
-            let secret_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
-            let secret = deserialize(&secret_bytes)?;
-            secrets.push(secret);
-        }
-
-        Ok(secrets)
-    }
-
-    /// Import given secret keys into the wallet.
-    /// The query uses INSERT, so if the key already exists, it will be skipped.
-    /// Returns the respective PublicKey objects for the imported keys.
-    pub async fn import_money_secrets(&self, secrets: Vec<SecretKey>) -> Result<Vec<PublicKey>> {
-        let mut ret = Vec::with_capacity(secrets.len());
-
-        for secret in secrets {
-            ret.push(PublicKey::from_secret(secret));
-            let is_default = 0;
-            let public = serialize(&PublicKey::from_secret(secret));
-            let secret = serialize(&secret);
-
-            let query = format!(
-                "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
-                MONEY_KEYS_TABLE,
-                MONEY_KEYS_COL_IS_DEFAULT,
-                MONEY_KEYS_COL_PUBLIC,
-                MONEY_KEYS_COL_SECRET,
-            );
-
-            let params = json!([
-                query,
-                QueryType::Integer as u8,
-                is_default,
-                QueryType::Blob as u8,
-                public,
-                QueryType::Blob as u8,
-                secret,
-            ]);
-
-            let req = JsonRequest::new("wallet.exec_sql", params);
-            let _ = self.rpc_client.request(req).await?;
-        }
-
-        Ok(ret)
-    }
-
-    /// Fetch pubkeys from the wallet and return the requested index.
-    pub async fn wallet_address(&self, idx: u64) -> Result<PublicKey> {
-        let query = format!(
-            "SELECT {} FROM {} WHERE {} = {};",
-            MONEY_KEYS_COL_PUBLIC, MONEY_KEYS_TABLE, MONEY_KEYS_COL_KEY_ID, idx
-        );
-
-        let params = json!([query, QueryType::Blob as u8, MONEY_KEYS_COL_PUBLIC]);
-        let req = JsonRequest::new("wallet.query_row_single", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let Some(arr) = rep.as_array() else {
-            return Err(anyhow!("[wallet_address] Unexpected response from darkfid: {}", rep))
-        };
-
-        if arr.len() != 1 {
-            return Err(anyhow!("Did not find pubkey with index {}", idx))
-        }
-
-        let key_bytes: Vec<u8> = serde_json::from_value(arr[0].clone())?;
-        let public_key: PublicKey = deserialize(&key_bytes)?;
-
-        Ok(public_key)
-    }
-
-    /// Fetch all coins and their metadata related to the Money contract from the wallet.
-    /// Optionally also fetch spent ones.
-    /// The boolean in the returned tuple notes if the coin was marked as spent.
-    pub async fn get_coins(&self, fetch_spent: bool) -> Result<Vec<(OwnCoin, bool)>> {
-        let query = if fetch_spent {
-            format!("SELECT * FROM {}", MONEY_COINS_TABLE)
-        } else {
-            format!(
-                "SELECT * FROM {} WHERE {} = {}",
-                MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, false,
-            )
-        };
-
-        let params = json!([
-            query,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_COIN,
-            QueryType::Integer as u8,
-            MONEY_COINS_COL_IS_SPENT,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_SERIAL,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_VALUE,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_TOKEN_ID,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_SPEND_HOOK,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_USER_DATA,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_VALUE_BLIND,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_TOKEN_BLIND,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_SECRET,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_NULLIFIER,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_LEAF_POSITION,
-            QueryType::Blob as u8,
-            MONEY_COINS_COL_MEMO,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_multi", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        // The returned thing should be an array of found rows.
-        let Some(rows) = rep.as_array() else {
-            return Err(anyhow!("[get_coins] Unexpected response from darkfid: {}", rep))
-        };
-
-        let mut owncoins = Vec::with_capacity(rows.len());
-
-        for row in rows {
-            let Some(row) = row.as_array() else {
-                return Err(anyhow!("[get_coins] Unexpected response from darkfid: {}", rep))
-            };
-
-            let coin_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
-            let coin: Coin = deserialize(&coin_bytes)?;
-
-            let is_spent: u64 = serde_json::from_value(row[1].clone())?;
-            let is_spent = is_spent > 0;
-
-            let serial_bytes: Vec<u8> = serde_json::from_value(row[2].clone())?;
-            let serial: pallas::Base = deserialize(&serial_bytes)?;
-
-            let value_bytes: Vec<u8> = serde_json::from_value(row[3].clone())?;
-            let value: u64 = deserialize(&value_bytes)?;
-
-            let token_id_bytes: Vec<u8> = serde_json::from_value(row[4].clone())?;
-            let token_id: TokenId = deserialize(&token_id_bytes)?;
-
-            let spend_hook_bytes: Vec<u8> = serde_json::from_value(row[5].clone())?;
-            let spend_hook: pallas::Base = deserialize(&spend_hook_bytes)?;
-
-            let user_data_bytes: Vec<u8> = serde_json::from_value(row[6].clone())?;
-            let user_data: pallas::Base = deserialize(&user_data_bytes)?;
-
-            let value_blind_bytes: Vec<u8> = serde_json::from_value(row[7].clone())?;
-            let value_blind: pallas::Scalar = deserialize(&value_blind_bytes)?;
-
-            let token_blind_bytes: Vec<u8> = serde_json::from_value(row[8].clone())?;
-            let token_blind: pallas::Base = deserialize(&token_blind_bytes)?;
-
-            let secret_bytes: Vec<u8> = serde_json::from_value(row[9].clone())?;
-            let secret: SecretKey = deserialize(&secret_bytes)?;
-
-            let nullifier_bytes: Vec<u8> = serde_json::from_value(row[10].clone())?;
-            let nullifier: Nullifier = deserialize(&nullifier_bytes)?;
-
-            let leaf_position_bytes: Vec<u8> = serde_json::from_value(row[11].clone())?;
-            let leaf_position: bridgetree::Position = deserialize(&leaf_position_bytes)?;
-
-            let memo: Vec<u8> = serde_json::from_value(row[12].clone())?;
-
-            let note = MoneyNote {
-                serial,
-                value,
-                token_id,
-                spend_hook,
-                user_data,
-                value_blind,
-                token_blind,
-                memo,
-            };
-            let owncoin = OwnCoin { coin, note, secret, nullifier, leaf_position };
-
-            owncoins.push((owncoin, is_spent))
-        }
-
-        Ok(owncoins)
-    }
-
-    /// Mark a coin in the wallet as spent
-    pub async fn mark_spent_coin(&self, coin: &Coin) -> Result<()> {
-        let query = format!(
-            "UPDATE {} SET {} = ?1 WHERE {} = ?2;",
-            MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_COIN
-        );
-
-        let params = json!([
-            query,
-            QueryType::Integer as u8,
-            1,
-            QueryType::Blob as u8,
-            serialize(&coin.inner())
-        ]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    /// Marks all coins in the wallet as spent, if their nullifier is in the given set
-    pub async fn mark_spent_coins(&self, nullifiers: &[Nullifier]) -> Result<()> {
-        if nullifiers.is_empty() {
-            return Ok(())
-        }
-
-        for (coin, _) in self.get_coins(false).await? {
-            if nullifiers.contains(&coin.nullifier) {
-                self.mark_spent_coin(&coin.coin).await?;
-            }
-        }
-
-        Ok(())
-    }
-
-    /// Mark a given coin in the wallet as unspent
-    pub async fn unspend_coin(&self, coin: &Coin) -> Result<()> {
-        let query = format!(
-            "UPDATE {} SET {} = ?1 WHERE {} = ?2;",
-            MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_COIN,
-        );
-
-        let params = json!([
-            query,
-            QueryType::Integer as u8,
-            0,
-            QueryType::Blob as u8,
-            serialize(&coin.inner())
-        ]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    /// Replace the Money Merkle tree in the wallet.
-    pub async fn put_money_tree(&self, tree: &MerkleTree) -> Result<()> {
-        let query = format!(
-            "DELETE FROM {}; INSERT INTO {} ({}) VALUES (?1);",
-            MONEY_TREE_TABLE, MONEY_TREE_TABLE, MONEY_TREE_COL_TREE,
-        );
-
-        let params = json!([query, QueryType::Blob as u8, serialize(tree)]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    /// Fetch the Money Merkle tree from the wallet
-    pub async fn get_money_tree(&self) -> Result<MerkleTree> {
-        let query = format!("SELECT * FROM {}", MONEY_TREE_TABLE);
-        let params = json!([query, QueryType::Blob as u8, MONEY_TREE_COL_TREE]);
-        let req = JsonRequest::new("wallet.query_row_single", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let tree_bytes: Vec<u8> = serde_json::from_value(rep[0].clone())?;
-        let tree = deserialize(&tree_bytes)?;
-        Ok(tree)
-    }
-
-    /// Reset the Money Merkle tree in the wallet
-    pub async fn reset_money_tree(&self) -> Result<()> {
-        eprintln!("Resetting Money Merkle tree");
-        let mut tree = MerkleTree::new(100);
-        tree.append(MerkleNode::from(pallas::Base::ZERO));
-        let _ = tree.mark().unwrap();
-        self.put_money_tree(&tree).await?;
-        eprintln!("Successfully reset Money Merkle tree");
-
-        Ok(())
-    }
-
-    /// Reset the Money coins in the wallet
-    pub async fn reset_money_coins(&self) -> Result<()> {
-        eprintln!("Resetting coins");
-        let query = format!("DELETE FROM {};", MONEY_COINS_TABLE);
-        let params = json!([query]);
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-        eprintln!("Successfully reset coins");
-
-        Ok(())
-    }
-
-    /// Fetch known unspent balances from the wallet and return them as a hashmap.
-    pub async fn money_balance(&self) -> Result<HashMap<String, u64>> {
-        let mut coins = self.get_coins(false).await?;
-        coins.retain(|x| x.0.note.spend_hook == pallas::Base::zero());
-
-        // Fill this map with balances
-        let mut balmap: HashMap<String, u64> = HashMap::new();
-
-        for coin in coins {
-            let mut value = coin.0.note.value;
-
-            if let Some(prev) = balmap.get(&coin.0.note.token_id.to_string()) {
-                value += prev;
-            }
-
-            balmap.insert(coin.0.note.token_id.to_string(), value);
-        }
-
-        Ok(balmap)
-    }
-
-    /// Append data related to Money contract transactions into the wallet database.
-    pub async fn apply_tx_money_data(&self, tx: &Transaction, _confirm: bool) -> Result<()> {
-        let cid = *MONEY_CONTRACT_ID;
-
-        let mut nullifiers: Vec<Nullifier> = vec![];
-        let mut outputs: Vec<Output> = vec![];
-        let mut freezes: Vec<TokenId> = vec![];
-
-        for (i, call) in tx.calls.iter().enumerate() {
-            if call.contract_id == cid && call.data[0] == MoneyFunction::TransferV1 as u8 {
-                eprintln!("Found Money::TransferV1 in call {}", i);
-                let params: MoneyTransferParamsV1 = deserialize(&call.data[1..])?;
-
-                for input in params.inputs {
-                    nullifiers.push(input.nullifier);
-                }
-
-                for output in params.outputs {
-                    outputs.push(output);
-                }
-
-                continue
-            }
-
-            if call.contract_id == cid && call.data[0] == MoneyFunction::OtcSwapV1 as u8 {
-                eprintln!("Found Money::OtcSwapV1 in call {}", i);
-                let params: MoneyTransferParamsV1 = deserialize(&call.data[1..])?;
-
-                for input in params.inputs {
-                    nullifiers.push(input.nullifier);
-                }
-
-                for output in params.outputs {
-                    outputs.push(output);
-                }
-
-                continue
-            }
-
-            if call.contract_id == cid && call.data[0] == MoneyFunction::TokenMintV1 as u8 {
-                eprintln!("Found Money::MintV1 in call {}", i);
-                let params: MoneyTokenMintParamsV1 = deserialize(&call.data[1..])?;
-                outputs.push(params.output);
-                continue
-            }
-
-            if call.contract_id == cid && call.data[0] == MoneyFunction::TokenFreezeV1 as u8 {
-                eprintln!("Found Money::FreezeV1 in call {}", i);
-                let params: MoneyTokenFreezeParamsV1 = deserialize(&call.data[1..])?;
-                let token_id = TokenId::derive_public(params.signature_public);
-                freezes.push(token_id);
-            }
-        }
-
-        let secrets = self.get_money_secrets().await?;
-        let dao_secrets = self.get_dao_secrets().await?;
-        let mut tree = self.get_money_tree().await?;
-
-        let mut owncoins = vec![];
-
-        for output in outputs {
-            let coin = output.coin;
-
-            // Append the new coin to the Merkle tree. Every coin has to be added.
-            tree.append(MerkleNode::from(coin.inner()));
-
-            // Attempt to decrypt the note
-            for secret in secrets.iter().chain(dao_secrets.iter()) {
-                if let Ok(note) = output.note.decrypt::<MoneyNote>(secret) {
-                    eprintln!("Successfully decrypted a Money Note");
-                    eprintln!("Witnessing coin in Merkle tree");
-                    let leaf_position = tree.mark().unwrap();
-
-                    let owncoin = OwnCoin {
-                        coin,
-                        note: note.clone(),
-                        secret: *secret,
-                        nullifier: Nullifier::from(poseidon_hash([secret.inner(), note.serial])),
-                        leaf_position,
-                    };
-
-                    owncoins.push(owncoin);
-                }
-            }
-        }
-
-        self.put_money_tree(&tree).await?;
-        if !nullifiers.is_empty() {
-            self.mark_spent_coins(&nullifiers).await?;
-        }
-
-        // This is the SQL query we'll be executing to insert new coins
-        // into the wallet
-        let query = format!(
-            "INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13);",
-            MONEY_COINS_TABLE,
-            MONEY_COINS_COL_COIN,
-            MONEY_COINS_COL_IS_SPENT,
-            MONEY_COINS_COL_SERIAL,
-            MONEY_COINS_COL_VALUE,
-            MONEY_COINS_COL_TOKEN_ID,
-            MONEY_COINS_COL_SPEND_HOOK,
-            MONEY_COINS_COL_USER_DATA,
-            MONEY_COINS_COL_VALUE_BLIND,
-            MONEY_COINS_COL_TOKEN_BLIND,
-            MONEY_COINS_COL_SECRET,
-            MONEY_COINS_COL_NULLIFIER,
-            MONEY_COINS_COL_LEAF_POSITION,
-            MONEY_COINS_COL_MEMO,
-        );
-
-        eprintln!("Found {} OwnCoin(s) in transaction", owncoins.len());
-        for owncoin in &owncoins {
-            eprintln!("OwnCoin: {:?}", owncoin.coin);
-            let params = json!([
-                query,
-                QueryType::Blob as u8,
-                serialize(&owncoin.coin),
-                QueryType::Integer as u8,
-                0, // <-- is_spent
-                QueryType::Blob as u8,
-                serialize(&owncoin.note.serial),
-                QueryType::Blob as u8,
-                serialize(&owncoin.note.value),
-                QueryType::Blob as u8,
-                serialize(&owncoin.note.token_id),
-                QueryType::Blob as u8,
-                serialize(&owncoin.note.spend_hook),
-                QueryType::Blob as u8,
-                serialize(&owncoin.note.user_data),
-                QueryType::Blob as u8,
-                serialize(&owncoin.note.value_blind),
-                QueryType::Blob as u8,
-                serialize(&owncoin.note.token_blind),
-                QueryType::Blob as u8,
-                serialize(&owncoin.secret),
-                QueryType::Blob as u8,
-                serialize(&owncoin.nullifier),
-                QueryType::Blob as u8,
-                serialize(&owncoin.leaf_position),
-                QueryType::Blob as u8,
-                serialize(&owncoin.note.memo),
-            ]);
-
-            let req = JsonRequest::new("wallet.exec_sql", params);
-            let _ = self.rpc_client.request(req).await?;
-        }
-
-        for token_id in freezes {
-            let query = format!(
-                "UPDATE {} SET {} = 1 WHERE {} = ?1;",
-                MONEY_TOKENS_TABLE, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_TOKEN_ID,
-            );
-
-            let params = json!([query, QueryType::Blob as u8, serialize(&token_id)]);
-
-            let req = JsonRequest::new("wallet.exec_sql", params);
-            let _ = self.rpc_client.request(req).await?;
-        }
-
-        if !owncoins.is_empty() {
-            kaching().await;
-        }
-
-        Ok(())
-    }
-
-    /// Get the last scanned slot from the wallet
-    pub async fn last_scanned_slot(&self) -> Result<u64> {
-        let query =
-            format!("SELECT {} FROM {};", MONEY_INFO_COL_LAST_SCANNED_SLOT, MONEY_INFO_TABLE);
-
-        let params = json!([query, QueryType::Integer as u8, MONEY_INFO_COL_LAST_SCANNED_SLOT]);
-        let req = JsonRequest::new("wallet.query_row_single", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        Ok(serde_json::from_value(rep[0].clone())?)
-    }
-
-    /// Create an alias record for provided Token ID
-    pub async fn add_alias(&self, alias: String, token_id: TokenId) -> Result<()> {
-        eprintln!("Generating alias {} for Token: {}", alias, token_id);
-        let query = format!(
-            "INSERT OR REPLACE INTO {} ({}, {}) VALUES (?1, ?2);",
-            MONEY_ALIASES_TABLE, MONEY_ALIASES_COL_ALIAS, MONEY_ALIASES_COL_TOKEN_ID,
-        );
-
-        let params = json!([
-            query,
-            QueryType::Blob as u8,
-            serialize(&alias),
-            QueryType::Blob as u8,
-            serialize(&token_id),
-        ]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        if rep == true {
-            eprintln!("Successfully added new alias to wallet");
-        } else {
-            eprintln!("[add_alias] Got unexpected reply from darkfid: {}", rep);
-        }
-
-        Ok(())
-    }
-
-    /// Fetch all aliases from the wallet.
-    /// Optionally filter using alias name and/or token id.
-    pub async fn get_aliases(
-        &self,
-        alias_filter: Option<String>,
-        token_id_filter: Option<TokenId>,
-    ) -> Result<HashMap<String, TokenId>> {
-        let query = format!("SELECT * FROM {}", MONEY_ALIASES_TABLE);
-        let params = json!([
-            query,
-            QueryType::Blob as u8,
-            MONEY_ALIASES_COL_ALIAS,
-            QueryType::Blob as u8,
-            MONEY_ALIASES_COL_TOKEN_ID,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_multi", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        // The returned thing should be an array of found rows.
-        let Some(rows) = rep.as_array() else {
-            return Err(anyhow!("[get_aliases] Unexpected response from darkfid: {}", rep))
-        };
-
-        // Fill this map with aliases
-        let mut map: HashMap<String, TokenId> = HashMap::new();
-        for row in rows {
-            let Some(row) = row.as_array() else {
-                return Err(anyhow!("[get_aliases] Unexpected response from darkfid: {}", rep))
-            };
-
-            let alias_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
-            let alias: String = deserialize(&alias_bytes)?;
-            if alias_filter.is_some() && alias_filter.as_ref().unwrap() != &alias {
-                continue
-            }
-
-            let token_id_bytes: Vec<u8> = serde_json::from_value(row[1].clone())?;
-            let token_id: TokenId = deserialize(&token_id_bytes)?;
-            if token_id_filter.is_some() && token_id_filter.as_ref().unwrap() != &token_id {
-                continue
-            }
-
-            map.insert(alias, token_id);
-        }
-
-        Ok(map)
-    }
-
-    /// Fetch all aliases from the wallet, mapped by token id.
-    pub async fn get_aliases_mapped_by_token(&self) -> Result<HashMap<String, String>> {
-        let aliases = self.get_aliases(None, None).await?;
-        let mut map: HashMap<String, String> = HashMap::new();
-        for (alias, token_id) in aliases {
-            let aliases_string = if let Some(prev) = map.get(&token_id.to_string()) {
-                format!("{}, {}", prev, alias)
-            } else {
-                alias
-            };
-
-            map.insert(token_id.to_string(), aliases_string);
-        }
-
-        Ok(map)
-    }
-
-    /// Retrieve token by provided string.
-    /// Input string represents either an alias or a token id.
-    pub async fn get_token(&self, input: String) -> Result<TokenId> {
-        // Check if input is an alias(max 5 characters)
-        if input.chars().count() <= 5 {
-            let aliases = self.get_aliases(Some(input.clone()), None).await?;
-            if let Some(token_id) = aliases.get(&input) {
-                return Ok(*token_id)
-            }
-        }
-        // Else parse input
-        Ok(TokenId::from_str(input.as_str())?)
-    }
-
-    /// Create an alias record for provided Token ID
-    pub async fn remove_alias(&self, alias: String) -> Result<()> {
-        eprintln!("Removing alias: {}", alias);
-        let query =
-            format!("DELETE FROM {} WHERE {} = ?1;", MONEY_ALIASES_TABLE, MONEY_ALIASES_COL_ALIAS,);
-
-        let params = json!([query, QueryType::Blob as u8, serialize(&alias),]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        if rep == true {
-            eprintln!("Successfully removed alias from wallet");
-        } else {
-            eprintln!("[remove_alias] Got unexpected reply from darkfid: {}", rep);
-        }
-
-        Ok(())
-    }
-}

+ 0 - 97
bin/drk/src/wallet_token.rs

@@ -1,97 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use anyhow::{anyhow, Result};
-use darkfi::{rpc::jsonrpc::JsonRequest, wallet::walletdb::QueryType};
-use darkfi_money_contract::client::{
-    MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_MINT_AUTHORITY, MONEY_TOKENS_COL_TOKEN_ID,
-    MONEY_TOKENS_TABLE,
-};
-use darkfi_sdk::crypto::{SecretKey, TokenId};
-use darkfi_serial::{deserialize, serialize};
-use serde_json::json;
-
-use super::Drk;
-
-impl Drk {
-    /// Import a token mint authority into the wallet
-    pub async fn import_mint_authority(&self, mint_authority: SecretKey) -> Result<()> {
-        let token_id = TokenId::derive(mint_authority);
-        let is_frozen = 0;
-
-        let query = format!(
-            "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
-            MONEY_TOKENS_TABLE,
-            MONEY_TOKENS_COL_MINT_AUTHORITY,
-            MONEY_TOKENS_COL_TOKEN_ID,
-            MONEY_TOKENS_COL_IS_FROZEN,
-        );
-
-        let params = json!([
-            query,
-            QueryType::Blob as u8,
-            serialize(&mint_authority),
-            QueryType::Blob as u8,
-            serialize(&token_id),
-            QueryType::Integer as u8,
-            is_frozen,
-        ]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    pub async fn list_tokens(&self) -> Result<Vec<(TokenId, SecretKey, bool)>> {
-        let mut ret = vec![];
-
-        let query = format!("SELECT * FROM {};", MONEY_TOKENS_TABLE);
-
-        let params = json!([
-            query,
-            QueryType::Blob as u8,
-            MONEY_TOKENS_COL_MINT_AUTHORITY,
-            QueryType::Blob as u8,
-            MONEY_TOKENS_COL_TOKEN_ID,
-            QueryType::Integer as u8,
-            MONEY_TOKENS_COL_IS_FROZEN,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_multi", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let Some(rows) = rep.as_array() else {
-            return Err(anyhow!("[list_tokens] Unexpected response from darkfid: {}", rep))
-        };
-
-        for row in rows {
-            let auth_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
-            let mint_authority = deserialize(&auth_bytes)?;
-
-            let token_bytes: Vec<u8> = serde_json::from_value(row[1].clone())?;
-            let token_id = deserialize(&token_bytes)?;
-
-            let frozen: i32 = serde_json::from_value(row[2].clone())?;
-
-            ret.push((token_id, mint_authority, frozen != 0));
-        }
-
-        Ok(ret)
-    }
-}

+ 0 - 192
bin/drk/src/wallet_txs_history.rs

@@ -1,192 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use anyhow::{anyhow, Result};
-use darkfi::{rpc::jsonrpc::JsonRequest, tx::Transaction, wallet::walletdb::QueryType};
-use darkfi_serial::{deserialize, serialize};
-use serde_json::json;
-
-use super::Drk;
-
-// Wallet SQL table constant names. These have to represent the `wallet.sql`
-// SQL schema.
-const WALLET_TXS_HISTORY_TABLE: &str = "transactions_history";
-const WALLET_TXS_HISTORY_COL_TX_HASH: &str = "transaction_hash";
-const WALLET_TXS_HISTORY_COL_STATUS: &str = "status";
-const WALLET_TXS_HISTORY_COL_TX: &str = "tx";
-
-impl Drk {
-    /// Fetch all transactions history records, excluding bytes column.
-    pub async fn get_txs_history(&self) -> Result<Vec<(String, String)>> {
-        let mut ret = vec![];
-
-        let query = format!(
-            "SELECT {}, {} FROM {};",
-            WALLET_TXS_HISTORY_COL_TX_HASH, WALLET_TXS_HISTORY_COL_STATUS, WALLET_TXS_HISTORY_TABLE
-        );
-
-        let params = json!([
-            query,
-            QueryType::Text as u8,
-            WALLET_TXS_HISTORY_COL_TX_HASH,
-            QueryType::Text as u8,
-            WALLET_TXS_HISTORY_COL_STATUS,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_multi", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let Some(rows) = rep.as_array() else {
-            return Err(anyhow!("[txs_history] Unexpected response from darkfid: {}", rep))
-        };
-
-        for row in rows {
-            let tx_hash: String = serde_json::from_value(row[0].clone())?;
-            let status: String = serde_json::from_value(row[1].clone())?;
-            ret.push((tx_hash, status));
-        }
-
-        Ok(ret)
-    }
-
-    /// Get a transaction history record.
-    pub async fn get_tx_history_record(
-        &self,
-        tx_hash: &str,
-    ) -> Result<(String, String, Transaction)> {
-        let query = format!(
-            "SELECT * FROM {} WHERE {} = {};",
-            WALLET_TXS_HISTORY_TABLE, WALLET_TXS_HISTORY_COL_TX_HASH, tx_hash
-        );
-
-        let params = json!([
-            query,
-            QueryType::Text as u8,
-            WALLET_TXS_HISTORY_COL_TX_HASH,
-            QueryType::Text as u8,
-            WALLET_TXS_HISTORY_COL_STATUS,
-            QueryType::Text as u8,
-            WALLET_TXS_HISTORY_COL_TX,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_single", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let Some(arr) = rep.as_array() else {
-            return Err(anyhow!("[get_tx_history_record] Unexpected response from darkfid: {}", rep))
-        };
-
-        if arr.len() != 3 {
-            return Err(anyhow!("Did not find transaction record with hash {}", tx_hash))
-        }
-
-        let tx_hash: String = serde_json::from_value(arr[0].clone())?;
-
-        let status: String = serde_json::from_value(arr[1].clone())?;
-
-        let tx_encoded: String = serde_json::from_value(arr[2].clone())?;
-        let tx_bytes: Vec<u8> = bs58::decode(&tx_encoded).into_vec()?;
-        let tx: Transaction = deserialize(&tx_bytes)?;
-
-        Ok((tx_hash, status, tx))
-    }
-
-    /// Insert a [`Transaction`] history record into the wallet.
-    pub async fn insert_tx_history_record(&self, tx: &Transaction) -> Result<()> {
-        let query = format!(
-            "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
-            WALLET_TXS_HISTORY_TABLE,
-            WALLET_TXS_HISTORY_COL_TX_HASH,
-            WALLET_TXS_HISTORY_COL_STATUS,
-            WALLET_TXS_HISTORY_COL_TX,
-        );
-
-        let params = json!([
-            query,
-            QueryType::Text as u8,
-            tx.hash().to_string(),
-            QueryType::Text as u8,
-            "Broadcasted",
-            QueryType::Text as u8,
-            bs58::encode(&serialize(tx)).into_string(),
-        ]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    /// Update a transactions history record status to the given one.
-    pub async fn update_tx_history_record_status(&self, tx_hash: &str, status: &str) -> Result<()> {
-        let query = format!(
-            "UPDATE {} SET {} = ?1 WHERE {} = ?2;",
-            WALLET_TXS_HISTORY_TABLE, WALLET_TXS_HISTORY_COL_STATUS, WALLET_TXS_HISTORY_COL_TX_HASH,
-        );
-
-        let params = json!([query, QueryType::Text as u8, status, QueryType::Text as u8, tx_hash,]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    /// Update given transactions history record statuses to the given one.
-    pub async fn update_tx_history_records_status(
-        &self,
-        txs: &Vec<Transaction>,
-        status: &str,
-    ) -> Result<()> {
-        if txs.is_empty() {
-            return Ok(())
-        }
-
-        let txs_hashes: Vec<String> = txs.iter().map(|tx| tx.hash().to_string()).collect();
-        let txs_hashes_string = format!("{:?}", txs_hashes).replace('[', "(").replace(']', ")");
-        let query = format!(
-            "UPDATE {} SET {} = ?1 WHERE {} IN {};",
-            WALLET_TXS_HISTORY_TABLE,
-            WALLET_TXS_HISTORY_COL_STATUS,
-            WALLET_TXS_HISTORY_COL_TX_HASH,
-            txs_hashes_string
-        );
-
-        let params = json!([query, QueryType::Text as u8, status,]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-
-    /// Update all transaction history records statuses to the given one.
-    pub async fn update_all_tx_history_records_status(&self, status: &str) -> Result<()> {
-        let query = format!(
-            "UPDATE {} SET {} = ?1",
-            WALLET_TXS_HISTORY_TABLE, WALLET_TXS_HISTORY_COL_STATUS,
-        );
-
-        let params = json!([query, QueryType::Text as u8, status,]);
-
-        let req = JsonRequest::new("wallet.exec_sql", params);
-        let _ = self.rpc_client.request(req).await?;
-
-        Ok(())
-    }
-}

+ 0 - 0
bin/drk2/src/walletdb.rs → bin/drk/src/walletdb.rs


+ 0 - 39
bin/drk2/Cargo.toml

@@ -1,39 +0,0 @@
-[package]
-name = "drk2"
-version = "0.4.1"
-homepage = "https://dark.fi"
-description = "Command-line client for darkfid"
-authors = ["Dyne.org foundation <foundation@dyne.org>"]
-repository = "https://github.com/darkrenaissance/darkfi"
-license = "AGPL-3.0-only"
-edition = "2021"
-
-[dependencies]
-# Darkfi
-darkfi = {path = "../../", features = ["async-daemonize", "bs58", "rpc"]}
-darkfi_money_contract = {path = "../../src/contract/money", features = ["no-entrypoint", "client"]}
-darkfi_dao_contract = {path = "../../src/contract/dao", features = ["no-entrypoint", "client"]}
-darkfi-sdk = {path = "../../src/sdk", features = ["async"]}
-darkfi-serial = {path = "../../src/serial"}
-
-# Misc
-blake3 = "1.5.0"
-bs58 = "0.5.0"
-log = "0.4.20"
-prettytable-rs = "0.10.0"
-rand = "0.8.5"
-rodio = {version = "0.17.3", default-features = false, features = ["minimp3"]}
-rusqlite = {version = "0.30.0", features = ["sqlcipher"]}
-url = "2.5.0"
-
-# Daemon
-easy-parallel = "3.3.1"
-signal-hook-async-std = "0.2.2"
-signal-hook = "0.3.17"
-simplelog = "0.12.1"
-smol = "1.3.0"
-
-# Argument parsing
-serde = {version = "1.0.195", features = ["derive"]}
-structopt = "0.3.26"
-structopt-toml = "0.5.1"

+ 0 - 469
bin/drk2/src/cli_util.rs

@@ -1,469 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-use std::{io::Cursor, process::exit, str::FromStr};
-
-use rodio::{source::Source, Decoder, OutputStream};
-use structopt_toml::clap::{App, Arg, Shell, SubCommand};
-
-use darkfi::{cli_desc, system::sleep, util::parse::decode_base10, Error, Result};
-use darkfi_sdk::crypto::TokenId;
-
-use crate::{money::BALANCE_BASE10_DECIMALS, Drk};
-
-/// Auxiliary function to parse provided string into a values pair.
-pub fn parse_value_pair(s: &str) -> Result<(u64, u64)> {
-    let v: Vec<&str> = s.split(':').collect();
-    if v.len() != 2 {
-        eprintln!("Invalid value pair. Use a pair such as 13.37:11.0");
-        exit(1);
-    }
-
-    let val0 = decode_base10(v[0], BALANCE_BASE10_DECIMALS, true);
-    let val1 = decode_base10(v[1], BALANCE_BASE10_DECIMALS, true);
-
-    if val0.is_err() || val1.is_err() {
-        eprintln!("Invalid value pair. Use a pair such as 13.37:11.0");
-        exit(1);
-    }
-
-    Ok((val0.unwrap(), val1.unwrap()))
-}
-
-/// Auxiliary function to parse provided string into a tokens pair.
-pub async fn parse_token_pair(drk: &Drk, s: &str) -> Result<(TokenId, TokenId)> {
-    let v: Vec<&str> = s.split(':').collect();
-    if v.len() != 2 {
-        eprintln!("Invalid token pair. Use a pair such as:");
-        eprintln!("WCKD:MLDY");
-        eprintln!("or");
-        eprintln!("A7f1RKsCUUHrSXA7a9ogmwg8p3bs6F47ggsW826HD4yd:FCuoMii64H5Ee4eVWBjP18WTFS8iLUJmGi16Qti1xFQ2");
-        exit(1);
-    }
-
-    let tok0 = drk.get_token(v[0].to_string()).await;
-    let tok1 = drk.get_token(v[1].to_string()).await;
-
-    if tok0.is_err() || tok1.is_err() {
-        eprintln!("Invalid token pair. Use a pair such as:");
-        eprintln!("WCKD:MLDY");
-        eprintln!("or");
-        eprintln!("A7f1RKsCUUHrSXA7a9ogmwg8p3bs6F47ggsW826HD4yd:FCuoMii64H5Ee4eVWBjP18WTFS8iLUJmGi16Qti1xFQ2");
-        exit(1);
-    }
-
-    Ok((tok0.unwrap(), tok1.unwrap()))
-}
-
-/// Fun police go away
-pub async fn kaching() {
-    const WALLET_MP3: &[u8] = include_bytes!("../wallet.mp3");
-
-    let cursor = Cursor::new(WALLET_MP3);
-
-    let Ok((_stream, stream_handle)) = OutputStream::try_default() else { return };
-
-    let Ok(source) = Decoder::new(cursor) else { return };
-
-    if stream_handle.play_raw(source.convert_samples()).is_err() {
-        return
-    }
-
-    sleep(2).await;
-}
-
-/// Auxiliary function to generate provided shell completions.
-pub fn generate_completions(shell: &str) -> Result<()> {
-    // Sub-commands
-
-    // Kaching
-    let kaching = SubCommand::with_name("kaching").about("Fun");
-
-    // Ping
-    let ping =
-        SubCommand::with_name("ping").about("Send a ping request to the darkfid RPC endpoint");
-
-    // Completions
-    let shell_arg = Arg::with_name("shell").help("The Shell you want to generate script for");
-
-    let completions = SubCommand::with_name("completions")
-        .about("Generate a SHELL completion script and print to stdout")
-        .arg(shell_arg);
-
-    // Wallet
-    let initialize =
-        Arg::with_name("initialize").long("initialize").help("Initialize wallet database");
-
-    let keygen =
-        Arg::with_name("keygen").long("keygen").help("Generate a new keypair in the wallet");
-
-    let balance =
-        Arg::with_name("balance").long("balance").help("Query the wallet for known balances");
-
-    let address =
-        Arg::with_name("address").long("address").help("Get the default address in the wallet");
-
-    let addresses =
-        Arg::with_name("addresses").long("addresses").help("Print all the addresses in the wallet");
-
-    let default_address = Arg::with_name("default-address")
-        .long("default-address")
-        .takes_value(true)
-        .help("Set the default address in the wallet");
-
-    let secrets =
-        Arg::with_name("secrets").long("secrets").help("Print all the secret keys from the wallet");
-
-    let import_secrets = Arg::with_name("import-secrets")
-        .long("import-secrets")
-        .help("Import secret keys from stdin into the wallet, separated by newlines");
-
-    let tree = Arg::with_name("tree").long("tree").help("Print the Merkle tree in the wallet");
-
-    let coins = Arg::with_name("coins").long("coins").help("Print all the coins in the wallet");
-
-    let wallet = SubCommand::with_name("wallet").about("Wallet operations").args(&vec![
-        initialize,
-        keygen,
-        balance,
-        address,
-        addresses,
-        default_address,
-        secrets,
-        import_secrets,
-        tree,
-        coins,
-    ]);
-
-    // Unspend
-    let coin = Arg::with_name("coin").help("base58-encoded coin to mark as unspent");
-
-    let unspend = SubCommand::with_name("unspend").about("Unspend a coin").arg(coin);
-
-    // Transfer
-    let amount = Arg::with_name("amount").help("Amount to send");
-
-    let token = Arg::with_name("token").help("Token ID to send");
-
-    let recipient = Arg::with_name("recipient").help("Recipient address");
-
-    let transfer = SubCommand::with_name("transfer")
-        .about("Create a payment transaction")
-        .args(&vec![amount, token, recipient]);
-
-    // Otc
-    let value_pair = Arg::with_name("value-pair")
-        .short("v")
-        .long("value-pair")
-        .takes_value(true)
-        .help("Value pair to send:recv (11.55:99.42)");
-
-    let token_pair = Arg::with_name("token-pair")
-        .short("t")
-        .long("token-pair")
-        .takes_value(true)
-        .help("Token pair to send:recv (f00:b4r)");
-
-    let init = SubCommand::with_name("init")
-        .about("Initialize the first half of the atomic swap")
-        .args(&vec![value_pair, token_pair]);
-
-    let join =
-        SubCommand::with_name("join").about("Build entire swap tx given the first half from stdin");
-
-    let inspect = SubCommand::with_name("inspect")
-        .about("Inspect a swap half or the full swap tx from stdin");
-
-    let sign = SubCommand::with_name("sign")
-        .about("Sign a transaction given from stdin as the first-half");
-
-    let otc = SubCommand::with_name("otc")
-        .about("OTC atomic swap")
-        .subcommands(vec![init, join, inspect, sign]);
-
-    // Inspect
-    let inspect = SubCommand::with_name("inspect").about("Inspect a transaction from stdin");
-
-    // Broadcast
-    let broadcast =
-        SubCommand::with_name("broadcast").about("Read a transaction from stdin and broadcast it");
-
-    // Subscribe
-    let subscribe = SubCommand::with_name("subscribe").about(
-        "This subscription will listen for incoming blocks from darkfid and look \
-                    through their transactions to see if there's any that interest us. \
-                    With `drk` we look at transactions calling the money contract so we can \
-                    find coins sent to us and fill our wallet with the necessary metadata.",
-    );
-
-    // DAO
-    let proposer_limit = Arg::with_name("proposer-limit")
-        .help("The minimum amount of governance tokens needed to open a proposal for this DAO");
-
-    let quorum = Arg::with_name("quorum")
-        .help("Minimal threshold of participating total tokens needed for a proposal to pass");
-
-    let approval_ratio = Arg::with_name("approval-ratio")
-        .help("The ratio of winning votes/total votes needed for a proposal to pass (2 decimals)");
-
-    let gov_token_id = Arg::with_name("gov-token-id").help("DAO's governance token ID");
-
-    let create = SubCommand::with_name("create").about("Create DAO parameters").args(&vec![
-        proposer_limit,
-        quorum,
-        approval_ratio,
-        gov_token_id,
-    ]);
-
-    let view = SubCommand::with_name("view").about("View DAO data from stdin");
-
-    let dao_name = Arg::with_name("dao-name").help("Named identifier for the DAO");
-
-    let import =
-        SubCommand::with_name("import").about("Import DAO data from stdin").args(&vec![dao_name]);
-
-    let dao_alias = Arg::with_name("dao-alias").help("Numeric identifier for the DAO (optional)");
-
-    let list = SubCommand::with_name("list")
-        .about("List imported DAOs (or info about a specific one)")
-        .args(&vec![dao_alias]);
-
-    let dao_alias = Arg::with_name("dao-alias").help("Name or numeric identifier for the DAO");
-
-    let balance = SubCommand::with_name("balance")
-        .about("Show the balance of a DAO")
-        .args(&vec![dao_alias.clone()]);
-
-    let mint = SubCommand::with_name("mint")
-        .about("Mint an imported DAO on-chain")
-        .args(&vec![dao_alias.clone()]);
-
-    let recipient =
-        Arg::with_name("recipient").help("Pubkey to send tokens to with proposal success");
-
-    let amount = Arg::with_name("amount").help("Amount to send from DAO with proposal success");
-
-    let token = Arg::with_name("token").help("Token ID to send from DAO with proposal success");
-
-    let propose = SubCommand::with_name("propose")
-        .about("Create a proposal for a DAO")
-        .args(&vec![dao_alias.clone(), recipient, amount, token]);
-
-    let proposals = SubCommand::with_name("proposals")
-        .about("List DAO proposals")
-        .args(&vec![dao_alias.clone()]);
-
-    let proposal_id = Arg::with_name("proposal-id").help("Numeric identifier for the proposal");
-
-    let proposal = SubCommand::with_name("proposal")
-        .about("View a DAO proposal data")
-        .args(&vec![dao_alias.clone(), proposal_id.clone()]);
-
-    let vote = Arg::with_name("vote").help("Vote (0 for NO, 1 for YES)");
-
-    let vote_weight =
-        Arg::with_name("vote-weight").help("Vote weight (amount of governance tokens)");
-
-    let vote = SubCommand::with_name("vote").about("Vote on a given proposal").args(&vec![
-        dao_alias.clone(),
-        proposal_id.clone(),
-        vote,
-        vote_weight,
-    ]);
-
-    let exec = SubCommand::with_name("exec")
-        .about("Execute a DAO proposal")
-        .args(&vec![dao_alias, proposal_id]);
-
-    let dao = SubCommand::with_name("dao").about("DAO functionalities").subcommands(vec![
-        create, view, import, list, balance, mint, propose, proposals, proposal, vote, exec,
-    ]);
-
-    // Scan
-    let reset = Arg::with_name("reset")
-        .long("reset")
-        .help("Reset Merkle tree and start scanning from first block");
-
-    let list = Arg::with_name("list").long("list").help("List all available checkpoints");
-
-    let checkpoint = Arg::with_name("checkpoint")
-        .long("checkpoint")
-        .takes_value(true)
-        .help("Reset Merkle tree to checkpoint index and start scanning");
-
-    let scan = SubCommand::with_name("scan")
-        .about("Scan the blockchain and parse relevant transactions")
-        .args(&vec![reset, list, checkpoint]);
-
-    // Explorer
-    let tx_hash = Arg::with_name("tx-hash").help("Transaction hash");
-
-    let full = Arg::with_name("full").long("full").help("Print the full transaction information");
-
-    let encode = Arg::with_name("encode").long("encode").help("Encode transaction to base58");
-
-    let fetch_tx = SubCommand::with_name("fetch-tx")
-        .about("Fetch a blockchain transaction by hash")
-        .args(&vec![tx_hash, full, encode]);
-
-    let simulate_tx =
-        SubCommand::with_name("simulate-tx").about("Read a transaction from stdin and simulate it");
-
-    let tx_hash = Arg::with_name("tx-hash").help("Transaction hash");
-
-    let encode = Arg::with_name("encode")
-        .long("encode")
-        .help("Encode specific history record transaction to base58");
-
-    let txs_history = SubCommand::with_name("txs-history")
-        .about("Fetch broadcasted transactions history")
-        .args(&vec![tx_hash, encode]);
-
-    let explorer = SubCommand::with_name("explorer")
-        .about("Explorer related subcommands")
-        .subcommands(vec![fetch_tx, simulate_tx, txs_history]);
-
-    // Alias
-    let alias = Arg::with_name("alias").help("Token alias");
-
-    let token = Arg::with_name("token").help("Token to create alias for");
-
-    let add = SubCommand::with_name("add").about("Create a Token alias").args(&vec![alias, token]);
-
-    let alias = Arg::with_name("alias")
-        .short("a")
-        .long("alias")
-        .takes_value(true)
-        .help("Token alias to search for");
-
-    let token = Arg::with_name("token")
-        .short("t")
-        .long("token")
-        .takes_value(true)
-        .help("Token to search alias for");
-
-    let show = SubCommand::with_name("show")
-        .about(
-            "Print alias info of optional arguments. \
-                    If no argument is provided, list all the aliases in the wallet.",
-        )
-        .args(&vec![alias, token]);
-
-    let alias = Arg::with_name("alias").help("Token alias to remove");
-
-    let remove = SubCommand::with_name("remove").about("Remove a Token alias").arg(alias);
-
-    let alias = SubCommand::with_name("alias")
-        .about("Manage Token aliases")
-        .subcommands(vec![add, show, remove]);
-
-    // Token
-    let import = SubCommand::with_name("import").about("Import a mint authority secret from stdin");
-
-    let generate_mint =
-        SubCommand::with_name("generate-mint").about("Generate a new mint authority");
-
-    let list =
-        SubCommand::with_name("list").about("List token IDs with available mint authorities");
-
-    let token = Arg::with_name("token").help("Token ID to mint");
-
-    let amount = Arg::with_name("amount").help("Amount to mint");
-
-    let recipient = Arg::with_name("recipient").help("Recipient of the minted tokens");
-
-    let mint =
-        SubCommand::with_name("mint").about("Mint tokens").args(&vec![token, amount, recipient]);
-
-    let token = Arg::with_name("token").help("Token ID to freeze");
-
-    let freeze = SubCommand::with_name("freeze").about("Freeze a token mint").arg(token);
-
-    let token = SubCommand::with_name("token").about("Token functionalities").subcommands(vec![
-        import,
-        generate_mint,
-        list,
-        mint,
-        freeze,
-    ]);
-
-    // Main arguments
-    let config = Arg::with_name("config")
-        .short("c")
-        .long("config")
-        .takes_value(true)
-        .help("Configuration file to use");
-
-    let wallet_path = Arg::with_name("wallet_path")
-        .long("wallet-path")
-        .takes_value(true)
-        .help("Path to wallet database");
-
-    let wallet_pass = Arg::with_name("wallet_pass")
-        .long("wallet-pass")
-        .takes_value(true)
-        .help("Password for the wallet database");
-
-    let endpoint = Arg::with_name("endpoint")
-        .short("e")
-        .long("endpoint")
-        .takes_value(true)
-        .help("darkfid JSON-RPC endpoint");
-
-    let command = vec![
-        kaching,
-        ping,
-        completions,
-        wallet,
-        unspend,
-        transfer,
-        otc,
-        inspect,
-        broadcast,
-        subscribe,
-        dao,
-        scan,
-        explorer,
-        alias,
-        token,
-    ];
-
-    let log = Arg::with_name("log")
-        .short("l")
-        .long("log")
-        .takes_value(true)
-        .help("Set log file to ouput into");
-
-    let verbose = Arg::with_name("verbose")
-        .short("v")
-        .multiple(true)
-        .help("Increase verbosity (-vvv supported)");
-
-    let mut app = App::new("drk")
-        .about(cli_desc!())
-        .args(&vec![config, wallet_path, wallet_pass, endpoint, log, verbose])
-        .subcommands(command);
-
-    let shell = match Shell::from_str(shell) {
-        Ok(s) => s,
-        Err(e) => return Err(Error::Custom(e)),
-    };
-
-    app.gen_completions_to("./drk", shell, &mut std::io::stdout());
-
-    Ok(())
-}

+ 0 - 1579
bin/drk2/src/main.rs

@@ -1,1579 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{
-    fs,
-    io::{stdin, Read},
-    process::exit,
-    str::FromStr,
-    sync::Arc,
-    time::Instant,
-};
-
-use prettytable::{format, row, Table};
-use rand::rngs::OsRng;
-use smol::stream::StreamExt;
-use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
-use url::Url;
-
-use darkfi::{
-    async_daemonize, cli_desc,
-    rpc::{client::RpcClient, jsonrpc::JsonRequest, util::JsonValue},
-    tx::Transaction,
-    util::{
-        parse::{decode_base10, encode_base10},
-        path::expand_path,
-    },
-    zk::halo2::Field,
-    Result,
-};
-use darkfi_money_contract::model::Coin;
-use darkfi_sdk::{
-    crypto::{PublicKey, SecretKey, TokenId},
-    pasta::{group::ff::PrimeField, pallas},
-};
-use darkfi_serial::{deserialize, serialize};
-
-/// Error codes
-mod error;
-
-/// darkfid JSON-RPC related methods
-mod rpc;
-
-/// Payment methods
-mod transfer;
-
-/// Swap methods
-mod swap;
-use swap::PartialSwapData;
-
-/// Token methods
-mod token;
-
-/// CLI utility functions
-mod cli_util;
-use cli_util::{generate_completions, kaching, parse_token_pair, parse_value_pair};
-
-/// Wallet functionality related to Money
-mod money;
-use money::BALANCE_BASE10_DECIMALS;
-
-/// Wallet functionality related to Dao
-mod dao;
-use dao::DaoParams;
-
-/// Wallet functionality related to transactions history
-mod txs_history;
-
-/// Wallet database operations handler
-mod walletdb;
-use walletdb::{WalletDb, WalletPtr};
-
-const CONFIG_FILE: &str = "drk_config.toml";
-const CONFIG_FILE_CONTENTS: &str = include_str!("../drk_config.toml");
-
-// Dev Note: when adding/modifying args here,
-// don't forget to update cli_util::generate_completions()
-#[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
-#[serde(default)]
-#[structopt(name = "drk", about = cli_desc!())]
-struct Args {
-    #[structopt(short, long)]
-    /// Configuration file to use
-    config: Option<String>,
-
-    #[structopt(long, default_value = "~/.local/darkfi/drk/wallet.db")]
-    /// Path to wallet database
-    wallet_path: String,
-
-    #[structopt(long, default_value = "changeme")]
-    /// Password for the wallet database
-    wallet_pass: String,
-
-    #[structopt(short, long, default_value = "tcp://127.0.0.1:8340")]
-    /// darkfid JSON-RPC endpoint
-    endpoint: Url,
-
-    #[structopt(subcommand)]
-    /// Sub command to execute
-    command: Subcmd,
-
-    #[structopt(short, long)]
-    /// Set log file to ouput into
-    log: Option<String>,
-
-    #[structopt(short, parse(from_occurrences))]
-    /// Increase verbosity (-vvv supported)
-    verbose: u8,
-}
-
-// Dev Note: when adding/modifying commands here,
-// don't forget to update cli_util::generate_completions()
-#[derive(Clone, Debug, Deserialize, StructOpt)]
-enum Subcmd {
-    /// Fun
-    Kaching,
-
-    /// Send a ping request to the darkfid RPC endpoint
-    Ping,
-
-    /// Generate a SHELL completion script and print to stdout
-    Completions {
-        /// The Shell you want to generate script for
-        shell: String,
-    },
-
-    /// Wallet operations
-    Wallet {
-        #[structopt(long)]
-        /// Initialize wallet database
-        initialize: bool,
-
-        #[structopt(long)]
-        /// Generate a new keypair in the wallet
-        keygen: bool,
-
-        #[structopt(long)]
-        /// Query the wallet for known balances
-        balance: bool,
-
-        #[structopt(long)]
-        /// Get the default address in the wallet
-        address: bool,
-
-        #[structopt(long)]
-        /// Print all the addresses in the wallet
-        addresses: bool,
-
-        #[structopt(long)]
-        /// Set the default address in the wallet
-        default_address: Option<usize>,
-
-        #[structopt(long)]
-        /// Print all the secret keys from the wallet
-        secrets: bool,
-
-        #[structopt(long)]
-        /// Import secret keys from stdin into the wallet, separated by newlines
-        import_secrets: bool,
-
-        #[structopt(long)]
-        /// Print the Merkle tree in the wallet
-        tree: bool,
-
-        #[structopt(long)]
-        /// Print all the coins in the wallet
-        coins: bool,
-    },
-
-    /// Unspend a coin
-    Unspend {
-        /// base58-encoded coin to mark as unspent
-        coin: String,
-    },
-
-    /// Create a payment transaction
-    Transfer {
-        /// Amount to send
-        amount: String,
-
-        /// Token ID to send
-        token: String,
-
-        /// Recipient address
-        recipient: String,
-    },
-
-    /// OTC atomic swap
-    Otc {
-        #[structopt(subcommand)]
-        /// Sub command to execute
-        command: OtcSubcmd,
-    },
-
-    /// Inspect a transaction from stdin
-    Inspect,
-
-    /// Read a transaction from stdin and broadcast it
-    Broadcast,
-
-    /// This subscription will listen for incoming blocks from darkfid and look
-    /// through their transactions to see if there's any that interest us.
-    /// With `drk` we look at transactions calling the money contract so we can
-    /// find coins sent to us and fill our wallet with the necessary metadata.
-    Subscribe,
-
-    /// DAO functionalities
-    Dao {
-        #[structopt(subcommand)]
-        /// Sub command to execute
-        command: DaoSubcmd,
-    },
-
-    /// Scan the blockchain and parse relevant transactions
-    Scan {
-        #[structopt(long)]
-        /// Reset Merkle tree and start scanning from first block
-        reset: bool,
-
-        #[structopt(long)]
-        /// List all available checkpoints
-        list: bool,
-
-        #[structopt(long)]
-        /// Reset Merkle tree to checkpoint index and start scanning
-        checkpoint: Option<u64>,
-    },
-
-    /// Explorer related subcommands
-    Explorer {
-        #[structopt(subcommand)]
-        /// Sub command to execute
-        command: ExplorerSubcmd,
-    },
-
-    /// Manage Token aliases
-    Alias {
-        #[structopt(subcommand)]
-        /// Sub command to execute
-        command: AliasSubcmd,
-    },
-
-    /// Token functionalities
-    Token {
-        #[structopt(subcommand)]
-        /// Sub command to execute
-        command: TokenSubcmd,
-    },
-}
-
-#[derive(Clone, Debug, Deserialize, StructOpt)]
-enum OtcSubcmd {
-    /// Initialize the first half of the atomic swap
-    Init {
-        /// Value pair to send:recv (11.55:99.42)
-        #[structopt(short, long)]
-        value_pair: String,
-
-        /// Token pair to send:recv (f00:b4r)
-        #[structopt(short, long)]
-        token_pair: String,
-    },
-
-    /// Build entire swap tx given the first half from stdin
-    Join,
-
-    /// Inspect a swap half or the full swap tx from stdin
-    Inspect,
-
-    /// Sign a transaction given from stdin as the first-half
-    Sign,
-}
-
-#[derive(Clone, Debug, Deserialize, StructOpt)]
-enum DaoSubcmd {
-    /// Create DAO parameters
-    Create {
-        /// The minimum amount of governance tokens needed to open a proposal for this DAO
-        proposer_limit: String,
-        /// Minimal threshold of participating total tokens needed for a proposal to pass
-        quorum: String,
-        /// The ratio of winning votes/total votes needed for a proposal to pass (2 decimals)
-        approval_ratio: f64,
-        /// DAO's governance token ID
-        gov_token_id: String,
-    },
-
-    /// View DAO data from stdin
-    View,
-
-    /// Import DAO data from stdin
-    Import {
-        /// Named identifier for the DAO
-        dao_name: String,
-    },
-
-    /// List imported DAOs (or info about a specific one)
-    List {
-        /// Numeric identifier for the DAO (optional)
-        dao_alias: Option<String>,
-    },
-
-    /// Show the balance of a DAO
-    Balance {
-        /// Name or numeric identifier for the DAO
-        dao_alias: String,
-    },
-
-    /// Mint an imported DAO on-chain
-    Mint {
-        /// Name or numeric identifier for the DAO
-        dao_alias: String,
-    },
-
-    /// Create a proposal for a DAO
-    Propose {
-        /// Name or numeric identifier for the DAO
-        dao_alias: String,
-
-        /// Pubkey to send tokens to with proposal success
-        recipient: String,
-
-        /// Amount to send from DAO with proposal success
-        amount: String,
-
-        /// Token ID to send from DAO with proposal success
-        token: String,
-    },
-
-    /// List DAO proposals
-    Proposals {
-        /// Name or numeric identifier for the DAO
-        dao_alias: String,
-    },
-
-    /// View a DAO proposal data
-    Proposal {
-        /// Name or numeric identifier for the DAO
-        dao_alias: String,
-
-        /// Numeric identifier for the proposal
-        proposal_id: u64,
-    },
-
-    /// Vote on a given proposal
-    Vote {
-        /// Name or numeric identifier for the DAO
-        dao_alias: String,
-
-        /// Numeric identifier for the proposal
-        proposal_id: u64,
-
-        /// Vote (0 for NO, 1 for YES)
-        vote: u8,
-
-        /// Vote weight (amount of governance tokens)
-        vote_weight: String,
-    },
-
-    /// Execute a DAO proposal
-    Exec {
-        /// Name or numeric identifier for the DAO
-        dao_alias: String,
-
-        /// Numeric identifier for the proposal
-        proposal_id: u64,
-    },
-}
-
-#[derive(Clone, Debug, Deserialize, StructOpt)]
-enum ExplorerSubcmd {
-    /// Fetch a blockchain transaction by hash
-    FetchTx {
-        /// Transaction hash
-        tx_hash: String,
-
-        #[structopt(long)]
-        /// Print the full transaction information
-        full: bool,
-
-        #[structopt(long)]
-        /// Encode transaction to base58
-        encode: bool,
-    },
-
-    /// Read a transaction from stdin and simulate it
-    SimulateTx,
-
-    /// Fetch broadcasted transactions history
-    TxsHistory {
-        /// Fetch specific history record (optional)
-        tx_hash: Option<String>,
-
-        #[structopt(long)]
-        /// Encode specific history record transaction to base58
-        encode: bool,
-    },
-}
-
-#[derive(Clone, Debug, Deserialize, StructOpt)]
-enum AliasSubcmd {
-    /// Create a Token alias
-    Add {
-        /// Token alias
-        alias: String,
-
-        /// Token to create alias for
-        token: String,
-    },
-
-    /// Print alias info of optional arguments.
-    /// If no argument is provided, list all the aliases in the wallet.
-    Show {
-        /// Token alias to search for
-        #[structopt(short, long)]
-        alias: Option<String>,
-
-        /// Token to search alias for
-        #[structopt(short, long)]
-        token: Option<String>,
-    },
-
-    /// Remove a Token alias
-    Remove {
-        /// Token alias to remove
-        alias: String,
-    },
-}
-
-#[derive(Clone, Debug, Deserialize, StructOpt)]
-enum TokenSubcmd {
-    /// Import a mint authority secret from stdin
-    Import,
-
-    /// Generate a new mint authority
-    GenerateMint,
-
-    /// List token IDs with available mint authorities
-    List,
-
-    /// Mint tokens
-    Mint {
-        /// Token ID to mint
-        token: String,
-
-        /// Amount to mint
-        amount: String,
-
-        /// Recipient of the minted tokens
-        recipient: String,
-    },
-
-    /// Freeze a token mint
-    Freeze {
-        /// Token ID to freeze
-        token: String,
-    },
-}
-
-/// CLI-util structure
-pub struct Drk {
-    /// Wallet database operations handler
-    pub wallet: WalletPtr,
-    /// JSON-RPC client to execute requests to darkfid daemon
-    pub rpc_client: RpcClient,
-}
-
-impl Drk {
-    async fn new(
-        wallet_path: String,
-        wallet_pass: String,
-        endpoint: Url,
-        ex: Arc<smol::Executor<'static>>,
-    ) -> Result<Self> {
-        // Initialize wallet
-        let wallet_path = expand_path(&wallet_path)?;
-        if !wallet_path.exists() {
-            if let Some(parent) = wallet_path.parent() {
-                fs::create_dir_all(parent)?;
-            }
-        }
-        let wallet = match WalletDb::new(Some(wallet_path), Some(&wallet_pass)) {
-            Ok(w) => w,
-            Err(e) => {
-                eprintln!("Error initializing wallet: {e:?}");
-                exit(2);
-            }
-        };
-
-        // Initialize rpc client
-        let rpc_client = RpcClient::new(endpoint, ex).await?;
-
-        Ok(Self { wallet, rpc_client })
-    }
-
-    /// Initialize wallet with tables for drk
-    async fn initialize_wallet(&self) -> Result<()> {
-        let wallet_schema = include_str!("../wallet.sql");
-        if let Err(e) = self.wallet.exec_batch_sql(wallet_schema).await {
-            eprintln!("Error initializing wallet: {e:?}");
-            exit(2);
-        }
-
-        Ok(())
-    }
-
-    /// Auxilliary function to ping configured darkfid daemon for liveness.
-    async fn ping(&self) -> Result<()> {
-        eprintln!("Executing ping request to darkfid...");
-        let latency = Instant::now();
-        let req = JsonRequest::new("ping", JsonValue::Array(vec![]));
-        let rep = self.rpc_client.oneshot_request(req).await?;
-        let latency = latency.elapsed();
-        eprintln!("Got reply: {rep:?}");
-        eprintln!("Latency: {latency:?}");
-        Ok(())
-    }
-}
-
-async_daemonize!(realmain);
-async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
-    match args.command {
-        Subcmd::Kaching => {
-            kaching().await;
-            Ok(())
-        }
-
-        Subcmd::Ping => {
-            let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-            drk.ping().await
-        }
-
-        Subcmd::Completions { shell } => generate_completions(&shell),
-
-        Subcmd::Wallet {
-            initialize,
-            keygen,
-            balance,
-            address,
-            addresses,
-            default_address,
-            secrets,
-            import_secrets,
-            tree,
-            coins,
-        } => {
-            if !initialize &&
-                !keygen &&
-                !balance &&
-                !address &&
-                !addresses &&
-                default_address.is_none() &&
-                !secrets &&
-                !tree &&
-                !coins &&
-                !import_secrets
-            {
-                eprintln!("Error: You must use at least one flag for this subcommand");
-                eprintln!("Run with \"wallet -h\" to see the subcommand usage.");
-                exit(2);
-            }
-
-            let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-
-            if initialize {
-                drk.initialize_wallet().await?;
-                if let Err(e) = drk.initialize_money().await {
-                    eprintln!("Failed to initialize Money: {e:?}");
-                    exit(2);
-                }
-                if let Err(e) = drk.initialize_dao().await {
-                    eprintln!("Failed to initialize DAO: {e:?}");
-                    exit(2);
-                }
-                return Ok(())
-            }
-
-            if keygen {
-                if let Err(e) = drk.money_keygen().await {
-                    eprintln!("Failed to generate keypair: {e:?}");
-                    exit(2);
-                }
-                return Ok(())
-            }
-
-            if balance {
-                let balmap = drk.money_balance().await?;
-
-                let aliases_map = drk.get_aliases_mapped_by_token().await?;
-
-                // Create a prettytable with the new data:
-                let mut table = Table::new();
-                table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row!["Token ID", "Aliases", "Balance"]);
-                for (token_id, balance) in balmap.iter() {
-                    let aliases = match aliases_map.get(token_id) {
-                        Some(a) => a,
-                        None => "-",
-                    };
-
-                    table.add_row(row![
-                        token_id,
-                        aliases,
-                        encode_base10(*balance, BALANCE_BASE10_DECIMALS)
-                    ]);
-                }
-
-                if table.is_empty() {
-                    eprintln!("No unspent balances found");
-                } else {
-                    eprintln!("{table}");
-                }
-
-                return Ok(())
-            }
-
-            if address {
-                let address = match drk.default_address().await {
-                    Ok(a) => a,
-                    Err(e) => {
-                        eprintln!("Failed to fetch default address: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                eprintln!("{address}");
-
-                return Ok(())
-            }
-
-            if addresses {
-                let addresses = drk.addresses().await?;
-
-                // Create a prettytable with the new data:
-                let mut table = Table::new();
-                table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row!["Key ID", "Public Key", "Secret Key", "Is Default"]);
-                for (key_id, public_key, secret_key, is_default) in addresses {
-                    let is_default = match is_default {
-                        1 => "*",
-                        _ => "",
-                    };
-                    table.add_row(row![key_id, public_key, secret_key, is_default]);
-                }
-
-                if table.is_empty() {
-                    eprintln!("No addresses found");
-                } else {
-                    eprintln!("{table}");
-                }
-
-                return Ok(())
-            }
-
-            if let Some(idx) = default_address {
-                if let Err(e) = drk.set_default_address(idx).await {
-                    eprintln!("Failed to set default address: {e:?}");
-                    exit(2);
-                }
-                return Ok(())
-            }
-
-            if secrets {
-                let v = drk.get_money_secrets().await?;
-
-                for i in v {
-                    eprintln!("{i}");
-                }
-
-                return Ok(())
-            }
-
-            if import_secrets {
-                let mut secrets = vec![];
-                let lines = stdin().lines();
-                for (i, line) in lines.enumerate() {
-                    if let Ok(line) = line {
-                        let bytes = bs58::decode(&line.trim()).into_vec()?;
-                        let Ok(secret) = deserialize(&bytes) else {
-                            eprintln!("Warning: Failed to deserialize secret on line {i}");
-                            continue
-                        };
-                        secrets.push(secret);
-                    }
-                }
-
-                let pubkeys = match drk.import_money_secrets(secrets).await {
-                    Ok(p) => p,
-                    Err(e) => {
-                        eprintln!("Failed to import secret keys into wallet: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                for key in pubkeys {
-                    eprintln!("{key}");
-                }
-
-                return Ok(())
-            }
-
-            if tree {
-                let tree = drk.get_money_tree().await?;
-
-                eprintln!("{tree:#?}");
-
-                return Ok(())
-            }
-
-            if coins {
-                let coins = drk.get_coins(true).await?;
-
-                let aliases_map = drk.get_aliases_mapped_by_token().await?;
-
-                if coins.is_empty() {
-                    return Ok(())
-                }
-
-                let mut table = Table::new();
-                table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row![
-                    "Coin",
-                    "Spent",
-                    "Token ID",
-                    "Aliases",
-                    "Value",
-                    "Spend Hook",
-                    "User Data"
-                ]);
-                let zero = pallas::Base::zero();
-                for coin in coins {
-                    let aliases = match aliases_map.get(&coin.0.note.token_id.to_string()) {
-                        Some(a) => a,
-                        None => "-",
-                    };
-
-                    let spend_hook = if coin.0.note.spend_hook != zero {
-                        bs58::encode(&serialize(&coin.0.note.spend_hook)).into_string().to_string()
-                    } else {
-                        String::from("-")
-                    };
-
-                    let user_data = if coin.0.note.user_data != zero {
-                        bs58::encode(&serialize(&coin.0.note.user_data)).into_string().to_string()
-                    } else {
-                        String::from("-")
-                    };
-
-                    table.add_row(row![
-                        bs58::encode(&serialize(&coin.0.coin.inner())).into_string().to_string(),
-                        coin.1,
-                        coin.0.note.token_id,
-                        aliases,
-                        format!("{} ({})", coin.0.note.value, encode_base10(coin.0.note.value, 8)),
-                        spend_hook,
-                        user_data
-                    ]);
-                }
-
-                eprintln!("{table}");
-
-                return Ok(())
-            }
-
-            unreachable!()
-        }
-
-        Subcmd::Unspend { coin } => {
-            let bytes: [u8; 32] = match bs58::decode(&coin).into_vec()?.try_into() {
-                Ok(b) => b,
-                Err(e) => {
-                    eprintln!("Invalid coin: {e:?}");
-                    exit(2);
-                }
-            };
-
-            let elem: pallas::Base = match pallas::Base::from_repr(bytes).into() {
-                Some(v) => v,
-                None => {
-                    eprintln!("Invalid coin");
-                    exit(2);
-                }
-            };
-
-            let coin = Coin::from(elem);
-            let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-            if let Err(e) = drk.unspend_coin(&coin).await {
-                eprintln!("Failed to mark coin as unspent: {e:?}");
-                exit(2);
-            }
-
-            Ok(())
-        }
-
-        Subcmd::Transfer { amount, token, recipient } => {
-            let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-
-            if let Err(e) = f64::from_str(&amount) {
-                eprintln!("Invalid amount: {e:?}");
-                exit(2);
-            }
-
-            let rcpt = match PublicKey::from_str(&recipient) {
-                Ok(r) => r,
-                Err(e) => {
-                    eprintln!("Invalid recipient: {e:?}");
-                    exit(2);
-                }
-            };
-
-            let token_id = match drk.get_token(token).await {
-                Ok(t) => t,
-                Err(e) => {
-                    eprintln!("Invalid token alias: {e:?}");
-                    exit(2);
-                }
-            };
-
-            let tx = match drk.transfer(&amount, token_id, rcpt).await {
-                Ok(t) => t,
-                Err(e) => {
-                    eprintln!("Failed to create payment transaction: {e:?}");
-                    exit(2);
-                }
-            };
-
-            println!("{}", bs58::encode(&serialize(&tx)).into_string());
-
-            Ok(())
-        }
-
-        Subcmd::Otc { command } => {
-            let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-
-            match command {
-                OtcSubcmd::Init { value_pair, token_pair } => {
-                    let (vp_send, vp_recv) = parse_value_pair(&value_pair)?;
-                    let (tp_send, tp_recv) = parse_token_pair(&drk, &token_pair).await?;
-
-                    let half = match drk.init_swap(vp_send, tp_send, vp_recv, tp_recv).await {
-                        Ok(h) => h,
-                        Err(e) => {
-                            eprintln!("Failed to create swap transaction half: {e:?}");
-                            exit(2);
-                        }
-                    };
-
-                    println!("{}", bs58::encode(&serialize(&half)).into_string());
-                    Ok(())
-                }
-
-                OtcSubcmd::Join => {
-                    let mut buf = String::new();
-                    stdin().read_to_string(&mut buf)?;
-                    let bytes = bs58::decode(&buf.trim()).into_vec()?;
-                    let partial: PartialSwapData = deserialize(&bytes)?;
-
-                    let tx = match drk.join_swap(partial).await {
-                        Ok(tx) => tx,
-                        Err(e) => {
-                            eprintln!("Failed to create a join swap transaction: {e:?}");
-                            exit(2);
-                        }
-                    };
-
-                    println!("{}", bs58::encode(&serialize(&tx)).into_string());
-                    Ok(())
-                }
-
-                OtcSubcmd::Inspect => {
-                    let mut buf = String::new();
-                    stdin().read_to_string(&mut buf)?;
-                    let bytes = bs58::decode(&buf.trim()).into_vec()?;
-
-                    if let Err(e) = drk.inspect_swap(bytes).await {
-                        eprintln!("Failed to inspect swap: {e:?}");
-                        exit(2);
-                    };
-
-                    Ok(())
-                }
-
-                OtcSubcmd::Sign => {
-                    let mut buf = String::new();
-                    stdin().read_to_string(&mut buf)?;
-                    let bytes = bs58::decode(&buf.trim()).into_vec()?;
-                    let mut tx: Transaction = deserialize(&bytes)?;
-
-                    if let Err(e) = drk.sign_swap(&mut tx).await {
-                        eprintln!("Failed to sign joined swap transaction: {e:?}");
-                        exit(2);
-                    };
-
-                    println!("{}", bs58::encode(&serialize(&tx)).into_string());
-                    Ok(())
-                }
-            }
-        }
-
-        Subcmd::Dao { command } => match command {
-            DaoSubcmd::Create { proposer_limit, quorum, approval_ratio, gov_token_id } => {
-                if let Err(e) = f64::from_str(&proposer_limit) {
-                    eprintln!("Invalid proposer limit: {e:?}");
-                    exit(2);
-                }
-                if let Err(e) = f64::from_str(&quorum) {
-                    eprintln!("Invalid quorum: {e:?}");
-                    exit(2);
-                }
-
-                let proposer_limit = decode_base10(&proposer_limit, BALANCE_BASE10_DECIMALS, true)?;
-                let quorum = decode_base10(&quorum, BALANCE_BASE10_DECIMALS, true)?;
-
-                if approval_ratio > 1.0 {
-                    eprintln!("Error: Approval ratio cannot be >1.0");
-                    exit(2);
-                }
-
-                let approval_ratio_base = 100_u64;
-                let approval_ratio_quot = (approval_ratio * approval_ratio_base as f64) as u64;
-
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let gov_token_id = match drk.get_token(gov_token_id).await {
-                    Ok(g) => g,
-                    Err(e) => {
-                        eprintln!("Invalid Token ID: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let secret_key = SecretKey::random(&mut OsRng);
-                let bulla_blind = pallas::Base::random(&mut OsRng);
-
-                let dao_params = DaoParams {
-                    proposer_limit,
-                    quorum,
-                    approval_ratio_base,
-                    approval_ratio_quot,
-                    gov_token_id,
-                    secret_key,
-                    bulla_blind,
-                };
-
-                let encoded = bs58::encode(&serialize(&dao_params)).into_string();
-                eprintln!("{encoded}");
-
-                Ok(())
-            }
-
-            DaoSubcmd::View => {
-                let mut buf = String::new();
-                stdin().read_to_string(&mut buf)?;
-                let bytes = bs58::decode(&buf.trim()).into_vec()?;
-                let dao_params: DaoParams = deserialize(&bytes)?;
-                eprintln!("{dao_params}");
-
-                Ok(())
-            }
-
-            DaoSubcmd::Import { dao_name } => {
-                let mut buf = String::new();
-                stdin().read_to_string(&mut buf)?;
-                let bytes = bs58::decode(&buf.trim()).into_vec()?;
-                let dao_params: DaoParams = deserialize(&bytes)?;
-
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-
-                if let Err(e) = drk.import_dao(dao_name, dao_params).await {
-                    eprintln!("Failed to import DAO: {e:?}");
-                    exit(2);
-                }
-
-                Ok(())
-            }
-
-            DaoSubcmd::List { dao_alias } => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                // We cannot use .map() since get_dao_id() uses ?
-                let dao_id = match dao_alias {
-                    Some(alias) => Some(drk.get_dao_id(&alias).await?),
-                    None => None,
-                };
-
-                if let Err(e) = drk.dao_list(dao_id).await {
-                    eprintln!("Failed to list DAO: {e:?}");
-                    exit(2);
-                }
-
-                Ok(())
-            }
-
-            DaoSubcmd::Balance { dao_alias } => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let dao_id = drk.get_dao_id(&dao_alias).await?;
-
-                let balmap = match drk.dao_balance(dao_id).await {
-                    Ok(b) => b,
-                    Err(e) => {
-                        eprintln!("Failed to fetch DAO balance: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let aliases_map = match drk.get_aliases_mapped_by_token().await {
-                    Ok(a) => a,
-                    Err(e) => {
-                        eprintln!("Failed to fetch wallet aliases: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                // Create a prettytable with the new data:
-                let mut table = Table::new();
-                table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row!["Token ID", "Aliases", "Balance"]);
-                for (token_id, balance) in balmap.iter() {
-                    let aliases = match aliases_map.get(token_id) {
-                        Some(a) => a,
-                        None => "-",
-                    };
-
-                    table.add_row(row![
-                        token_id,
-                        aliases,
-                        encode_base10(*balance, BALANCE_BASE10_DECIMALS)
-                    ]);
-                }
-
-                if table.is_empty() {
-                    eprintln!("No unspent balances found");
-                } else {
-                    eprintln!("{table}");
-                }
-
-                Ok(())
-            }
-
-            DaoSubcmd::Mint { dao_alias } => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let dao_id = drk.get_dao_id(&dao_alias).await?;
-
-                let tx = match drk.dao_mint(dao_id).await {
-                    Ok(tx) => tx,
-                    Err(e) => {
-                        eprintln!("Failed to mint DAO: {e:?}");
-                        exit(2);
-                    }
-                };
-                eprintln!("{}", bs58::encode(&serialize(&tx)).into_string());
-                Ok(())
-            }
-
-            DaoSubcmd::Propose { dao_alias, recipient, amount, token } => {
-                if let Err(e) = f64::from_str(&amount) {
-                    eprintln!("Invalid amount: {e:?}");
-                    exit(2);
-                }
-                let amount = decode_base10(&amount, BALANCE_BASE10_DECIMALS, true)?;
-                let rcpt = match PublicKey::from_str(&recipient) {
-                    Ok(r) => r,
-                    Err(e) => {
-                        eprintln!("Invalid recipient: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let dao_id = drk.get_dao_id(&dao_alias).await?;
-                let token_id = match drk.get_token(token).await {
-                    Ok(t) => t,
-                    Err(e) => {
-                        eprintln!("Invalid token alias: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let tx = match drk.dao_propose(dao_id, rcpt, amount, token_id).await {
-                    Ok(tx) => tx,
-                    Err(e) => {
-                        eprintln!("Failed to create DAO proposal: {e:?}");
-                        exit(2);
-                    }
-                };
-                eprintln!("{}", bs58::encode(&serialize(&tx)).into_string());
-                Ok(())
-            }
-
-            DaoSubcmd::Proposals { dao_alias } => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let dao_id = drk.get_dao_id(&dao_alias).await?;
-
-                let proposals = drk.get_dao_proposals(dao_id).await?;
-
-                for proposal in proposals {
-                    eprintln!("[{}] {:?}", proposal.id, proposal.bulla());
-                }
-
-                Ok(())
-            }
-
-            DaoSubcmd::Proposal { dao_alias, proposal_id } => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let dao_id = drk.get_dao_id(&dao_alias).await?;
-
-                let proposals = drk.get_dao_proposals(dao_id).await?;
-                let Some(proposal) = proposals.iter().find(|x| x.id == proposal_id) else {
-                    eprintln!("No such DAO proposal found");
-                    exit(2);
-                };
-
-                eprintln!("{proposal}");
-
-                let votes = drk.get_dao_proposal_votes(proposal_id).await?;
-                eprintln!("votes:");
-                for vote in votes {
-                    let option = if vote.vote_option { "yes" } else { "no " };
-                    eprintln!("  {option} {}", vote.all_vote_value);
-                }
-
-                Ok(())
-            }
-
-            DaoSubcmd::Vote { dao_alias, proposal_id, vote, vote_weight } => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let dao_id = drk.get_dao_id(&dao_alias).await?;
-
-                if let Err(e) = f64::from_str(&vote_weight) {
-                    eprintln!("Invalid vote weight: {e:?}");
-                    exit(2);
-                }
-                let weight = decode_base10(&vote_weight, BALANCE_BASE10_DECIMALS, true)?;
-
-                if vote > 1 {
-                    eprintln!("Vote can be either 0 (NO) or 1 (YES)");
-                    exit(2);
-                }
-                let vote = vote != 0;
-
-                let tx = match drk.dao_vote(dao_id, proposal_id, vote, weight).await {
-                    Ok(tx) => tx,
-                    Err(e) => {
-                        eprintln!("Failed to create DAO Vote transaction: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                // TODO: Write our_vote in the proposal sql.
-
-                eprintln!("{}", bs58::encode(&serialize(&tx)).into_string());
-
-                Ok(())
-            }
-
-            DaoSubcmd::Exec { dao_alias, proposal_id } => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let dao_id = drk.get_dao_id(&dao_alias).await?;
-                let dao = drk.get_dao_by_id(dao_id).await?;
-                let proposal = drk.get_dao_proposal_by_id(proposal_id).await?;
-                assert!(proposal.dao_bulla == dao.bulla());
-
-                let tx = match drk.dao_exec(dao, proposal).await {
-                    Ok(tx) => tx,
-                    Err(e) => {
-                        eprintln!("Failed to execute DAO proposal: {e:?}");
-                        exit(2);
-                    }
-                };
-                eprintln!("{}", bs58::encode(&serialize(&tx)).into_string());
-
-                Ok(())
-            }
-        },
-
-        Subcmd::Inspect => {
-            let mut buf = String::new();
-            stdin().read_to_string(&mut buf)?;
-            let bytes = bs58::decode(&buf.trim()).into_vec()?;
-            let tx: Transaction = deserialize(&bytes)?;
-            eprintln!("{tx:#?}");
-            Ok(())
-        }
-
-        Subcmd::Broadcast => {
-            eprintln!("Reading transaction from stdin...");
-            let mut buf = String::new();
-            stdin().read_to_string(&mut buf)?;
-            let bytes = bs58::decode(&buf.trim()).into_vec()?;
-            let tx = deserialize(&bytes)?;
-
-            let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-
-            let txid = match drk.broadcast_tx(&tx).await {
-                Ok(t) => t,
-                Err(e) => {
-                    eprintln!("Failed to broadcast transaction: {e:?}");
-                    exit(2);
-                }
-            };
-
-            eprintln!("Transaction ID: {txid}");
-
-            Ok(())
-        }
-
-        Subcmd::Subscribe => {
-            let drk =
-                Drk::new(args.wallet_path, args.wallet_pass, args.endpoint.clone(), ex.clone())
-                    .await?;
-
-            if let Err(e) = drk.subscribe_blocks(args.endpoint, ex).await {
-                eprintln!("Block subscription failed: {e:?}");
-                exit(2);
-            }
-
-            Ok(())
-        }
-
-        Subcmd::Scan { reset, list, checkpoint } => {
-            let drk =
-                Drk::new(args.wallet_path, args.wallet_pass, args.endpoint.clone(), ex.clone())
-                    .await?;
-
-            if reset {
-                eprintln!("Reset requested.");
-                if let Err(e) = drk.scan_blocks(true).await {
-                    eprintln!("Failed during scanning: {e:?}");
-                    exit(2);
-                }
-                eprintln!("Finished scanning blockchain");
-
-                return Ok(())
-            }
-
-            if list {
-                eprintln!("List requested.");
-                // TODO: implement
-                unimplemented!()
-            }
-
-            if let Some(c) = checkpoint {
-                eprintln!("Checkpoint requested: {c}");
-                // TODO: implement
-                unimplemented!()
-            }
-
-            if let Err(e) = drk.scan_blocks(false).await {
-                eprintln!("Failed during scanning: {e:?}");
-                exit(2);
-            }
-            eprintln!("Finished scanning blockchain");
-
-            Ok(())
-        }
-
-        Subcmd::Explorer { command } => match command {
-            ExplorerSubcmd::FetchTx { tx_hash, full, encode } => {
-                let tx_hash = blake3::Hash::from_hex(&tx_hash)?;
-
-                let drk =
-                    Drk::new(args.wallet_path, args.wallet_pass, args.endpoint.clone(), ex.clone())
-                        .await?;
-
-                let tx = match drk.get_tx(&tx_hash).await {
-                    Ok(tx) => tx,
-                    Err(e) => {
-                        eprintln!("Failed to fetch transaction: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let Some(tx) = tx else {
-                    eprintln!("Transaction was not found");
-                    exit(1);
-                };
-
-                // Make sure the tx is correct
-                assert_eq!(tx.hash()?, tx_hash);
-
-                if encode {
-                    eprintln!("{}", bs58::encode(&serialize(&tx)).into_string());
-                    exit(1)
-                }
-
-                eprintln!("Transaction ID: {tx_hash}");
-                if full {
-                    eprintln!("{tx:?}");
-                }
-
-                Ok(())
-            }
-
-            ExplorerSubcmd::SimulateTx => {
-                eprintln!("Reading transaction from stdin...");
-                let mut buf = String::new();
-                stdin().read_to_string(&mut buf)?;
-                let bytes = bs58::decode(&buf.trim()).into_vec()?;
-                let tx = deserialize(&bytes)?;
-
-                let drk =
-                    Drk::new(args.wallet_path, args.wallet_pass, args.endpoint.clone(), ex.clone())
-                        .await?;
-
-                let is_valid = match drk.simulate_tx(&tx).await {
-                    Ok(b) => b,
-                    Err(e) => {
-                        eprintln!("Failed to simulate tx: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                eprintln!("Transaction ID: {}", tx.hash()?);
-                eprintln!("State: {}", if is_valid { "valid" } else { "invalid" });
-
-                Ok(())
-            }
-
-            ExplorerSubcmd::TxsHistory { tx_hash, encode } => {
-                let drk =
-                    Drk::new(args.wallet_path, args.wallet_pass, args.endpoint.clone(), ex.clone())
-                        .await?;
-
-                if let Some(c) = tx_hash {
-                    let (tx_hash, status, tx) = drk.get_tx_history_record(&c).await?;
-
-                    if encode {
-                        println!("{}", bs58::encode(&serialize(&tx)).into_string());
-                        exit(1)
-                    }
-
-                    eprintln!("Transaction ID: {tx_hash}");
-                    eprintln!("Status: {status}");
-                    eprintln!("{tx:?}");
-
-                    return Ok(())
-                }
-
-                let map = match drk.get_txs_history().await {
-                    Ok(m) => m,
-                    Err(e) => {
-                        eprintln!("Failed to retrieve transactions history records: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                // Create a prettytable with the new data:
-                let mut table = Table::new();
-                table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row!["Transaction Hash", "Status"]);
-                for (txs_hash, status) in map.iter() {
-                    table.add_row(row![txs_hash, status]);
-                }
-
-                if table.is_empty() {
-                    eprintln!("No transactions found");
-                } else {
-                    eprintln!("{table}");
-                }
-
-                Ok(())
-            }
-        },
-
-        Subcmd::Alias { command } => match command {
-            AliasSubcmd::Add { alias, token } => {
-                if alias.chars().count() > 5 {
-                    eprintln!("Error: Alias exceeds 5 characters");
-                    exit(2);
-                }
-
-                let token_id = match TokenId::from_str(token.as_str()) {
-                    Ok(t) => t,
-                    Err(e) => {
-                        eprintln!("Invalid Token ID: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                if let Err(e) = drk.add_alias(alias, token_id).await {
-                    eprintln!("Failed to add alias: {e:?}");
-                    exit(2);
-                }
-
-                Ok(())
-            }
-
-            AliasSubcmd::Show { alias, token } => {
-                let token_id = match token {
-                    Some(t) => match TokenId::from_str(t.as_str()) {
-                        Ok(t) => Some(t),
-                        Err(e) => {
-                            eprintln!("Invalid Token ID: {e:?}");
-                            exit(2);
-                        }
-                    },
-                    None => None,
-                };
-
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let map = drk.get_aliases(alias, token_id).await?;
-
-                // Create a prettytable with the new data:
-                let mut table = Table::new();
-                table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row!["Alias", "Token ID"]);
-                for (alias, token_id) in map.iter() {
-                    table.add_row(row![alias, token_id]);
-                }
-
-                if table.is_empty() {
-                    eprintln!("No aliases found");
-                } else {
-                    eprintln!("{table}");
-                }
-
-                Ok(())
-            }
-
-            AliasSubcmd::Remove { alias } => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                if let Err(e) = drk.remove_alias(alias).await {
-                    eprintln!("Failed to remove alias: {e:?}");
-                    exit(2);
-                }
-
-                Ok(())
-            }
-        },
-
-        Subcmd::Token { command } => match command {
-            TokenSubcmd::Import => {
-                let mut buf = String::new();
-                stdin().read_to_string(&mut buf)?;
-                let mint_authority = match SecretKey::from_str(buf.trim()) {
-                    Ok(ma) => ma,
-                    Err(e) => {
-                        eprintln!("Invalid secret key: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                if let Err(e) = drk.import_mint_authority(mint_authority).await {
-                    eprintln!("Importing mint authority failed: {e:?}");
-                    exit(2);
-                };
-
-                let token_id = TokenId::derive(mint_authority);
-                eprintln!("Successfully imported mint authority for token ID: {token_id}");
-
-                Ok(())
-            }
-
-            TokenSubcmd::GenerateMint => {
-                let mint_authority = SecretKey::random(&mut OsRng);
-
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-
-                if let Err(e) = drk.import_mint_authority(mint_authority).await {
-                    eprintln!("Importing mint authority failed: {e:?}");
-                    exit(2);
-                };
-
-                let token_id = TokenId::derive(mint_authority);
-                eprintln!("Successfully imported mint authority for token ID: {token_id}");
-
-                Ok(())
-            }
-
-            TokenSubcmd::List => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let tokens = drk.list_tokens().await?;
-                let aliases_map = match drk.get_aliases_mapped_by_token().await {
-                    Ok(map) => map,
-                    Err(e) => {
-                        eprintln!("Failed to fetch wallet aliases: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let mut table = Table::new();
-                table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row!["Token ID", "Aliases", "Mint Authority", "Frozen"]);
-
-                for (token_id, authority, frozen) in tokens {
-                    let aliases = match aliases_map.get(&token_id.to_string()) {
-                        Some(a) => a,
-                        None => "-",
-                    };
-
-                    table.add_row(row![token_id, aliases, authority, frozen]);
-                }
-
-                if table.is_empty() {
-                    eprintln!("No tokens found");
-                } else {
-                    eprintln!("{table}");
-                }
-
-                Ok(())
-            }
-
-            // TODO: Mint directly into DAO treasury
-            TokenSubcmd::Mint { token, amount, recipient } => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-
-                if let Err(e) = f64::from_str(&amount) {
-                    eprintln!("Invalid amount: {e:?}");
-                    exit(2);
-                }
-
-                let rcpt = match PublicKey::from_str(&recipient) {
-                    Ok(r) => r,
-                    Err(e) => {
-                        eprintln!("Invalid recipient: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let token_id = match drk.get_token(token).await {
-                    Ok(t) => t,
-                    Err(e) => {
-                        eprintln!("Invalid Token ID: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let tx = match drk.mint_token(&amount, rcpt, token_id).await {
-                    Ok(tx) => tx,
-                    Err(e) => {
-                        eprintln!("Failed to create token mint transaction: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                eprintln!("{}", bs58::encode(&serialize(&tx)).into_string());
-
-                Ok(())
-            }
-
-            TokenSubcmd::Freeze { token } => {
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, args.endpoint, ex).await?;
-                let token_id = match drk.get_token(token).await {
-                    Ok(t) => t,
-                    Err(e) => {
-                        eprintln!("Invalid Token ID: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                let tx = match drk.freeze_token(token_id).await {
-                    Ok(tx) => tx,
-                    Err(e) => {
-                        eprintln!("Failed to create token freeze transaction: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                eprintln!("{}", bs58::encode(&serialize(&tx)).into_string());
-
-                Ok(())
-            }
-        },
-    }
-}

BIN
bin/drk2/wallet.mp3


+ 0 - 9
bin/drk2/wallet.sql

@@ -1,9 +0,0 @@
--- Wallet definitions for drk.
--- We store data that is needed for wallet operations.
-
--- Broadcasted transactions history
-CREATE TABLE IF NOT EXISTS transactions_history (
-    transaction_hash TEXT PRIMARY KEY NOT NULL,
-    status TEXT NOT NULL,
-	tx BLOB NOT NULL
-);

Неке датотеке нису приказане због велике количине промена