From b6b545f5f48de5b7bfeab7fd5808fafd28e7b10a Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 3 Jun 2024 10:59:37 -0400 Subject: [PATCH 1/5] Remove unnecessary App Clone trait --- payjoin-cli/src/app/v2.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/payjoin-cli/src/app/v2.rs b/payjoin-cli/src/app/v2.rs index 91352fe28..9659b8b19 100644 --- a/payjoin-cli/src/app/v2.rs +++ b/payjoin-cli/src/app/v2.rs @@ -15,7 +15,6 @@ use super::config::AppConfig; use super::{App as AppTrait, SeenInputs}; use crate::app::http_agent; -#[derive(Clone)] pub(crate) struct App { config: AppConfig, receive_store: Arc>, From 39008592abe71b29fffacb9544a86b5b3b0bc1c0 Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 3 Jun 2024 11:32:02 -0400 Subject: [PATCH 2/5] Track seen inputs with a sled database `sled` is a rust 1.63.0 compatible embedded DB. BDK also uses sled. --- .gitignore | 2 +- Cargo.lock | 564 +++++++++++++++++++++++++++++++--- payjoin-cli/Cargo.toml | 1 + payjoin-cli/src/app/config.rs | 8 + payjoin-cli/src/app/mod.rs | 37 +-- payjoin-cli/src/app/v1.rs | 17 +- payjoin-cli/src/app/v2.rs | 17 +- payjoin-cli/src/db.rs | 25 ++ payjoin-cli/src/main.rs | 2 + payjoin-cli/tests/e2e.rs | 7 + 10 files changed, 574 insertions(+), 106 deletions(-) create mode 100644 payjoin-cli/src/db.rs diff --git a/.gitignore b/.gitignore index 5ad2dafb0..00659271a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ target *config.toml !example.config.toml -*seen_inputs.json *_store.json +*payjoin.sled diff --git a/Cargo.lock b/Cargo.lock index 6c168b767..4aa5c86e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -584,6 +584,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crypto-common" version = "0.1.6" @@ -677,6 +692,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "digest" version = "0.9.0" @@ -697,6 +721,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "dlv-list" version = "0.3.0" @@ -781,6 +816,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "futures" version = "0.3.30" @@ -870,6 +915,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1022,6 +1076,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "hpke" version = "0.10.0" @@ -1134,7 +1197,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -1214,6 +1277,25 @@ dependencies = [ "webpki-roots 0.26.3", ] +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.3.1", + "hyper-util", + "rustls 0.23.10", + "rustls-native-certs 0.7.0", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", + "webpki-roots 0.26.3", +] + [[package]] name = "hyper-tungstenite" version = "0.13.0" @@ -1249,6 +1331,124 @@ dependencies = [ "tracing", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1257,12 +1457,14 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "44a986806a1cc899952ba462bc1f28afbfd5850ab6cb030ccb20dd02cc527a24" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -1284,6 +1486,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "ipnet" version = "2.9.0" @@ -1373,6 +1584,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -1469,6 +1686,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num_cpus" version = "1.16.0" @@ -1596,6 +1819,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -1603,7 +1837,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", ] [[package]] @@ -1675,6 +1923,7 @@ dependencies = [ "reqwest", "rustls 0.22.4", "serde", + "sled", "tokio", "url", ] @@ -1848,6 +2097,12 @@ dependencies = [ "universal-hash 0.5.1", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1887,6 +2142,53 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.10", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" +dependencies = [ + "bytes", + "rand", + "ring 0.17.8", + "rustc-hash", + "rustls 0.23.10", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" +dependencies = [ + "libc", + "once_cell", + "socket2 0.5.7", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.36" @@ -1940,7 +2242,7 @@ checksum = "52c4f3084aa3bc7dfbba4eff4fab2a54db4324965d8872ab933565e6fbd83bc6" dependencies = [ "pem", "ring 0.16.20", - "time 0.3.20", + "time 0.3.36", "yasna", ] @@ -1965,6 +2267,15 @@ dependencies = [ "url", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -1985,14 +2296,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.6" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.9", - "regex-syntax 0.7.5", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -2006,13 +2317,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.4", ] [[package]] @@ -2023,15 +2334,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ "base64 0.22.1", "bytes", @@ -2041,7 +2352,7 @@ dependencies = [ "http-body 1.0.0", "http-body-util", "hyper 1.3.1", - "hyper-rustls 0.26.0", + "hyper-rustls 0.27.2", "hyper-util", "ipnet", "js-sys", @@ -2050,7 +2361,8 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.22.4", + "quinn", + "rustls 0.23.10", "rustls-native-certs 0.7.0", "rustls-pemfile 2.1.2", "rustls-pki-types", @@ -2059,7 +2371,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tower-service", "url", "wasm-bindgen", @@ -2126,6 +2438,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.38.34" @@ -2165,6 +2483,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls" +version = "0.23.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +dependencies = [ + "once_cell", + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -2444,6 +2776,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "sled" +version = "0.34.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot 0.11.2", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -2482,6 +2830,12 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.10.0" @@ -2518,9 +2872,20 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] [[package]] name = "tar" @@ -2623,19 +2988,32 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ + "deranged", + "num-conv", + "powerfmt", "serde", "time-core", ] [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] [[package]] name = "tinyvec" @@ -2663,7 +3041,7 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", "socket2 0.5.7", @@ -2703,6 +3081,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.10", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-tungstenite" version = "0.21.0" @@ -2862,27 +3251,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - [[package]] name = "universal-hash" version = "0.4.0" @@ -2917,9 +3291,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", @@ -2933,6 +3307,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "valuable" version = "0.1.0" @@ -3059,13 +3445,14 @@ dependencies = [ [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] @@ -3248,6 +3635,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "x25519-dalek" version = "2.0.0-pre.1" @@ -3285,7 +3684,52 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.20", + "time 0.3.36", +] + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", ] [[package]] @@ -3308,6 +3752,28 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "zip" version = "0.5.13" diff --git a/payjoin-cli/Cargo.toml b/payjoin-cli/Cargo.toml index 859fb9ecf..cc8b9cf27 100644 --- a/payjoin-cli/Cargo.toml +++ b/payjoin-cli/Cargo.toml @@ -38,6 +38,7 @@ rcgen = { version = "0.11.1", optional = true } reqwest = { version = "0.12", default-features = false } rustls = { version = "0.22.2", optional = true } serde = { version = "1.0.160", features = ["derive"] } +sled = "0.34" tokio = { version = "1.12.0", features = ["full"] } url = { version = "2.3.1", features = ["serde"] } diff --git a/payjoin-cli/src/app/config.rs b/payjoin-cli/src/app/config.rs index 3e2c4663d..37ee9b95b 100644 --- a/payjoin-cli/src/app/config.rs +++ b/payjoin-cli/src/app/config.rs @@ -6,12 +6,15 @@ use config::{Config, ConfigError, File, FileFormat}; use serde::Deserialize; use url::Url; +use crate::db; + #[derive(Debug, Clone, Deserialize)] pub struct AppConfig { pub bitcoind_rpchost: Url, pub bitcoind_cookie: Option, pub bitcoind_rpcuser: String, pub bitcoind_rpcpassword: String, + pub db_path: PathBuf, // v2 only #[cfg(feature = "v2")] @@ -51,6 +54,11 @@ impl AppConfig { "bitcoind_rpcpassword", matches.get_one::("rpcpassword").map(|s| s.as_str()), )? + .set_default("db_path", db::DB_PATH)? + .set_override_option( + "db_path", + matches.get_one::("db_path").map(|s| s.as_str()), + )? // Subcommand defaults without which file serialization fails. .set_default("port", "3000")? .set_default("pj_endpoint", "https://localhost:3000")? diff --git a/payjoin-cli/src/app/mod.rs b/payjoin-cli/src/app/mod.rs index b50e3dfb8..9170c9ca3 100644 --- a/payjoin-cli/src/app/mod.rs +++ b/payjoin-cli/src/app/mod.rs @@ -1,16 +1,14 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::convert::TryFrom; use std::str::FromStr; use anyhow::{anyhow, Context, Result}; use bitcoincore_rpc::bitcoin::Amount; -use bitcoincore_rpc::jsonrpc::serde_json; use bitcoincore_rpc::RpcApi; use payjoin::bitcoin::psbt::Psbt; use payjoin::bitcoin::{self, base64}; use payjoin::send::RequestContext; use payjoin::Uri; -use serde::{Deserialize, Serialize}; pub mod config; use crate::app::config::AppConfig; @@ -103,39 +101,6 @@ pub trait App { } } -struct SeenInputs { - set: OutPointSet, - file: std::fs::File, -} - -impl SeenInputs { - fn new() -> Result { - // read from file - let mut file = - OpenOptions::new().write(true).read(true).create(true).open("seen_inputs.json")?; - let set = serde_json::from_reader(&mut file).unwrap_or_else(|_| OutPointSet::new()); - Ok(Self { set, file }) - } - - fn insert(&mut self, input: bitcoin::OutPoint) -> Result { - use std::io::Write; - - let unseen = self.set.insert(input); - let serialized = serde_json::to_string(&self.set)?; - self.file.write_all(serialized.as_bytes())?; - Ok(unseen) - } -} -#[derive(Debug, Serialize, Deserialize)] -struct OutPointSet(HashSet); - -use std::fs::OpenOptions; -impl OutPointSet { - fn new() -> Self { Self(HashSet::new()) } - - fn insert(&mut self, input: bitcoin::OutPoint) -> bool { self.0.insert(input) } -} - fn try_contributing_inputs( payjoin: &mut payjoin::receive::ProvisionalProposal, bitcoind: &bitcoincore_rpc::Client, diff --git a/payjoin-cli/src/app/v1.rs b/payjoin-cli/src/app/v1.rs index ae0bb2627..0ec7a30ce 100644 --- a/payjoin-cli/src/app/v1.rs +++ b/payjoin-cli/src/app/v1.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::net::SocketAddr; use std::str::FromStr; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use anyhow::{anyhow, Context, Result}; use bitcoincore_rpc::bitcoin::Amount; @@ -14,22 +14,23 @@ use payjoin::receive::{PayjoinProposal, UncheckedProposal}; use payjoin::{base64, Error, PjUriBuilder}; use super::config::AppConfig; -use super::{App as AppTrait, SeenInputs}; +use super::App as AppTrait; use crate::app::{http_agent, try_contributing_inputs, Headers}; +use crate::db::Database; #[cfg(feature = "danger-local-https")] pub const LOCAL_CERT_FILE: &str = "localhost.der"; #[derive(Clone)] pub(crate) struct App { config: AppConfig, - seen_inputs: Arc>, + db: Arc, } #[async_trait::async_trait] impl AppTrait for App { fn new(config: AppConfig) -> Result { - let seen_inputs = Arc::new(Mutex::new(SeenInputs::new()?)); - let app = Self { config, seen_inputs }; + let db = Arc::new(Database::create(&config.db_path)?); + let app = Self { config, db }; app.bitcoind()? .get_blockchain_info() .context("Failed to connect to bitcoind. Check config RPC connection.")?; @@ -286,7 +287,7 @@ impl App { // Receive Check 4: have we seen this input before? More of a check for non-interactive i.e. payment processor receivers. let payjoin = proposal.check_no_inputs_seen_before(|input| { - Ok(!self.insert_input_seen_before(*input).map_err(|e| Error::Server(e.into()))?) + self.db.insert_input_seen_before(*input).map_err(|e| Error::Server(e.into())) })?; log::trace!("check4"); @@ -329,8 +330,4 @@ impl App { ); Ok(payjoin_proposal) } - - fn insert_input_seen_before(&self, input: bitcoin::OutPoint) -> Result { - self.seen_inputs.lock().expect("mutex lock failed").insert(input) - } } diff --git a/payjoin-cli/src/app/v2.rs b/payjoin-cli/src/app/v2.rs index 9659b8b19..e08394d20 100644 --- a/payjoin-cli/src/app/v2.rs +++ b/payjoin-cli/src/app/v2.rs @@ -1,6 +1,6 @@ use std::fs::OpenOptions; use std::str::FromStr; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use anyhow::{anyhow, Context, Result}; use bitcoincore_rpc::jsonrpc::serde_json; @@ -12,23 +12,24 @@ use payjoin::{base64, bitcoin, Error, PjUriBuilder}; use tokio::sync::Mutex as AsyncMutex; use super::config::AppConfig; -use super::{App as AppTrait, SeenInputs}; +use super::App as AppTrait; use crate::app::http_agent; +use crate::db::Database; pub(crate) struct App { config: AppConfig, receive_store: Arc>, send_store: Arc>, - seen_inputs: Arc>, + db: Database, } #[async_trait::async_trait] impl AppTrait for App { fn new(config: AppConfig) -> Result { - let seen_inputs = Arc::new(Mutex::new(SeenInputs::new()?)); + let db = Database::create(&config.db_path)?; let receive_store = Arc::new(AsyncMutex::new(ReceiveStore::new()?)); let send_store = Arc::new(AsyncMutex::new(SendStore::new()?)); - let app = Self { config, receive_store, send_store, seen_inputs }; + let app = Self { config, receive_store, send_store, db }; app.bitcoind()? .get_blockchain_info() .context("Failed to connect to bitcoind. Check config RPC connection.")?; @@ -266,7 +267,7 @@ impl App { // Receive Check 4: have we seen this input before? More of a check for non-interactive i.e. payment processor receivers. let payjoin = proposal.check_no_inputs_seen_before(|input| { - Ok(!self.insert_input_seen_before(*input).map_err(|e| Error::Server(e.into()))?) + self.db.insert_input_seen_before(*input).map_err(|e| Error::Server(e.into())) })?; log::trace!("check4"); @@ -306,10 +307,6 @@ impl App { log::debug!("Receiver's Payjoin proposal PSBT Rsponse: {:#?}", payjoin_proposal_psbt); Ok(payjoin_proposal) } - - fn insert_input_seen_before(&self, input: bitcoin::OutPoint) -> Result { - self.seen_inputs.lock().expect("mutex lock failed").insert(input) - } } async fn unwrap_ohttp_keys_or_else_fetch(config: &AppConfig) -> Result { diff --git a/payjoin-cli/src/db.rs b/payjoin-cli/src/db.rs new file mode 100644 index 000000000..cb39ffee0 --- /dev/null +++ b/payjoin-cli/src/db.rs @@ -0,0 +1,25 @@ +use std::path::Path; + +use anyhow::Result; +use payjoin::bitcoin::consensus::encode::serialize; +use payjoin::bitcoin::OutPoint; +use sled::IVec; + +pub(crate) const DB_PATH: &str = "payjoin.sled"; + +pub(crate) struct Database(sled::Db); + +impl Database { + pub(crate) fn create(path: impl AsRef) -> Result { + let db = sled::open(path)?; + Ok(Self(db)) + } + + /// Inserts the input and returns true if the input was seen before, false otherwise. + pub(crate) fn insert_input_seen_before(&self, input: OutPoint) -> Result { + let key = serialize(&input); + let was_seen_before = self.0.insert(key.as_slice(), IVec::from(vec![]))?.is_some(); + self.0.flush()?; + Ok(was_seen_before) + } +} diff --git a/payjoin-cli/src/main.rs b/payjoin-cli/src/main.rs index 35556772a..701a5a10d 100644 --- a/payjoin-cli/src/main.rs +++ b/payjoin-cli/src/main.rs @@ -5,6 +5,7 @@ use clap::{arg, value_parser, Arg, ArgMatches, Command}; use url::Url; mod app; +mod db; #[cfg(not(feature = "v2"))] use app::v1::App; @@ -77,6 +78,7 @@ fn cli() -> ArgMatches { .action(clap::ArgAction::SetTrue) .help("Retry the asynchronous payjoin request if it did not yet complete"), ) + .arg(Arg::new("db_path").short('d').long("db-path").help("Sets a custom database path")) .subcommand_required(true); // Conditional arguments based on features diff --git a/payjoin-cli/tests/e2e.rs b/payjoin-cli/tests/e2e.rs index c79426832..4ccccdfcc 100644 --- a/payjoin-cli/tests/e2e.rs +++ b/payjoin-cli/tests/e2e.rs @@ -47,6 +47,9 @@ mod e2e { let receiver_rpchost = format!("http://{}/wallet/receiver", bitcoind.params.rpc_socket); let sender_rpchost = format!("http://{}/wallet/sender", bitcoind.params.rpc_socket); + let temp_dir = env::temp_dir(); + let receiver_db_path = temp_dir.join("receiver_db"); + let sender_db_path = temp_dir.join("sender_db"); let cookie_file = &bitcoind.params.cookie_file; let port = find_free_port(); let pj_endpoint = format!("https://localhost:{}", port); @@ -58,6 +61,8 @@ mod e2e { .arg(&receiver_rpchost) .arg("--cookie-file") .arg(&cookie_file) + .arg("--db-path") + .arg(&receiver_db_path) .arg("receive") .arg(RECEIVE_SATS) .arg("--port") @@ -95,6 +100,8 @@ mod e2e { .arg(&sender_rpchost) .arg("--cookie-file") .arg(&cookie_file) + .arg("--db-path") + .arg(&sender_db_path) .arg("send") .arg(&bip21) .arg("--fee-rate") From 051d8b2d8999d49bb56e1f6bb52812b1688eb7a5 Mon Sep 17 00:00:00 2001 From: DanGould Date: Tue, 4 Jun 2024 13:24:19 -0400 Subject: [PATCH 3/5] Restore `Enrolled::public_key(...)` to ID sessions --- payjoin/src/receive/v2.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/payjoin/src/receive/v2.rs b/payjoin/src/receive/v2.rs index b4a4caeff..3ff0099dc 100644 --- a/payjoin/src/receive/v2.rs +++ b/payjoin/src/receive/v2.rs @@ -249,6 +249,9 @@ impl Enrolled { let pubkey_base64 = base64::encode_config(pubkey, b64_config); format!("{}{}", &self.directory, pubkey_base64) } + + /// The per-session public key to use as an identifier + pub fn public_key(&self) -> PublicKey { self.s.public_key() } } /// The sender's original PSBT and optional parameters From 9d48f810c338a4c1e8a0d9dc823980f535f6ded0 Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 3 Jun 2024 13:59:16 -0400 Subject: [PATCH 4/5] Replace Send/Receive Stores with a Database table --- .gitignore | 1 - payjoin-cli/src/app/v2.rs | 112 +++------------------------ payjoin-cli/src/{db.rs => db/mod.rs} | 3 + payjoin-cli/src/db/v2.rs | 57 ++++++++++++++ payjoin/src/send/mod.rs | 6 ++ 5 files changed, 78 insertions(+), 101 deletions(-) rename payjoin-cli/src/{db.rs => db/mod.rs} (95%) create mode 100644 payjoin-cli/src/db/v2.rs diff --git a/.gitignore b/.gitignore index 00659271a..5421cccb8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ target *config.toml !example.config.toml -*_store.json *payjoin.sled diff --git a/payjoin-cli/src/app/v2.rs b/payjoin-cli/src/app/v2.rs index e08394d20..d35924573 100644 --- a/payjoin-cli/src/app/v2.rs +++ b/payjoin-cli/src/app/v2.rs @@ -1,15 +1,11 @@ -use std::fs::OpenOptions; use std::str::FromStr; -use std::sync::Arc; use anyhow::{anyhow, Context, Result}; -use bitcoincore_rpc::jsonrpc::serde_json; use bitcoincore_rpc::RpcApi; use payjoin::bitcoin::consensus::encode::serialize_hex; use payjoin::bitcoin::psbt::Psbt; use payjoin::bitcoin::Amount; use payjoin::{base64, bitcoin, Error, PjUriBuilder}; -use tokio::sync::Mutex as AsyncMutex; use super::config::AppConfig; use super::App as AppTrait; @@ -18,8 +14,6 @@ use crate::db::Database; pub(crate) struct App { config: AppConfig, - receive_store: Arc>, - send_store: Arc>, db: Database, } @@ -27,9 +21,7 @@ pub(crate) struct App { impl AppTrait for App { fn new(config: AppConfig) -> Result { let db = Database::create(&config.db_path)?; - let receive_store = Arc::new(AsyncMutex::new(ReceiveStore::new()?)); - let send_store = Arc::new(AsyncMutex::new(SendStore::new()?)); - let app = Self { config, receive_store, send_store, db }; + let app = Self { config, db }; app.bitcoind()? .get_blockchain_info() .context("Failed to connect to bitcoind. Check config RPC connection.")?; @@ -54,21 +46,19 @@ impl AppTrait for App { } async fn send_payjoin(&self, bip21: &str, fee_rate: &f32, is_retry: bool) -> Result<()> { - let mut session = self.send_store.lock().await; - let req_ctx = if is_retry { + let mut req_ctx = if is_retry { log::debug!("Resuming session"); // Get a reference to RequestContext - session.req_ctx.as_mut().expect("RequestContext is missing") + self.db.get_send_session()?.ok_or(anyhow!("No session found"))? } else { - let req_ctx = self.create_pj_request(bip21, fee_rate)?; - session.write(req_ctx)?; - log::debug!("Writing req_ctx"); - session.req_ctx.as_mut().expect("RequestContext is missing") + let mut req_ctx = self.create_pj_request(bip21, fee_rate)?; + self.db.insert_send_session(&mut req_ctx)?; + req_ctx }; log::debug!("Awaiting response"); - let res = self.long_poll_post(req_ctx).await?; + let res = self.long_poll_post(&mut req_ctx).await?; self.process_pj_response(res)?; - session.clear()?; + self.db.clear_send_session()?; Ok(()) } @@ -97,12 +87,12 @@ impl AppTrait for App { let enrolled = enroller .process_res(ohttp_response.bytes().await?.to_vec().as_slice(), ctx) .map_err(|_| anyhow!("Enrollment failed"))?; - self.receive_store.lock().await.write(enrolled.clone())?; + self.db.insert_recv_session(enrolled.clone())?; enrolled } else { - let session = self.receive_store.lock().await; + let session = self.db.get_recv_session()?; println!("Resuming Payjoin session"); // TODO include session pubkey / payjoin directory - session.session.clone().ok_or(anyhow!("No session found"))? + session.ok_or(anyhow!("No session found"))? }; println!("Receive session established"); @@ -137,7 +127,7 @@ impl AppTrait for App { "Response successful. Watch mempool for successful Payjoin. TXID: {}", payjoin_psbt.extract_tx().clone().txid() ); - self.receive_store.lock().await.clear()?; + self.db.clear_recv_session()?; Ok(()) } } @@ -339,81 +329,3 @@ fn map_reqwest_err(e: reqwest::Error) -> anyhow::Error { None => anyhow!("No HTTP response: {}", e), } } - -struct SendStore { - req_ctx: Option, - file: std::fs::File, -} - -impl SendStore { - fn new() -> Result { - let mut file = - OpenOptions::new().write(true).read(true).create(true).open("send_store.json")?; - let session = match serde_json::from_reader(&mut file) { - Ok(session) => Some(session), - Err(e) => { - log::debug!("error reading send session store: {}", e); - None - } - }; - - Ok(Self { req_ctx: session, file }) - } - - fn write( - &mut self, - session: payjoin::send::RequestContext, - ) -> Result<&mut payjoin::send::RequestContext> { - use std::io::Write; - - let session = self.req_ctx.insert(session); - let serialized = serde_json::to_string(session)?; - self.file.write_all(serialized.as_bytes())?; - Ok(session) - } - - fn clear(&mut self) -> Result<()> { - let file = OpenOptions::new().write(true).open("send_store.json")?; - file.set_len(0)?; - Ok(()) - } -} - -struct ReceiveStore { - session: Option, - file: std::fs::File, -} - -impl ReceiveStore { - fn new() -> Result { - let mut file = - OpenOptions::new().write(true).read(true).create(true).open("receive_store.json")?; - let session = match serde_json::from_reader(&mut file) { - Ok(session) => Some(session), - Err(e) => { - log::debug!("error reading receive session store: {}", e); - None - } - }; - - Ok(Self { session, file }) - } - - fn write( - &mut self, - session: payjoin::receive::v2::Enrolled, - ) -> Result<&mut payjoin::receive::v2::Enrolled> { - use std::io::Write; - - let session = self.session.insert(session); - let serialized = serde_json::to_string(session)?; - self.file.write_all(serialized.as_bytes())?; - Ok(session) - } - - fn clear(&mut self) -> Result<()> { - let file = OpenOptions::new().write(true).open("receive_store.json")?; - file.set_len(0)?; - Ok(()) - } -} diff --git a/payjoin-cli/src/db.rs b/payjoin-cli/src/db/mod.rs similarity index 95% rename from payjoin-cli/src/db.rs rename to payjoin-cli/src/db/mod.rs index cb39ffee0..0c7de6b6a 100644 --- a/payjoin-cli/src/db.rs +++ b/payjoin-cli/src/db/mod.rs @@ -23,3 +23,6 @@ impl Database { Ok(was_seen_before) } } + +#[cfg(feature = "v2")] +mod v2; diff --git a/payjoin-cli/src/db/v2.rs b/payjoin-cli/src/db/v2.rs new file mode 100644 index 000000000..cf67ba047 --- /dev/null +++ b/payjoin-cli/src/db/v2.rs @@ -0,0 +1,57 @@ +use bitcoincore_rpc::jsonrpc::serde_json; +use payjoin::receive::v2::Enrolled; +use payjoin::send::RequestContext; +use sled::{IVec, Tree}; + +use super::*; + +impl Database { + pub(crate) fn insert_recv_session(&self, session: Enrolled) -> Result<()> { + let recv_tree = self.0.open_tree("recv_sessions")?; + let key = &session.public_key().serialize(); + let value = serde_json::to_string(&session)?; + recv_tree.insert(key.as_slice(), IVec::from(value.as_str()))?; + recv_tree.flush()?; + Ok(()) + } + + pub(crate) fn get_recv_session(&self) -> Result> { + if let Some((_, val)) = self.0.open_tree("recv_sessions")?.first()? { + let session: Enrolled = serde_json::from_slice(&val)?; + Ok(Some(session)) + } else { + Ok(None) + } + } + + pub(crate) fn clear_recv_session(&self) -> Result<()> { + let recv_tree: Tree = self.0.open_tree("recv_sessions")?; + recv_tree.clear()?; + recv_tree.flush()?; + Ok(()) + } + + pub(crate) fn insert_send_session(&self, session: &mut RequestContext) -> Result<()> { + let send_tree: Tree = self.0.open_tree("send_sessions")?; + let key = &session.public_key().serialize(); + let value = serde_json::to_string(session)?; + send_tree.insert(key, IVec::from(value.as_str()))?; + send_tree.flush()?; + Ok(()) + } + + pub(crate) fn get_send_session(&self) -> Result> { + if let Some((_, val)) = self.0.open_tree("send_sessions")?.first()? { + let session: RequestContext = serde_json::from_slice(&val)?; + Ok(Some(session)) + } else { + Ok(None) + } + } + + pub(crate) fn clear_send_session(&self) -> Result<()> { + self.0.remove("send_sessions")?; + self.0.flush()?; + Ok(()) + } +} diff --git a/payjoin/src/send/mod.rs b/payjoin/src/send/mod.rs index 8596a4c73..cc020fcb6 100644 --- a/payjoin/src/send/mod.rs +++ b/payjoin/src/send/mod.rs @@ -390,6 +390,12 @@ impl RequestContext { Ok(bitcoin::secp256k1::PublicKey::from_slice(&pubkey_bytes) .map_err(InternalCreateRequestError::SubdirectoryInvalidPubkey)?) } + + #[cfg(feature = "v2")] + pub fn public_key(&self) -> PublicKey { + let secp = bitcoin::secp256k1::Secp256k1::new(); + self.e.public_key(&secp) + } } #[cfg(feature = "v2")] From 6af37f4922eb864a97618dbf059dc11e63c741de Mon Sep 17 00:00:00 2001 From: DanGould Date: Wed, 5 Jun 2024 13:30:26 -0400 Subject: [PATCH 5/5] Structure `db::Error`s for context --- payjoin-cli/src/db/error.rs | 34 ++++++++++++++++++++++++++++++++++ payjoin-cli/src/db/mod.rs | 4 +++- payjoin-cli/src/db/v2.rs | 9 +++++---- 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 payjoin-cli/src/db/error.rs diff --git a/payjoin-cli/src/db/error.rs b/payjoin-cli/src/db/error.rs new file mode 100644 index 000000000..0e2281b66 --- /dev/null +++ b/payjoin-cli/src/db/error.rs @@ -0,0 +1,34 @@ +use std::fmt; + +#[cfg(feature = "v2")] +use bitcoincore_rpc::jsonrpc::serde_json; +use sled::Error as SledError; + +pub(crate) type Result = std::result::Result; + +#[derive(Debug)] +pub(crate) enum Error { + Sled(SledError), + #[cfg(feature = "v2")] + Serialize(serde_json::Error), + #[cfg(feature = "v2")] + Deserialize(serde_json::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Sled(e) => write!(f, "Database operation failed: {}", e), + #[cfg(feature = "v2")] + Error::Serialize(e) => write!(f, "Serialization failed: {}", e), + #[cfg(feature = "v2")] + Error::Deserialize(e) => write!(f, "Deserialization failed: {}", e), + } + } +} + +impl std::error::Error for Error {} + +impl From for Error { + fn from(error: SledError) -> Self { Error::Sled(error) } +} diff --git a/payjoin-cli/src/db/mod.rs b/payjoin-cli/src/db/mod.rs index 0c7de6b6a..42f74e3d4 100644 --- a/payjoin-cli/src/db/mod.rs +++ b/payjoin-cli/src/db/mod.rs @@ -1,10 +1,12 @@ use std::path::Path; -use anyhow::Result; use payjoin::bitcoin::consensus::encode::serialize; use payjoin::bitcoin::OutPoint; use sled::IVec; +pub(crate) mod error; +use error::*; + pub(crate) const DB_PATH: &str = "payjoin.sled"; pub(crate) struct Database(sled::Db); diff --git a/payjoin-cli/src/db/v2.rs b/payjoin-cli/src/db/v2.rs index cf67ba047..6ecbc871f 100644 --- a/payjoin-cli/src/db/v2.rs +++ b/payjoin-cli/src/db/v2.rs @@ -9,7 +9,7 @@ impl Database { pub(crate) fn insert_recv_session(&self, session: Enrolled) -> Result<()> { let recv_tree = self.0.open_tree("recv_sessions")?; let key = &session.public_key().serialize(); - let value = serde_json::to_string(&session)?; + let value = serde_json::to_string(&session).map_err(Error::Serialize)?; recv_tree.insert(key.as_slice(), IVec::from(value.as_str()))?; recv_tree.flush()?; Ok(()) @@ -17,7 +17,7 @@ impl Database { pub(crate) fn get_recv_session(&self) -> Result> { if let Some((_, val)) = self.0.open_tree("recv_sessions")?.first()? { - let session: Enrolled = serde_json::from_slice(&val)?; + let session: Enrolled = serde_json::from_slice(&val).map_err(Error::Deserialize)?; Ok(Some(session)) } else { Ok(None) @@ -34,7 +34,7 @@ impl Database { pub(crate) fn insert_send_session(&self, session: &mut RequestContext) -> Result<()> { let send_tree: Tree = self.0.open_tree("send_sessions")?; let key = &session.public_key().serialize(); - let value = serde_json::to_string(session)?; + let value = serde_json::to_string(session).map_err(Error::Serialize)?; send_tree.insert(key, IVec::from(value.as_str()))?; send_tree.flush()?; Ok(()) @@ -42,7 +42,8 @@ impl Database { pub(crate) fn get_send_session(&self) -> Result> { if let Some((_, val)) = self.0.open_tree("send_sessions")?.first()? { - let session: RequestContext = serde_json::from_slice(&val)?; + let session: RequestContext = + serde_json::from_slice(&val).map_err(Error::Deserialize)?; Ok(Some(session)) } else { Ok(None)