1
0
mirror of https://github.com/romanz/electrs.git synced 2024-11-19 09:54:09 +01:00

merge upstream for supporting testnet4

This commit is contained in:
oneforalone 2024-11-11 16:11:13 +08:00
commit 3c40771e7f
No known key found for this signature in database
GPG Key ID: E68BD83DAE6C37B5
19 changed files with 596 additions and 279 deletions

316
Cargo.lock generated
View File

@ -13,9 +13,15 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.76" version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]] [[package]]
name = "ascii" name = "ascii"
@ -29,6 +35,16 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base58ck"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f"
dependencies = [
"bitcoin-internals",
"bitcoin_hashes",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.13.1" version = "0.13.1"
@ -37,9 +53,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "bech32" name = "bech32"
version = "0.10.0-beta" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d"
[[package]] [[package]]
name = "bindgen" name = "bindgen"
@ -63,12 +79,15 @@ dependencies = [
[[package]] [[package]]
name = "bitcoin" name = "bitcoin"
version = "0.31.1" version = "0.32.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd00f3c09b5f21fb357abe32d29946eb8bb7a0862bae62c0b5e4a692acbbe73c" checksum = "788902099d47c8682efe6a7afb01c8d58b9794ba66c06affd81c3d6b560743eb"
dependencies = [ dependencies = [
"base58ck",
"bech32", "bech32",
"bitcoin-internals", "bitcoin-internals",
"bitcoin-io",
"bitcoin-units",
"bitcoin_hashes", "bitcoin_hashes",
"hex-conservative", "hex-conservative",
"hex_lit", "hex_lit",
@ -78,13 +97,19 @@ dependencies = [
[[package]] [[package]]
name = "bitcoin-internals" name = "bitcoin-internals"
version = "0.2.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 = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "bitcoin-io"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56"
[[package]] [[package]]
name = "bitcoin-test-data" name = "bitcoin-test-data"
version = "0.2.0" version = "0.2.0"
@ -92,21 +117,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c188654f9dce3bc6ce1bfa9c49777ad514bcad37e421b5f53e9d0ee10603f34" checksum = "0c188654f9dce3bc6ce1bfa9c49777ad514bcad37e421b5f53e9d0ee10603f34"
[[package]] [[package]]
name = "bitcoin_hashes" name = "bitcoin-units"
version = "0.13.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" checksum = "cb54da0b28892f3c52203a7191534033e051b6f4b52bc15480681b57b7e036f5"
dependencies = [ dependencies = [
"bitcoin-internals", "bitcoin-internals",
"serde",
]
[[package]]
name = "bitcoin_hashes"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
dependencies = [
"bitcoin-io",
"hex-conservative", "hex-conservative",
"serde", "serde",
] ]
[[package]] [[package]]
name = "bitcoin_slices" name = "bitcoin_slices"
version = "0.7.0" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b221249ba4685e0f737f0df2c7bb656d527fdd8ede83ac066c4c8ce95898f42" checksum = "b0c2d2aa6c95757d94701123e5da8392fef1a4b8462564045d9309a8e11b0d22"
dependencies = [ dependencies = [
"bitcoin", "bitcoin",
"bitcoin_hashes", "bitcoin_hashes",
@ -115,9 +150,9 @@ dependencies = [
[[package]] [[package]]
name = "bitcoincore-rpc" name = "bitcoincore-rpc"
version = "0.18.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eb70725a621848c83b3809913d5314c0d20ca84877d99dd909504b564edab00" checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee"
dependencies = [ dependencies = [
"bitcoincore-rpc-json", "bitcoincore-rpc-json",
"jsonrpc", "jsonrpc",
@ -128,9 +163,9 @@ dependencies = [
[[package]] [[package]]
name = "bitcoincore-rpc-json" name = "bitcoincore-rpc-json"
version = "0.18.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "856ffbee2e492c23bca715d72ea34aae80d58400f2bda26a82015d6bc2ec3662" checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a"
dependencies = [ dependencies = [
"bitcoin", "bitcoin",
"serde", "serde",
@ -177,12 +212,12 @@ dependencies = [
[[package]] [[package]]
name = "cargo_toml" name = "cargo_toml"
version = "0.12.4" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a621d5d6d6c8d086dbaf1fe659981da41a1b63c6bdbba30b4dbb592c6d3bd49" checksum = "1521c5948ab432e084eabee0c9e4e965483f73156eaa0b04fc192e3f61205438"
dependencies = [ dependencies = [
"serde", "serde",
"toml", "toml 0.7.1",
] ]
[[package]] [[package]]
@ -236,20 +271,20 @@ dependencies = [
"parse_arg", "parse_arg",
"serde", "serde",
"serde_derive", "serde_derive",
"toml", "toml 0.5.11",
] ]
[[package]] [[package]]
name = "configure_me_codegen" name = "configure_me_codegen"
version = "0.4.4" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad4bfdee5b1410b1d3b4239172e6f4573d2d47a14a11cd397dac083233dbe3e3" checksum = "5e56840275667a19b0e8ab80219c81fb0bd924e567366d9f12aa385fb45511ea"
dependencies = [ dependencies = [
"cargo_toml", "cargo_toml",
"fmt2io", "fmt2io",
"serde", "serde",
"serde_derive", "serde_derive",
"toml", "toml 0.5.11",
"unicode-segmentation", "unicode-segmentation",
"void", "void",
] ]
@ -265,9 +300,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.11" version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
@ -353,7 +388,7 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]] [[package]]
name = "electrs" name = "electrs"
version = "0.10.2" version = "0.10.7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitcoin", "bitcoin",
@ -429,15 +464,15 @@ dependencies = [
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.0.1" version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]] [[package]]
name = "fmt2io" name = "fmt2io"
version = "0.1.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9db8691f0820ad11ce6eb94057d0dd9c456500da04da0c12a85c90d6f979cc9" checksum = "6b6129284da9f7e5296cc22183a63f24300e945e297705dcc0672f7df01d62c8"
[[package]] [[package]]
name = "fnv" name = "fnv"
@ -472,6 +507,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.3" version = "0.3.3"
@ -486,9 +527,12 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hex-conservative" name = "hex-conservative"
version = "0.1.1" 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 = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" checksum = "e1aa273bf451e37ed35ced41c71a5e2a4e29064afb104158f2514bcd71c2c986"
dependencies = [
"arrayvec",
]
[[package]] [[package]]
name = "hex_lit" name = "hex_lit"
@ -508,6 +552,16 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.11" version = "1.0.11"
@ -526,7 +580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"rustix 0.38.28", "rustix 0.38.37",
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
@ -547,11 +601,12 @@ dependencies = [
[[package]] [[package]]
name = "jsonrpc" name = "jsonrpc"
version = "0.14.1" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8128f36b47411cd3f044be8c1f5cc0c9e24d1d1bfdc45f0a57897b32513053f2" checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf"
dependencies = [ dependencies = [
"base64", "base64",
"minreq",
"serde", "serde",
"serde_json", "serde_json",
] ]
@ -570,9 +625,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.151" version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]] [[package]]
name = "libloading" name = "libloading"
@ -614,9 +669,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.12" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@ -630,9 +685,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.20" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]] [[package]]
name = "memchr" name = "memchr"
@ -655,6 +710,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "minreq"
version = "2.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fdef521c74c2884a4f3570bcdb6d2a77b3c533feb6b27ac2ae72673cc221c64"
dependencies = [
"log",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -665,6 +731,21 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "nom8"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.1" version = "0.12.1"
@ -798,9 +879,9 @@ dependencies = [
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.8.1" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [ dependencies = [
"either", "either",
"rayon-core", "rayon-core",
@ -887,14 +968,14 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.28" version = "0.38.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
dependencies = [ dependencies = [
"bitflags 2.4.1", "bitflags 2.4.1",
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.4.12", "linux-raw-sys 0.4.14",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
@ -912,9 +993,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "secp256k1" name = "secp256k1"
version = "0.28.0" version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3"
dependencies = [ dependencies = [
"bitcoin_hashes", "bitcoin_hashes",
"rand", "rand",
@ -924,9 +1005,9 @@ dependencies = [
[[package]] [[package]]
name = "secp256k1-sys" name = "secp256k1-sys"
version = "0.9.1" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b"
dependencies = [ dependencies = [
"cc", "cc",
] ]
@ -962,6 +1043,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.8" version = "0.10.8"
@ -1000,9 +1090,9 @@ dependencies = [
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.11.2" version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]] [[package]]
name = "syn" name = "syn"
@ -1028,40 +1118,40 @@ dependencies = [
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.9.0" version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"fastrand", "fastrand",
"redox_syscall", "once_cell",
"rustix 0.38.28", "rustix 0.38.37",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.4.0" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.51" version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.51" version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1089,6 +1179,40 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "toml"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "772c1426ab886e7362aedf4abc9c0d1348a979517efedfc25862944d10137af0"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90a238ee2e6ede22fb95350acc78e21dc40da00bb66c0334bde83de4ed89424e"
dependencies = [
"indexmap",
"nom8",
"serde",
"serde_spanned",
"toml_datetime",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"
@ -1186,7 +1310,16 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets 0.52.0", "windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -1221,17 +1354,18 @@ dependencies = [
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.52.0", "windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.0", "windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.0", "windows_i686_gnu 0.52.6",
"windows_i686_msvc 0.52.0", "windows_i686_gnullvm",
"windows_x86_64_gnu 0.52.0", "windows_i686_msvc 0.52.6",
"windows_x86_64_gnullvm 0.52.0", "windows_x86_64_gnu 0.52.6",
"windows_x86_64_msvc 0.52.0", "windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
] ]
[[package]] [[package]]
@ -1248,9 +1382,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
@ -1266,9 +1400,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
@ -1284,9 +1418,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
@ -1302,9 +1442,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
@ -1320,9 +1460,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
@ -1338,9 +1478,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
@ -1356,9 +1496,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "zstd-sys" name = "zstd-sys"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "electrs" name = "electrs"
version = "0.10.2" version = "0.10.7"
authors = ["Roman Zeyde <me@romanzey.de>"] authors = ["Roman Zeyde <me@romanzey.de>"]
description = "An efficient re-implementation of Electrum Server in Rust" description = "An efficient re-implementation of Electrum Server in Rust"
license = "MIT" license = "MIT"
@ -22,9 +22,9 @@ spec = "internal/config_specification.toml"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
bitcoin = { version = "0.31.1", features = ["serde", "rand-std"] } bitcoin = { version = "0.32.4", features = ["serde", "rand-std"] }
bitcoin_slices = { version = "0.7", features = ["bitcoin", "sha2"] } bitcoin_slices = { version = "0.9", features = ["bitcoin", "sha2"] }
bitcoincore-rpc = { version = "0.18" } bitcoincore-rpc = { version = "0.19.0" }
configure_me = "0.4" configure_me = "0.4"
crossbeam-channel = "0.5" crossbeam-channel = "0.5"
dirs-next = "2.0" dirs-next = "2.0"
@ -32,7 +32,7 @@ env_logger = "0.10"
log = "0.4" log = "0.4"
parking_lot = "0.12" parking_lot = "0.12"
prometheus = { version = "0.13", optional = true } prometheus = { version = "0.13", optional = true }
rayon = "1.8" rayon = "1.9"
serde = "1.0" serde = "1.0"
serde_derive = "1.0, <=1.0.171" # avoid precompiled binaries (https://github.com/serde-rs/serde/issues/2538) serde_derive = "1.0, <=1.0.171" # avoid precompiled binaries (https://github.com/serde-rs/serde/issues/2538)
serde_json = "1.0" serde_json = "1.0"
@ -48,9 +48,12 @@ default-features = false
features = ["zstd", "snappy"] features = ["zstd", "snappy"]
[build-dependencies] [build-dependencies]
configure_me_codegen = { version = "0.4.4", default-features = false } configure_me_codegen = { version = "0.4.8", default-features = false }
[dev-dependencies] [dev-dependencies]
bitcoin-test-data = "0.2.0" bitcoin-test-data = "0.2.0"
hex_lit = "0.1.1" hex_lit = "0.1.1"
tempfile = "3.9" tempfile = "3.13"
[profile.release]
lto = true

View File

@ -2,12 +2,12 @@
# The maintainers of electrs are not deeply familiar with Docker, so you should DYOR. # The maintainers of electrs are not deeply familiar with Docker, so you should DYOR.
# If you are not familiar with Docker either it's probably be safer to NOT use it. # If you are not familiar with Docker either it's probably be safer to NOT use it.
FROM debian:bookworm-slim as base FROM debian:bookworm-slim AS base
RUN apt-get update -qqy RUN apt-get update -qqy
RUN apt-get install -qqy librocksdb-dev curl RUN apt-get install -qqy librocksdb-dev curl
### Electrum Rust Server ### ### Electrum Rust Server ###
FROM base as electrs-build FROM base AS electrs-build
RUN apt-get install -qqy cargo clang cmake RUN apt-get install -qqy cargo clang cmake
# Install electrs # Install electrs
@ -17,7 +17,7 @@ ENV ROCKSDB_INCLUDE_DIR=/usr/include
ENV ROCKSDB_LIB_DIR=/usr/lib ENV ROCKSDB_LIB_DIR=/usr/lib
RUN cargo install --locked --path . RUN cargo install --locked --path .
FROM base as result FROM base AS result
# Copy the binaries # Copy the binaries
COPY --from=electrs-build /root/.cargo/bin/electrs /usr/bin/electrs COPY --from=electrs-build /root/.cargo/bin/electrs /usr/bin/electrs

View File

@ -22,7 +22,7 @@ FROM base as bitcoin-build
# Download # Download
WORKDIR /build/bitcoin WORKDIR /build/bitcoin
ARG ARCH=x86_64 ARG ARCH=x86_64
ARG BITCOIND_VERSION=26.0 ARG BITCOIND_VERSION=28.0
RUN wget -q https://bitcoincore.org/bin/bitcoin-core-$BITCOIND_VERSION/bitcoin-$BITCOIND_VERSION-$ARCH-linux-gnu.tar.gz RUN wget -q https://bitcoincore.org/bin/bitcoin-core-$BITCOIND_VERSION/bitcoin-$BITCOIND_VERSION-$ARCH-linux-gnu.tar.gz
RUN tar xvf bitcoin-$BITCOIND_VERSION-$ARCH-linux-gnu.tar.gz RUN tar xvf bitcoin-$BITCOIND_VERSION-$ARCH-linux-gnu.tar.gz
RUN mv -v bitcoin-$BITCOIND_VERSION/bin/bitcoind . RUN mv -v bitcoin-$BITCOIND_VERSION/bin/bitcoind .
@ -40,7 +40,7 @@ WORKDIR /build/
RUN apt-get install -qqy git libsecp256k1-1 python3-cryptography python3-setuptools python3-venv python3-pip jq curl RUN apt-get install -qqy git libsecp256k1-1 python3-cryptography python3-setuptools python3-venv python3-pip jq curl
RUN git clone --recurse-submodules https://github.com/spesmilo/electrum/ && cd electrum/ && git log -1 RUN git clone --recurse-submodules https://github.com/spesmilo/electrum/ && cd electrum/ && git log -1
RUN python3 -m venv --system-site-packages venv && \ RUN python3 -m venv --system-site-packages venv && \
venv/bin/pip install -e electrum/ && \ ELECTRUM_ECC_DONT_COMPILE=1 venv/bin/pip install -e electrum/ && \
ln /build/venv/bin/electrum /usr/bin/electrum ln /build/venv/bin/electrum /usr/bin/electrum
RUN electrum version --offline RUN electrum version --offline

View File

@ -16,6 +16,8 @@ allowing the user to keep real-time track of balances and transaction history us
Since it runs on the user's own machine, there is no need for the wallet to communicate with external Electrum servers, Since it runs on the user's own machine, there is no need for the wallet to communicate with external Electrum servers,
thus preserving the privacy of the user's addresses and balances. thus preserving the privacy of the user's addresses and balances.
[BTC Prague 2024 dev/hack/day](https://btcprague.com/dev-hack-day/) slides are here: https://bit.ly/electrs
## Usage ## Usage
**Please prefer to use OUR usage guide!** **Please prefer to use OUR usage guide!**

View File

@ -1,3 +1,33 @@
# 0.10.7 (Nov 05 2024)
* Support testnet4
* Enable LTO in release build
* Don't sync mempool when bitcoind mempool is not yet loaded
* Update dependencies (`bitcoin`, `bitcoin_slices`)
# 0.10.6 (Sep 29 2024)
* Update dependencies (`bitcoin`, `configure_me_codegen`, `crossbeam-channel`, `log`)
* Deprecate unused config option `timestamp`
* Don't fail if bitcoind fee estimation is disabled
* Save on allocations by using fixed size types for database rows
* Add BTC Prague 2024 dev/hack/day slides
# 0.10.5 (May 18 2024)
* Update dependencies (`bitcoin`, `bitcoin_slices`, `bitcoincore-rpc`, `rayon`)
* Support latest bitcoind (https://github.com/rust-bitcoin/rust-bitcoincore-rpc/pull/353 & https://github.com/rust-bitcoin/rust-bitcoincore-rpc/pull/356)
# 0.10.4 (Mar 15 2024)
* Don't fail mempool sync on missing transactions (#997)
* Update dependencies (`anyhow`, `crossbeam-channel`, `log`, `secp256k1`, `secp256k1-sys`, `smallvec`, `tempfile`, `termcolor`, `thiserror`, `thiserror-impl`)
# 0.10.3 (Feb 10 2024)
* Update dependencies (`serde_json`, `tempfile`, `env_logger`, `rayon`, `bitcoin`, `crossbeam-channel`, `shlex`)
* Fix build on Debian 12 (#1001)
# 0.10.2 (Dec 31 2023) # 0.10.2 (Dec 31 2023)
* Use batched RPC to fetch mempool entries & transactions (#979) * Use batched RPC to fetch mempool entries & transactions (#979)

View File

@ -206,6 +206,11 @@ Relevant issues: [#134](https://github.com/romanz/electrs/issues/134) and [#391]
#### Dynamic linking #### Dynamic linking
Note that if you have previously done a static linking build, it is recommended to clean the build artifacts to avoid build errors (e.g. https://github.com/romanz/electrs/issues/1001):
```
$ cargo clean
```
``` ```
$ ROCKSDB_INCLUDE_DIR=/usr/include ROCKSDB_LIB_DIR=/usr/lib cargo build --locked --release $ ROCKSDB_INCLUDE_DIR=/usr/include ROCKSDB_LIB_DIR=/usr/lib cargo build --locked --release
``` ```

View File

@ -2,7 +2,7 @@ use anyhow::{Context, Result};
use electrs_rocksdb::{ColumnFamilyDescriptor, IteratorMode, Options, DB}; use electrs_rocksdb::{ColumnFamilyDescriptor, IteratorMode, Options, DB};
fn main() -> Result<()> { fn main() -> Result<()> {
let path = std::env::args().skip(1).next().context("missing DB path")?; let path = std::env::args().nth(1).context("missing DB path")?;
let cf_names = DB::list_cf(&Options::default(), &path)?; let cf_names = DB::list_cf(&Options::default(), &path)?;
let cfs: Vec<_> = cf_names let cfs: Vec<_> = cf_names
.iter() .iter()

View File

@ -58,7 +58,7 @@ doc = "JSONRPC authentication cookie file (default: ~/.bitcoin/.cookie)"
name = "network" name = "network"
type = "crate::config::BitcoinNetwork" type = "crate::config::BitcoinNetwork"
convert_into = "::bitcoin::Network" convert_into = "::bitcoin::Network"
doc = "Select Bitcoin network type ('bitcoin', 'testnet', 'regtest' or 'signet')" doc = "Select Bitcoin network type ('bitcoin', 'testnet', 'testnet4', 'regtest' or 'signet')"
default = "Default::default()" default = "Default::default()"
[[param]] [[param]]

View File

@ -57,11 +57,11 @@ impl Chain {
} }
/// Load the chain from a collection of headers, up to the given tip /// Load the chain from a collection of headers, up to the given tip
pub(crate) fn load(&mut self, headers: Vec<BlockHeader>, tip: BlockHash) { pub(crate) fn load(&mut self, headers: impl Iterator<Item = BlockHeader>, tip: BlockHash) {
let genesis_hash = self.headers[0].0; let genesis_hash = self.headers[0].0;
let header_map: HashMap<BlockHash, BlockHeader> = let header_map: HashMap<BlockHash, BlockHeader> =
headers.into_iter().map(|h| (h.block_hash(), h)).collect(); headers.map(|h| (h.block_hash(), h)).collect();
let mut blockhash = tip; let mut blockhash = tip;
let mut new_headers: Vec<&BlockHeader> = Vec::with_capacity(header_map.len()); let mut new_headers: Vec<&BlockHeader> = Vec::with_capacity(header_map.len());
while blockhash != genesis_hash { while blockhash != genesis_hash {
@ -202,7 +202,10 @@ hex!("000000200030d7f9c11ef35b89a0eefb9a5e449909339b5e7854d99804ea8d6a49bf900a03
// test loading from a list of headers and tip // test loading from a list of headers and tip
let mut regtest = Chain::new(Regtest); let mut regtest = Chain::new(Regtest);
regtest.load(headers.clone(), headers.last().unwrap().block_hash()); regtest.load(
headers.iter().copied(),
headers.last().unwrap().block_hash(),
);
assert_eq!(regtest.height(), headers.len()); assert_eq!(regtest.height(), headers.len());
// test getters // test getters
@ -239,7 +242,10 @@ hex!("000000200030d7f9c11ef35b89a0eefb9a5e449909339b5e7854d99804ea8d6a49bf900a03
// test reorg // test reorg
let mut regtest = Chain::new(Regtest); let mut regtest = Chain::new(Regtest);
regtest.load(headers.clone(), headers.last().unwrap().block_hash()); regtest.load(
headers.iter().copied(),
headers.last().unwrap().block_hash(),
);
let height = regtest.height(); let height = regtest.height();
let new_header: BlockHeader = deserialize(&hex!("000000200030d7f9c11ef35b89a0eefb9a5e449909339b5e7854d99804ea8d6a49bf900a0304d2e55fe0b6415949cff9bca0f88c0717884a5e5797509f89f856af93624a7a6bcc60ffff7f2000000000")).unwrap(); let new_header: BlockHeader = deserialize(&hex!("000000200030d7f9c11ef35b89a0eefb9a5e449909339b5e7854d99804ea8d6a49bf900a0304d2e55fe0b6415949cff9bca0f88c0717884a5e5797509f89f856af93624a7a6bcc60ffff7f2000000000")).unwrap();

View File

@ -111,7 +111,10 @@ impl FromStr for BitcoinNetwork {
impl ::configure_me::parse_arg::ParseArgFromStr for BitcoinNetwork { impl ::configure_me::parse_arg::ParseArgFromStr for BitcoinNetwork {
fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result { fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
write!(writer, "either 'bitcoin', 'testnet', 'regtest' or 'signet'") write!(
writer,
"either 'bitcoin', 'testnet', 'testnet4', 'regtest' or 'signet'"
)
} }
} }
@ -128,7 +131,6 @@ pub struct Config {
pub network: Network, pub network: Network,
pub db_path: PathBuf, pub db_path: PathBuf,
pub db_log_dir: Option<PathBuf>, pub db_log_dir: Option<PathBuf>,
pub daemon_dir: PathBuf,
pub daemon_auth: SensitiveAuth, pub daemon_auth: SensitiveAuth,
pub daemon_rpc_addr: SocketAddr, pub daemon_rpc_addr: SocketAddr,
pub daemon_p2p_addr: SocketAddr, pub daemon_p2p_addr: SocketAddr,
@ -146,7 +148,6 @@ pub struct Config {
pub disable_electrum_rpc: bool, pub disable_electrum_rpc: bool,
pub server_banner: String, pub server_banner: String,
pub signet_magic: Magic, pub signet_magic: Magic,
pub args: Vec<String>,
} }
pub struct SensitiveAuth(pub Auth); pub struct SensitiveAuth(pub Auth);
@ -183,7 +184,7 @@ fn default_daemon_dir() -> PathBuf {
fn default_config_files() -> Vec<OsString> { fn default_config_files() -> Vec<OsString> {
let mut files = vec![OsString::from("electrs.toml")]; // cwd let mut files = vec![OsString::from("electrs.toml")]; // cwd
if let Some(mut path) = home_dir() { if let Some(mut path) = home_dir() {
path.extend(&[".electrs", "config.toml"]); path.extend([".electrs", "config.toml"]);
files.push(OsString::from(path)) // home directory files.push(OsString::from(path)) // home directory
} }
files.push(OsString::from("/etc/electrs/config.toml")); // system-wide files.push(OsString::from("/etc/electrs/config.toml")); // system-wide
@ -195,7 +196,7 @@ impl Config {
pub fn from_args() -> Config { pub fn from_args() -> Config {
use internal::ResultExt; use internal::ResultExt;
let (mut config, args) = let (mut config, _args) =
internal::Config::including_optional_config_files(default_config_files()) internal::Config::including_optional_config_files(default_config_files())
.unwrap_or_exit(); .unwrap_or_exit();
@ -207,6 +208,7 @@ impl Config {
let db_subdir = match config.network { let db_subdir = match config.network {
Network::Bitcoin => "bitcoin", Network::Bitcoin => "bitcoin",
Network::Testnet => "testnet", Network::Testnet => "testnet",
Network::Testnet4 => "testnet4",
Network::Regtest => "regtest", Network::Regtest => "regtest",
Network::Signet => "signet", Network::Signet => "signet",
unsupported => unsupported_network(unsupported), unsupported => unsupported_network(unsupported),
@ -217,6 +219,7 @@ impl Config {
let default_daemon_rpc_port = match config.network { let default_daemon_rpc_port = match config.network {
Network::Bitcoin => 8332, Network::Bitcoin => 8332,
Network::Testnet => 18332, Network::Testnet => 18332,
Network::Testnet4 => 48332,
Network::Regtest => 18443, Network::Regtest => 18443,
Network::Signet => 38332, Network::Signet => 38332,
unsupported => unsupported_network(unsupported), unsupported => unsupported_network(unsupported),
@ -224,6 +227,7 @@ impl Config {
let default_daemon_p2p_port = match config.network { let default_daemon_p2p_port = match config.network {
Network::Bitcoin => 8333, Network::Bitcoin => 8333,
Network::Testnet => 18333, Network::Testnet => 18333,
Network::Testnet4 => 48333,
Network::Regtest => 18444, Network::Regtest => 18444,
Network::Signet => 38333, Network::Signet => 38333,
unsupported => unsupported_network(unsupported), unsupported => unsupported_network(unsupported),
@ -231,6 +235,7 @@ impl Config {
let default_electrum_port = match config.network { let default_electrum_port = match config.network {
Network::Bitcoin => 50001, Network::Bitcoin => 50001,
Network::Testnet => 60001, Network::Testnet => 60001,
Network::Testnet4 => 40001,
Network::Regtest => 60401, Network::Regtest => 60401,
Network::Signet => 60601, Network::Signet => 60601,
unsupported => unsupported_network(unsupported), unsupported => unsupported_network(unsupported),
@ -238,6 +243,7 @@ impl Config {
let default_monitoring_port = match config.network { let default_monitoring_port = match config.network {
Network::Bitcoin => 4224, Network::Bitcoin => 4224,
Network::Testnet => 14224, Network::Testnet => 14224,
Network::Testnet4 => 44224,
Network::Regtest => 24224, Network::Regtest => 24224,
Network::Signet => 34224, Network::Signet => 34224,
unsupported => unsupported_network(unsupported), unsupported => unsupported_network(unsupported),
@ -285,11 +291,31 @@ impl Config {
match config.network { match config.network {
Network::Bitcoin => (), Network::Bitcoin => (),
Network::Testnet => config.daemon_dir.push("testnet3"), Network::Testnet => config.daemon_dir.push("testnet3"),
Network::Testnet4 => config.daemon_dir.push("testnet4"),
Network::Regtest => config.daemon_dir.push("regtest"), Network::Regtest => config.daemon_dir.push("regtest"),
Network::Signet => config.daemon_dir.push("signet"), Network::Signet => config.daemon_dir.push("signet"),
unsupported => unsupported_network(unsupported), unsupported => unsupported_network(unsupported),
} }
let mut deprecated_options_used = false;
if config.timestamp {
eprintln!(
"Error: `timestamp` is deprecated, timestamps on logs is (and was) always \
enabled, please remove this option."
);
deprecated_options_used = true;
}
if config.verbose > 0 {
eprintln!("Error: please use `log_filters` to set logging verbosity",);
deprecated_options_used = true;
}
if deprecated_options_used {
std::process::exit(1);
}
let daemon_dir = &config.daemon_dir; let daemon_dir = &config.daemon_dir;
let daemon_auth = SensitiveAuth(match (config.auth, config.cookie_file) { let daemon_auth = SensitiveAuth(match (config.auth, config.cookie_file) {
(None, None) => Auth::CookieFile(daemon_dir.join(".cookie")), (None, None) => Auth::CookieFile(daemon_dir.join(".cookie")),
@ -308,10 +334,6 @@ impl Config {
} }
}); });
if config.verbose > 0 {
eprintln!("Error: please use `log_filters` to set logging verbosity",);
std::process::exit(1);
}
let log_filters = config.log_filters; let log_filters = config.log_filters;
let index_lookup_limit = match config.index_lookup_limit { let index_lookup_limit = match config.index_lookup_limit {
@ -336,7 +358,6 @@ impl Config {
network: config.network, network: config.network,
db_path: config.db_dir, db_path: config.db_dir,
db_log_dir: config.db_log_dir, db_log_dir: config.db_log_dir,
daemon_dir: config.daemon_dir,
daemon_auth, daemon_auth,
daemon_rpc_addr, daemon_rpc_addr,
daemon_p2p_addr, daemon_p2p_addr,
@ -354,7 +375,6 @@ impl Config {
disable_electrum_rpc: config.disable_electrum_rpc, disable_electrum_rpc: config.disable_electrum_rpc,
server_banner: config.server_banner, server_banner: config.server_banner,
signet_magic: magic, signet_magic: magic,
args: args.map(|a| a.into_string().unwrap()).collect(),
}; };
eprintln!( eprintln!(
"Starting electrs {} on {} {} with {:?}", "Starting electrs {} on {} {} with {:?}",

View File

@ -5,7 +5,8 @@ use bitcoin::{Amount, BlockHash, Transaction, Txid};
use bitcoincore_rpc::{json, jsonrpc, Auth, Client, RpcApi}; use bitcoincore_rpc::{json, jsonrpc, Auth, Client, RpcApi};
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use parking_lot::Mutex; use parking_lot::Mutex;
use serde_json::{json, Value}; use serde::Serialize;
use serde_json::{json, value::RawValue, Value};
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
@ -147,11 +148,15 @@ impl Daemon {
} }
pub(crate) fn estimate_fee(&self, nblocks: u16) -> Result<Option<Amount>> { pub(crate) fn estimate_fee(&self, nblocks: u16) -> Result<Option<Amount>> {
Ok(self let res = self.rpc.estimate_smart_fee(nblocks, None);
.rpc if let Err(bitcoincore_rpc::Error::JsonRpc(jsonrpc::Error::Rpc(RpcError {
.estimate_smart_fee(nblocks, None) code: -32603,
.context("failed to estimate fee")? ..
.fee_rate) }))) = res
{
return Ok(None); // don't fail when fee estimation is disabled (e.g. with `-blocksonly=1`)
}
Ok(res.context("failed to estimate fee")?.fee_rate)
} }
pub(crate) fn get_relay_fee(&self) -> Result<Amount> { pub(crate) fn get_relay_fee(&self) -> Result<Amount> {
@ -214,6 +219,12 @@ impl Daemon {
.tx) .tx)
} }
pub(crate) fn get_mempool_info(&self) -> Result<json::GetMempoolInfoResult> {
self.rpc
.get_mempool_info()
.context("failed to get mempool info")
}
pub(crate) fn get_mempool_txids(&self) -> Result<Vec<Txid>> { pub(crate) fn get_mempool_txids(&self) -> Result<Vec<Txid>> {
self.rpc self.rpc
.get_raw_mempool() .get_raw_mempool()
@ -223,25 +234,16 @@ impl Daemon {
pub(crate) fn get_mempool_entries( pub(crate) fn get_mempool_entries(
&self, &self,
txids: &[Txid], txids: &[Txid],
) -> Result<Vec<Result<json::GetMempoolEntryResult>>> { ) -> Result<Vec<Option<json::GetMempoolEntryResult>>> {
let client = self.rpc.get_jsonrpc_client(); let results = batch_request(self.rpc.get_jsonrpc_client(), "getmempoolentry", txids)?;
debug!("getting {} mempool entries", txids.len()); Ok(results
let args: Vec<_> = txids
.iter()
.map(|txid| vec![serde_json::value::to_raw_value(txid).unwrap()])
.collect();
let reqs: Vec<_> = args
.iter()
.map(|a| client.build_request("getmempoolentry", a))
.collect();
let res = client.send_batch(&reqs).context("batch request failed")?;
debug!("got {} mempool entries", res.len());
Ok(res
.into_iter() .into_iter()
.map(|r| { .map(|r| match r?.result::<json::GetMempoolEntryResult>() {
r.context("missing response")? Ok(entry) => Some(entry),
.result::<json::GetMempoolEntryResult>() Err(err) => {
.context("invalid response") debug!("failed to get mempool entry: {}", err); // probably due to RBF
None
}
}) })
.collect()) .collect())
} }
@ -249,28 +251,32 @@ impl Daemon {
pub(crate) fn get_mempool_transactions( pub(crate) fn get_mempool_transactions(
&self, &self,
txids: &[Txid], txids: &[Txid],
) -> Result<Vec<Result<Transaction>>> { ) -> Result<Vec<Option<Transaction>>> {
let client = self.rpc.get_jsonrpc_client(); let results = batch_request(self.rpc.get_jsonrpc_client(), "getrawtransaction", txids)?;
debug!("getting {} transactions", txids.len()); Ok(results
let args: Vec<_> = txids
.iter()
.map(|txid| vec![serde_json::value::to_raw_value(txid).unwrap()])
.collect();
let reqs: Vec<_> = args
.iter()
.map(|a| client.build_request("getrawtransaction", a))
.collect();
let res = client.send_batch(&reqs).context("batch request failed")?;
debug!("got {} mempool transactions", res.len());
Ok(res
.into_iter() .into_iter()
.map(|r| -> Result<Transaction> { .map(|r| -> Option<Transaction> {
let tx_hex = r let tx_hex = match r?.result::<String>() {
.context("missing response")? Ok(tx_hex) => Some(tx_hex),
.result::<String>() Err(err) => {
.context("invalid response")?; debug!("failed to get mempool tx: {}", err); // probably due to RBF
let tx_bytes = Vec::from_hex(&tx_hex).context("non-hex transaction")?; None
deserialize(&tx_bytes).context("invalid transaction") }
}?;
let tx_bytes = match Vec::from_hex(&tx_hex) {
Ok(tx_bytes) => Some(tx_bytes),
Err(err) => {
warn!("got non-hex transaction {}: {}", tx_hex, err);
None
}
}?;
match deserialize(&tx_bytes) {
Ok(tx) => Some(tx),
Err(err) => {
warn!("got invalid tx {}: {}", tx_hex, err);
None
}
}
}) })
.collect()) .collect())
} }
@ -303,3 +309,29 @@ pub(crate) fn extract_bitcoind_error(err: &bitcoincore_rpc::Error) -> Option<&Rp
_ => None, _ => None,
} }
} }
fn batch_request<T>(
client: &jsonrpc::Client,
name: &str,
items: &[T],
) -> Result<Vec<Option<jsonrpc::Response>>>
where
T: Serialize,
{
debug!("calling {} on {} items", name, items.len());
let args: Vec<Box<RawValue>> = items
.iter()
.map(|item| jsonrpc::try_arg([item]).context("failed to serialize into JSON"))
.collect::<Result<Vec<_>>>()?;
let reqs: Vec<jsonrpc::Request> = args
.iter()
.map(|arg| client.build_request(name, Some(arg)))
.collect();
match client.send_batch(&reqs) {
Ok(values) => {
assert_eq!(items.len(), values.len());
Ok(values)
}
Err(err) => bail!("batch {} request failed: {}", name, err),
}
}

141
src/db.rs
View File

@ -4,15 +4,15 @@ use electrs_rocksdb as rocksdb;
use std::path::Path; use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
pub(crate) type Row = Box<[u8]>; use crate::types::{HashPrefix, SerializedHashPrefixRow, SerializedHeaderRow};
#[derive(Default)] #[derive(Default)]
pub(crate) struct WriteBatch { pub(crate) struct WriteBatch {
pub(crate) tip_row: Row, pub(crate) tip_row: [u8; 32],
pub(crate) header_rows: Vec<Row>, pub(crate) header_rows: Vec<SerializedHeaderRow>,
pub(crate) funding_rows: Vec<Row>, pub(crate) funding_rows: Vec<SerializedHashPrefixRow>,
pub(crate) spending_rows: Vec<Row>, pub(crate) spending_rows: Vec<SerializedHashPrefixRow>,
pub(crate) txid_rows: Vec<Row>, pub(crate) txid_rows: Vec<SerializedHashPrefixRow>,
} }
impl WriteBatch { impl WriteBatch {
@ -42,7 +42,7 @@ const CONFIG_KEY: &str = "C";
const TIP_KEY: &[u8] = b"T"; const TIP_KEY: &[u8] = b"T";
// Taken from https://github.com/facebook/rocksdb/blob/master/include/rocksdb/db.h#L654-L689 // Taken from https://github.com/facebook/rocksdb/blob/master/include/rocksdb/db.h#L654-L689
const DB_PROPERIES: &[&str] = &[ const DB_PROPERTIES: &[&str] = &[
"rocksdb.num-immutable-mem-table", "rocksdb.num-immutable-mem-table",
"rocksdb.mem-table-flush-pending", "rocksdb.mem-table-flush-pending",
"rocksdb.compaction-pending", "rocksdb.compaction-pending",
@ -218,39 +218,50 @@ impl DBStore {
self.db.cf_handle(HEADERS_CF).expect("missing HEADERS_CF") self.db.cf_handle(HEADERS_CF).expect("missing HEADERS_CF")
} }
pub(crate) fn iter_funding(&self, prefix: Row) -> impl Iterator<Item = Row> + '_ { pub(crate) fn iter_funding(
&self,
prefix: HashPrefix,
) -> impl Iterator<Item = SerializedHashPrefixRow> + '_ {
self.iter_prefix_cf(self.funding_cf(), prefix) self.iter_prefix_cf(self.funding_cf(), prefix)
} }
pub(crate) fn iter_spending(&self, prefix: Row) -> impl Iterator<Item = Row> + '_ { pub(crate) fn iter_spending(
&self,
prefix: HashPrefix,
) -> impl Iterator<Item = SerializedHashPrefixRow> + '_ {
self.iter_prefix_cf(self.spending_cf(), prefix) self.iter_prefix_cf(self.spending_cf(), prefix)
} }
pub(crate) fn iter_txid(&self, prefix: Row) -> impl Iterator<Item = Row> + '_ { pub(crate) fn iter_txid(
&self,
prefix: HashPrefix,
) -> impl Iterator<Item = SerializedHashPrefixRow> + '_ {
self.iter_prefix_cf(self.txid_cf(), prefix) self.iter_prefix_cf(self.txid_cf(), prefix)
} }
fn iter_cf<const N: usize>(
&self,
cf: &rocksdb::ColumnFamily,
readopts: rocksdb::ReadOptions,
prefix: Option<HashPrefix>,
) -> impl Iterator<Item = [u8; N]> + '_ {
DBIterator::new(self.db.raw_iterator_cf_opt(cf, readopts), prefix)
}
fn iter_prefix_cf( fn iter_prefix_cf(
&self, &self,
cf: &rocksdb::ColumnFamily, cf: &rocksdb::ColumnFamily,
prefix: Row, prefix: HashPrefix,
) -> impl Iterator<Item = Row> + '_ { ) -> impl Iterator<Item = SerializedHashPrefixRow> + '_ {
let mode = rocksdb::IteratorMode::From(&prefix, rocksdb::Direction::Forward);
let mut opts = rocksdb::ReadOptions::default(); let mut opts = rocksdb::ReadOptions::default();
opts.set_prefix_same_as_start(true); // requires .set_prefix_extractor() above. opts.set_prefix_same_as_start(true); // requires .set_prefix_extractor() above.
self.db self.iter_cf(cf, opts, Some(prefix))
.iterator_cf_opt(cf, opts, mode)
.map(|row| row.expect("prefix iterator failed").0) // values are empty in prefix-scanned CFs
} }
pub(crate) fn read_headers(&self) -> Vec<Row> { pub(crate) fn iter_headers(&self) -> impl Iterator<Item = SerializedHeaderRow> + '_ {
let mut opts = rocksdb::ReadOptions::default(); let mut opts = rocksdb::ReadOptions::default();
opts.fill_cache(false); opts.fill_cache(false);
self.db self.iter_cf(self.headers_cf(), opts, None)
.iterator_cf_opt(self.headers_cf(), opts, rocksdb::IteratorMode::Start)
.map(|row| row.expect("header iterator failed").0) // extract key from row
.filter(|key| &key[..] != TIP_KEY) // headers' rows are longer than TIP_KEY
.collect()
} }
pub(crate) fn get_tip(&self) -> Option<Vec<u8>> { pub(crate) fn get_tip(&self) -> Option<Vec<u8>> {
@ -273,7 +284,7 @@ impl DBStore {
for key in &batch.header_rows { for key in &batch.header_rows {
db_batch.put_cf(self.headers_cf(), key, b""); db_batch.put_cf(self.headers_cf(), key, b"");
} }
db_batch.put_cf(self.headers_cf(), TIP_KEY, &batch.tip_row); db_batch.put_cf(self.headers_cf(), TIP_KEY, batch.tip_row);
let mut opts = rocksdb::WriteOptions::new(); let mut opts = rocksdb::WriteOptions::new();
let bulk_import = self.bulk_import.load(Ordering::Relaxed); let bulk_import = self.bulk_import.load(Ordering::Relaxed);
@ -315,7 +326,7 @@ impl DBStore {
) -> impl Iterator<Item = (&'static str, &'static str, u64)> + '_ { ) -> impl Iterator<Item = (&'static str, &'static str, u64)> + '_ {
COLUMN_FAMILIES.iter().flat_map(move |cf_name| { COLUMN_FAMILIES.iter().flat_map(move |cf_name| {
let cf = self.db.cf_handle(cf_name).expect("missing CF"); let cf = self.db.cf_handle(cf_name).expect("missing CF");
DB_PROPERIES.iter().filter_map(move |property_name| { DB_PROPERTIES.iter().filter_map(move |property_name| {
let value = self let value = self
.db .db
.property_int_value_cf(cf, *property_name) .property_int_value_cf(cf, *property_name)
@ -354,6 +365,57 @@ impl DBStore {
} }
} }
struct DBIterator<'a, const N: usize> {
raw: rocksdb::DBRawIterator<'a>,
prefix: Option<HashPrefix>,
done: bool,
}
impl<'a, const N: usize> DBIterator<'a, N> {
fn new(mut raw: rocksdb::DBRawIterator<'a>, prefix: Option<HashPrefix>) -> Self {
match prefix {
Some(key) => raw.seek(key),
None => raw.seek_to_first(),
};
Self {
raw,
prefix,
done: false,
}
}
}
impl<const N: usize> Iterator for DBIterator<'_, N> {
type Item = [u8; N];
fn next(&mut self) -> Option<Self::Item> {
while !self.done {
let key = match self.raw.key() {
Some(key) => key,
None => {
self.raw.status().expect("DB scan failed");
break; // end of scan
}
};
let prefix_match = match self.prefix {
Some(key_prefix) => key.starts_with(&key_prefix),
None => true,
};
if !prefix_match {
break; // prefix mismatch
}
let result: Option<[u8; N]> = key.try_into().ok();
self.raw.next();
match result {
Some(value) => return Some(value),
None => continue, // skip keys with size != N
}
}
self.done = true;
None
}
}
impl Drop for DBStore { impl Drop for DBStore {
fn drop(&mut self) { fn drop(&mut self) {
info!("closing DB at {}", self.db.path().display()); info!("closing DB at {}", self.db.path().display());
@ -424,31 +486,24 @@ mod tests {
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();
let store = DBStore::open(dir.path(), None, true).unwrap(); let store = DBStore::open(dir.path(), None, true).unwrap();
let items: &[&[u8]] = &[ let items = [
b"ab", *b"ab ",
b"abcdefgh", *b"abcdefgh ",
b"abcdefghj", *b"abcdefghj ",
b"abcdefghjk", *b"abcdefghjk ",
b"abcdefghxyz", *b"abcdefghxyz ",
b"abcdefgi", *b"abcdefgi ",
b"b", *b"b ",
b"c", *b"c ",
]; ];
store.write(&WriteBatch { store.write(&WriteBatch {
txid_rows: to_rows(items), txid_rows: items.to_vec(),
..Default::default() ..Default::default()
}); });
let rows = store.iter_txid(b"abcdefgh".to_vec().into_boxed_slice()); let rows = store.iter_txid(*b"abcdefgh");
assert_eq!(rows.collect::<Vec<_>>(), to_rows(&items[1..5])); assert_eq!(rows.collect::<Vec<_>>(), items[1..5]);
}
fn to_rows(values: &[&[u8]]) -> Vec<Box<[u8]>> {
values
.iter()
.map(|v| v.to_vec().into_boxed_slice())
.collect()
} }
#[test] #[test]

View File

@ -1,5 +1,6 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use bitcoin::consensus::{deserialize, serialize, Decodable}; use bitcoin::consensus::{deserialize, Decodable, Encodable};
use bitcoin::hashes::Hash;
use bitcoin::{BlockHash, OutPoint, Txid}; use bitcoin::{BlockHash, OutPoint, Txid};
use bitcoin_slices::{bsl, Visit, Visitor}; use bitcoin_slices::{bsl, Visit, Visitor};
use std::ops::ControlFlow; use std::ops::ControlFlow;
@ -7,7 +8,7 @@ use std::ops::ControlFlow;
use crate::{ use crate::{
chain::{Chain, NewHeader}, chain::{Chain, NewHeader},
daemon::Daemon, daemon::Daemon,
db::{DBStore, Row, WriteBatch}, db::{DBStore, WriteBatch},
metrics::{self, Gauge, Histogram, Metrics}, metrics::{self, Gauge, Histogram, Metrics},
signals::ExitFlag, signals::ExitFlag,
types::{ types::{
@ -48,8 +49,8 @@ impl Stats {
self.update_duration.observe_duration(label, f) self.update_duration.observe_duration(label, f)
} }
fn observe_size(&self, label: &str, rows: &[Row]) { fn observe_size<const N: usize>(&self, label: &str, rows: &[[u8; N]]) {
self.update_size.observe(label, db_rows_size(rows) as f64); self.update_size.observe(label, (rows.len() * N) as f64);
} }
fn observe_batch(&self, batch: &WriteBatch) { fn observe_batch(&self, batch: &WriteBatch) {
@ -101,10 +102,8 @@ impl Index {
if let Some(row) = store.get_tip() { if let Some(row) = store.get_tip() {
let tip = deserialize(&row).expect("invalid tip"); let tip = deserialize(&row).expect("invalid tip");
let headers = store let headers = store
.read_headers() .iter_headers()
.into_iter() .map(|row| HeaderRow::from_db_row(row).header);
.map(|row| HeaderRow::from_db_row(&row).header)
.collect();
chain.load(headers, tip); chain.load(headers, tip);
chain.drop_last_headers(reindex_last_blocks); chain.drop_last_headers(reindex_last_blocks);
}; };
@ -141,7 +140,7 @@ impl Index {
pub(crate) fn filter_by_txid(&self, txid: Txid) -> impl Iterator<Item = BlockHash> + '_ { pub(crate) fn filter_by_txid(&self, txid: Txid) -> impl Iterator<Item = BlockHash> + '_ {
self.store self.store
.iter_txid(TxidRow::scan_prefix(txid)) .iter_txid(TxidRow::scan_prefix(txid))
.map(|row| HashPrefixRow::from_db_row(&row).height()) .map(|row| HashPrefixRow::from_db_row(row).height())
.filter_map(move |height| self.chain.get_block_hash(height)) .filter_map(move |height| self.chain.get_block_hash(height))
} }
@ -151,7 +150,7 @@ impl Index {
) -> impl Iterator<Item = BlockHash> + '_ { ) -> impl Iterator<Item = BlockHash> + '_ {
self.store self.store
.iter_funding(ScriptHashRow::scan_prefix(scripthash)) .iter_funding(ScriptHashRow::scan_prefix(scripthash))
.map(|row| HashPrefixRow::from_db_row(&row).height()) .map(|row| HashPrefixRow::from_db_row(row).height())
.filter_map(move |height| self.chain.get_block_hash(height)) .filter_map(move |height| self.chain.get_block_hash(height))
} }
@ -161,7 +160,7 @@ impl Index {
) -> impl Iterator<Item = BlockHash> + '_ { ) -> impl Iterator<Item = BlockHash> + '_ {
self.store self.store
.iter_spending(SpendingPrefixRow::scan_prefix(outpoint)) .iter_spending(SpendingPrefixRow::scan_prefix(outpoint))
.map(|row| HashPrefixRow::from_db_row(&row).height()) .map(|row| HashPrefixRow::from_db_row(row).height())
.filter_map(move |height| self.chain.get_block_hash(height)) .filter_map(move |height| self.chain.get_block_hash(height))
} }
@ -236,10 +235,6 @@ impl Index {
} }
} }
fn db_rows_size(rows: &[Row]) -> usize {
rows.iter().map(|key| key.len()).sum()
}
fn index_single_block( fn index_single_block(
block_hash: BlockHash, block_hash: BlockHash,
block: SerBlock, block: SerBlock,
@ -251,7 +246,7 @@ fn index_single_block(
height: usize, height: usize,
} }
impl<'a> Visitor for IndexBlockVisitor<'a> { impl Visitor for IndexBlockVisitor<'_> {
fn visit_transaction(&mut self, tx: &bsl::Transaction) -> ControlFlow<()> { fn visit_transaction(&mut self, tx: &bsl::Transaction) -> ControlFlow<()> {
let txid = bsl_txid(tx); let txid = bsl_txid(tx);
self.batch self.batch
@ -263,7 +258,7 @@ fn index_single_block(
fn visit_tx_out(&mut self, _vout: usize, tx_out: &bsl::TxOut) -> ControlFlow<()> { fn visit_tx_out(&mut self, _vout: usize, tx_out: &bsl::TxOut) -> ControlFlow<()> {
let script = bitcoin::Script::from_bytes(tx_out.script_pubkey()); let script = bitcoin::Script::from_bytes(tx_out.script_pubkey());
// skip indexing unspendable outputs // skip indexing unspendable outputs
if !script.is_provably_unspendable() { if !script.is_op_return() {
let row = ScriptHashRow::row(ScriptHash::new(script), self.height); let row = ScriptHashRow::row(ScriptHash::new(script), self.height);
self.batch.funding_rows.push(row.to_db_row()); self.batch.funding_rows.push(row.to_db_row());
} }
@ -292,5 +287,9 @@ fn index_single_block(
let mut index_block = IndexBlockVisitor { batch, height }; let mut index_block = IndexBlockVisitor { batch, height };
bsl::Block::visit(&block, &mut index_block).expect("core returned invalid block"); bsl::Block::visit(&block, &mut index_block).expect("core returned invalid block");
batch.tip_row = serialize(&block_hash).into_boxed_slice();
let len = block_hash
.consensus_encode(&mut (&mut batch.tip_row as &mut [u8]))
.expect("in-memory writers don't error");
debug_assert_eq!(len, BlockHash::LEN);
} }

View File

@ -82,8 +82,20 @@ impl MempoolSyncUpdate {
.iter() .iter()
.zip(entries.into_iter().zip(txs.into_iter())) .zip(entries.into_iter().zip(txs.into_iter()))
.filter_map(|(txid, (entry, tx))| { .filter_map(|(txid, (entry, tx))| {
let tx = tx.ok()?; let entry = match entry {
let entry = entry.ok()?; Some(entry) => entry,
None => {
debug!("missing mempool entry: {}", txid);
return None;
}
};
let tx = match tx {
Some(tx) => tx,
None => {
debug!("missing mempool tx: {}", txid);
return None;
}
};
Some(Entry { Some(Entry {
txid: *txid, txid: *txid,
tx, tx,
@ -199,6 +211,18 @@ impl Mempool {
} }
pub fn sync(&mut self, daemon: &Daemon, exit_flag: &ExitFlag) { pub fn sync(&mut self, daemon: &Daemon, exit_flag: &ExitFlag) {
let loaded = match daemon.get_mempool_info() {
Ok(info) => info.loaded.unwrap_or(true),
Err(e) => {
warn!("mempool sync failed: {}", e);
return;
}
};
if !loaded {
warn!("mempool not loaded");
return;
}
let old_txids = HashSet::<Txid>::from_iter(self.entries.keys().copied()); let old_txids = HashSet::<Txid>::from_iter(self.entries.keys().copied());
let poll_result = MempoolSyncUpdate::poll(daemon, old_txids, exit_flag); let poll_result = MempoolSyncUpdate::poll(daemon, old_txids, exit_flag);
@ -332,7 +356,7 @@ impl Serialize for FeeHistogram {
let mut seq = serializer.serialize_seq(Some(self.vsize.len()))?; let mut seq = serializer.serialize_seq(Some(self.vsize.len()))?;
// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html#mempool-get-fee-histogram // https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html#mempool-get-fee-histogram
let fee_rates = let fee_rates =
(0..FeeHistogram::BINS).map(|i| std::u64::MAX.checked_shr(i as u32).unwrap_or(0)); (0..FeeHistogram::BINS).map(|i| u64::MAX.checked_shr(i as u32).unwrap_or(0));
fee_rates fee_rates
.zip(self.vsize.iter().copied()) .zip(self.vsize.iter().copied())
.skip_while(|(_fee_rate, vsize)| *vsize == 0) .skip_while(|(_fee_rate, vsize)| *vsize == 0)

View File

@ -108,6 +108,6 @@ mod tests {
.join(block_hash_hex); .join(block_hash_hex);
let data = std::fs::read(path).unwrap(); let data = std::fs::read(path).unwrap();
let block: Block = deserialize(&data).unwrap(); let block: Block = deserialize(&data).unwrap();
block.txdata.iter().map(|tx| tx.txid()).collect() block.txdata.iter().map(|tx| tx.compute_txid()).collect()
} }
} }

View File

@ -7,6 +7,7 @@ use bitcoin::{
Decodable, Decodable,
}, },
hashes::Hash, hashes::Hash,
io,
p2p::{ p2p::{
self, address, self, address,
message::{self, CommandString, NetworkMessage}, message::{self, CommandString, NetworkMessage},
@ -19,9 +20,8 @@ use bitcoin::{
use bitcoin_slices::{bsl, Parse}; use bitcoin_slices::{bsl, Parse};
use crossbeam_channel::{bounded, select, Receiver, Sender}; use crossbeam_channel::{bounded, select, Receiver, Sender};
use std::io::{self, ErrorKind, Write}; use std::io::Write;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use crate::types::SerBlock; use crate::types::SerBlock;
@ -141,10 +141,11 @@ impl Connection {
metrics: &Metrics, metrics: &Metrics,
magic: Magic, magic: Magic,
) -> Result<Self> { ) -> Result<Self> {
let conn = Arc::new( let recv_conn = TcpStream::connect(address)
TcpStream::connect(address) .with_context(|| format!("{} p2p failed to connect: {:?}", network, address))?;
.with_context(|| format!("{} p2p failed to connect: {:?}", network, address))?, let mut send_conn = recv_conn
); .try_clone()
.context("failed to clone connection")?;
let (tx_send, tx_recv) = bounded::<NetworkMessage>(1); let (tx_send, tx_recv) = bounded::<NetworkMessage>(1);
let (rx_send, rx_recv) = bounded::<RawNetworkMessage>(1); let (rx_send, rx_recv) = bounded::<RawNetworkMessage>(1);
@ -180,7 +181,6 @@ impl Connection {
default_duration_buckets(), default_duration_buckets(),
); );
let stream = Arc::clone(&conn);
let mut buffer = vec![]; let mut buffer = vec![];
crate::thread::spawn("p2p_send", move || loop { crate::thread::spawn("p2p_send", move || loop {
use std::net::Shutdown; use std::net::Shutdown;
@ -190,7 +190,7 @@ impl Connection {
// p2p_loop is closed, so tx_send is disconnected // p2p_loop is closed, so tx_send is disconnected
debug!("closing p2p_send thread: no more messages to send"); debug!("closing p2p_send thread: no more messages to send");
// close the stream reader (p2p_recv thread may block on it) // close the stream reader (p2p_recv thread may block on it)
if let Err(e) = stream.shutdown(Shutdown::Read) { if let Err(e) = send_conn.shutdown(Shutdown::Read) {
warn!("failed to shutdown p2p connection: {}", e) warn!("failed to shutdown p2p connection: {}", e)
} }
return Ok(()); return Ok(());
@ -203,16 +203,16 @@ impl Connection {
raw_msg raw_msg
.consensus_encode(&mut buffer) .consensus_encode(&mut buffer)
.expect("in-memory writers don't error"); .expect("in-memory writers don't error");
(&*stream) send_conn
.write_all(buffer.as_slice()) .write_all(buffer.as_slice())
.context("p2p failed to send") .context("p2p failed to send")
})?; })?;
}); });
let stream = Arc::clone(&conn); let mut stream_reader = std::io::BufReader::new(recv_conn);
crate::thread::spawn("p2p_recv", move || loop { crate::thread::spawn("p2p_recv", move || loop {
let start = Instant::now(); let start = Instant::now();
let raw_msg = RawNetworkMessage::consensus_decode(&mut &*stream); let raw_msg = RawNetworkMessage::consensus_decode(&mut stream_reader);
{ {
let duration = duration_to_seconds(start.elapsed()); let duration = duration_to_seconds(start.elapsed());
let label = format!( let label = format!(
@ -232,7 +232,7 @@ impl Connection {
} }
raw_msg raw_msg
} }
Err(encode::Error::Io(e)) if e.kind() == ErrorKind::UnexpectedEof => { Err(encode::Error::Io(e)) if e.kind() == io::ErrorKind::UnexpectedEof => {
debug!("closing p2p_recv thread: connection closed"); debug!("closing p2p_recv thread: connection closed");
return Ok(()); return Ok(());
} }
@ -390,7 +390,7 @@ enum ParsedNetworkMessage {
} }
impl Decodable for RawNetworkMessage { impl Decodable for RawNetworkMessage {
fn consensus_decode<D: io::Read + ?Sized>(d: &mut D) -> Result<Self, encode::Error> { fn consensus_decode<D: bitcoin::io::Read + ?Sized>(d: &mut D) -> Result<Self, encode::Error> {
let magic = Decodable::consensus_decode(d)?; let magic = Decodable::consensus_decode(d)?;
let cmd = Decodable::consensus_decode(d)?; let cmd = Decodable::consensus_decode(d)?;

View File

@ -240,7 +240,7 @@ impl ScriptHashStatus {
fn confirmed_height_entries<'a>( fn confirmed_height_entries<'a>(
&'a self, &'a self,
chain: &'a Chain, chain: &'a Chain,
) -> impl Iterator<Item = (usize, &[TxEntry])> + 'a { ) -> impl Iterator<Item = (usize, &'a [TxEntry])> + 'a {
self.confirmed self.confirmed
.iter() .iter()
.filter_map(move |(blockhash, entries)| { .filter_map(move |(blockhash, entries)| {
@ -252,7 +252,7 @@ impl ScriptHashStatus {
/// Iterate through confirmed TxEntries. /// Iterate through confirmed TxEntries.
/// Skip entries from stale blocks. /// Skip entries from stale blocks.
fn confirmed_entries<'a>(&'a self, chain: &'a Chain) -> impl Iterator<Item = &TxEntry> + 'a { fn confirmed_entries<'a>(&'a self, chain: &'a Chain) -> impl Iterator<Item = &'a TxEntry> + 'a {
self.confirmed_height_entries(chain) self.confirmed_height_entries(chain)
.flat_map(|(_height, entries)| entries) .flat_map(|(_height, entries)| entries)
} }
@ -565,7 +565,7 @@ fn filter_block_txs_inputs(
pos: usize, pos: usize,
} }
impl<'a> Visitor for FindInputs<'a> { impl Visitor for FindInputs<'_> {
fn visit_transaction(&mut self, tx: &bsl::Transaction) -> ControlFlow<()> { fn visit_transaction(&mut self, tx: &bsl::Transaction) -> ControlFlow<()> {
if !self.buffer.is_empty() { if !self.buffer.is_empty() {
let result = std::mem::take(&mut self.buffer); let result = std::mem::take(&mut self.buffer);

View File

@ -6,20 +6,18 @@ use bitcoin::blockdata::block::Header as BlockHeader;
use bitcoin::{ use bitcoin::{
consensus::encode::{deserialize, Decodable, Encodable}, consensus::encode::{deserialize, Decodable, Encodable},
hashes::{hash_newtype, sha256, Hash}, hashes::{hash_newtype, sha256, Hash},
OutPoint, Script, Txid, io, OutPoint, Script, Txid,
}; };
use bitcoin_slices::bsl; use bitcoin_slices::bsl;
use crate::db;
macro_rules! impl_consensus_encoding { macro_rules! impl_consensus_encoding {
($thing:ident, $($field:ident),+) => ( ($thing:ident, $($field:ident),+) => (
impl Encodable for $thing { impl Encodable for $thing {
#[inline] #[inline]
fn consensus_encode<S: ::std::io::Write + ?Sized>( fn consensus_encode<S: io::Write + ?Sized>(
&self, &self,
s: &mut S, s: &mut S,
) -> Result<usize, std::io::Error> { ) -> Result<usize, io::Error> {
let mut len = 0; let mut len = 0;
$(len += self.$field.consensus_encode(s)?;)+ $(len += self.$field.consensus_encode(s)?;)+
Ok(len) Ok(len)
@ -28,7 +26,7 @@ macro_rules! impl_consensus_encoding {
impl Decodable for $thing { impl Decodable for $thing {
#[inline] #[inline]
fn consensus_decode<D: ::std::io::Read + ?Sized>( fn consensus_decode<D: io::Read + ?Sized>(
d: &mut D, d: &mut D,
) -> Result<$thing, bitcoin::consensus::encode::Error> { ) -> Result<$thing, bitcoin::consensus::encode::Error> {
Ok($thing { Ok($thing {
@ -39,33 +37,34 @@ macro_rules! impl_consensus_encoding {
); );
} }
const HASH_PREFIX_LEN: usize = 8; pub const HASH_PREFIX_LEN: usize = 8;
const HEIGHT_SIZE: usize = 4; const HEIGHT_SIZE: usize = 4;
type HashPrefix = [u8; HASH_PREFIX_LEN]; pub(crate) type HashPrefix = [u8; HASH_PREFIX_LEN];
pub(crate) type SerializedHashPrefixRow = [u8; HASH_PREFIX_ROW_SIZE];
type Height = u32; type Height = u32;
pub(crate) type SerBlock = Vec<u8>; pub(crate) type SerBlock = Vec<u8>;
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, Serialize, Deserialize, PartialEq)]
pub(crate) struct HashPrefixRow { pub(crate) struct HashPrefixRow {
prefix: [u8; HASH_PREFIX_LEN], prefix: HashPrefix,
height: Height, // transaction confirmed height height: Height, // transaction confirmed height
} }
const HASH_PREFIX_ROW_SIZE: usize = HASH_PREFIX_LEN + HEIGHT_SIZE; pub const HASH_PREFIX_ROW_SIZE: usize = HASH_PREFIX_LEN + HEIGHT_SIZE;
impl HashPrefixRow { impl HashPrefixRow {
pub(crate) fn to_db_row(&self) -> db::Row { pub(crate) fn to_db_row(&self) -> SerializedHashPrefixRow {
let mut vec = Vec::with_capacity(HASH_PREFIX_ROW_SIZE); let mut row = [0; HASH_PREFIX_ROW_SIZE];
let len = self let len = self
.consensus_encode(&mut vec) .consensus_encode(&mut (&mut row as &mut [u8]))
.expect("in-memory writers don't error"); .expect("in-memory writers don't error");
debug_assert_eq!(len, HASH_PREFIX_ROW_SIZE); debug_assert_eq!(len, HASH_PREFIX_ROW_SIZE);
vec.into_boxed_slice() row
} }
pub(crate) fn from_db_row(row: &[u8]) -> Self { pub(crate) fn from_db_row(row: SerializedHashPrefixRow) -> Self {
deserialize(row).expect("bad HashPrefixRow") deserialize(&row).expect("bad HashPrefixRow")
} }
pub fn height(&self) -> usize { pub fn height(&self) -> usize {
@ -96,8 +95,8 @@ impl ScriptHash {
pub(crate) struct ScriptHashRow; pub(crate) struct ScriptHashRow;
impl ScriptHashRow { impl ScriptHashRow {
pub(crate) fn scan_prefix(scripthash: ScriptHash) -> Box<[u8]> { pub(crate) fn scan_prefix(scripthash: ScriptHash) -> HashPrefix {
scripthash.0[..HASH_PREFIX_LEN].to_vec().into_boxed_slice() scripthash.0[..HASH_PREFIX_LEN].try_into().unwrap()
} }
pub(crate) fn row(scripthash: ScriptHash, height: usize) -> HashPrefixRow { pub(crate) fn row(scripthash: ScriptHash, height: usize) -> HashPrefixRow {
@ -118,7 +117,7 @@ hash_newtype! {
// *************************************************************************** // ***************************************************************************
fn spending_prefix(prev: OutPoint) -> HashPrefix { fn spending_prefix(prev: OutPoint) -> HashPrefix {
let txid_prefix = <[u8; HASH_PREFIX_LEN]>::try_from(&prev.txid[..HASH_PREFIX_LEN]).unwrap(); let txid_prefix = HashPrefix::try_from(&prev.txid[..HASH_PREFIX_LEN]).unwrap();
let value = u64::from_be_bytes(txid_prefix); let value = u64::from_be_bytes(txid_prefix);
let value = value.wrapping_add(prev.vout.into()); let value = value.wrapping_add(prev.vout.into());
value.to_be_bytes() value.to_be_bytes()
@ -127,8 +126,8 @@ fn spending_prefix(prev: OutPoint) -> HashPrefix {
pub(crate) struct SpendingPrefixRow; pub(crate) struct SpendingPrefixRow;
impl SpendingPrefixRow { impl SpendingPrefixRow {
pub(crate) fn scan_prefix(outpoint: OutPoint) -> Box<[u8]> { pub(crate) fn scan_prefix(outpoint: OutPoint) -> HashPrefix {
Box::new(spending_prefix(outpoint)) spending_prefix(outpoint)
} }
pub(crate) fn row(outpoint: OutPoint, height: usize) -> HashPrefixRow { pub(crate) fn row(outpoint: OutPoint, height: usize) -> HashPrefixRow {
@ -150,8 +149,8 @@ fn txid_prefix(txid: &Txid) -> HashPrefix {
pub(crate) struct TxidRow; pub(crate) struct TxidRow;
impl TxidRow { impl TxidRow {
pub(crate) fn scan_prefix(txid: Txid) -> Box<[u8]> { pub(crate) fn scan_prefix(txid: Txid) -> HashPrefix {
Box::new(txid_prefix(&txid)) txid_prefix(&txid)
} }
pub(crate) fn row(txid: Txid, height: usize) -> HashPrefixRow { pub(crate) fn row(txid: Txid, height: usize) -> HashPrefixRow {
@ -164,12 +163,14 @@ impl TxidRow {
// *************************************************************************** // ***************************************************************************
pub(crate) type SerializedHeaderRow = [u8; HEADER_ROW_SIZE];
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub(crate) struct HeaderRow { pub(crate) struct HeaderRow {
pub(crate) header: BlockHeader, pub(crate) header: BlockHeader,
} }
const HEADER_ROW_SIZE: usize = 80; pub const HEADER_ROW_SIZE: usize = 80;
impl_consensus_encoding!(HeaderRow, header); impl_consensus_encoding!(HeaderRow, header);
@ -178,17 +179,17 @@ impl HeaderRow {
Self { header } Self { header }
} }
pub(crate) fn to_db_row(&self) -> db::Row { pub(crate) fn to_db_row(&self) -> SerializedHeaderRow {
let mut vec = Vec::with_capacity(HEADER_ROW_SIZE); let mut row = [0; HEADER_ROW_SIZE];
let len = self let len = self
.consensus_encode(&mut vec) .consensus_encode(&mut (&mut row as &mut [u8]))
.expect("in-memory writers don't error"); .expect("in-memory writers don't error");
debug_assert_eq!(len, HEADER_ROW_SIZE); debug_assert_eq!(len, HEADER_ROW_SIZE);
vec.into_boxed_slice() row
} }
pub(crate) fn from_db_row(row: &[u8]) -> Self { pub(crate) fn from_db_row(row: SerializedHeaderRow) -> Self {
deserialize(row).expect("bad HeaderRow") deserialize(&row).expect("bad HeaderRow")
} }
} }
@ -219,8 +220,8 @@ mod tests {
let scripthash: ScriptHash = from_str(hex).unwrap(); let scripthash: ScriptHash = from_str(hex).unwrap();
let row1 = ScriptHashRow::row(scripthash, 123456); let row1 = ScriptHashRow::row(scripthash, 123456);
let db_row = row1.to_db_row(); let db_row = row1.to_db_row();
assert_eq!(&*db_row, &hex!("a384491d38929fcc40e20100")); assert_eq!(db_row, hex!("a384491d38929fcc40e20100"));
let row2 = HashPrefixRow::from_db_row(&db_row); let row2 = HashPrefixRow::from_db_row(db_row);
assert_eq!(row1, row2); assert_eq!(row1, row2);
} }
@ -247,8 +248,8 @@ mod tests {
let row1 = TxidRow::row(txid, 91812); let row1 = TxidRow::row(txid, 91812);
let row2 = TxidRow::row(txid, 91842); let row2 = TxidRow::row(txid, 91842);
assert_eq!(&*row1.to_db_row(), &hex!("9985d82954e10f22a4660100")); assert_eq!(row1.to_db_row(), hex!("9985d82954e10f22a4660100"));
assert_eq!(&*row2.to_db_row(), &hex!("9985d82954e10f22c2660100")); assert_eq!(row2.to_db_row(), hex!("9985d82954e10f22c2660100"));
} }
#[test] #[test]
@ -261,8 +262,8 @@ mod tests {
let row2 = TxidRow::row(txid, 91880); let row2 = TxidRow::row(txid, 91880);
// low-endian encoding => rows should be sorted according to block height // low-endian encoding => rows should be sorted according to block height
assert_eq!(&*row1.to_db_row(), &hex!("68b45f58b674e94e4a660100")); assert_eq!(row1.to_db_row(), hex!("68b45f58b674e94e4a660100"));
assert_eq!(&*row2.to_db_row(), &hex!("68b45f58b674e94ee8660100")); assert_eq!(row2.to_db_row(), hex!("68b45f58b674e94ee8660100"));
} }
#[test] #[test]