From 51fa9057e8b975d5d1cb2b53051291553f06b948 Mon Sep 17 00:00:00 2001 From: Boyu Yang Date: Wed, 20 Mar 2024 07:59:58 +0800 Subject: [PATCH] feat: add the first version of implementation --- Cargo.lock | 3599 +++++++++++++++++++++ Cargo.toml | 53 + Makefile | 27 + README.md | 69 + src/cli/deploy.rs | 126 + src/cli/init.rs | 410 +++ src/cli/mod.rs | 142 + src/cli/serve.rs | 348 ++ src/components/api_service.rs | 228 ++ src/components/bitcoin_client.rs | 255 ++ src/components/ckb_client.rs | 42 + src/components/mod.rs | 14 + src/components/spv_service.rs | 185 ++ src/components/storage/internal/cache.rs | 9 + src/components/storage/internal/mmr.rs | 33 + src/components/storage/internal/mod.rs | 95 + src/components/storage/internal/reader.rs | 95 + src/components/storage/internal/writer.rs | 58 + src/components/storage/mod.rs | 9 + src/components/storage/prelude.rs | 288 ++ src/components/storage/result.rs | 43 + src/components/storage/schemas/columns.rs | 13 + src/components/storage/schemas/keys.rs | 16 + src/components/storage/schemas/mod.rs | 5 + src/main.rs | 10 + src/prelude.rs | 2 + src/result.rs | 53 + src/utilities/key.rs | 75 + src/utilities/mod.rs | 8 + src/utilities/type_id.rs | 11 + src/utilities/value_parsers.rs | 215 ++ 31 files changed, 6536 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Makefile create mode 100644 src/cli/deploy.rs create mode 100644 src/cli/init.rs create mode 100644 src/cli/mod.rs create mode 100644 src/cli/serve.rs create mode 100644 src/components/api_service.rs create mode 100644 src/components/bitcoin_client.rs create mode 100644 src/components/ckb_client.rs create mode 100644 src/components/mod.rs create mode 100644 src/components/spv_service.rs create mode 100644 src/components/storage/internal/cache.rs create mode 100644 src/components/storage/internal/mmr.rs create mode 100644 src/components/storage/internal/mod.rs create mode 100644 src/components/storage/internal/reader.rs create mode 100644 src/components/storage/internal/writer.rs create mode 100644 src/components/storage/mod.rs create mode 100644 src/components/storage/prelude.rs create mode 100644 src/components/storage/result.rs create mode 100644 src/components/storage/schemas/columns.rs create mode 100644 src/components/storage/schemas/keys.rs create mode 100644 src/components/storage/schemas/mod.rs create mode 100644 src/main.rs create mode 100644 src/prelude.rs create mode 100644 src/result.rs create mode 100644 src/utilities/key.rs create mode 100644 src/utilities/mod.rs create mode 100644 src/utilities/type_id.rs create mode 100644 src/utilities/value_parsers.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..342143d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3599 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.12", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bech32" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" + +[[package]] +name = "bech32" +version = "0.10.0-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" + +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.4.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.53", + "which", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitcoin" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd00f3c09b5f21fb357abe32d29946eb8bb7a0862bae62c0b5e4a692acbbe73c" +dependencies = [ + "bech32 0.10.0-beta", + "bitcoin-internals", + "bitcoin_hashes", + "hex-conservative", + "hex_lit", + "secp256k1 0.28.2", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" +dependencies = [ + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "hex-conservative", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2b-ref" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "294d17c72e0ba59fad763caa112368d0672083779cdebbb97164f4bb4c1e339a" + +[[package]] +name = "blake2b-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89a8565807f21b913288968e391819e7f9b2f0f46c7b89549c051cccf3a2771" +dependencies = [ + "cc", + "cty", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +dependencies = [ + "serde", +] + +[[package]] +name = "cacache" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142316461ed3a3dfcba10417317472da5bfd0461e4d276bf7c07b330766d9490" +dependencies = [ + "digest", + "either", + "futures", + "hex", + "libc", + "memmap2", + "miette", + "reflink-copy", + "serde", + "serde_derive", + "serde_json", + "sha1", + "sha2", + "ssri", + "tempfile", + "thiserror", + "tokio", + "tokio-stream", + "walkdir", +] + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ckb-bitcoin-spv-service" +version = "0.1.0" +dependencies = [ + "anyhow", + "bitcoin", + "ckb-bitcoin-spv-verifier", + "ckb-hash", + "ckb-jsonrpc-types", + "ckb-rocksdb", + "ckb-sdk", + "ckb-types", + "clap", + "clap-verbosity-flag", + "env_logger", + "faster-hex 0.9.0", + "jsonrpc-core", + "jsonrpc-derive", + "jsonrpc-http-server", + "jsonrpc-server-utils", + "log", + "reqwest", + "secp256k1 0.24.3", + "serde", + "serde_json", + "thiserror", + "tokio", + "url", + "zeroize", +] + +[[package]] +name = "ckb-bitcoin-spv-verifier" +version = "0.1.0" +source = "git+https://github.com/ckb-cell/ckb-bitcoin-spv?rev=2464c8f#2464c8fb6059f285e6d1b9157b49fd08f7b6efff" +dependencies = [ + "bitcoin", + "bitcoin_hashes", + "ckb-merkle-mountain-range 0.6.0", + "ethereum-types", + "log", + "molecule", +] + +[[package]] +name = "ckb-chain-spec" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8212f21a597c1e9e27641b142b6559a4aa7042571ccec2a1191a7f3b680cd3b1" +dependencies = [ + "cacache", + "ckb-constant", + "ckb-crypto", + "ckb-dao-utils", + "ckb-error", + "ckb-hash", + "ckb-jsonrpc-types", + "ckb-logger", + "ckb-pow", + "ckb-rational", + "ckb-resource", + "ckb-traits", + "ckb-types", + "serde", + "toml", +] + +[[package]] +name = "ckb-channel" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b22e70542c9a1ec346851bfa999eb52bf6f10dac062dc7c3e83490f3129ea8" +dependencies = [ + "crossbeam-channel", +] + +[[package]] +name = "ckb-constant" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3a59ed091a196cd2dac439917049f6b846f9aaa7aafd9b29df183994cc25e" + +[[package]] +name = "ckb-crypto" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20a2e281c9b4090c68343c03f90200e005d0f8be43a539cd6ecc7d46562cab8" +dependencies = [ + "ckb-fixed-hash", + "faster-hex 0.6.1", + "lazy_static", + "rand 0.7.3", + "secp256k1 0.24.3", + "thiserror", +] + +[[package]] +name = "ckb-dao-utils" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae082aedbb90a27364323bdf8a5d80bf916e8aafac96041168498eddc4efc0c7" +dependencies = [ + "byteorder", + "ckb-error", + "ckb-types", +] + +[[package]] +name = "ckb-error" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6d7bc8a43e036195079e3b6a144547970b6c6e1a5591ecbeea7a5478bfebcfa" +dependencies = [ + "anyhow", + "ckb-occupied-capacity", + "derive_more", + "thiserror", +] + +[[package]] +name = "ckb-fixed-hash" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab27de1271bc1064ed242d08fca2d79ca64e3f99b2680b145ac63ba069dc907" +dependencies = [ + "ckb-fixed-hash-core", + "ckb-fixed-hash-macros", +] + +[[package]] +name = "ckb-fixed-hash-core" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b63d1bc55ac6e578cdc9ad861f427e46b225a4a48bf5d0a55f6fa4a127a822" +dependencies = [ + "faster-hex 0.6.1", + "serde", + "thiserror", +] + +[[package]] +name = "ckb-fixed-hash-macros" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008f7221e3d20c4de5c7e23910d343d4938002fa9eb509e3e6fa5898dc4ecba9" +dependencies = [ + "ckb-fixed-hash-core", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ckb-gen-types" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e565b056266184d99aab11115245531127e4a8d13b7700c4839b469763a3c54" +dependencies = [ + "cfg-if 1.0.0", + "ckb-error", + "ckb-fixed-hash", + "ckb-hash", + "ckb-occupied-capacity", + "molecule", + "numext-fixed-uint", +] + +[[package]] +name = "ckb-hash" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99970478850566472a03e5cc4d57e388bb7a8255771f4308b42036f0d8a623d8" +dependencies = [ + "blake2b-ref", + "blake2b-rs", +] + +[[package]] +name = "ckb-jsonrpc-types" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c9d60394ebc5c4f5416ca851622fe66bea9f64ef13060c2e22b2cf856af97b" +dependencies = [ + "ckb-types", + "faster-hex 0.6.1", + "serde", + "serde_json", +] + +[[package]] +name = "ckb-librocksdb-sys" +version = "8.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e68e0993f54ba0d21152419a0668caa92adc928b5a9b01e45871ec055a31ce2" +dependencies = [ + "bindgen", + "cc", + "glob", + "libc", + "pkg-config", + "rust-ini", +] + +[[package]] +name = "ckb-logger" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b83910b3e92dd7b1b9b27c353f51ae16a48228f2b1c1f070aab7c5ea83d4df2" +dependencies = [ + "log", +] + +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15193decfa1e0b151ce19e42d118db048459a27720fb3de7d3103c30adccb12" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "ckb-mock-tx-types" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f9cbd58716844c28caf3e292154ebfb6dda3e824039b606a1048f1fe5e0120" +dependencies = [ + "ckb-jsonrpc-types", + "ckb-traits", + "ckb-types", + "serde", +] + +[[package]] +name = "ckb-occupied-capacity" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8adc8c7723b98636ae3aeb7d14417c68edcc61cf2913414e42eca1eb2b4f7b" +dependencies = [ + "ckb-occupied-capacity-core", + "ckb-occupied-capacity-macros", +] + +[[package]] +name = "ckb-occupied-capacity-core" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1589ebe2d556a85b70d02f8dd0d022b990de1420a5de66c356eb3ea8a5526b6c" +dependencies = [ + "serde", +] + +[[package]] +name = "ckb-occupied-capacity-macros" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb9842edc4f65f556e57176ecce956ed32fcddcd272118a3a688116d2aba8c80" +dependencies = [ + "ckb-occupied-capacity-core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ckb-pow" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9285eae2b22b8ec1425763f1621d25e6704b26e92a4548ac1da08dafae7a279" +dependencies = [ + "byteorder", + "ckb-hash", + "ckb-types", + "eaglesong", + "log", + "serde", +] + +[[package]] +name = "ckb-rational" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119dbe89e82f7f0f1a5ae310f40775324987c4c0b9128c49b2cfe184a90a8295" +dependencies = [ + "numext-fixed-uint", + "serde", +] + +[[package]] +name = "ckb-resource" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710939ea5dd2e70d602b312d57b7bbaf2d380a8a473fb63b1d8af80c0cca74b8" +dependencies = [ + "ckb-system-scripts", + "ckb-types", + "includedir", + "includedir_codegen", + "phf", + "serde", + "walkdir", +] + +[[package]] +name = "ckb-rocksdb" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "050f7c70a451b9606a5d4e1cf696fec2517e65c3bfcbefadb6b114cc0fa7673b" +dependencies = [ + "ckb-librocksdb-sys", + "libc", + "tempfile", +] + +[[package]] +name = "ckb-script" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2216f261b1169c41f5a6860e3b6362df806bb40915e388a2396514fb08f08ea0" +dependencies = [ + "byteorder", + "ckb-chain-spec", + "ckb-error", + "ckb-hash", + "ckb-logger", + "ckb-traits", + "ckb-types", + "ckb-vm", + "faster-hex 0.6.1", + "serde", +] + +[[package]] +name = "ckb-sdk" +version = "3.1.0" +source = "git+https://github.com/nervosnetwork/ckb-sdk-rust?rev=b792ee7#b792ee7dbb1868fe80ebc8a7682f86924aa5e6f7" +dependencies = [ + "anyhow", + "bech32 0.8.1", + "bitflags 1.3.2", + "bytes", + "ckb-chain-spec", + "ckb-crypto", + "ckb-dao-utils", + "ckb-hash", + "ckb-jsonrpc-types", + "ckb-mock-tx-types", + "ckb-resource", + "ckb-script", + "ckb-traits", + "ckb-types", + "dashmap", + "derive-getters", + "dyn-clone", + "enum-repr-derive", + "futures", + "jsonrpc-core", + "lazy_static", + "log", + "lru", + "parking_lot 0.12.1", + "reqwest", + "secp256k1 0.24.3", + "serde", + "serde_derive", + "serde_json", + "sha3", + "sparse-merkle-tree", + "thiserror", + "tokio", + "tokio-util 0.7.10", +] + +[[package]] +name = "ckb-system-scripts" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa5c59063142de7a68cfad4449c6b3863563856219a2925dfb8c5f019ec2aa47" +dependencies = [ + "blake2b-rs", + "faster-hex 0.6.1", + "includedir", + "includedir_codegen", + "phf", +] + +[[package]] +name = "ckb-traits" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c211d2c46be6847ef80088786bb0dff8341eb99df2c32b546c147e884c4df" +dependencies = [ + "ckb-types", +] + +[[package]] +name = "ckb-types" +version = "0.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e62857acdfe95b3aec162731329a19cd6ea29e8335055ebbbc0dfc8b359d2b4" +dependencies = [ + "bit-vec", + "bytes", + "ckb-channel", + "ckb-constant", + "ckb-error", + "ckb-fixed-hash", + "ckb-gen-types", + "ckb-hash", + "ckb-merkle-mountain-range 0.5.2", + "ckb-occupied-capacity", + "ckb-rational", + "derive_more", + "golomb-coded-set", + "merkle-cbt", + "molecule", + "numext-fixed-uint", + "once_cell", + "paste", +] + +[[package]] +name = "ckb-vm" +version = "0.24.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8332997ee3beacb0c1b9e2489e17b33af855a0ec28d7c08a81170fae6b204340" +dependencies = [ + "byteorder", + "bytes", + "cc", + "ckb-vm-definitions", + "derive_more", + "goblin 0.2.3", + "goblin 0.4.0", + "rand 0.7.3", + "scroll", + "serde", +] + +[[package]] +name = "ckb-vm-definitions" +version = "0.24.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f6fa54fd079938807cce5b11b4fbb9b21984568b887204ea96a02dbd907c2f" +dependencies = [ + "paste", +] + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap-verbosity-flag" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb9b20c0dd58e4c2e991c8d203bbeb76c11304d1011659686b5b644bc29aa478" +dependencies = [ + "clap", + "log", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.12", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if 1.0.0", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "derive-getters" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0122f262bf9c9a367829da84f808d9fb128c10ef283bbe7b0922a77cf07b2747" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "eaglesong" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d978bd5d343e8ab9b5c0fc8d93ff9c602fdc96616ffff9c05ac7a155419b824" + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "enum-repr-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f2936062c28214e84685742fa4affc52a39d036e8a3dcf98034810e449ec95" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash", + "primitive-types", + "uint", +] + +[[package]] +name = "faster-hex" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e2ce894d53b295cf97b05685aa077950ff3e8541af83217fc720a6437169f8" + +[[package]] +name = "faster-hex" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" +dependencies = [ + "serde", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "goblin" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d20fd25aa456527ce4f544271ae4fea65d2eda4a6561ea56f39fb3ee4f7e3884" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "goblin" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532a09cd3df2c6bbfc795fb0434bff8f22255d1d07328180e918a2e6ce122d4d" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "golomb-coded-set" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7076c0cd6257d84b785b0f22c36443dd47a5e86a1256d7ef82c8cb88ea9a7e" +dependencies = [ + "siphasher", +] + +[[package]] +name = "h2" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util 0.7.10", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heapsize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" +dependencies = [ + "winapi", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "includedir" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afd126bd778c00c43a9dc76d1609a0894bf4222088088b2217ccc0ce9e816db7" +dependencies = [ + "flate2", + "phf", +] + +[[package]] +name = "includedir_codegen" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ac1500c9780957c9808c4ec3b94002f35aab01483833f5a8bce7dfb243e3148" +dependencies = [ + "flate2", + "phf_codegen", + "walkdir", +] + +[[package]] +name = "indexmap" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "jsonrpc-derive" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" +dependencies = [ + "proc-macro-crate 0.1.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpc-http-server" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" +dependencies = [ + "futures", + "hyper", + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "net2", + "parking_lot 0.11.2", + "unicase", +] + +[[package]] +name = "jsonrpc-server-utils" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" +dependencies = [ + "bytes", + "futures", + "globset", + "jsonrpc-core", + "lazy_static", + "log", + "tokio", + "tokio-stream", + "tokio-util 0.6.10", + "unicase", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if 1.0.0", + "windows-targets 0.52.4", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "merkle-cbt" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171d2f700835121c3b04ccf0880882987a050fd5c7ae88148abf537d33dd3a56" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "miette" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" +dependencies = [ + "miette-derive", + "once_cell", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "molecule" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd9767ab5e5f2ea40f71ff4c8bdb633c50509052e093c2fdd0e390a749dfa3" +dependencies = [ + "bytes", + "cfg-if 1.0.0", + "faster-hex 0.6.1", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "numext-constructor" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "621fe0f044729f810c6815cdd77e8f5e0cd803ce4f6a38380ebfc1322af98661" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "numext-fixed-uint" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c68c76f96d589d1009a666c5072f37f3114d682696505f2cf445f27766c7d70" +dependencies = [ + "numext-fixed-uint-core", + "numext-fixed-uint-hack", +] + +[[package]] +name = "numext-fixed-uint-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aab1d6457b97b49482f22a92f0f58a2f39bdd7f3b2f977eae67e8bc206aa980" +dependencies = [ + "heapsize", + "numext-constructor", + "rand 0.7.3", + "serde", + "thiserror", +] + +[[package]] +name = "numext-fixed-uint-hack" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200f8d55c36ec1b6a8cf810115be85d4814f045e0097dfd50033ba25adb4c9e" +dependencies = [ + "numext-fixed-uint-core", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.4.2", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.2.3+3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-multimap" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" +dependencies = [ + "dlv-list", + "hashbrown 0.13.2", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +dependencies = [ + "proc-macro-crate 2.0.2", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared", + "rand 0.7.3", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn 2.0.53", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.12", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "reflink-copy" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b1349400e2ffd64a9fb5ed9008e33c0b8ef86bd5bae8f73080839c7082f1d5" +dependencies = [ + "cfg-if 1.0.0", + "rustix", + "windows", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "getrandom 0.2.12", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rust-ini" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" +dependencies = [ + "cfg-if 1.0.0", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +dependencies = [ + "secp256k1-sys 0.6.1", +] + +[[package]] +name = "secp256k1" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +dependencies = [ + "bitcoin_hashes", + "secp256k1-sys 0.9.2", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +dependencies = [ + "cc", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "sparse-merkle-tree" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8851f6c92491ebe5528eabc1244292175a739eb0162974f9f9670a7dc748748b" +dependencies = [ + "blake2b-rs", + "cc", + "cfg-if 0.1.10", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "ssri" +version = "9.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7a2b3c2bc9693bcb40870c4e9b5bf0d79f9cb46273321bf855ec513e919082" +dependencies = [ + "base64", + "digest", + "hex", + "miette", + "serde", + "sha-1", + "sha2", + "thiserror", + "xxhash-rust", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.53", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-result" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if 1.0.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b7c498f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "ckb-bitcoin-spv-service" +version = "0.1.0" +authors = ["Boyu Yang "] +edition = "2021" +license = "MIT" +description = "A Bitcoin SPV service which works on CKB." +homepage = "https://github.com/yangby-cryptape/ckb-bitcoin-spv-service" +repository = "https://github.com/yangby-cryptape/ckb-bitcoin-spv-service" + +[dependencies] +thiserror = "1.0" +anyhow = "1.0" +log = "0.4" +env_logger = "0.11" +clap = { version = "4.5", features = ["derive"] } +clap-verbosity-flag = "2.2" +faster-hex = "0.9" +zeroize = { version = "1.7", features = ["derive"] } +url = "2.5" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +reqwest = { version = "0.11", default-features = false, features = ["json", "blocking"] } +jsonrpc-core = "18.0" +jsonrpc-derive = "18.0" +jsonrpc-http-server = "18.0" +jsonrpc-server-utils = "18.0" +tokio = { version = "1.36", features = ["rt-multi-thread", "full"] } +rocksdb = { package = "ckb-rocksdb", version ="=0.21.1", features = ["snappy"], default-features = false } +secp256k1 = "0.24" + +bitcoin = { version = "0.31", features = ["serde"] } +ckb-sdk = "3.1" +ckb-types = "0.114" +ckb-jsonrpc-types = "0.114" +ckb-hash = "0.114" + +[dependencies.ckb-bitcoin-spv-verifier] +version = "0.1.0" +git = "https://github.com/ckb-cell/ckb-bitcoin-spv" +rev = "2464c8f" + +[features] +default = ["default-tls"] +default-tls = ["ckb-sdk/default-tls", "reqwest/default-tls"] +native-tls-vendored = ["ckb-sdk/native-tls-vendored", "reqwest/native-tls-vendored"] +rustls-tls = ["ckb-sdk/rustls-tls", "reqwest/rustls-tls"] + +# TODO Remove this after `ckb-sdk>3.1` released. +[patch.crates-io.ckb-sdk] +git = "https://github.com/nervosnetwork/ckb-sdk-rust" +rev = "b792ee7" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..760d0f1 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +CARGO := @cargo + +# +# Check +# + +check: + ${CARGO} check --workspace + +fmt: + ${CARGO} fmt --all --check + +clippy: + ${CARGO} clippy --workspace --tests -- --deny warnings + +# +# Build +# + +doc: + ${CARGO} doc --workspace --no-deps + +build: + ${CARGO} build --workspace + +release: + ${CARGO} build --workspace --release diff --git a/README.md b/README.md index 41fe3d6..51f128a 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,72 @@ they can be verified on [CKB]. [License]: https://img.shields.io/badge/License-MIT-blue.svg +## Usage + +With the command line option `-h`(alias of `--help`), help will be printed. + +### JSON-RPC API Reference + +- Method `getTxProof` + + Arguments: + + - `txid` (a hexadecimal string) + + A bitcoin transaction id. + + **No `0x`-prefix, as same as [the format in Bitcoin RPC APIs](https://developer.bitcoin.org/reference/rpc/gettxoutproof.html#argument-1-txids).** + + + - `tx-index` (an unsigned integer) + + The index of a transaction in the block; starts from 0. + + **Service doesn't check it, just pack it into the final proof.** + + - `confirmations` (an unsigned integer) + + Represents the required acceptance of the transaction by the bitcoin network. + + Result: + + - `spv_client` ([type: `OutPoint`]) + + An out point of a SPV client cell in CKB. + + - `proof` ([type: `JsonBytes`]) + + The full proof of a bitcoin transaction, which could be verified with + above SPV client in CKB. + +
An example: + + + Request: + + ```shell + curl -X POST -H "Content-Type: application/json" \ + -d '{"jsonrpc": "2.0", "method":"getTxProof", "params": ["95231964950ce016b1333ecc1ec98bf4effc3d5d579c5c7232dddd7c2200f124", 9, 10], "id": 1}' \ + http://127.0.0.1:8888 + ``` + + Output: + ```json + { + "jsonrpc": "2.0", + "result": { + "proof": "0x3d03000014000000180000001c000000f90100000900000040bc0c00d9010000000018228d263ff4070e2fdb31b654704e99f850a4f31762155003000000000000000000f79ccd11440a9d383b1438ee60692fc7b78a4b8800faf6ec61ec5fc37a078387f998f265595a03175799484bca0700000c99f30ab6b29578c47b5e290383c73016dc51ceb8d8e4e4772b8914074d261cea95fae35702bd7842c06314b56f2fccf4f1e7a7a1e9316510eac47b8da580bd8c24f100227cdddd32725c9c575d3dfceff48bc91ecc3e33b116e00c95641923959c4eadf6c5194ce92e4af499234915ae16ef6ce925319ecdb782c15b7b79b8e0add7f560c4894d1a6373d3a0f0b2b7809de8c68881212e5804c973d810fac6ab81b4d8171567ed7b4c562cfd92f340f372d4d783ddaec72f4ae0898fa4f1a1609462a8aaa2c0038416074bef53effa98f61229bf5fba767cead3344362b1d9b440b1367c8597e5f42c52fb8b861ff7dd06ce2b6f8a1e7cb41197909a476e625c11f26490c00f5c00aa247fbe7b82cb4d42255596005d6aea0c313c425044da0155c1e3eb8cc65279a99de185af0c57f18f41ead7d93ba6dc6613f2ad338a0bc73d7c8c15eabf47c5de917babf44ce66982367ff38d9a586589fa525b72e6d8c7814386fdb76da0db6f786b020a558e7966dc94e5f75d9de94784297048c9c80103ff2e000800000041bc0c0041bc0c0083ccd2727719f195c487e165c7cebebabe280f08c4060100000000000000000042bc0c0043bc0c001e1ae1b8a976a99d3000d708b5cf41bd34a3610e18d33c7ea12090182c84fa7544bc0c0047bc0c0043bd45f3f7425544324bbdd6303adb92fe446555b5ec5548f4e481bdced123e748bc0c004fbc0c00bd692cd46c3fec36a58f2a963e8b88fb183b46848e2743dd3e599486c466bd7450bc0c005fbc0c000e8775e71c17af3afd9129e0b9cf76d1af0cc7cd49ef5e98d0c25a72b66349ac60bc0c007fbc0c001f8d8f32d7fd85ee07f5e5e9c280bab9ae9fe148bf2c03e45e786fc23024116280bc0c00bfbc0c0016b40a5455bd596f89b58207e272a8f333c03f1ec7e9dd9a1a47c20d4371eb63c0bc0c00c8bc0c006d3b9e55c56dd1fb5777bc115db1faca7a7f6e33cd9823f3a223581c740f833b", + "spv_client": { + "index": "0x1", + "tx_hash": "0xcaee4eac91a43642464ee2c6582a86835f2bc10d55b15fcd22083ae9d273a1dc" + } + }, + "id": 1 + } + ``` + +
+ ## Related Projects - [The Core Library of CKB Bitcoin SPV][Bitcoin SPV on CKB] @@ -25,4 +91,7 @@ Licensed under [MIT License]. [CKB]: https://github.com/nervosnetwork/ckb [Bitcoin SPV on CKB]: https://github.com/ckb-cell/ckb-bitcoin-spv +[type: `OutPoint`]: https://github.com/nervosnetwork/ckb/tree/v0.114.0/rpc#type-outpoint +[type: `JsonBytes`]: https://github.com/nervosnetwork/ckb/tree/v0.114.0/rpc#type-jsonbytes + [MIT License]: LICENSE diff --git a/src/cli/deploy.rs b/src/cli/deploy.rs new file mode 100644 index 0000000..814d8a0 --- /dev/null +++ b/src/cli/deploy.rs @@ -0,0 +1,126 @@ +//! The `deploy` sub-command. + +use ckb_jsonrpc_types::TransactionView; +use ckb_sdk::{ + transaction::{ + builder::{CkbTransactionBuilder, SimpleTransactionBuilder}, + input::InputIterator, + signer::{SignContexts, TransactionSigner}, + TransactionBuilderConfiguration, + }, + types::{ + Address as CkbAddress, AddressPayload as CkbAddressPayload, HumanCapacity, NetworkInfo, + }, + SECP256K1, +}; +use ckb_types::{bytes::Bytes, core::Capacity, packed, prelude::*}; +use clap::Parser; +use secp256k1::SecretKey; + +use crate::{ + prelude::*, + result::{Error, Result}, + utilities::value_parsers, +}; + +#[derive(Parser)] +pub struct Args { + #[clap(flatten)] + pub(crate) common: super::CommonArgs, + + #[clap(flatten)] + pub(crate) ckb: super::CkbArgs, + + /// A binary file, which should contain the Bitcoin SPV contract. + /// + /// The repository of the contract source code is + /// . + /// + /// ### Warnings + /// + /// Under the development phase, the compatibility has chance to be broken + /// without any declaration. + /// + /// Please always use the latest versions of both the service and the contract. + /// + /// TODO Matched versions of the contracts should be list. + #[arg( + long = "contract-file", value_name = "CONTRACT_FILE", required = true, + value_parser = value_parsers::BinaryFileValueParser + )] + pub(crate) contract_data: Bytes, + + /// The contract owner's address. + #[arg(long="contract-owner", value_parser = value_parsers::AddressValueParser)] + pub(crate) contract_owner: CkbAddress, + + /// Perform all steps without sending. + #[arg(long, hide = true)] + pub(crate) dry_run: bool, +} + +impl Args { + // TODO Deploy the Bitcoin SPV contract as type script. + pub fn execute(&self) -> Result<()> { + log::info!("Try to deploy a contract on CKB."); + + if self.contract_owner.network() != self.ckb.network { + let msg = "The input addresses and the selected network are not matched."; + return Err(Error::Cli(msg.to_owned())); + } + + let contract_data_capacity = Capacity::bytes(self.contract_data.len()).map_err(|err| { + let msg = format!("failed to calculate the capacity for contract data since {err}"); + Error::other(msg) + })?; + log::info!( + "The contract requires {} CKBytes for its data.", + HumanCapacity::from(contract_data_capacity.as_u64()) + ); + + let network_info = + NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned()); + let configuration = { + let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?; + tmp.fee_rate = self.ckb.fee_rate; + tmp + }; + + let output = packed::CellOutput::new_builder() + .lock((&self.contract_owner).into()) + .build_exact_capacity(contract_data_capacity) + .map_err(|err| { + let msg = format!("failed to calculate the capacity for the output since {err}"); + Error::other(msg) + })?; + + let (deployer, deployer_key) = SecretKey::from_slice(&self.common.private_key.as_ref()[..]) + .map(|sk| { + let pk = sk.public_key(&SECP256K1); + let payload = CkbAddressPayload::from_pubkey(&pk); + let address = CkbAddress::new(self.ckb.network, payload, true); + (address, sk) + })?; + log::info!("The contract deployer is {deployer}."); + + let iterator = InputIterator::new_with_address(&[deployer], &network_info); + let mut builder = SimpleTransactionBuilder::new(configuration, iterator); + builder.add_output_and_data(output, self.contract_data.pack()); + let data_hash = packed::CellOutput::calc_data_hash(&self.contract_data); + log::info!("The contract data hash is {data_hash:#x}."); + + let mut tx_with_groups = builder.build(&Default::default())?; + + TransactionSigner::new(&network_info).sign_transaction( + &mut tx_with_groups, + &SignContexts::new_sighash(vec![deployer_key]), + )?; + + let tx_json = TransactionView::from(tx_with_groups.get_tx_view().clone()); + self.ckb + .client() + .send_transaction_ext(tx_json, self.dry_run)?; + + Ok(()) + } +} diff --git a/src/cli/init.rs b/src/cli/init.rs new file mode 100644 index 0000000..3be3ea7 --- /dev/null +++ b/src/cli/init.rs @@ -0,0 +1,410 @@ +//! The `init` sub-command. + +use std::{collections::HashMap, path::PathBuf}; + +use bitcoin::blockdata::constants::DIFFCHANGE_INTERVAL; +use ckb_bitcoin_spv_verifier::types::{core::Hash as BitcoinHash, packed, prelude::Pack as VPack}; +use ckb_jsonrpc_types::TransactionView; +use ckb_sdk::{ + core::TransactionBuilder, + transaction::{ + builder::{ChangeBuilder, DefaultChangeBuilder}, + handler::HandlerContexts, + input::InputIterator, + signer::{SignContexts, TransactionSigner}, + TransactionBuilderConfiguration, + }, + types::{ + Address as CkbAddress, AddressPayload as CkbAddressPayload, NetworkInfo, ScriptGroup, + TransactionWithScriptGroups, + }, + SECP256K1, +}; +use ckb_types::{ + core::{Capacity, DepType, ScriptHashType}, + packed::{ + Byte32, Bytes as PackedBytes, BytesOpt, CellDep, CellOutput, OutPoint, Script, WitnessArgs, + }, + prelude::*, + H256, +}; +use clap::Parser; +use secp256k1::SecretKey; + +use crate::{ + components::Storage, + prelude::*, + result::{Error, Result}, + utilities::{calculate_type_id, value_parsers}, +}; + +#[derive(Parser)] +pub struct Args { + #[clap(flatten)] + pub(crate) common: super::CommonArgs, + + /// The directory, which stores all cached data. + #[arg(long)] + pub(crate) data_dir: PathBuf, + + #[clap(flatten)] + pub(crate) ckb: super::CkbArgs, + + #[clap(flatten)] + pub(crate) bitcoin: super::BitcoinArgs, + + /// The start height of the new Bitcoin SPV instance. + /// + /// This height should be multiples of number 2016. + #[arg(long, required = true)] + pub(crate) bitcoin_start_height: u32, + + /// How many SPV clients will be created for the new Bitcoin SPV instance. + #[arg(long, required = true)] + pub(crate) spv_clients_count: u8, + + /// The data hash of the Bitcoin SPV contract. + #[arg(long, value_parser = value_parsers::H256ValueParser)] + pub(crate) spv_contract_data_hash: H256, + + /// The out point of the Bitcoin SPV contract. + #[arg(long, value_parser = value_parsers::OutPointValueParser)] + pub(crate) spv_contract_out_point: OutPoint, + + /// The out point of the lock contract. + /// + /// The lock contract has to satisfy that: + /// - If total capacity of cells which use this lock script were not + /// decreased, any non-owner users can update them. + #[arg(long, value_parser = value_parsers::OutPointValueParser)] + pub(crate) lock_contract_out_point: OutPoint, + + /// The owner of Bitcoin SPV cells. + #[arg(long, value_parser = value_parsers::AddressValueParser)] + pub(crate) spv_owner: CkbAddress, + + /// Perform all steps without sending. + #[arg(long, hide = true)] + pub(crate) dry_run: bool, +} + +impl Args { + // TODO Split this method into several smaller methods. + pub fn execute(&self) -> Result<()> { + log::info!("Try to initialize a Bitcoin SPV instance on CKB."); + + self.check_inputs()?; + log::info!("The bitcoin start height is {}", self.bitcoin_start_height); + self.check_remotes()?; + + let btc_start_header = self + .bitcoin + .client() + .check_then_fetch_header(self.bitcoin_start_height)?; + + let storage = Storage::new(&self.data_dir)?; + let spv_client = storage.initialize_with(self.bitcoin_start_height, btc_start_header)?; + + let network_info = + NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned()); + let configuration = { + let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?; + tmp.fee_rate = self.ckb.fee_rate; + tmp + }; + + let (deployer, deployer_key) = SecretKey::from_slice(&self.common.private_key.as_ref()[..]) + .map(|sk| { + let pk = sk.public_key(&SECP256K1); + let payload = CkbAddressPayload::from_pubkey(&pk); + let address = CkbAddress::new(self.ckb.network, payload, true); + (address, sk) + })?; + log::info!("The contract deployer is {deployer}."); + + let spv_outputs_data = { + let spv_info = packed::SpvInfo::new_builder().build(); + let mut outputs_data = vec![spv_info.as_bytes()]; + let mut spv_client = spv_client.clone(); + for id in 0..self.spv_clients_count { + spv_client.id = id; + let packed_client: packed::SpvClient = spv_client.pack(); + outputs_data.push(packed_client.as_bytes()); + } + outputs_data + }; + + let mut iterator = InputIterator::new_with_address(&[deployer.clone()], &network_info); + let mut tx_builder = TransactionBuilder::default(); + + let spv_contract_cell_dep = CellDep::new_builder() + .out_point(self.spv_contract_out_point.clone()) + .dep_type(DepType::Code.into()) + .build(); + let lock_contract_cell_dep = CellDep::new_builder() + .out_point(self.lock_contract_out_point.clone()) + .dep_type(DepType::Code.into()) + .build(); + tx_builder.cell_dep(spv_contract_cell_dep.clone()); + + log::debug!("Try to find the first live cell for {deployer}."); + let input0 = iterator + .next() + .transpose() + .map_err(|err| { + let msg = format!("failed to find any live cell for {deployer} since {err}"); + Error::other(msg) + })? + .ok_or_else(|| { + let msg = format!("{deployer} has no live cell."); + Error::other(msg) + })?; + + let spv_type_script = { + let cells_count = usize::from(self.spv_clients_count) + 1; + let type_id_array = calculate_type_id(input0.cell_input(), cells_count); + let type_id = BitcoinHash::from_bytes_ref(&type_id_array); + let args = packed::SpvTypeArgs::new_builder() + .type_id(type_id.pack()) + .clients_count(self.spv_clients_count.into()) + .build(); + Script::new_builder() + .code_hash(self.spv_contract_data_hash.pack()) + .hash_type(ScriptHashType::Data1.into()) + .args(Pack::pack(&args.as_bytes())) + .build() + }; + + storage.save_cells_state( + spv_type_script.clone(), + spv_contract_cell_dep, + lock_contract_cell_dep, + )?; + + let spv_outputs = { + let spv_info_capacity = Capacity::bytes(spv_outputs_data[0].len()).map_err(|err| { + let msg = format!( + "failed to calculate the capacity for Bitcoin SPV info cell since {err}" + ); + Error::other(msg) + })?; + let spv_client_capacity = + Capacity::bytes(spv_outputs_data[1].len()).map_err(|err| { + let msg = format!( + "failed to calculate the capacity for Bitcoin SPV client cell since {err}" + ); + Error::other(msg) + })?; + let spv_cell = CellOutput::new_builder() + .lock((&self.spv_owner).into()) + .type_(Some(spv_type_script).pack()) + .build(); + let spv_info = spv_cell + .clone() + .as_builder() + .build_exact_capacity(spv_info_capacity) + .map_err(|err| { + let msg = format!( + "failed to sum the total capacity for Bitcoin SPV info cell since {err}" + ); + Error::other(msg) + })?; + let spv_client = spv_cell + .as_builder() + .build_exact_capacity(spv_client_capacity) + .map_err(|err| { + let msg = format!( + "failed to sum the total capacity for Bitcoin SPV client cell since {err}" + ); + Error::other(msg) + })?; + let mut outputs = vec![spv_client.clone(); usize::from(self.spv_clients_count) + 1]; + outputs[0] = spv_info; + outputs + }; + + tx_builder.outputs(spv_outputs); + tx_builder.outputs_data(spv_outputs_data.iter().map(Pack::pack)); + + #[allow(clippy::mutable_key_type)] + let mut lock_groups: HashMap = HashMap::default(); + #[allow(clippy::mutable_key_type)] + let mut type_groups: HashMap = HashMap::default(); + + for (output_idx, output) in tx_builder.get_outputs().clone().iter().enumerate() { + if let Some(type_script) = &output.type_().to_opt() { + type_groups + .entry(type_script.calc_script_hash()) + .or_insert_with(|| ScriptGroup::from_type_script(type_script)) + .output_indices + .push(output_idx); + } + } + + let mut change_builder = + DefaultChangeBuilder::new(&configuration, input0.live_cell.output.lock(), Vec::new()); + change_builder.init(&mut tx_builder); + + tx_builder.input(input0.cell_input()); + let previous_output0 = input0.previous_output(); + let lock_script0 = previous_output0.lock(); + lock_groups + .entry(lock_script0.calc_script_hash()) + .or_insert_with(|| ScriptGroup::from_lock_script(&lock_script0)) + .input_indices + .push(0); + + let witness = { + let bootstrap = packed::SpvBootstrap::new_builder() + .height(VPack::pack(&self.bitcoin_start_height)) + .header(btc_start_header.pack()) + .build(); + let type_args = BytesOpt::new_builder() + .set(Some(Pack::pack(bootstrap.as_slice()))) + .build(); + let witness_args = WitnessArgs::new_builder().output_type(type_args).build(); + Pack::pack(&witness_args.as_bytes()) + }; + tx_builder.witness(witness); + + let contexts = HandlerContexts::default(); + + let mut tx_with_groups = if change_builder.check_balance(input0, &mut tx_builder) { + let mut script_groups: Vec = lock_groups + .into_values() + .chain(type_groups.into_values()) + .collect(); + for script_group in script_groups.iter_mut() { + for handler in configuration.get_script_handlers() { + for context in &contexts.contexts { + if handler.build_transaction( + &mut tx_builder, + script_group, + context.as_ref(), + )? { + break; + } + } + } + } + let tx_view = change_builder.finalize(tx_builder); + + Some(TransactionWithScriptGroups::new(tx_view, script_groups)) + } else { + let mut check_result = None; + for (mut input_index, input) in iterator.enumerate() { + input_index += 1; // The first input has been handled. + log::debug!("Try to find the {input_index}-th live cell for {deployer}."); + let input = input.map_err(|err| { + let msg = format!( + "failed to find {input_index}-th live cell for {deployer} since {err}" + ); + Error::other(msg) + })?; + tx_builder.input(input.cell_input()); + tx_builder.witness(PackedBytes::default()); + + let previous_output = input.previous_output(); + let lock_script = previous_output.lock(); + lock_groups + .entry(lock_script.calc_script_hash()) + .or_insert_with(|| ScriptGroup::from_lock_script(&lock_script)) + .input_indices + .push(input_index); + + if change_builder.check_balance(input, &mut tx_builder) { + let mut script_groups: Vec = lock_groups + .into_values() + .chain(type_groups.into_values()) + .collect(); + for script_group in script_groups.iter_mut() { + for handler in configuration.get_script_handlers() { + for context in &contexts.contexts { + if handler.build_transaction( + &mut tx_builder, + script_group, + context.as_ref(), + )? { + break; + } + } + } + } + let tx_view = change_builder.finalize(tx_builder); + + check_result = Some(TransactionWithScriptGroups::new(tx_view, script_groups)); + break; + } + } + check_result + } + .ok_or_else(|| { + let msg = format!("{deployer}'s live cells are not enough."); + Error::other(msg) + })?; + + TransactionSigner::new(&network_info).sign_transaction( + &mut tx_with_groups, + &SignContexts::new_sighash(vec![deployer_key]), + )?; + + let tx_json = TransactionView::from(tx_with_groups.get_tx_view().clone()); + self.ckb + .client() + .send_transaction_ext(tx_json, self.dry_run)?; + + Ok(()) + } + + fn check_inputs(&self) -> Result<()> { + if self.spv_owner.network() != self.ckb.network { + let msg = "The input addresses and the selected network are not matched."; + return Err(Error::cli(msg)); + } + + if self.spv_clients_count < 3 { + let msg = format!( + "The Bitcoint SPV clients count should be 3 at least but got {}", + self.spv_clients_count + ); + return Err(Error::cli(msg)); + } + + if self.bitcoin_start_height % DIFFCHANGE_INTERVAL != 0 { + let msg = format!( + "invalid Bitcoint start height, expected multiples of \ + {DIFFCHANGE_INTERVAL} but got {}", + self.bitcoin_start_height + ); + return Err(Error::cli(msg)); + } + + Ok(()) + } + + fn check_remotes(&self) -> Result<()> { + if self.spv_owner.network() != self.ckb.network { + let msg = "The input addresses and the selected network are not matched."; + return Err(Error::cli(msg)); + } + + if self.spv_clients_count < 3 { + let msg = format!( + "The Bitcoint SPV clients count should be 3 at least but got {}", + self.spv_clients_count + ); + return Err(Error::cli(msg)); + } + + if self.bitcoin_start_height % DIFFCHANGE_INTERVAL != 0 { + let msg = format!( + "invalid Bitcoint start height, expected multiples of \ + {DIFFCHANGE_INTERVAL} but got {}", + self.bitcoin_start_height + ); + return Err(Error::cli(msg)); + } + + Ok(()) + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..e8a09fa --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,142 @@ +//! The command line argument. + +use ckb_sdk::{rpc::CkbRpcClient, types::NetworkType}; +use clap::{Parser, Subcommand}; +use clap_verbosity_flag::{InfoLevel, Verbosity}; +use url::Url; + +use crate::{ + components::BitcoinClient, + result::Result, + utilities::{value_parsers, Key256Bits}, +}; + +mod deploy; +mod init; +mod serve; + +#[derive(Parser)] +#[command(author, version, about)] +pub struct Cli { + #[command(subcommand)] + pub command: Commands, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Subcommand)] +pub enum Commands { + /// Deploy a contract on CKB. + /// + /// This command is used to deploy the Bitcoin SPV contract. + /// Also, users can deploy the contract in their own way. + Deploy(deploy::Args), + /// Initialize a Bitcoin SPV instance on CKB. + Init(init::Args), + /// Run a service to sync bitcoin headers to CKB. + Serve(serve::Args), +} + +#[derive(Parser)] +pub struct CommonArgs { + #[command(flatten)] + pub(crate) verbose: Verbosity, + + /// A binary file, which contains a secp256k1 private key. + /// This private key will be used to provide all CKBytes. + /// + /// Tip: After starting the service, this file should be deleted, for safety. + #[arg(long = "key-file", value_name = "KEY_FILE")] + pub(crate) private_key: Key256Bits, +} + +#[derive(Parser)] +pub struct CkbArgs { + /// CKB JSON-RPC APIs endpoint. + #[arg(long)] + pub(crate) ckb_endpoint: Url, + + /// The network type of the CKB chain which connected. + #[arg( + long = "network-type", + value_parser = value_parsers::NetworkTypeValueParser, + default_value = "testnet" + )] + pub network: NetworkType, + + /// The fee rate for CKB transactions. + #[arg(long = "ckb-fee-rate", default_value = "1000")] + pub(crate) fee_rate: u64, +} + +#[derive(Parser)] +pub struct BitcoinArgs { + /// Bitcoin JSON-RPC APIs endpoint. + /// + /// Required Methods: `getbestblockhash`, `getblockhash`, `getblockstats`, `getblockheader` and `gettxoutproof`. + /// + /// Ref: + #[arg(long = "bitcoin-endpoint", value_name = "BITCOIN_ENDPOINT")] + pub(crate) endpoint: Url, + /// Username for the Bitcoin JSON-RPC APIs endpoint. + #[arg( + long = "bitcoin-endpoint-username", + value_name = "BITCOIN_ENDPOINT_USERNAME" + )] + pub(crate) username: Option, + /// Password for the Bitcoin JSON-RPC APIs endpoint. + #[arg( + long = "bitcoin-endpoint-password", + value_name = "BITCOIN_ENDPOINT_PASSWORD" + )] + pub(crate) password: Option, +} + +impl Cli { + pub fn parse() -> Self { + ::parse() + } + + pub fn execute(self) -> Result<()> { + self.configure_logger(); + log::info!("Bitcoin SPV on CKB service is starting ..."); + match self.command { + Commands::Deploy(args) => args.execute()?, + Commands::Init(args) => args.execute()?, + Commands::Serve(args) => args.execute()?, + } + log::info!("Bitcoin SPV on CKB service is stopped."); + Ok(()) + } + + pub fn configure_logger(&self) { + match self.command { + Commands::Deploy(ref args) => args.common.configure_logger(), + Commands::Init(ref args) => args.common.configure_logger(), + Commands::Serve(ref args) => args.common.configure_logger(), + } + } +} + +impl CommonArgs { + pub fn configure_logger(&self) { + env_logger::Builder::new() + .filter_level(self.verbose.log_level_filter()) + .init(); + } +} + +impl CkbArgs { + pub fn client(&self) -> CkbRpcClient { + CkbRpcClient::new(self.ckb_endpoint.as_str()) + } +} + +impl BitcoinArgs { + pub fn client(&self) -> BitcoinClient { + BitcoinClient::new( + self.endpoint.clone(), + self.username.clone(), + self.password.clone(), + ) + } +} diff --git a/src/cli/serve.rs b/src/cli/serve.rs new file mode 100644 index 0000000..b21f9c5 --- /dev/null +++ b/src/cli/serve.rs @@ -0,0 +1,348 @@ +//! The `serve` sub-command. + +use std::{cmp::Ordering, collections::HashMap, net::SocketAddr, path::PathBuf, thread, time}; + +use ckb_bitcoin_spv_verifier::types::{core::SpvClient, packed, prelude::Pack as VPack}; +use ckb_jsonrpc_types::{Status, TransactionView}; +use ckb_sdk::{ + core::TransactionBuilder, + transaction::{ + builder::{ChangeBuilder, DefaultChangeBuilder}, + handler::HandlerContexts, + input::{InputIterator, TransactionInput}, + signer::{SignContexts, TransactionSigner}, + TransactionBuilderConfiguration, + }, + types::{ + Address as CkbAddress, AddressPayload as CkbAddressPayload, NetworkInfo, ScriptGroup, + TransactionWithScriptGroups, + }, + SECP256K1, +}; +use ckb_types::{ + core::DepType, + packed::{Byte32, Bytes as PackedBytes, BytesOpt, CellDep, CellInput, CellOutput, WitnessArgs}, + prelude::*, + H256, +}; +use clap::Parser; +use secp256k1::SecretKey; + +use crate::{ + components::{ApiServiceConfig, SpvClientCell, SpvInfoCell, SpvService, Storage}, + prelude::*, + result::{Error, Result}, +}; + +#[derive(Parser)] +pub struct Args { + #[clap(flatten)] + pub(crate) common: super::CommonArgs, + + /// The directory, which stores all cached data. + #[arg(long)] + pub(crate) data_dir: PathBuf, + + #[clap(flatten)] + pub(crate) ckb: super::CkbArgs, + + #[clap(flatten)] + pub(crate) bitcoin: super::BitcoinArgs, + + /// The JSON-RPC server's listen address. + #[arg(long)] + pub(crate) listen_address: SocketAddr, + + /// A interval in seconds. + /// + /// - When no better bitcoin blocks, waiting for several seconds. + /// - After a CKB transaction is sent, waiting for several seconds. + #[arg(long, default_value = "30")] + pub(crate) interval: u64, + + /// Take a break after download some bitcoin headers, + /// to avoid some API limits. + #[arg(long, default_value = "10")] + pub(crate) bitcoin_headers_download_limit: u32, + + /// Don't update all headers in one CKB transaction, + /// to avoid size limit or cycles limit. + #[arg(long, default_value = "10")] + pub(crate) spv_headers_update_limit: u32, + + /// Perform all steps without sending. + #[arg(long, hide = true)] + pub(crate) dry_run: bool, +} + +impl Args { + pub fn execute(&self) -> Result<()> { + log::info!("Starting the Bitcoin SPV service."); + + let storage = Storage::new(&self.data_dir)?; + if !storage.is_initialized()? { + let msg = format!( + "user-provided data directory \"{}\" is empty, please initialize it", + self.data_dir.display() + ); + return Err(Error::other(msg)); + } + let ckb_cli = self.ckb.client(); + let btc_cli = self.bitcoin.client(); + + let spv_service = SpvService { + ckb_cli: ckb_cli.clone(), + btc_cli: btc_cli.clone(), + storage: storage.clone(), + }; + + let _api_service = ApiServiceConfig::new(self.listen_address).start(spv_service.clone()); + + let (mut stg_tip_height, _) = storage.tip_state()?; + log::info!("Tip height in local storage is {stg_tip_height}"); + + let mut prev_tx_hash: Option = None; + + loop { + let btc_tip_height = btc_cli.get_tip_height()?; + log::info!("Tip height from Bitcoin endpoint is {btc_tip_height}"); + + let required_download = stg_tip_height < btc_tip_height; + if required_download { + let end_height_limit = stg_tip_height + self.bitcoin_headers_download_limit; + let end_height = if end_height_limit < btc_tip_height { + end_height_limit + } else { + btc_tip_height + }; + let headers = btc_cli.get_headers(stg_tip_height + 1, end_height)?; + (stg_tip_height, _) = storage.append_headers(headers)?; + } + + if let Some(ref tx_hash) = prev_tx_hash { + let tx_status = ckb_cli + .get_transaction_status(tx_hash.to_owned())? + .tx_status + .status; + + match tx_status { + Status::Pending | Status::Proposed => { + // To avoid PoolRejectedDuplicatedTransaction + log::debug!("Waiting for the previous transaction {tx_hash:#x}"); + self.take_a_break(); + continue; + } + Status::Committed | Status::Unknown | Status::Rejected => {} + } + } + + let (spv_info, spv_client_curr, spv_client_next) = + spv_service.find_spv_cells_for_update()?; + log::info!("Tip SPV client is {}", spv_client_curr.client.id); + let spv_tip_height = spv_client_curr.client.headers_mmr_root.max_height; + log::info!("Tip height in Bitcoin SPV instance is {spv_tip_height}"); + + match stg_tip_height.cmp(&spv_tip_height) { + Ordering::Less | Ordering::Equal => { + self.take_a_break(); + continue; + } + Ordering::Greater => {} + } + + let (spv_client, spv_update) = storage.generate_spv_client_and_spv_update( + spv_tip_height, + self.spv_headers_update_limit, + )?; + + let tx_hash = self.update_spv_cells( + &spv_service, + (spv_info, spv_client_curr, spv_client_next), + spv_client, + spv_update, + )?; + + prev_tx_hash = Some(tx_hash); + } + + // TODO Handle Ctrl-C and clean resources before exit. + } + + pub(crate) fn update_spv_cells( + &self, + spv: &SpvService, + cells: (SpvInfoCell, SpvClientCell, SpvClientCell), + mut spv_client: SpvClient, + spv_update: packed::SpvUpdate, + ) -> Result { + let (spv_info_cell, spv_client_curr_cell, spv_client_next_cell) = cells; + + let network_info = + NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned()); + let configuration = { + let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?; + tmp.fee_rate = self.ckb.fee_rate; + tmp + }; + + let (deployer, deployer_key) = SecretKey::from_slice(&self.common.private_key.as_ref()[..]) + .map(|sk| { + let pk = sk.public_key(&SECP256K1); + let payload = CkbAddressPayload::from_pubkey(&pk); + let address = CkbAddress::new(self.ckb.network, payload, true); + (address, sk) + })?; + log::debug!("The SPV cells will be updated by {deployer}."); + + let iterator = InputIterator::new_with_address(&[deployer.clone()], &network_info); + let mut tx_builder = TransactionBuilder::default(); + + let spv_inputs = { + let spv_info_input = CellInput::new_builder() + .previous_output(spv_info_cell.cell.out_point.clone()) + .build(); + let spv_client_input = CellInput::new_builder() + .previous_output(spv_client_next_cell.cell.out_point.clone()) + .build(); + vec![spv_info_input, spv_client_input] + }; + tx_builder.inputs(spv_inputs); + + let spv_contract_cell_dep = spv.storage.spv_contract_cell_dep()?; + let lock_contract_cell_dep = spv.storage.lock_contract_cell_dep()?; + tx_builder.cell_dep(spv_contract_cell_dep); + tx_builder.cell_dep(lock_contract_cell_dep); + let spv_client_curr_cell_dep = CellDep::new_builder() + .out_point(spv_client_curr_cell.cell.out_point) + .dep_type(DepType::Code.into()) + .build(); + tx_builder.cell_dep(spv_client_curr_cell_dep); + + let spv_outputs: Vec = vec![ + spv_info_cell.cell.output.clone(), + spv_client_next_cell.cell.output.clone(), + ]; + let spv_outputs_data = { + spv_client.id = spv_client_next_cell.client.id; + let mut spv_info = spv_info_cell.info; + spv_info.tip_client_id = spv_client.id; + let packed_spv_info: packed::SpvInfo = spv_info.pack(); + let packed_spv_client: packed::SpvClient = spv_client.pack(); + vec![packed_spv_info.as_bytes(), packed_spv_client.as_bytes()] + }; + tx_builder.outputs(spv_outputs); + tx_builder.outputs_data(spv_outputs_data.iter().map(Pack::pack)); + + #[allow(clippy::mutable_key_type)] + let mut lock_groups: HashMap = HashMap::default(); + #[allow(clippy::mutable_key_type)] + let mut type_groups: HashMap = HashMap::default(); + + for (output_idx, output) in tx_builder.get_outputs().clone().iter().enumerate() { + if let Some(type_script) = &output.type_().to_opt() { + type_groups + .entry(type_script.calc_script_hash()) + .or_insert_with(|| ScriptGroup::from_type_script(type_script)) + .output_indices + .push(output_idx); + } + } + + let witness = { + let type_args = BytesOpt::new_builder() + .set(Some(Pack::pack(spv_update.as_slice()))) + .build(); + let witness_args = WitnessArgs::new_builder().output_type(type_args).build(); + Pack::pack(&witness_args.as_bytes()) + }; + tx_builder.witness(witness); + tx_builder.witness(PackedBytes::default()); + + let mut change_builder = + DefaultChangeBuilder::new(&configuration, (&deployer).into(), Vec::new()); + change_builder.init(&mut tx_builder); + { + let spv_info_input = TransactionInput { + live_cell: spv_info_cell.cell.clone(), + since: 0, + }; + let spv_client_input = TransactionInput { + live_cell: spv_client_next_cell.cell.clone(), + since: 0, + }; + let _ = change_builder.check_balance(spv_info_input, &mut tx_builder); + let _ = change_builder.check_balance(spv_client_input, &mut tx_builder); + }; + let contexts = HandlerContexts::default(); + + let mut tx_with_groups = { + let mut check_result = None; + for (mut input_index, input) in iterator.enumerate() { + input_index += 2; // The first 2 inputs are SPV cells. + log::debug!("Try to find the {input_index}-th live cell for {deployer}."); + let input = input.map_err(|err| { + let msg = format!( + "failed to find {input_index}-th live cell for {deployer} since {err}" + ); + Error::other(msg) + })?; + tx_builder.input(input.cell_input()); + tx_builder.witness(PackedBytes::default()); + + let previous_output = input.previous_output(); + let lock_script = previous_output.lock(); + lock_groups + .entry(lock_script.calc_script_hash()) + .or_insert_with(|| ScriptGroup::from_lock_script(&lock_script)) + .input_indices + .push(input_index); + + if change_builder.check_balance(input, &mut tx_builder) { + let mut script_groups: Vec = lock_groups + .into_values() + .chain(type_groups.into_values()) + .collect(); + for script_group in script_groups.iter_mut() { + for handler in configuration.get_script_handlers() { + for context in &contexts.contexts { + if handler.build_transaction( + &mut tx_builder, + script_group, + context.as_ref(), + )? { + break; + } + } + } + } + let tx_view = change_builder.finalize(tx_builder); + + check_result = Some(TransactionWithScriptGroups::new(tx_view, script_groups)); + break; + } + } + check_result + } + .ok_or_else(|| { + let msg = format!("{deployer}'s live cells are not enough."); + Error::other(msg) + })?; + + TransactionSigner::new(&network_info).sign_transaction( + &mut tx_with_groups, + &SignContexts::new_sighash(vec![deployer_key]), + )?; + + let tx_json = TransactionView::from(tx_with_groups.get_tx_view().clone()); + let tx_hash = self + .ckb + .client() + .send_transaction_ext(tx_json, self.dry_run)?; + + Ok(tx_hash) + } + + fn take_a_break(&self) { + thread::sleep(time::Duration::from_secs(self.interval)); + } +} diff --git a/src/components/api_service.rs b/src/components/api_service.rs new file mode 100644 index 0000000..eed889e --- /dev/null +++ b/src/components/api_service.rs @@ -0,0 +1,228 @@ +//! JSON-RPC APIs service. + +use std::net::SocketAddr; + +use bitcoin::Txid; +use ckb_bitcoin_spv_verifier::types::{ + core::{Bytes, Hash}, + packed, + prelude::*, +}; +use ckb_jsonrpc_types::{JsonBytes, OutPoint}; +use jsonrpc_core::{Error as RpcError, ErrorCode as RpcErrorCode, IoHandler, Result as RpcResult}; +use jsonrpc_derive::rpc; +use jsonrpc_http_server::{Server, ServerBuilder}; +use jsonrpc_server_utils::{cors::AccessControlAllowOrigin, hosts::DomainsValidation}; +use serde::Serialize; + +use crate::{ + components::{SpvClientCell, SpvService}, + prelude::*, + result::{Error, Result}, +}; + +pub struct ApiServiceConfig { + listen_address: SocketAddr, +} + +#[derive(Serialize, Clone)] +pub struct BitcoinTxProof { + pub(crate) spv_client: OutPoint, + pub(crate) proof: JsonBytes, +} + +#[rpc(server)] +pub trait SpvRpc { + #[rpc(name = "getTxProof")] + fn get_tx_proof( + &self, + tx_hash: Txid, + tx_index: u32, + confirmations: u32, + ) -> RpcResult; +} + +pub struct SpvRpcImpl { + spv_service: SpvService, +} + +impl ApiServiceConfig { + pub fn new(listen_address: SocketAddr) -> Self { + Self { listen_address } + } + + pub fn start(&self, spv_service: SpvService) -> Result { + log::info!("Starting the JSON-RPC service ..."); + let mut io_handler = IoHandler::new(); + let spv_rpc_impl = SpvRpcImpl::new(spv_service); + io_handler.extend_with(spv_rpc_impl.to_delegate()); + + ServerBuilder::new(io_handler) + .cors(DomainsValidation::AllowOnly(vec![ + AccessControlAllowOrigin::Null, + AccessControlAllowOrigin::Any, + ])) + .health_api(("/ping", "ping")) + .start_http(&self.listen_address) + .map_err(Error::other) + } +} + +impl SpvRpcImpl { + pub fn new(spv_service: SpvService) -> Self { + Self { spv_service } + } +} + +impl SpvRpc for SpvRpcImpl { + fn get_tx_proof( + &self, + txid: Txid, + tx_index: u32, + confirmations: u32, + ) -> RpcResult { + log::trace!("Call getTxProof with params [{txid:#x}, {confirmations}]"); + let spv = &self.spv_service; + + let (target_height, target_hash, raw_tx_out_proof) = + tokio::task::block_in_place(|| -> RpcResult<(u32, Hash, Vec)> { + let (merkle_block, raw_tx_out_proof) = + spv.btc_cli.get_tx_out_proof(txid).map_err(|err| { + let message = format!( + "failed to get tx out proof for {txid:#x} from remote since {err}" + ); + RpcError { + code: RpcErrorCode::InternalError, + message, + data: None, + } + })?; + let block_hash = merkle_block.header.block_hash(); + log::trace!(">>> the input tx in header {block_hash:#x}"); + let block_height = spv.btc_cli.get_block_height(block_hash).map_err(|err| { + let message = format!("failed to get block height from remote since {err}"); + RpcError { + code: RpcErrorCode::InternalError, + message, + data: None, + } + })?; + log::trace!(">>> the input tx in header {block_height}"); + Ok((block_height, block_hash.into(), raw_tx_out_proof)) + })?; + + let (stg_tip_height, _) = spv.storage.tip_state().map_err(|err| { + let message = + format!("failed to read tip bitcoin height from local storage since {err}"); + RpcError { + code: RpcErrorCode::InternalError, + message, + data: None, + } + })?; + log::trace!(">>> tip height in local storage is {stg_tip_height}"); + + // TODO Define server errors with enum. + if stg_tip_height < target_height { + let message = format!( + "target transaction is in header#{target_height}, \ + but the tip header in server is header#{stg_tip_height}" + ); + return Err(RpcError { + code: RpcErrorCode::ServerError(-1), + message, + data: None, + }); + } + if stg_tip_height < target_height + confirmations { + let message = format!( + "target transaction is in header#{target_height} \ + and it requires {confirmations} confirmations, \ + but the tip header in server is header#{stg_tip_height}" + ); + return Err(RpcError { + code: RpcErrorCode::ServerError(-2), + message, + data: None, + }); + } + let stg_target_hash = spv + .storage + .bitcoin_header_hash(target_height) + .map_err(|err| { + let message = format!("server doesn't have header#{target_height} since {err}"); + RpcError { + code: RpcErrorCode::ServerError(-3), + message, + data: None, + } + })?; + if target_hash != stg_target_hash { + let message = format!( + "target transaction is in header#{target_height}, \ + the header hash from remote is {target_hash:#x}, \ + its hash in server is {stg_target_hash:#x}" + ); + return Err(RpcError { + code: RpcErrorCode::ServerError(-4), + message, + data: None, + }); + } + + let spv_client_cell = tokio::task::block_in_place(|| -> RpcResult { + spv.find_best_spv_client(stg_tip_height).map_err(|err| { + let message = format!("failed to get SPV cell from remote since {err}"); + RpcError { + code: RpcErrorCode::InternalError, + message, + data: None, + } + }) + })?; + log::trace!(">>> the best SPV client is {}", spv_client_cell.client); + + if spv_client_cell.client.headers_mmr_root.max_height < target_height + confirmations { + let message = format!( + "target transaction is in header#{target_height} \ + and it requires {confirmations} confirmations, \ + but the best SPV header is header#{}", + spv_client_cell.client.headers_mmr_root.max_height + ); + return Err(RpcError { + code: RpcErrorCode::ServerError(-5), + message, + data: None, + }); + } + + let header_proof = spv + .storage + .generate_headers_proof( + spv_client_cell.client.headers_mmr_root.max_height, + vec![target_height], + ) + .map_err(|err| { + let message = format!("failed to generate headers MMR proof since {err}"); + RpcError { + code: RpcErrorCode::InternalError, + message, + data: None, + } + })?; + + let tx_proof: Bytes = packed::TransactionProof::new_builder() + .tx_index(tx_index.pack()) + .height(target_height.pack()) + .transaction_proof(Bytes::from(raw_tx_out_proof).pack()) + .header_proof(header_proof.pack()) + .build() + .as_bytes(); + + let btc_tx_proof = BitcoinTxProof { + spv_client: spv_client_cell.cell.out_point.into(), + proof: JsonBytes::from_bytes(tx_proof), + }; + Ok(btc_tx_proof) + } +} diff --git a/src/components/bitcoin_client.rs b/src/components/bitcoin_client.rs new file mode 100644 index 0000000..6758bf2 --- /dev/null +++ b/src/components/bitcoin_client.rs @@ -0,0 +1,255 @@ +//! A bitcoin client to communicate with a Bitcoin chain. + +use std::sync::atomic::{AtomicU64, Ordering}; + +use bitcoin::{consensus::deserialize, BlockHash, MerkleBlock, Txid}; +use ckb_bitcoin_spv_verifier::types::core::Header; +use faster_hex::hex_decode; +use jsonrpc_core::{Error as RpcError, ErrorCode as RpcErrorCode, Id as RpcId, Value as RpcValue}; +use reqwest::blocking::Client; +use serde::{Deserialize, Serialize}; +use url::Url; + +use crate::result::{BtcRpcError, BtcRpcResult, Error, Result}; + +pub struct BitcoinClient { + client: Client, + endpoint: Url, + username: Option, + password: Option, + id: AtomicU64, +} + +impl Clone for BitcoinClient { + fn clone(&self) -> Self { + Self::new( + self.endpoint.clone(), + self.username.clone(), + self.password.clone(), + ) + } +} + +// ### Warning +// +// If parameters contain only one parameter: +// - `serde_json::to_value(($($arg,)+))` +// - `serde_json::to_value(($($arg),+))` +// are different. +macro_rules! serialize_parameters { + () => ( serde_json::Value::Null ); + ($($arg:ident),+) => ( serde_json::to_value(($($arg,)+))?) +} + +/// JSON-RPC 1.0 compatible response. +/// +/// ### Warning +/// +/// Do NOT use `jsonrpc_core::types::Output` directly. +/// +/// Differences with jsonrpc_core::types::Output`: +/// - JSON-RPC version. +/// - Fields `result` and `error` could be returned in one response. +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Output { + /// Protocol version: 1.0 or 2.0? Don't care about it. Just ignore it. + #[serde(skip_serializing_if = "Option::is_none")] + pub jsonrpc: Option, + /// Result + pub result: Option, + /// Error + pub error: Option, + /// Correlation id + pub id: RpcId, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct BlockHeight { + height: u32, +} + +/// Implement simple JSON-RPC methods. +impl BitcoinClient { + pub fn new(endpoint: Url, username: Option, password: Option) -> Self { + let client = Client::new(); + Self { + client, + endpoint, + username, + password, + id: 0.into(), + } + } + + pub fn post(&self, method: &str, params: PARAM) -> BtcRpcResult + where + PARAM: serde::ser::Serialize, + RET: serde::de::DeserializeOwned, + { + let params = serde_json::to_value(params)?; + let id = self.id.fetch_add(1, Ordering::Relaxed); + log::trace!("params \"{params}\", id: {id}"); + + let mut req_json = serde_json::Map::new(); + req_json.insert("id".to_owned(), serde_json::json!(id)); + req_json.insert("jsonrpc".to_owned(), serde_json::json!("1.0")); + req_json.insert("method".to_owned(), serde_json::json!(method)); + req_json.insert("params".to_owned(), params); + log::trace!("request data \"{:?}\"", serde_json::to_string(&req_json)); + + log::trace!( + "username \"{:?}\", have-password: {}", + self.username, + self.password.is_some() + ); + + let req = self.client.post(self.endpoint.clone()); + let req = match (&self.username, &self.password) { + (Some(ref username), password) => req.basic_auth(username, password.clone()), + (None, Some(ref password)) => req.basic_auth("", Some(password)), + (None, None) => req, + } + .header(reqwest::header::CONTENT_TYPE, "text/plain") + .json(&req_json); + log::trace!("request: {req:?}"); + let resp = req.send()?; + log::trace!("response: {resp:?}"); + + let output = resp.error_for_status()?.json::()?; + match (output.result, output.error) { + (_, Some(error)) => Err(error.into()), + (Some(result), None) => serde_json::from_value(result).map_err(Into::into), + (None, None) => { + let error = RpcError { + code: RpcErrorCode::InternalError, + message: "result is empty withtout errors".to_owned(), + data: None, + }; + Err(error.into()) + } + } + } + + pub fn get_best_block_hash(&self) -> BtcRpcResult { + let params = serialize_parameters!(); + self.post("getbestblockhash", params) + } + + pub fn get_tip_height(&self) -> BtcRpcResult { + // Two way to get the tip height: + // - getblockcount + // - getbestblockhash -> getblockstats(height) + self.get_best_block_hash() + .and_then(|hash| self.get_block_height(hash)) + } + + pub fn get_block_hash(&self, height: u32) -> BtcRpcResult { + let params = serialize_parameters!(height); + self.post("getblockhash", params) + } + + pub fn get_block_height(&self, hash: BlockHash) -> BtcRpcResult { + let stats = &["height"]; + let params = serialize_parameters!(hash, stats); + let height: BlockHeight = self.post("getblockstats", params)?; + Ok(height.height) + } + + pub fn get_raw_block_header(&self, hash: BlockHash) -> BtcRpcResult> { + let params = serialize_parameters!(hash, false); + self.post("getblockheader", params).and_then(|hex: String| { + let mut bin = vec![0; hex.len() / 2]; + hex_decode(hex.as_bytes(), &mut bin).map_err(|err| { + let error = RpcError { + code: RpcErrorCode::ParseError, + message: format!("failed to decode the hex string \"{hex}\" since {err}"), + data: None, + }; + >::into(error) + })?; + Ok(bin) + }) + } + + pub fn get_block_header(&self, hash: BlockHash) -> BtcRpcResult
{ + self.get_raw_block_header(hash).and_then(|bin| { + deserialize(&bin).map_err(|err| { + let error = RpcError { + code: RpcErrorCode::ParseError, + message: format!("failed to deserialize header from hex string since {err}"), + data: None, + }; + error.into() + }) + }) + } + + pub fn get_block_header_by_height(&self, height: u32) -> BtcRpcResult
{ + self.get_block_hash(height) + .and_then(|hash| self.get_block_header(hash)) + } + + pub fn get_raw_tx_out_proof(&self, txid: Txid) -> BtcRpcResult> { + let txids = vec![txid]; + let params = serialize_parameters!(txids); + self.post("gettxoutproof", params).and_then(|hex: String| { + let mut bin = vec![0; hex.len() / 2]; + hex_decode(hex.as_bytes(), &mut bin).map_err(|err| { + let error = RpcError { + code: RpcErrorCode::ParseError, + message: format!("failed to decode the hex string \"{hex}\" since {err}"), + data: None, + }; + >::into(error) + })?; + Ok(bin) + }) + } + + pub fn get_tx_out_proof(&self, txid: Txid) -> BtcRpcResult<(MerkleBlock, Vec)> { + self.get_raw_tx_out_proof(txid).and_then(|bin| { + deserialize(&bin) + .map_err(|err| { + let error = RpcError { + code: RpcErrorCode::ParseError, + message: format!( + "failed to deserialize tx out proof from hex string since {err}" + ), + data: None, + }; + error.into() + }) + .map(|mb| (mb, bin)) + }) + } +} + +/// Implement combined methods. +impl BitcoinClient { + pub fn check_then_fetch_header(&self, height: u32) -> Result
{ + let tip_height = self.get_tip_height()?; + log::debug!("The height of the best bitcoin block is {tip_height}"); + if height > tip_height { + let msg = format!( + "the tip height of bitcoin ({tip_height}) is less than + the required height {height}" + ); + return Err(Error::other(msg)); + } + let header = self.get_block_header_by_height(height)?; + log::debug!("The bitcoin header#{height} is {header:?}"); + Ok(header) + } + + pub fn get_headers(&self, start: u32, end: u32) -> Result> { + log::trace!("Download headers from {start} to {end}"); + let mut headers = Vec::new(); + for height in start..=end { + let header = self.get_block_header_by_height(height)?; + headers.push(header); + } + Ok(headers) + } +} diff --git a/src/components/ckb_client.rs b/src/components/ckb_client.rs new file mode 100644 index 0000000..addb40d --- /dev/null +++ b/src/components/ckb_client.rs @@ -0,0 +1,42 @@ +//! Expand the functionality of the original CKB RPC client. + +use ckb_jsonrpc_types::TransactionView; +use ckb_sdk::rpc::CkbRpcClient; +use ckb_types::{packed, prelude::*, H256}; + +use crate::result::Result; + +pub trait CkbRpcClientExtension { + fn send_transaction_ext(&self, tx_json: TransactionView, dry_run: bool) -> Result; +} + +impl CkbRpcClientExtension for CkbRpcClient { + fn send_transaction_ext(&self, tx_json: TransactionView, dry_run: bool) -> Result { + if log::log_enabled!(log::Level::Trace) { + match serde_json::to_string_pretty(&tx_json) { + Ok(tx_json_str) => { + log::trace!("transaction: {tx_json_str}") + } + Err(err) => { + log::warn!("failed to convert the transaction into json string since {err}") + } + } + } + + let tx: packed::Transaction = tx_json.inner.clone().into(); + let tx_hash = tx.calc_tx_hash().unpack(); + + if log::log_enabled!(log::Level::Debug) { + let cycles: u64 = self.estimate_cycles(tx_json.inner.clone())?.cycles.into(); + log::debug!("Estimated cycles for {tx_hash:#x}: {cycles}"); + } + + if !dry_run { + let tx_hash = self.send_transaction(tx_json.inner, None)?; + log::info!("Transaction hash: {tx_hash:#x}."); + println!("Send transaction: {tx_hash:#x}"); + } + + Ok(tx_hash) + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs new file mode 100644 index 0000000..67ab1fb --- /dev/null +++ b/src/components/mod.rs @@ -0,0 +1,14 @@ +//! Components of the whole service. + +mod bitcoin_client; +mod ckb_client; +pub(crate) mod storage; + +mod api_service; +mod spv_service; + +pub use api_service::ApiServiceConfig; +pub use bitcoin_client::BitcoinClient; +pub use ckb_client::CkbRpcClientExtension; +pub use spv_service::{SpvClientCell, SpvInfoCell, SpvService}; +pub use storage::{Error as StorageError, Storage}; diff --git a/src/components/spv_service.rs b/src/components/spv_service.rs new file mode 100644 index 0000000..ea653b0 --- /dev/null +++ b/src/components/spv_service.rs @@ -0,0 +1,185 @@ +//! Internal SPV service. + +use std::collections::HashMap; + +use ckb_bitcoin_spv_verifier::types::{ + core::{SpvClient, SpvInfo}, + packed, + prelude::Unpack as VUnpack, +}; +use ckb_sdk::{ + rpc::{ + ckb_indexer::{Order, SearchKey}, + CkbRpcClient, + }, + traits::{CellQueryOptions, LiveCell, PrimaryScriptType}, +}; +use ckb_types::prelude::*; + +use crate::{ + components::{BitcoinClient, Storage}, + prelude::*, + result::{Error, Result}, +}; + +#[derive(Clone)] +pub struct SpvService { + pub(crate) ckb_cli: CkbRpcClient, + pub(crate) btc_cli: BitcoinClient, + pub(crate) storage: Storage, +} + +#[derive(Clone)] +pub struct SpvInfoCell { + pub(crate) info: SpvInfo, + pub(crate) cell: LiveCell, + pub(crate) clients_count: u8, +} + +#[derive(Clone)] +pub struct SpvClientCell { + pub(crate) client: SpvClient, + pub(crate) cell: LiveCell, +} + +impl SpvInfoCell { + pub(crate) fn prev_tip_client_id(&self) -> u8 { + let current = self.info.tip_client_id; + if current == 0 { + self.clients_count - 1 + } else { + current - 1 + } + } + + pub(crate) fn next_tip_client_id(&self) -> u8 { + let next = self.info.tip_client_id + 1; + if next < self.clients_count { + next + } else { + 0 + } + } +} + +impl SpvService { + pub(crate) fn find_spv_cells_for_update( + &self, + ) -> Result<(SpvInfoCell, SpvClientCell, SpvClientCell)> { + let (spv_info, spv_clients) = self.find_spv_cells()?; + let spv_client_curr = spv_clients + .get(&spv_info.info.tip_client_id) + .ok_or_else(|| { + let msg = format!( + "the current tip SPV client (id={}) is not found", + spv_info.info.tip_client_id + ); + Error::other(msg) + })? + .to_owned(); + let next_tip_client_id = spv_info.next_tip_client_id(); + let spv_client_next = spv_clients + .get(&next_tip_client_id) + .ok_or_else(|| { + let msg = + format!("the next tip SPV client (id={next_tip_client_id}) is not found",); + Error::other(msg) + })? + .to_owned(); + Ok((spv_info, spv_client_curr, spv_client_next)) + } + + pub(crate) fn find_best_spv_client(&self, height: u32) -> Result { + let (mut spv_info, spv_clients) = self.find_spv_cells()?; + for _ in 0..spv_clients.len() { + let spv_client = spv_clients + .get(&spv_info.info.tip_client_id) + .ok_or_else(|| { + let msg = format!( + "the current tip SPV client (id={}) is not found", + spv_info.info.tip_client_id + ); + Error::other(msg) + })?; + if spv_client.client.headers_mmr_root.max_height <= height { + return Ok(spv_client.to_owned()); + } + spv_info.info.tip_client_id = spv_info.prev_tip_client_id(); + } + let msg = format!("all SPV clients have better heights than server has (height: {height})"); + Err(Error::other(msg)) + } + + pub(crate) fn find_spv_cells(&self) -> Result<(SpvInfoCell, HashMap)> { + let cells = self.find_raw_spv_cells()?; + parse_raw_spv_cells(cells) + } + + pub(crate) fn find_raw_spv_cells(&self) -> Result> { + let spv_type_script = self.storage.spv_contract_type_script()?; + let args_data = spv_type_script.args().raw_data(); + let args = packed::SpvTypeArgsReader::from_slice(&args_data) + .map_err(|err| { + let msg = format!("the args of the SPV type script is invalid since {err}"); + Error::other(msg) + })? + .unpack(); + + let query = CellQueryOptions::new(spv_type_script, PrimaryScriptType::Type); + let order = Order::Desc; + let search_key = SearchKey::from(query); + + self.ckb_cli + .get_cells(search_key, order, u32::MAX.into(), None) + .map_err(Into::into) + .map(|res| res.objects) + .and_then(|cells| { + let actual = cells.len(); + let expected = usize::from(args.clients_count) + 1; + if actual == expected { + Ok(cells.into_iter().map(Into::into).collect()) + } else { + let msg = format!( + "the count of SPV cells is incorrect, expect {expected} but got {actual}" + ); + Err(Error::other(msg)) + } + }) + } +} + +fn parse_raw_spv_cells(cells: Vec) -> Result<(SpvInfoCell, HashMap)> { + let mut spv_info_opt = None; + let mut spv_clients = HashMap::new(); + let clients_count = (cells.len() - 1) as u8; // Checked when fetch SPV cells. + for cell in cells.into_iter() { + let data = &cell.output_data; + if let Ok(client) = packed::SpvClientReader::from_slice(data) { + let spv_cell = SpvClientCell { + client: client.unpack(), + cell, + }; + spv_clients.insert(spv_cell.client.id, spv_cell); + } else if let Ok(info) = packed::SpvInfoReader::from_slice(data) { + if spv_info_opt.is_some() { + let msg = "the SPV info cell should be unique"; + return Err(Error::other(msg)); + } + let spv_cell = SpvInfoCell { + info: info.unpack(), + cell, + clients_count, + }; + spv_info_opt = Some(spv_cell); + } else { + let msg = "the data of the SPV cell is unexpected"; + return Err(Error::other(msg)); + } + } + if let Some(spv_info) = spv_info_opt { + Ok((spv_info, spv_clients)) + } else { + let msg = "the SPV info cell is missing"; + Err(Error::other(msg)) + } +} diff --git a/src/components/storage/internal/cache.rs b/src/components/storage/internal/cache.rs new file mode 100644 index 0000000..48b5a35 --- /dev/null +++ b/src/components/storage/internal/cache.rs @@ -0,0 +1,9 @@ +//! Memory cache for the storage. + +use std::sync::RwLock; + +#[derive(Default)] +pub(crate) struct Cache { + pub(crate) base_bitcoin_height: RwLock>, + // TODO Cache headers by their heights. +} diff --git a/src/components/storage/internal/mmr.rs b/src/components/storage/internal/mmr.rs new file mode 100644 index 0000000..2b43347 --- /dev/null +++ b/src/components/storage/internal/mmr.rs @@ -0,0 +1,33 @@ +//! Implement MMR-related traits for the storage. + +use ckb_bitcoin_spv_verifier::{ + types::packed, + utilities::mmr::lib::{ + Error as MMRError, MMRStoreReadOps, MMRStoreWriteOps, Result as MMRResult, + }, +}; + +use crate::components::storage::{prelude::*, Storage}; + +impl MMRStoreReadOps for Storage { + fn get_elem(&self, pos: u64) -> MMRResult> { + self.get_bitcoin_header_digest(pos).map_err(|err| { + MMRError::StoreError(format!( + "Failed to read position {} from MMR, DB error {}", + pos, err + )) + }) + } +} + +impl MMRStoreWriteOps for Storage { + fn append(&mut self, pos: u64, elems: Vec) -> MMRResult<()> { + for (offset, elem) in elems.iter().enumerate() { + let pos: u64 = pos + (offset as u64); + self.put_bitcoin_header_digest(pos, elem).map_err(|err| { + MMRError::StoreError(format!("Failed to append to MMR, DB error {}", err)) + })?; + } + Ok(()) + } +} diff --git a/src/components/storage/internal/mod.rs b/src/components/storage/internal/mod.rs new file mode 100644 index 0000000..88e5fe1 --- /dev/null +++ b/src/components/storage/internal/mod.rs @@ -0,0 +1,95 @@ +//! Implement the `Storage`. + +use std::{path::Path, sync::Arc}; + +use rocksdb::{ + prelude::{ + GetColumnFamilys as _, GetPinned as _, GetPinnedCF as _, OpenCF as _, Put as _, PutCF as _, + }, + ColumnFamily, ColumnFamilyDescriptor, DBPinnableSlice, Options, DB, +}; + +use crate::components::storage::{ + result::{Error, Result}, + schemas::columns::{self, Column}, +}; + +mod cache; +mod mmr; +mod reader; +mod writer; + +use cache::Cache; + +#[derive(Clone)] +pub struct Storage { + pub(crate) db: Arc, + pub(crate) cache: Arc, +} + +impl Storage { + pub fn new>(path: P) -> Result { + let cf_names = { + let mut cf_names = Vec::with_capacity(columns::COUNT); + cf_names.push(columns::COLUMN_BITCOIN_HEADER_MMR.to_string()); + cf_names.push(columns::COLUMN_BITCOIN_HEADERS.to_string()); + cf_names + }; + let cf_descriptors: Vec<_> = cf_names + .iter() + .map(|c| ColumnFamilyDescriptor::new(c, Options::default())) + .collect(); + + let opts = { + let mut opts = Options::default(); + opts.create_if_missing(true); + opts.create_missing_column_families(true); + opts + }; + + let db = DB::open_cf_descriptors(&opts, path.as_ref(), cf_descriptors)?; + let cache = Cache::default(); + let storage = Self { + db: Arc::new(db), + cache: Arc::new(cache), + }; + + Ok(storage) + } + + pub(crate) fn get>(&self, key: K) -> Result> { + self.db.get_pinned(key.as_ref()).map_err(Into::into) + } + + pub(crate) fn put, V: AsRef<[u8]>>(&self, key: K, value: V) -> Result<()> { + self.db + .put(key.as_ref(), value.as_ref()) + .map_err(Into::into) + } + + pub(crate) fn get_cf>( + &self, + col: Column, + key: K, + ) -> Result> { + let cf = cf_handle(&self.db, col)?; + self.db.get_pinned_cf(cf, key.as_ref()).map_err(Into::into) + } + + pub(crate) fn put_cf, V: AsRef<[u8]>>( + &self, + col: Column, + key: K, + value: V, + ) -> Result<()> { + let cf = cf_handle(&self.db, col)?; + self.db + .put_cf(cf, key.as_ref(), value.as_ref()) + .map_err(Into::into) + } +} + +pub(crate) fn cf_handle(db: &DB, col: Column) -> Result<&ColumnFamily> { + db.cf_handle(col) + .ok_or_else(|| Error::storage(format!("column {} not found", col))) +} diff --git a/src/components/storage/internal/reader.rs b/src/components/storage/internal/reader.rs new file mode 100644 index 0000000..fc97576 --- /dev/null +++ b/src/components/storage/internal/reader.rs @@ -0,0 +1,95 @@ +//! Implement reading data from the storage. + +use bitcoin::consensus::deserialize; +use ckb_bitcoin_spv_verifier::types::{core::Header, packed, prelude::*}; +use ckb_types::packed::{CellDep, CellDepReader, Script, ScriptReader}; + +use crate::components::storage::{ + prelude::StorageReader, + result::{Error, Result}, + schemas::{columns, keys}, + Storage, +}; + +impl StorageReader for Storage { + fn get_base_bitcoin_height(&self) -> Result> { + let height_opt = *self + .cache + .base_bitcoin_height + .read() + .map_err(Error::storage)?; + if let Some(height) = height_opt { + Ok(Some(height)) + } else { + let height_opt = self + .get(keys::BASE_BITCOIN_HEIGHT)? + .map(|raw| packed::Uint32Reader::from_slice(&raw).map(|reader| reader.unpack())) + .transpose()?; + if let Some(height) = height_opt { + *self + .cache + .base_bitcoin_height + .write() + .map_err(Error::storage)? = Some(height); + } + Ok(height_opt) + } + } + + fn get_tip_bitcoin_height(&self) -> Result { + self.get(keys::TIP_BITCOIN_HEIGHT)? + .map(|raw| packed::Uint32Reader::from_slice(&raw).map(|reader| reader.unpack())) + .transpose() + .map_err(Into::into) + .and_then(|opt| opt.ok_or_else(|| Error::not_found("tip bitcoin height"))) + } + + fn get_bitcoin_header(&self, height: u32) -> Result
{ + let key = height.to_be_bytes(); + self.get_cf(columns::COLUMN_BITCOIN_HEADERS, key)? + .map(|raw| { + deserialize(&raw).map_err(|err| { + let msg = + format!("failed to decode the header#{height} from storage since {err}"); + Error::data(msg) + }) + }) + .transpose() + .and_then(|opt| opt.ok_or_else(|| Error::not_found(format!("header#{height}")))) + } + + fn get_bitcoin_header_digest(&self, position: u64) -> Result> { + let key = position.to_be_bytes(); + self.get_cf(columns::COLUMN_BITCOIN_HEADER_MMR, key)? + .map(|raw| { + packed::HeaderDigestReader::from_slice(&raw) + .map(|reader| reader.to_entity()) + .map_err(Into::into) + }) + .transpose() + } + + fn get_spv_contract_type_script(&self) -> Result