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

View File

@ -1,6 +1,6 @@
[package]
name = "electrs"
version = "0.10.2"
version = "0.10.7"
authors = ["Roman Zeyde <me@romanzey.de>"]
description = "An efficient re-implementation of Electrum Server in Rust"
license = "MIT"
@ -22,9 +22,9 @@ spec = "internal/config_specification.toml"
[dependencies]
anyhow = "1.0"
bitcoin = { version = "0.31.1", features = ["serde", "rand-std"] }
bitcoin_slices = { version = "0.7", features = ["bitcoin", "sha2"] }
bitcoincore-rpc = { version = "0.18" }
bitcoin = { version = "0.32.4", features = ["serde", "rand-std"] }
bitcoin_slices = { version = "0.9", features = ["bitcoin", "sha2"] }
bitcoincore-rpc = { version = "0.19.0" }
configure_me = "0.4"
crossbeam-channel = "0.5"
dirs-next = "2.0"
@ -32,7 +32,7 @@ env_logger = "0.10"
log = "0.4"
parking_lot = "0.12"
prometheus = { version = "0.13", optional = true }
rayon = "1.8"
rayon = "1.9"
serde = "1.0"
serde_derive = "1.0, <=1.0.171" # avoid precompiled binaries (https://github.com/serde-rs/serde/issues/2538)
serde_json = "1.0"
@ -48,9 +48,12 @@ default-features = false
features = ["zstd", "snappy"]
[build-dependencies]
configure_me_codegen = { version = "0.4.4", default-features = false }
configure_me_codegen = { version = "0.4.8", default-features = false }
[dev-dependencies]
bitcoin-test-data = "0.2.0"
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.
# 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 install -qqy librocksdb-dev curl
### Electrum Rust Server ###
FROM base as electrs-build
FROM base AS electrs-build
RUN apt-get install -qqy cargo clang cmake
# Install electrs
@ -17,7 +17,7 @@ ENV ROCKSDB_INCLUDE_DIR=/usr/include
ENV ROCKSDB_LIB_DIR=/usr/lib
RUN cargo install --locked --path .
FROM base as result
FROM base AS result
# Copy the binaries
COPY --from=electrs-build /root/.cargo/bin/electrs /usr/bin/electrs

View File

@ -22,7 +22,7 @@ FROM base as bitcoin-build
# Download
WORKDIR /build/bitcoin
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 tar xvf bitcoin-$BITCOIND_VERSION-$ARCH-linux-gnu.tar.gz
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 git clone --recurse-submodules https://github.com/spesmilo/electrum/ && cd electrum/ && git log -1
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
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,
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
**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)
* 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
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
```

View File

@ -2,7 +2,7 @@ use anyhow::{Context, Result};
use electrs_rocksdb::{ColumnFamilyDescriptor, IteratorMode, Options, DB};
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 cfs: Vec<_> = cf_names
.iter()

View File

@ -58,7 +58,7 @@ doc = "JSONRPC authentication cookie file (default: ~/.bitcoin/.cookie)"
name = "network"
type = "crate::config::BitcoinNetwork"
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()"
[[param]]

View File

@ -57,11 +57,11 @@ impl Chain {
}
/// 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 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 new_headers: Vec<&BlockHeader> = Vec::with_capacity(header_map.len());
while blockhash != genesis_hash {
@ -202,7 +202,10 @@ hex!("000000200030d7f9c11ef35b89a0eefb9a5e449909339b5e7854d99804ea8d6a49bf900a03
// test loading from a list of headers and tip
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());
// test getters
@ -239,7 +242,10 @@ hex!("000000200030d7f9c11ef35b89a0eefb9a5e449909339b5e7854d99804ea8d6a49bf900a03
// test reorg
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 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 {
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 db_path: PathBuf,
pub db_log_dir: Option<PathBuf>,
pub daemon_dir: PathBuf,
pub daemon_auth: SensitiveAuth,
pub daemon_rpc_addr: SocketAddr,
pub daemon_p2p_addr: SocketAddr,
@ -146,7 +148,6 @@ pub struct Config {
pub disable_electrum_rpc: bool,
pub server_banner: String,
pub signet_magic: Magic,
pub args: Vec<String>,
}
pub struct SensitiveAuth(pub Auth);
@ -183,7 +184,7 @@ fn default_daemon_dir() -> PathBuf {
fn default_config_files() -> Vec<OsString> {
let mut files = vec![OsString::from("electrs.toml")]; // cwd
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("/etc/electrs/config.toml")); // system-wide
@ -195,7 +196,7 @@ impl Config {
pub fn from_args() -> Config {
use internal::ResultExt;
let (mut config, args) =
let (mut config, _args) =
internal::Config::including_optional_config_files(default_config_files())
.unwrap_or_exit();
@ -207,6 +208,7 @@ impl Config {
let db_subdir = match config.network {
Network::Bitcoin => "bitcoin",
Network::Testnet => "testnet",
Network::Testnet4 => "testnet4",
Network::Regtest => "regtest",
Network::Signet => "signet",
unsupported => unsupported_network(unsupported),
@ -217,6 +219,7 @@ impl Config {
let default_daemon_rpc_port = match config.network {
Network::Bitcoin => 8332,
Network::Testnet => 18332,
Network::Testnet4 => 48332,
Network::Regtest => 18443,
Network::Signet => 38332,
unsupported => unsupported_network(unsupported),
@ -224,6 +227,7 @@ impl Config {
let default_daemon_p2p_port = match config.network {
Network::Bitcoin => 8333,
Network::Testnet => 18333,
Network::Testnet4 => 48333,
Network::Regtest => 18444,
Network::Signet => 38333,
unsupported => unsupported_network(unsupported),
@ -231,6 +235,7 @@ impl Config {
let default_electrum_port = match config.network {
Network::Bitcoin => 50001,
Network::Testnet => 60001,
Network::Testnet4 => 40001,
Network::Regtest => 60401,
Network::Signet => 60601,
unsupported => unsupported_network(unsupported),
@ -238,6 +243,7 @@ impl Config {
let default_monitoring_port = match config.network {
Network::Bitcoin => 4224,
Network::Testnet => 14224,
Network::Testnet4 => 44224,
Network::Regtest => 24224,
Network::Signet => 34224,
unsupported => unsupported_network(unsupported),
@ -285,11 +291,31 @@ impl Config {
match config.network {
Network::Bitcoin => (),
Network::Testnet => config.daemon_dir.push("testnet3"),
Network::Testnet4 => config.daemon_dir.push("testnet4"),
Network::Regtest => config.daemon_dir.push("regtest"),
Network::Signet => config.daemon_dir.push("signet"),
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_auth = SensitiveAuth(match (config.auth, config.cookie_file) {
(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 index_lookup_limit = match config.index_lookup_limit {
@ -336,7 +358,6 @@ impl Config {
network: config.network,
db_path: config.db_dir,
db_log_dir: config.db_log_dir,
daemon_dir: config.daemon_dir,
daemon_auth,
daemon_rpc_addr,
daemon_p2p_addr,
@ -354,7 +375,6 @@ impl Config {
disable_electrum_rpc: config.disable_electrum_rpc,
server_banner: config.server_banner,
signet_magic: magic,
args: args.map(|a| a.into_string().unwrap()).collect(),
};
eprintln!(
"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 crossbeam_channel::Receiver;
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::io::Read;
@ -147,11 +148,15 @@ impl Daemon {
}
pub(crate) fn estimate_fee(&self, nblocks: u16) -> Result<Option<Amount>> {
Ok(self
.rpc
.estimate_smart_fee(nblocks, None)
.context("failed to estimate fee")?
.fee_rate)
let res = self.rpc.estimate_smart_fee(nblocks, None);
if let Err(bitcoincore_rpc::Error::JsonRpc(jsonrpc::Error::Rpc(RpcError {
code: -32603,
..
}))) = 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> {
@ -214,6 +219,12 @@ impl Daemon {
.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>> {
self.rpc
.get_raw_mempool()
@ -223,25 +234,16 @@ impl Daemon {
pub(crate) fn get_mempool_entries(
&self,
txids: &[Txid],
) -> Result<Vec<Result<json::GetMempoolEntryResult>>> {
let client = self.rpc.get_jsonrpc_client();
debug!("getting {} mempool entries", txids.len());
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
) -> Result<Vec<Option<json::GetMempoolEntryResult>>> {
let results = batch_request(self.rpc.get_jsonrpc_client(), "getmempoolentry", txids)?;
Ok(results
.into_iter()
.map(|r| {
r.context("missing response")?
.result::<json::GetMempoolEntryResult>()
.context("invalid response")
.map(|r| match r?.result::<json::GetMempoolEntryResult>() {
Ok(entry) => Some(entry),
Err(err) => {
debug!("failed to get mempool entry: {}", err); // probably due to RBF
None
}
})
.collect())
}
@ -249,28 +251,32 @@ impl Daemon {
pub(crate) fn get_mempool_transactions(
&self,
txids: &[Txid],
) -> Result<Vec<Result<Transaction>>> {
let client = self.rpc.get_jsonrpc_client();
debug!("getting {} transactions", txids.len());
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
) -> Result<Vec<Option<Transaction>>> {
let results = batch_request(self.rpc.get_jsonrpc_client(), "getrawtransaction", txids)?;
Ok(results
.into_iter()
.map(|r| -> Result<Transaction> {
let tx_hex = r
.context("missing response")?
.result::<String>()
.context("invalid response")?;
let tx_bytes = Vec::from_hex(&tx_hex).context("non-hex transaction")?;
deserialize(&tx_bytes).context("invalid transaction")
.map(|r| -> Option<Transaction> {
let tx_hex = match r?.result::<String>() {
Ok(tx_hex) => Some(tx_hex),
Err(err) => {
debug!("failed to get mempool tx: {}", err); // probably due to RBF
None
}
}?;
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())
}
@ -303,3 +309,29 @@ pub(crate) fn extract_bitcoind_error(err: &bitcoincore_rpc::Error) -> Option<&Rp
_ => 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::sync::atomic::{AtomicBool, Ordering};
pub(crate) type Row = Box<[u8]>;
use crate::types::{HashPrefix, SerializedHashPrefixRow, SerializedHeaderRow};
#[derive(Default)]
pub(crate) struct WriteBatch {
pub(crate) tip_row: Row,
pub(crate) header_rows: Vec<Row>,
pub(crate) funding_rows: Vec<Row>,
pub(crate) spending_rows: Vec<Row>,
pub(crate) txid_rows: Vec<Row>,
pub(crate) tip_row: [u8; 32],
pub(crate) header_rows: Vec<SerializedHeaderRow>,
pub(crate) funding_rows: Vec<SerializedHashPrefixRow>,
pub(crate) spending_rows: Vec<SerializedHashPrefixRow>,
pub(crate) txid_rows: Vec<SerializedHashPrefixRow>,
}
impl WriteBatch {
@ -42,7 +42,7 @@ const CONFIG_KEY: &str = "C";
const TIP_KEY: &[u8] = b"T";
// 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.mem-table-flush-pending",
"rocksdb.compaction-pending",
@ -218,39 +218,50 @@ impl DBStore {
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)
}
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)
}
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)
}
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(
&self,
cf: &rocksdb::ColumnFamily,
prefix: Row,
) -> impl Iterator<Item = Row> + '_ {
let mode = rocksdb::IteratorMode::From(&prefix, rocksdb::Direction::Forward);
prefix: HashPrefix,
) -> impl Iterator<Item = SerializedHashPrefixRow> + '_ {
let mut opts = rocksdb::ReadOptions::default();
opts.set_prefix_same_as_start(true); // requires .set_prefix_extractor() above.
self.db
.iterator_cf_opt(cf, opts, mode)
.map(|row| row.expect("prefix iterator failed").0) // values are empty in prefix-scanned CFs
self.iter_cf(cf, opts, Some(prefix))
}
pub(crate) fn read_headers(&self) -> Vec<Row> {
pub(crate) fn iter_headers(&self) -> impl Iterator<Item = SerializedHeaderRow> + '_ {
let mut opts = rocksdb::ReadOptions::default();
opts.fill_cache(false);
self.db
.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()
self.iter_cf(self.headers_cf(), opts, None)
}
pub(crate) fn get_tip(&self) -> Option<Vec<u8>> {
@ -273,7 +284,7 @@ impl DBStore {
for key in &batch.header_rows {
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 bulk_import = self.bulk_import.load(Ordering::Relaxed);
@ -315,7 +326,7 @@ impl DBStore {
) -> impl Iterator<Item = (&'static str, &'static str, u64)> + '_ {
COLUMN_FAMILIES.iter().flat_map(move |cf_name| {
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
.db
.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 {
fn drop(&mut self) {
info!("closing DB at {}", self.db.path().display());
@ -424,31 +486,24 @@ mod tests {
let dir = tempfile::tempdir().unwrap();
let store = DBStore::open(dir.path(), None, true).unwrap();
let items: &[&[u8]] = &[
b"ab",
b"abcdefgh",
b"abcdefghj",
b"abcdefghjk",
b"abcdefghxyz",
b"abcdefgi",
b"b",
b"c",
let items = [
*b"ab ",
*b"abcdefgh ",
*b"abcdefghj ",
*b"abcdefghjk ",
*b"abcdefghxyz ",
*b"abcdefgi ",
*b"b ",
*b"c ",
];
store.write(&WriteBatch {
txid_rows: to_rows(items),
txid_rows: items.to_vec(),
..Default::default()
});
let rows = store.iter_txid(b"abcdefgh".to_vec().into_boxed_slice());
assert_eq!(rows.collect::<Vec<_>>(), to_rows(&items[1..5]));
}
fn to_rows(values: &[&[u8]]) -> Vec<Box<[u8]>> {
values
.iter()
.map(|v| v.to_vec().into_boxed_slice())
.collect()
let rows = store.iter_txid(*b"abcdefgh");
assert_eq!(rows.collect::<Vec<_>>(), items[1..5]);
}
#[test]

View File

@ -1,5 +1,6 @@
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_slices::{bsl, Visit, Visitor};
use std::ops::ControlFlow;
@ -7,7 +8,7 @@ use std::ops::ControlFlow;
use crate::{
chain::{Chain, NewHeader},
daemon::Daemon,
db::{DBStore, Row, WriteBatch},
db::{DBStore, WriteBatch},
metrics::{self, Gauge, Histogram, Metrics},
signals::ExitFlag,
types::{
@ -48,8 +49,8 @@ impl Stats {
self.update_duration.observe_duration(label, f)
}
fn observe_size(&self, label: &str, rows: &[Row]) {
self.update_size.observe(label, db_rows_size(rows) as f64);
fn observe_size<const N: usize>(&self, label: &str, rows: &[[u8; N]]) {
self.update_size.observe(label, (rows.len() * N) as f64);
}
fn observe_batch(&self, batch: &WriteBatch) {
@ -101,10 +102,8 @@ impl Index {
if let Some(row) = store.get_tip() {
let tip = deserialize(&row).expect("invalid tip");
let headers = store
.read_headers()
.into_iter()
.map(|row| HeaderRow::from_db_row(&row).header)
.collect();
.iter_headers()
.map(|row| HeaderRow::from_db_row(row).header);
chain.load(headers, tip);
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> + '_ {
self.store
.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))
}
@ -151,7 +150,7 @@ impl Index {
) -> impl Iterator<Item = BlockHash> + '_ {
self.store
.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))
}
@ -161,7 +160,7 @@ impl Index {
) -> impl Iterator<Item = BlockHash> + '_ {
self.store
.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))
}
@ -236,10 +235,6 @@ impl Index {
}
}
fn db_rows_size(rows: &[Row]) -> usize {
rows.iter().map(|key| key.len()).sum()
}
fn index_single_block(
block_hash: BlockHash,
block: SerBlock,
@ -251,7 +246,7 @@ fn index_single_block(
height: usize,
}
impl<'a> Visitor for IndexBlockVisitor<'a> {
impl Visitor for IndexBlockVisitor<'_> {
fn visit_transaction(&mut self, tx: &bsl::Transaction) -> ControlFlow<()> {
let txid = bsl_txid(tx);
self.batch
@ -263,7 +258,7 @@ fn index_single_block(
fn visit_tx_out(&mut self, _vout: usize, tx_out: &bsl::TxOut) -> ControlFlow<()> {
let script = bitcoin::Script::from_bytes(tx_out.script_pubkey());
// skip indexing unspendable outputs
if !script.is_provably_unspendable() {
if !script.is_op_return() {
let row = ScriptHashRow::row(ScriptHash::new(script), self.height);
self.batch.funding_rows.push(row.to_db_row());
}
@ -292,5 +287,9 @@ fn index_single_block(
let mut index_block = IndexBlockVisitor { batch, height };
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()
.zip(entries.into_iter().zip(txs.into_iter()))
.filter_map(|(txid, (entry, tx))| {
let tx = tx.ok()?;
let entry = entry.ok()?;
let entry = match entry {
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 {
txid: *txid,
tx,
@ -199,6 +211,18 @@ impl Mempool {
}
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 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()))?;
// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html#mempool-get-fee-histogram
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
.zip(self.vsize.iter().copied())
.skip_while(|(_fee_rate, vsize)| *vsize == 0)

View File

@ -108,6 +108,6 @@ mod tests {
.join(block_hash_hex);
let data = std::fs::read(path).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,
},
hashes::Hash,
io,
p2p::{
self, address,
message::{self, CommandString, NetworkMessage},
@ -19,9 +20,8 @@ use bitcoin::{
use bitcoin_slices::{bsl, Parse};
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::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use crate::types::SerBlock;
@ -141,10 +141,11 @@ impl Connection {
metrics: &Metrics,
magic: Magic,
) -> Result<Self> {
let conn = Arc::new(
TcpStream::connect(address)
.with_context(|| format!("{} p2p failed to connect: {:?}", network, address))?,
);
let recv_conn = TcpStream::connect(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 (rx_send, rx_recv) = bounded::<RawNetworkMessage>(1);
@ -180,7 +181,6 @@ impl Connection {
default_duration_buckets(),
);
let stream = Arc::clone(&conn);
let mut buffer = vec![];
crate::thread::spawn("p2p_send", move || loop {
use std::net::Shutdown;
@ -190,7 +190,7 @@ impl Connection {
// p2p_loop is closed, so tx_send is disconnected
debug!("closing p2p_send thread: no more messages to send");
// 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)
}
return Ok(());
@ -203,16 +203,16 @@ impl Connection {
raw_msg
.consensus_encode(&mut buffer)
.expect("in-memory writers don't error");
(&*stream)
send_conn
.write_all(buffer.as_slice())
.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 {
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 label = format!(
@ -232,7 +232,7 @@ impl Connection {
}
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");
return Ok(());
}
@ -390,7 +390,7 @@ enum ParsedNetworkMessage {
}
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 cmd = Decodable::consensus_decode(d)?;

View File

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

View File

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