Pre-release v0.2.0
This commit is contained in:
513
Cargo.lock
generated
513
Cargo.lock
generated
@@ -17,6 +17,17 @@ version = "2.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.7.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.16",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.18"
|
version = "0.6.18"
|
||||||
@@ -67,6 +78,24 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-channel"
|
||||||
|
version = "2.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"event-listener-strategy",
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.88"
|
version = "0.1.88"
|
||||||
@@ -99,6 +128,12 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.21.7"
|
version = "0.21.7"
|
||||||
@@ -125,11 +160,17 @@ checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bech32",
|
"bech32",
|
||||||
"bitcoin-private",
|
"bitcoin-private",
|
||||||
"bitcoin_hashes",
|
"bitcoin_hashes 0.12.0",
|
||||||
"hex_lit",
|
"hex_lit",
|
||||||
"secp256k1",
|
"secp256k1 0.27.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitcoin-io"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcoin-private"
|
name = "bitcoin-private"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -145,6 +186,16 @@ dependencies = [
|
|||||||
"bitcoin-private",
|
"bitcoin-private",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitcoin_hashes"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
|
||||||
|
dependencies = [
|
||||||
|
"bitcoin-io",
|
||||||
|
"hex-conservative",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@@ -255,6 +306,34 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concurrent-queue"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "config"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23738e11972c7643e4ec947840fc463b6a571afcd3e735bdfce7d03c7a784aca"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"json5",
|
||||||
|
"lazy_static",
|
||||||
|
"nom",
|
||||||
|
"pathdiff",
|
||||||
|
"ron",
|
||||||
|
"rust-ini",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"toml",
|
||||||
|
"yaml-rust",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console"
|
name = "console"
|
||||||
version = "0.15.11"
|
version = "0.15.11"
|
||||||
@@ -293,34 +372,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-channel"
|
|
||||||
version = "0.5.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-deque"
|
|
||||||
version = "0.8.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-epoch",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-epoch"
|
|
||||||
version = "0.9.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.21"
|
version = "0.8.21"
|
||||||
@@ -339,23 +390,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cryptohunter"
|
name = "cryptohunter"
|
||||||
version = "0.1.6"
|
version = "0.1.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-channel",
|
||||||
|
"async-trait",
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
|
"bytes",
|
||||||
"clap",
|
"clap",
|
||||||
"crossbeam-channel",
|
"config",
|
||||||
"csv",
|
"csv",
|
||||||
|
"deadpool-postgres",
|
||||||
|
"futures",
|
||||||
|
"hex",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"lazy_static",
|
|
||||||
"log",
|
"log",
|
||||||
"postgres",
|
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rayon",
|
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"secp256k1 0.31.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml",
|
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-postgres",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -379,6 +434,40 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deadpool"
|
||||||
|
version = "0.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f"
|
||||||
|
dependencies = [
|
||||||
|
"deadpool-runtime",
|
||||||
|
"num_cpus",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deadpool-postgres"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d697d376cbfa018c23eb4caab1fd1883dd9c906a8c034e8d9a3cb06a7e0bef9"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"deadpool",
|
||||||
|
"getrandom 0.2.16",
|
||||||
|
"tokio",
|
||||||
|
"tokio-postgres",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deadpool-runtime"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b"
|
||||||
|
dependencies = [
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -411,10 +500,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "dlv-list"
|
||||||
version = "1.15.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
@@ -447,6 +536,27 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener"
|
||||||
|
version = "5.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"parking",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener-strategy"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
|
||||||
|
dependencies = [
|
||||||
|
"event-listener",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fallible-iterator"
|
name = "fallible-iterator"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -489,6 +599,21 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -505,6 +630,17 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -540,6 +676,7 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
@@ -568,8 +705,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -609,6 +748,15 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.3"
|
version = "0.15.3"
|
||||||
@@ -621,6 +769,27 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex-conservative"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex_lit"
|
name = "hex_lit"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -821,7 +990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown 0.15.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -865,6 +1034,17 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "json5"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_derive",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -877,6 +1057,12 @@ version = "0.2.172"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@@ -927,6 +1113,12 @@ version = "0.3.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.8"
|
version = "0.8.8"
|
||||||
@@ -964,12 +1156,32 @@ dependencies = [
|
|||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_threads"
|
name = "num_threads"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@@ -1050,6 +1262,22 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-multimap"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
|
||||||
|
dependencies = [
|
||||||
|
"dlv-list",
|
||||||
|
"hashbrown 0.12.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.4"
|
version = "0.12.4"
|
||||||
@@ -1073,12 +1301,63 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathdiff"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"thiserror",
|
||||||
|
"ucd-trie",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_derive"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_generator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_generator"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_meta",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_meta"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"pest",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.11.3"
|
version = "0.11.3"
|
||||||
@@ -1121,20 +1400,6 @@ version = "1.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
|
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "postgres"
|
|
||||||
version = "0.19.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "363e6dfbdd780d3aa3597b6eb430db76bb315fa9bad7fae595bb8def808b8470"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"fallible-iterator",
|
|
||||||
"futures-util",
|
|
||||||
"log",
|
|
||||||
"tokio",
|
|
||||||
"tokio-postgres",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "postgres-protocol"
|
name = "postgres-protocol"
|
||||||
version = "0.6.8"
|
version = "0.6.8"
|
||||||
@@ -1271,26 +1536,6 @@ dependencies = [
|
|||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon"
|
|
||||||
version = "1.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
"rayon-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon-core"
|
|
||||||
version = "1.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.12"
|
version = "0.5.12"
|
||||||
@@ -1340,6 +1585,27 @@ dependencies = [
|
|||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ron"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.13.1",
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-ini"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"ordered-multimap",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
@@ -1401,9 +1667,20 @@ version = "0.27.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f"
|
checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin_hashes",
|
"bitcoin_hashes 0.12.0",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"secp256k1-sys",
|
"secp256k1-sys 0.8.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "secp256k1"
|
||||||
|
version = "0.31.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a3dff2d01c9aa65c3186a45ff846bfea52cbe6de3b6320ed2a358d90dad0d76"
|
||||||
|
dependencies = [
|
||||||
|
"bitcoin_hashes 0.14.0",
|
||||||
|
"rand 0.9.1",
|
||||||
|
"secp256k1-sys 0.11.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1415,6 +1692,15 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "secp256k1-sys"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "2.11.1"
|
version = "2.11.1"
|
||||||
@@ -1482,19 +1768,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_yaml"
|
|
||||||
version = "0.9.34+deprecated"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap",
|
|
||||||
"itoa",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
"unsafe-libyaml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.9"
|
version = "0.10.9"
|
||||||
@@ -1655,6 +1928,26 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.41"
|
version = "0.3.41"
|
||||||
@@ -1791,6 +2084,15 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.5.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@@ -1804,9 +2106,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.33"
|
version = "0.1.33"
|
||||||
@@ -1828,6 +2142,12 @@ version = "1.18.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.18"
|
version = "0.3.18"
|
||||||
@@ -1861,12 +2181,6 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unsafe-libyaml"
|
|
||||||
version = "0.2.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.4"
|
version = "2.5.4"
|
||||||
@@ -2207,6 +2521,15 @@ version = "0.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaml-rust"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||||
|
dependencies = [
|
||||||
|
"linked-hash-map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
|||||||
18
Cargo.toml
18
Cargo.toml
@@ -4,18 +4,22 @@ version = "0.1.7"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
futures = "0.3.31"
|
||||||
serde_yaml = "0.9"
|
bytes = "1.0"
|
||||||
clap = { version = "4.0", features = ["derive"] }
|
clap = { version = "4.0", features = ["derive"] }
|
||||||
postgres = "0.19"
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
config = { version = "0.13", features = ["yaml", "json"] }
|
||||||
|
async-channel = "2.1"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
bitcoin = { version = "0.30", features = ["rand"] }
|
bitcoin = { version = "0.30", features = ["rand"] }
|
||||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
secp256k1 = { version = "0.31.0", features = ["rand"] }
|
||||||
|
hex = "0.4.3"
|
||||||
|
async-trait = "0.1.88"
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
tokio-postgres = "0.7.13"
|
||||||
|
deadpool-postgres = "0.14.1"
|
||||||
|
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||||
csv = "1.1"
|
csv = "1.1"
|
||||||
crossbeam-channel = "0.5"
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
simple_logger = "4.0"
|
simple_logger = "4.0"
|
||||||
lazy_static = "1.4"
|
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
rayon = "1.10.0"
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- RUST_LOG=info
|
- RUST_LOG=info
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.yaml:/app/config.yaml
|
- ./settings.yaml:/app/settings.yaml
|
||||||
- ./snapshots:/snapshots
|
- ./snapshots:/snapshots
|
||||||
depends_on:
|
depends_on:
|
||||||
database:
|
database:
|
||||||
@@ -15,7 +15,7 @@ services:
|
|||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: cryptohunter
|
POSTGRES_USER: cryptohunter
|
||||||
POSTGRES_PASSWORD: ${CRYPTOHUNTER_DATABASE_PASSWORD:-12345678}
|
POSTGRES_PASSWORD: ${CRYPTOHUNTER_DATABASE_PASSWORD:-Passw0rd}
|
||||||
POSTGRES_DB: cryptohunter
|
POSTGRES_DB: cryptohunter
|
||||||
volumes:
|
volumes:
|
||||||
- database-data:/var/lib/postgresql/data
|
- database-data:/var/lib/postgresql/data
|
||||||
|
|||||||
235
src/blockchains/bitcoin.rs
Normal file
235
src/blockchains/bitcoin.rs
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use bitcoin::{
|
||||||
|
secp256k1::Secp256k1,
|
||||||
|
secp256k1::SecretKey,
|
||||||
|
Address,
|
||||||
|
Network as BitcoinNetwork,
|
||||||
|
PrivateKey,
|
||||||
|
PublicKey,
|
||||||
|
};
|
||||||
|
use bytes::Bytes;
|
||||||
|
use config::{Config as ConfigBuilder, Value};
|
||||||
|
use deadpool_postgres;
|
||||||
|
use futures::{SinkExt, pin_mut};
|
||||||
|
use hex;
|
||||||
|
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use tokio::fs::File;
|
||||||
|
use tokio::io::{AsyncReadExt, BufReader};
|
||||||
|
|
||||||
|
use crate::key_generators::KeyAlgorithm;
|
||||||
|
use crate::utils::DynError;
|
||||||
|
use super::enums::Blockchain;
|
||||||
|
use super::traits::{SnapshotLoader, WalletChecker};
|
||||||
|
|
||||||
|
pub struct Bitcoin {
|
||||||
|
database_table: String,
|
||||||
|
pool: deadpool_postgres::Pool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct BitcoinParameters {
|
||||||
|
database_url: String,
|
||||||
|
#[serde(default = "default_database_table")]
|
||||||
|
database_table: String,
|
||||||
|
#[serde(default = "default_database_max_connections")]
|
||||||
|
database_max_connections: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_database_table() -> String {
|
||||||
|
"bitcoin".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_database_max_connections() -> usize {
|
||||||
|
20
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bitcoin {
|
||||||
|
pub fn new(data: &HashMap<String, Value>) -> Result<Self, DynError> {
|
||||||
|
let mut builder = ConfigBuilder::builder();
|
||||||
|
for (key, value) in data {
|
||||||
|
builder = builder.set_override(key, value.clone())?;
|
||||||
|
}
|
||||||
|
let parameters: BitcoinParameters = builder.build()?.try_deserialize()?;
|
||||||
|
|
||||||
|
let pg_config = parameters.database_url.parse::<tokio_postgres::Config>()?;
|
||||||
|
let mgr_config = deadpool_postgres::ManagerConfig {
|
||||||
|
recycling_method: deadpool_postgres::RecyclingMethod::Fast,
|
||||||
|
};
|
||||||
|
let mgr = deadpool_postgres::Manager::from_config(pg_config, tokio_postgres::NoTls, mgr_config);
|
||||||
|
let pool = deadpool_postgres::Pool::builder(mgr)
|
||||||
|
.config(deadpool_postgres::PoolConfig::new(parameters.database_max_connections))
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
database_table: parameters.database_table,
|
||||||
|
pool,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_client(&self) -> Result<deadpool_postgres::Client, DynError> {
|
||||||
|
self.pool.get().await.map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl WalletChecker for Bitcoin {
|
||||||
|
fn get_key_algorithm(&self) -> KeyAlgorithm {
|
||||||
|
KeyAlgorithm::Secp256k1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_blockchain(&self) -> Blockchain {
|
||||||
|
Blockchain::Bitcoin
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_wallet_info(&self, key_hex: &str) -> Result<Option<String>, DynError> {
|
||||||
|
let bytes = hex::decode(key_hex)?;
|
||||||
|
let secret_key = SecretKey::from_slice(&bytes)?;
|
||||||
|
let secp = Secp256k1::new();
|
||||||
|
let private_key = PrivateKey::new(secret_key, BitcoinNetwork::Bitcoin);
|
||||||
|
let public_key = PublicKey::from_private_key(&secp, &private_key);
|
||||||
|
let address = Address::p2pkh(&public_key, BitcoinNetwork::Bitcoin).to_string();
|
||||||
|
|
||||||
|
let client = self.get_client().await?;
|
||||||
|
let row = client.query_opt(
|
||||||
|
&format!("SELECT balance FROM {} WHERE address = $1", self.database_table),
|
||||||
|
&[&address],
|
||||||
|
).await?;
|
||||||
|
let balance = row.map(|r| r.get(0)).unwrap_or(0);
|
||||||
|
if balance == 0 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(format!(
|
||||||
|
"**Blockchain**: Bitcoin\n**Address**: `{}`\n**Balance**: `{}`",
|
||||||
|
address,
|
||||||
|
balance,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl SnapshotLoader for Bitcoin {
|
||||||
|
fn get_blockchain(&self) -> Blockchain {
|
||||||
|
Blockchain::Bitcoin
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn load_snapshot(&self, snapshot_path: &str) -> Result<(), DynError> {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
let client = self.get_client().await?;
|
||||||
|
let file = File::open(snapshot_path).await?;
|
||||||
|
let file_metadata = file.metadata().await?;
|
||||||
|
let file_size = file_metadata.len();
|
||||||
|
let index_name = format!("{}__address__ix", self.database_table);
|
||||||
|
let multi_progress = MultiProgress::new();
|
||||||
|
|
||||||
|
// 1. Preparing database
|
||||||
|
let prep_pb = multi_progress.add(ProgressBar::new_spinner());
|
||||||
|
prep_pb.set_style(
|
||||||
|
ProgressStyle::default_spinner()
|
||||||
|
.tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ")
|
||||||
|
.template("{spinner} {msg}").unwrap()
|
||||||
|
);
|
||||||
|
prep_pb.set_message("⚙️ Preparing database...");
|
||||||
|
prep_pb.enable_steady_tick(Duration::from_millis(100));
|
||||||
|
|
||||||
|
client.execute("SET synchronous_commit = off", &[]).await?;
|
||||||
|
client.execute("SET maintenance_work_mem = '4GB'", &[]).await?;
|
||||||
|
client.execute("SET work_mem = '2GB'", &[]).await?;
|
||||||
|
client.execute(
|
||||||
|
&format!("DROP TABLE IF EXISTS {}", self.database_table),
|
||||||
|
&[],
|
||||||
|
).await?;
|
||||||
|
client.execute(
|
||||||
|
&format!("DROP INDEX IF EXISTS {}", index_name),
|
||||||
|
&[],
|
||||||
|
).await?;
|
||||||
|
client.execute(
|
||||||
|
&format!(
|
||||||
|
"CREATE TABLE {} (address TEXT, balance BIGINT)",
|
||||||
|
self.database_table
|
||||||
|
),
|
||||||
|
&[],
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
prep_pb.finish_with_message("✅ Database prepared");
|
||||||
|
|
||||||
|
// 2. Copy data to database
|
||||||
|
let copy_pb = multi_progress.add(ProgressBar::new(file_size));
|
||||||
|
copy_pb.set_style(ProgressStyle::default_bar()
|
||||||
|
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta}) | {binary_bytes_per_sec}")
|
||||||
|
.unwrap()
|
||||||
|
.progress_chars("#>-"));
|
||||||
|
copy_pb.set_message("️📥 Copy data to database...");
|
||||||
|
|
||||||
|
let copy_stmt = format!(
|
||||||
|
"COPY {} FROM STDIN WITH (FORMAT csv, DELIMITER E'\t', HEADER)",
|
||||||
|
self.database_table,
|
||||||
|
);
|
||||||
|
let sink = client.copy_in(©_stmt).await?;
|
||||||
|
pin_mut!(sink);
|
||||||
|
let file = File::open(snapshot_path).await?;
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
let mut buffer = vec![0u8; 65536];
|
||||||
|
|
||||||
|
while let Ok(bytes_read) = reader.read(&mut buffer).await {
|
||||||
|
if bytes_read == 0 { break }
|
||||||
|
sink.as_mut()
|
||||||
|
.send(Bytes::copy_from_slice(&buffer[..bytes_read]))
|
||||||
|
.await?;
|
||||||
|
copy_pb.inc(bytes_read as u64);
|
||||||
|
}
|
||||||
|
sink.as_mut().close().await?;
|
||||||
|
copy_pb.set_position(file_size);
|
||||||
|
copy_pb.finish_with_message("✅ Data copied to database");
|
||||||
|
|
||||||
|
// 3. Creating index
|
||||||
|
let index_pb = multi_progress.add(ProgressBar::new_spinner());
|
||||||
|
index_pb.set_message("🔀 Creating index...");
|
||||||
|
index_pb.enable_steady_tick(Duration::from_millis(100));
|
||||||
|
|
||||||
|
client.execute(
|
||||||
|
&format!(
|
||||||
|
"CREATE INDEX {} ON {} USING HASH (address)",
|
||||||
|
index_name,
|
||||||
|
self.database_table,
|
||||||
|
),
|
||||||
|
&[],
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
index_pb.finish_with_message("✅ Index created");
|
||||||
|
|
||||||
|
// 4. Reseting temporary settings
|
||||||
|
let final_pb = multi_progress.add(ProgressBar::new_spinner());
|
||||||
|
final_pb.set_message("🔁 Reset database settings");
|
||||||
|
final_pb.enable_steady_tick(Duration::from_millis(100));
|
||||||
|
|
||||||
|
client.execute("RESET synchronous_commit", &[]).await?;
|
||||||
|
client.execute("RESET maintenance_work_mem", &[]).await?;
|
||||||
|
client.execute("RESET work_mem", &[]).await?;
|
||||||
|
|
||||||
|
final_pb.finish_with_message("✅ Database settings reseted");
|
||||||
|
|
||||||
|
// 5. Final
|
||||||
|
multi_progress.clear()?;
|
||||||
|
|
||||||
|
let records_count: i64 = client.query_one(
|
||||||
|
&format!(
|
||||||
|
"SELECT COUNT(*) FROM {}",
|
||||||
|
self.database_table,
|
||||||
|
),
|
||||||
|
&[],
|
||||||
|
).await?.get(0);
|
||||||
|
|
||||||
|
let data_size: f64 = file_size as f64 / (1024 * 1024 * 1024) as f64;
|
||||||
|
println!("\n🎉 Snapshot loaded successfully!");
|
||||||
|
println!("📊 Statistics:");
|
||||||
|
println!(" Total records processed: {}", records_count);
|
||||||
|
println!(" Data size: {:.2} GB", data_size);
|
||||||
|
println!(" Execution time: {:?}", start_time.elapsed());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/blockchains/enums.rs
Normal file
20
src/blockchains/enums.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
use clap;
|
||||||
|
use serde;
|
||||||
|
use std::fmt::{Display, Formatter, Result};
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, Copy, PartialEq, Eq, Hash,
|
||||||
|
clap::ValueEnum, serde::Serialize, serde::Deserialize,
|
||||||
|
)]
|
||||||
|
pub enum Blockchain {
|
||||||
|
#[serde(alias = "bitcoin")]
|
||||||
|
Bitcoin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Blockchain {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||||
|
match self {
|
||||||
|
Blockchain::Bitcoin => write!(f, "bitcoin"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/blockchains/fabrics.rs
Normal file
25
src/blockchains/fabrics.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use config::Value;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::utils::DynError;
|
||||||
|
use super::bitcoin::Bitcoin;
|
||||||
|
use super::enums::Blockchain;
|
||||||
|
use super::traits::{SnapshotLoader, WalletChecker};
|
||||||
|
|
||||||
|
pub fn create_wallet_checker(
|
||||||
|
blockchain: &Blockchain,
|
||||||
|
data: &HashMap<String, Value>,
|
||||||
|
) -> Result<Box<dyn WalletChecker>, DynError> {
|
||||||
|
match blockchain {
|
||||||
|
Blockchain::Bitcoin => Ok(Box::new(Bitcoin::new(data)?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_snapshot_loader(
|
||||||
|
blockchain: &Blockchain,
|
||||||
|
data: &HashMap<String, Value>,
|
||||||
|
) -> Result<Box<dyn SnapshotLoader>, DynError> {
|
||||||
|
match blockchain {
|
||||||
|
Blockchain::Bitcoin => Ok(Box::new(Bitcoin::new(data)?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/blockchains/mod.rs
Normal file
8
src/blockchains/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
mod bitcoin;
|
||||||
|
mod enums;
|
||||||
|
mod fabrics;
|
||||||
|
mod traits;
|
||||||
|
|
||||||
|
pub use enums::Blockchain;
|
||||||
|
pub use fabrics::{create_snapshot_loader, create_wallet_checker};
|
||||||
|
pub use traits::{SnapshotLoader, WalletChecker};
|
||||||
18
src/blockchains/traits.rs
Normal file
18
src/blockchains/traits.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::key_generators::KeyAlgorithm;
|
||||||
|
use crate::utils::DynError;
|
||||||
|
use super::enums::Blockchain;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait WalletChecker: Send + Sync {
|
||||||
|
fn get_blockchain(&self) -> Blockchain;
|
||||||
|
fn get_key_algorithm(&self) -> KeyAlgorithm;
|
||||||
|
async fn get_wallet_info(&self, key_hex: &str) -> Result<Option<String>, DynError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait SnapshotLoader: Send + Sync {
|
||||||
|
fn get_blockchain(&self) -> Blockchain;
|
||||||
|
async fn load_snapshot(&self, snapshot_path: &str) -> Result<(), DynError>;
|
||||||
|
}
|
||||||
13
src/cli.rs
13
src/cli.rs
@@ -1,10 +1,11 @@
|
|||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
use crate::blockchains::Blockchain;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[arg(short, long, default_value = "config.yaml")]
|
#[arg(short, long, default_value = "settings.yaml")]
|
||||||
pub config: String,
|
pub settings: String,
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Commands,
|
pub command: Commands,
|
||||||
@@ -12,16 +13,14 @@ pub struct Cli {
|
|||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum Commands {
|
pub enum Commands {
|
||||||
/// Search for wallets
|
|
||||||
Search {
|
Search {
|
||||||
network: Option<String>,
|
blockchain: Option<Blockchain>,
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: SearchSubcommand,
|
command: SearchSubcommand,
|
||||||
},
|
},
|
||||||
/// Manage snapshots
|
|
||||||
Snapshots {
|
Snapshots {
|
||||||
network: String,
|
blockchain: Blockchain,
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: SnapshotSubcommand,
|
command: SnapshotSubcommand,
|
||||||
@@ -30,12 +29,10 @@ pub enum Commands {
|
|||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum SearchSubcommand {
|
pub enum SearchSubcommand {
|
||||||
/// Run search process
|
|
||||||
Run,
|
Run,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum SnapshotSubcommand {
|
pub enum SnapshotSubcommand {
|
||||||
/// Load snapshot into database
|
|
||||||
Load { path: String },
|
Load { path: String },
|
||||||
}
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
use serde::Deserialize;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct Config {
|
|
||||||
pub networks: HashMap<String, NetworkConfig>,
|
|
||||||
pub notifications: NotificationConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
pub struct NetworkConfig {
|
|
||||||
pub key_generator_tasks: usize,
|
|
||||||
pub balance_checker_tasks: usize,
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub params: HashMap<String, serde_yaml::Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
pub struct NotificationConfig {
|
|
||||||
pub telegram_bot_token: String,
|
|
||||||
pub telegram_user_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
|
|
||||||
let mut file = File::open(path)?;
|
|
||||||
let mut contents = String::new();
|
|
||||||
file.read_to_string(&mut contents)?;
|
|
||||||
let config: Config = serde_yaml::from_str(&contents)?;
|
|
||||||
Ok(config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
20
src/key_generators/enums.rs
Normal file
20
src/key_generators/enums.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
use clap;
|
||||||
|
use serde;
|
||||||
|
use std::fmt::{Display, Formatter, Result};
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, Copy, PartialEq, Eq, Hash,
|
||||||
|
clap::ValueEnum, serde::Serialize, serde::Deserialize
|
||||||
|
)]
|
||||||
|
pub enum KeyAlgorithm {
|
||||||
|
#[serde(alias = "secp256k1")]
|
||||||
|
Secp256k1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for KeyAlgorithm {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||||
|
match self {
|
||||||
|
KeyAlgorithm::Secp256k1 => write!(f, "secp256k1"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/key_generators/fabrics.rs
Normal file
16
src/key_generators/fabrics.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use config::Value;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::utils::DynError;
|
||||||
|
use super::enums::KeyAlgorithm;
|
||||||
|
use super::secp256k1::Secp256k1Generator;
|
||||||
|
use super::traits::KeyGenerator;
|
||||||
|
|
||||||
|
pub fn create_key_generator(
|
||||||
|
algorithm: &KeyAlgorithm,
|
||||||
|
data: &HashMap<String, Value>,
|
||||||
|
) -> Result<Box<dyn KeyGenerator>, DynError> {
|
||||||
|
match algorithm {
|
||||||
|
KeyAlgorithm::Secp256k1 => Ok(Box::new(Secp256k1Generator::new(data)?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/key_generators/mod.rs
Normal file
8
src/key_generators/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
mod enums;
|
||||||
|
mod fabrics;
|
||||||
|
mod secp256k1;
|
||||||
|
mod traits;
|
||||||
|
|
||||||
|
pub use enums::KeyAlgorithm;
|
||||||
|
pub use fabrics::create_key_generator;
|
||||||
|
pub use traits::KeyGenerator;
|
||||||
29
src/key_generators/secp256k1.rs
Normal file
29
src/key_generators/secp256k1.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
use config::Value;
|
||||||
|
use hex;
|
||||||
|
use secp256k1::{rand, SecretKey, Secp256k1};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::utils::DynError;
|
||||||
|
use super::enums::KeyAlgorithm;
|
||||||
|
use super::traits::KeyGenerator;
|
||||||
|
|
||||||
|
pub struct Secp256k1Generator {}
|
||||||
|
|
||||||
|
impl Secp256k1Generator {
|
||||||
|
pub fn new(data: &HashMap<String, Value>) -> Result<Self, DynError> {
|
||||||
|
Ok(Secp256k1Generator{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyGenerator for Secp256k1Generator {
|
||||||
|
fn get_key_algorithm(&self) -> KeyAlgorithm {
|
||||||
|
KeyAlgorithm::Secp256k1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_key_hex(&self) -> String {
|
||||||
|
let secp = Secp256k1::new();
|
||||||
|
let secret_key = SecretKey::new(&mut rand::rng());
|
||||||
|
let secret_key_bytes = secret_key.secret_bytes();
|
||||||
|
hex::encode(secret_key_bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/key_generators/traits.rs
Normal file
6
src/key_generators/traits.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
use super::enums::KeyAlgorithm;
|
||||||
|
|
||||||
|
pub trait KeyGenerator: Send + Sync {
|
||||||
|
fn get_key_algorithm(&self) -> KeyAlgorithm;
|
||||||
|
fn generate_key_hex(&self) -> String;
|
||||||
|
}
|
||||||
258
src/main.rs
258
src/main.rs
@@ -1,58 +1,51 @@
|
|||||||
|
mod blockchains;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod config;
|
mod key_generators;
|
||||||
mod networks;
|
|
||||||
mod notification;
|
mod notification;
|
||||||
|
mod settings;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use async_channel;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cli::Cli;
|
use cli::Cli;
|
||||||
use config::Config;
|
|
||||||
use crossbeam_channel::{bounded, unbounded};
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use networks::create_network;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
use crate::blockchains::{
|
||||||
|
create_snapshot_loader,
|
||||||
|
create_wallet_checker,
|
||||||
|
Blockchain,
|
||||||
|
};
|
||||||
|
use crate::key_generators::{create_key_generator, KeyAlgorithm};
|
||||||
|
use crate::notification::send_telegram_message;
|
||||||
|
use crate::settings::Settings;
|
||||||
|
use crate::utils::DynError;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), DynError> {
|
||||||
simple_logger::init_with_level(log::Level::Info)?;
|
simple_logger::init_with_level(log::Level::Info)?;
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
let config = Config::from_file(&cli.config)?;
|
let settings = Settings::load(&cli.settings)?;
|
||||||
|
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
cli::Commands::Snapshots { network, command } => {
|
cli::Commands::Snapshots { blockchain, command } => {
|
||||||
let network_config = config
|
let blockchain_settings = settings
|
||||||
.networks
|
.blockchains
|
||||||
.get(network)
|
.get(blockchain)
|
||||||
.ok_or(format!("Config for network {} not found", network))?;
|
.ok_or(format!("Settings for blockchain {} not found", blockchain))?;
|
||||||
let network_obj = create_network(network, &network_config.params)?;
|
let snapshot_loader = create_snapshot_loader(blockchain, &blockchain_settings.data)?;
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
cli::SnapshotSubcommand::Load { path } => {
|
cli::SnapshotSubcommand::Load { path } => {
|
||||||
network_obj.load_snapshot(path)?;
|
snapshot_loader.load_snapshot(path).await?;
|
||||||
log::info!("Snapshot loaded successfully for {}", network);
|
log::info!("Snapshot loaded successfully for {}", blockchain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cli::Commands::Search { network, command } => {
|
cli::Commands::Search { blockchain, command } => {
|
||||||
match command {
|
match command {
|
||||||
cli::SearchSubcommand::Run => {
|
cli::SearchSubcommand::Run => run_search(blockchain.as_ref(), &settings).await?,
|
||||||
let networks_to_run: HashMap<_, _> = match network {
|
|
||||||
Some(net) => {
|
|
||||||
let cfg = config.networks.get(net).ok_or("Network not found")?;
|
|
||||||
[(net.as_str(), cfg)].into()
|
|
||||||
}
|
|
||||||
None => config
|
|
||||||
.networks
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| (k.as_str(), v))
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (name, net_cfg) in networks_to_run {
|
|
||||||
run_network_pipeline(name, net_cfg, &config.notifications)?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,117 +53,102 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_network_pipeline(
|
async fn run_search(
|
||||||
network_name: &str,
|
blockchain: Option<&Blockchain>,
|
||||||
config: &config::NetworkConfig,
|
settings: &Settings,
|
||||||
notification_cfg: &config::NotificationConfig,
|
) -> Result<(), DynError> {
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
let mut key_senders: HashMap<KeyAlgorithm, broadcast::Sender<String>> = HashMap::new();
|
||||||
log::info!("Starting pipeline for {}", network_name);
|
for algorithm in settings.key_generators.keys() {
|
||||||
|
let (sender, _) = broadcast::channel(1_000_000);
|
||||||
let network = Arc::new(create_network(network_name, &config.params)?);
|
key_senders.insert(algorithm.clone(), sender);
|
||||||
let (keypair_sender, keypair_receiver) = bounded(100);
|
|
||||||
let (notification_sender, notification_receiver) = unbounded();
|
|
||||||
|
|
||||||
// Клонируем имя сети для использования в замыканиях
|
|
||||||
let network_name_str = network_name.to_string();
|
|
||||||
|
|
||||||
// Key generation workers
|
|
||||||
for _ in 0..config.key_generator_tasks {
|
|
||||||
let sender = keypair_sender.clone();
|
|
||||||
let net = network.clone();
|
|
||||||
thread::spawn(move || loop {
|
|
||||||
let keypair = net.generate_keypair();
|
|
||||||
if let Err(e) = sender.send(keypair) {
|
|
||||||
log::error!("Keypair send error: {}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Balance checker workers
|
let mut key_gen_handles = vec![];
|
||||||
for _ in 0..config.balance_checker_tasks {
|
for (algorithm, algo_settings) in &settings.key_generators {
|
||||||
let receiver = keypair_receiver.clone();
|
let sender = key_senders.get(algorithm).unwrap().clone();
|
||||||
let sender = notification_sender.clone();
|
|
||||||
let net = network.clone();
|
for _ in 0..algo_settings.workers {
|
||||||
let name = network_name_str.clone(); // Клонируем String
|
let algo_settings = algo_settings.clone();
|
||||||
thread::spawn(move || loop {
|
let algorithm = algorithm.clone();
|
||||||
match receiver.recv() {
|
let sender = sender.clone(); // Клонируем sender для каждого потока
|
||||||
Ok((private_key, address)) => {
|
let handle = thread::spawn(move || {
|
||||||
match net.check_balance(&address) {
|
let key_generator = create_key_generator(&algorithm, &algo_settings.data)
|
||||||
Ok(balance) if balance > 0 => {
|
.expect("Failed to create key generator");
|
||||||
let formatted_balance = format_balance(balance);
|
loop {
|
||||||
let message = format!(
|
let key_hex = key_generator.generate_key_hex();
|
||||||
"💰 *Balance found!*\n\n*Network:* {}\n*Address:* `{}`\n*Private Key:* `{}`\n*Balance:* {}",
|
if let Err(e) = sender.send(key_hex) {
|
||||||
name, address, private_key, formatted_balance,
|
log::error!("Key generator for {:?} failed to send: {}", algorithm, e);
|
||||||
);
|
|
||||||
if let Err(e) = sender.send(message) {
|
|
||||||
log::error!("Notification send error: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(_) => {} // Баланс нулевой, ничего не делаем
|
|
||||||
Err(e) => log::error!("Balance check error: {}", e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
});
|
||||||
log::error!("Keypair receive error: {}", e);
|
key_gen_handles.push(handle);
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notification worker
|
let (notify_sender, notify_receiver) = async_channel::unbounded::<String>();
|
||||||
let notifier_cfg = notification_cfg.clone();
|
|
||||||
thread::spawn(move || {
|
let mut wallet_checker_handles = vec![];
|
||||||
for message in notification_receiver {
|
for (blockchain, blockchain_settings) in &settings.blockchains {
|
||||||
if let Err(e) = notification::send_telegram_message(
|
let wallet_checker = create_wallet_checker(blockchain, &blockchain_settings.data)?;
|
||||||
¬ifier_cfg.telegram_bot_token,
|
let key_algorithm = wallet_checker.get_key_algorithm();
|
||||||
¬ifier_cfg.telegram_user_id,
|
let key_sender = key_senders.get(&key_algorithm).ok_or(format!(
|
||||||
&message,
|
"No key sender for algorithm {:?} (blockchain {:?})",
|
||||||
) {
|
key_algorithm, blockchain
|
||||||
log::error!("Telegram send error: {}", e);
|
))?;
|
||||||
|
let key_receiver = key_sender.subscribe();
|
||||||
|
|
||||||
|
for _ in 0..blockchain_settings.workers {
|
||||||
|
let mut key_receiver = key_receiver.resubscribe();
|
||||||
|
let wallet_checker = create_wallet_checker(blockchain, &blockchain_settings.data)?;
|
||||||
|
let notify_sender = notify_sender.clone();
|
||||||
|
|
||||||
|
let handle = tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
match key_receiver.recv().await {
|
||||||
|
Ok(key_hex) => {
|
||||||
|
match wallet_checker.get_wallet_info(&key_hex).await {
|
||||||
|
Ok(Some(info)) => {
|
||||||
|
if let Err(e) = notify_sender.send(info).await {
|
||||||
|
log::error!("Failed to send notification: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => {}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Error getting wallet info: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(broadcast::error::RecvError::Closed) => {
|
||||||
|
log::info!("Key channel closed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(broadcast::error::RecvError::Lagged(skipped)) => {
|
||||||
|
log::warn!("Skipped {} keys", skipped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
wallet_checker_handles.push(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Клонируем необходимые данные для нотификации
|
||||||
|
let bot_token = settings.notifications.telegram_bot_token.clone();
|
||||||
|
let user_id = settings.notifications.telegram_user_id.clone();
|
||||||
|
|
||||||
|
let _ = tokio::spawn(async move {
|
||||||
|
while let Ok(message) = notify_receiver.recv().await {
|
||||||
|
if let Err(e) = send_telegram_message(&bot_token, &user_id, &message).await {
|
||||||
|
log::error!("Failed to send Telegram message: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
log::info!("Pipeline started for {}", network_name);
|
// Ждем сигнала Ctrl+C для завершения
|
||||||
loop {
|
tokio::signal::ctrl_c().await?;
|
||||||
thread::sleep(Duration::from_secs(60));
|
log::info!("Shutting down");
|
||||||
}
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref THRESHOLDS: Vec<(u64, &'static str)> = vec![
|
|
||||||
(1_000_000_000_000_000_000u64, " E"),
|
|
||||||
(1_000_000_000_000_000u64, " P"),
|
|
||||||
(1_000_000_000_000u64, " T"),
|
|
||||||
(1_000_000_000u64, " G"),
|
|
||||||
(1_000_000u64, " M"),
|
|
||||||
(1_000u64, " k"),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_balance(balance: i64) -> String {
|
|
||||||
let abs_balance = balance.abs() as u64;
|
|
||||||
|
|
||||||
let mut divider = 1;
|
|
||||||
let mut suffix = " sat";
|
|
||||||
|
|
||||||
for (thresh, s) in THRESHOLDS.iter() {
|
|
||||||
if abs_balance >= *thresh {
|
|
||||||
divider = thresh.clone();
|
|
||||||
suffix = s;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let quotient = abs_balance / divider;
|
|
||||||
let remainder = abs_balance % divider;
|
|
||||||
|
|
||||||
if remainder == 0 {
|
|
||||||
format!("{}{}", quotient, suffix)
|
|
||||||
} else {
|
|
||||||
let fractional = remainder as f64 / divider as f64;
|
|
||||||
format!("{:.4}{}", quotient as f64 + fractional, suffix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
use bitcoin::{
|
|
||||||
secp256k1, Address, Network, PrivateKey, PublicKey,
|
|
||||||
};
|
|
||||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
|
||||||
use postgres::{Client, NoTls};
|
|
||||||
use serde_yaml::Value;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufReader, Read, Write};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
pub struct Bitcoin {
|
|
||||||
database_url: String,
|
|
||||||
database_table: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Bitcoin {
|
|
||||||
pub fn new(params: &HashMap<String, Value>) -> Result<Self, Box<dyn Error>> {
|
|
||||||
let database_url = params
|
|
||||||
.get("database_url")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.ok_or("Missing database_url for Bitcoin")?
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let database_table = params
|
|
||||||
.get("database_table")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.ok_or("Missing database_table for Bitcoin")?
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
database_url,
|
|
||||||
database_table,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn connect(&self) -> Result<Client, Box<dyn Error>> {
|
|
||||||
let client = Client::connect(&self.database_url, NoTls)?;
|
|
||||||
Ok(client)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl super::Network for Bitcoin {
|
|
||||||
fn generate_keypair(&self) -> (String, String) {
|
|
||||||
let secp = secp256k1::Secp256k1::new();
|
|
||||||
let private_key = PrivateKey::new(secp256k1::SecretKey::new(&mut rand::thread_rng()), Network::Bitcoin);
|
|
||||||
let public_key = PublicKey::from_private_key(&secp, &private_key);
|
|
||||||
let address = Address::p2pkh(&public_key, Network::Bitcoin).to_string();
|
|
||||||
|
|
||||||
(private_key.to_wif(), address)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_balance(&self, address: &str) -> Result<i64, Box<dyn Error>> {
|
|
||||||
let mut client = self.connect()?;
|
|
||||||
let row = client.query_opt(
|
|
||||||
&format!(
|
|
||||||
"SELECT balance FROM {} WHERE address = $1",
|
|
||||||
self.database_table
|
|
||||||
),
|
|
||||||
&[&address],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(row.map(|r| r.get(0)).unwrap_or(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_snapshot(&self, snapshot_path: &str) -> Result<(), Box<dyn Error>> {
|
|
||||||
let start_time = Instant::now();
|
|
||||||
let mut client = self.connect()?;
|
|
||||||
let file = File::open(snapshot_path)?;
|
|
||||||
let file_size = file.metadata()?.len();
|
|
||||||
let index_name = format!("{}__address__ix", self.database_table);
|
|
||||||
let multi_progress = MultiProgress::new();
|
|
||||||
|
|
||||||
// 1. Preparing database
|
|
||||||
let prep_pb = multi_progress.add(ProgressBar::new_spinner());
|
|
||||||
prep_pb.set_style(
|
|
||||||
ProgressStyle::default_spinner()
|
|
||||||
.tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ")
|
|
||||||
.template("{spinner} {msg}").unwrap()
|
|
||||||
);
|
|
||||||
prep_pb.set_message("⚙️ Preparing database...");
|
|
||||||
prep_pb.enable_steady_tick(Duration::from_millis(100));
|
|
||||||
|
|
||||||
client.execute("SET synchronous_commit = off", &[])?;
|
|
||||||
client.execute("SET maintenance_work_mem = '4GB'", &[])?;
|
|
||||||
client.execute("SET work_mem = '2GB'", &[])?;
|
|
||||||
client.execute(
|
|
||||||
&format!("DROP TABLE IF EXISTS {}", self.database_table),
|
|
||||||
&[],
|
|
||||||
)?;
|
|
||||||
client.execute(
|
|
||||||
&format!("DROP INDEX IF EXISTS {}", index_name),
|
|
||||||
&[],
|
|
||||||
)?;
|
|
||||||
client.execute(
|
|
||||||
&format!(
|
|
||||||
"CREATE TABLE {} (address TEXT, balance BIGINT)",
|
|
||||||
self.database_table
|
|
||||||
),
|
|
||||||
&[],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
prep_pb.finish_with_message("✅ Database prepared");
|
|
||||||
|
|
||||||
// 2. Copy data to database
|
|
||||||
let copy_pb = multi_progress.add(ProgressBar::new(file_size));
|
|
||||||
copy_pb.set_style(ProgressStyle::default_bar()
|
|
||||||
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta}) | {binary_bytes_per_sec}")
|
|
||||||
.unwrap()
|
|
||||||
.progress_chars("#>-"));
|
|
||||||
copy_pb.set_message("️📥 Copy data to database...");
|
|
||||||
|
|
||||||
let copy_stmt = format!(
|
|
||||||
"COPY {} FROM STDIN WITH (FORMAT csv, DELIMITER E'\t', HEADER)",
|
|
||||||
self.database_table,
|
|
||||||
);
|
|
||||||
let mut writer = client.copy_in(©_stmt)?;
|
|
||||||
|
|
||||||
let file = File::open(snapshot_path)?;
|
|
||||||
let mut reader = BufReader::new(file);
|
|
||||||
let mut buffer = [0u8; 65536]; // 64KB буфер
|
|
||||||
let mut total_bytes = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let bytes_read = reader.read(&mut buffer)?;
|
|
||||||
if bytes_read == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.write_all(&buffer[..bytes_read])?;
|
|
||||||
total_bytes += bytes_read as u64;
|
|
||||||
copy_pb.set_position(total_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.finish()?;
|
|
||||||
copy_pb.set_position(file_size);
|
|
||||||
copy_pb.finish_with_message("✅ Data copied to database");
|
|
||||||
|
|
||||||
// 3. Creating index
|
|
||||||
let index_pb = multi_progress.add(ProgressBar::new_spinner());
|
|
||||||
index_pb.set_message("🔀 Creating index...");
|
|
||||||
index_pb.enable_steady_tick(Duration::from_millis(100));
|
|
||||||
|
|
||||||
client.execute(
|
|
||||||
&format!(
|
|
||||||
"CREATE INDEX {} ON {} USING HASH (address)",
|
|
||||||
index_name,
|
|
||||||
self.database_table,
|
|
||||||
),
|
|
||||||
&[],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
index_pb.finish_with_message("✅ Index created");
|
|
||||||
|
|
||||||
// 4. Reseting temporary settings
|
|
||||||
let final_pb = multi_progress.add(ProgressBar::new_spinner());
|
|
||||||
final_pb.set_message("🔁 Reset database settings");
|
|
||||||
final_pb.enable_steady_tick(Duration::from_millis(100));
|
|
||||||
|
|
||||||
client.execute("RESET synchronous_commit", &[])?;
|
|
||||||
client.execute("RESET maintenance_work_mem", &[])?;
|
|
||||||
client.execute("RESET work_mem", &[])?;
|
|
||||||
|
|
||||||
final_pb.finish_with_message("✅ Database settings reseted");
|
|
||||||
|
|
||||||
// 5. Final
|
|
||||||
multi_progress.clear()?;
|
|
||||||
|
|
||||||
let records_count: i64 = client.query_one(
|
|
||||||
&format!(
|
|
||||||
"SELECT COUNT(*) FROM {}",
|
|
||||||
self.database_table,
|
|
||||||
),
|
|
||||||
&[],
|
|
||||||
).map(|row| row.get(0))?;
|
|
||||||
|
|
||||||
let data_size: f64 = file_size as f64 / (1024 * 1024 * 1024) as f64;
|
|
||||||
println!("\n🎉 Snapshot loaded successfully!");
|
|
||||||
println!("📊 Statistics:");
|
|
||||||
println!(" Total records processed: {}", records_count);
|
|
||||||
println!(" Data size: {:.2} GB", data_size);
|
|
||||||
println!(" Execution time: {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
pub mod bitcoin;
|
|
||||||
|
|
||||||
use serde_yaml::Value;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
pub trait Network: Send + Sync {
|
|
||||||
fn generate_keypair(&self) -> (String, String);
|
|
||||||
fn check_balance(&self, address: &str) -> Result<i64, Box<dyn Error>>;
|
|
||||||
// TODO: Remove
|
|
||||||
fn load_snapshot(&self, snapshot_path: &str) -> Result<(), Box<dyn Error>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait SnapshotLoader {
|
|
||||||
fn load_snapshot(&self, snapshot_path: &str, format: Option<&str>) -> Result<(), Box<dyn Error>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_network(
|
|
||||||
name: &str,
|
|
||||||
params: &HashMap<String, Value>,
|
|
||||||
) -> Result<Box<dyn Network>, Box<dyn Error>> {
|
|
||||||
match name {
|
|
||||||
"bitcoin" => Ok(Box::new(bitcoin::Bitcoin::new(params)?)),
|
|
||||||
_ => Err(format!("Unsupported network: {}", name).into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_snapshot_loader(
|
|
||||||
name: &str,
|
|
||||||
params: &HashMap<String, Value>,
|
|
||||||
) -> Result<Box<dyn SnapshotLoader>, Box<dyn Error>> {
|
|
||||||
match name {
|
|
||||||
_ => Err(format!("Unsupported network: {}", name).into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
use reqwest::blocking::Client;
|
use reqwest::blocking::Client;
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
pub fn send_telegram_message(
|
use crate::utils::DynError;
|
||||||
|
|
||||||
|
pub async fn send_telegram_message(
|
||||||
bot_token: &str,
|
bot_token: &str,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
message: &str,
|
message: &str,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), DynError> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"https://api.telegram.org/bot{}/sendMessage",
|
"https://api.telegram.org/bot{}/sendMessage",
|
||||||
bot_token
|
bot_token
|
||||||
|
|||||||
52
src/settings.rs
Normal file
52
src/settings.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use config::{Config, File, Value};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::thread::available_parallelism;
|
||||||
|
|
||||||
|
use crate::blockchains::Blockchain;
|
||||||
|
use crate::key_generators::KeyAlgorithm;
|
||||||
|
use crate::utils::DynError;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub key_generators: HashMap<KeyAlgorithm, KeyGeneratorSettings>,
|
||||||
|
pub blockchains: HashMap<Blockchain, BlockchainSettings>,
|
||||||
|
pub notifications: NotificationSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct KeyGeneratorSettings {
|
||||||
|
#[serde(default = "default_workers")]
|
||||||
|
pub workers: usize,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub data: HashMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct BlockchainSettings {
|
||||||
|
#[serde(default = "default_workers")]
|
||||||
|
pub workers: usize,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub data: HashMap<String, Value>,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_workers() -> usize {
|
||||||
|
available_parallelism().unwrap().get()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct NotificationSettings {
|
||||||
|
pub telegram_bot_token: String,
|
||||||
|
pub telegram_user_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn load(path: &str) -> Result<Self, DynError> {
|
||||||
|
Config::builder()
|
||||||
|
.add_source(File::with_name(path).required(false))
|
||||||
|
.build()?
|
||||||
|
.try_deserialize()
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/utils.rs
Normal file
3
src/utils.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub type DynError = Box<dyn Error + Send + Sync>;
|
||||||
Reference in New Issue
Block a user