diff --git a/.gitignore b/.gitignore index e252b6329..cfcb37e57 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /results/ **/*.rs.bk .idea/azure/ +.idea/inspectionProfiles/Project_Default.xml ### Node node_modules diff --git a/automap/Cargo.lock b/automap/Cargo.lock index c877524c0..f84ea1c9d 100644 --- a/automap/Cargo.lock +++ b/automap/Cargo.lock @@ -76,6 +76,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "arrayref" version = "0.3.6" @@ -119,9 +128,9 @@ checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "automap" @@ -136,7 +145,7 @@ dependencies = [ "masq_lib", "port_scanner", "pretty-hex", - "rand 0.7.3", + "rand 0.8.5", ] [[package]] @@ -249,12 +258,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" -[[package]] -name = "cc" -version = "1.0.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff" - [[package]] name = "cfg-if" version = "0.1.10" @@ -267,26 +270,13 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "time 0.1.43", - "winapi 0.3.9", -] - [[package]] name = "clap" version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ - "ansi_term", + "ansi_term 0.11.0", "atty", "bitflags", "strsim", @@ -360,7 +350,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "cfg-if 0.1.10", "crossbeam-utils 0.7.2", "lazy_static", @@ -396,7 +386,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "cfg-if 0.1.10", "lazy_static", ] @@ -407,7 +397,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "cfg-if 1.0.0", "lazy_static", ] @@ -521,18 +511,19 @@ dependencies = [ [[package]] name = "flexi_logger" -version = "0.17.1" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab94b6ac8eb69f1496a6993f26f785b5fd6d99b7416023eb2a6175c0b242b1" +checksum = "f4a12e3b5a8775259ee83ac38aea8cdf9c3a1667c02178d207378c0837808fa9" dependencies = [ + "ansi_term 0.12.1", "atty", - "chrono", "glob", "lazy_static", "log 0.4.13", "regex", + "rustversion", "thiserror", - "yansi", + "time 0.3.11", ] [[package]] @@ -729,7 +720,7 @@ checksum = "4c4e7ee8b51e541486d7040883fe1f00e2a9954bcc24fd155b7e4f03ed4b93dd" dependencies = [ "attohttpc", "log 0.4.13", - "rand 0.8.2", + "rand 0.8.5", "url 2.2.0", "xmltree", ] @@ -828,9 +819,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.112" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" [[package]] name = "libsecp256k1" @@ -959,7 +950,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -968,7 +959,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -993,7 +984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" dependencies = [ "adler", - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -1051,34 +1042,16 @@ dependencies = [ [[package]] name = "nix" -version = "0.23.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" dependencies = [ + "autocfg 1.1.0", "bitflags", - "cc", "cfg-if 1.0.0", "libc", "memoffset 0.6.5", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg 1.0.1", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg 1.0.1", + "pin-utils", ] [[package]] @@ -1194,6 +1167,12 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "port_scanner" version = "0.1.5" @@ -1208,9 +1187,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "pretty-hex" -version = "0.1.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be91bcc43e73799dc46a6c194a55e7aae1d86cc867c860fd4a436019af21bd8c" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" [[package]] name = "primitive-types" @@ -1300,7 +1279,7 @@ dependencies = [ "rand_isaac", "rand_jitter", "rand_os", - "rand_pcg 0.1.2", + "rand_pcg", "rand_xorshift", "winapi 0.3.9", ] @@ -1316,19 +1295,17 @@ dependencies = [ "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc 0.2.0", - "rand_pcg 0.2.1", ] [[package]] name = "rand" -version = "0.8.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.0", "rand_core 0.6.1", - "rand_hc 0.3.0", ] [[package]] @@ -1412,15 +1389,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_hc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" -dependencies = [ - "rand_core 0.6.1", -] - [[package]] name = "rand_isaac" version = "0.1.1" @@ -1465,15 +1433,6 @@ dependencies = [ "rand_core 0.4.2", ] -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "rand_xorshift" version = "0.1.1" @@ -1555,6 +1514,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + [[package]] name = "ryu" version = "1.0.5" @@ -1596,9 +1561,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.133" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" [[package]] name = "serde_derive" @@ -1793,8 +1758,15 @@ dependencies = [ "itoa 1.0.1", "libc", "num_threads", + "time-macros", ] +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + [[package]] name = "tiny-hderive" version = "0.3.0" @@ -2343,9 +2315,3 @@ checksum = "d046fd42d4137234742eae0d05b4fb6fbdda9aed7c78e523ae890fd87c7e11dd" dependencies = [ "xml-rs", ] - -[[package]] -name = "yansi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" diff --git a/automap/Cargo.toml b/automap/Cargo.toml index 2cbc0fb0c..ab653dac0 100644 --- a/automap/Cargo.toml +++ b/automap/Cargo.toml @@ -11,14 +11,14 @@ edition = "2021" [dependencies] crossbeam-channel = "0.5.0" igd = "0.12.0" -flexi_logger = "0.17.1" +flexi_logger = "0.23.3" lazy_static = "1.4.0" local_ipaddress = "0.1.3" log = "0.4.8" masq_lib = { path = "../masq_lib" } port_scanner = "0.1.5" -pretty-hex = "0.1.0" -rand = {version = "0.7.0", features = ["getrandom", "small_rng"]} +pretty-hex = "0.3.0" +rand = {version = "0.8.5", features = ["getrandom", "small_rng"]} [[bin]] name = "automap" diff --git a/automap/src/logger.rs b/automap/src/logger.rs index 8a76ed3e7..a5dd88fa5 100644 --- a/automap/src/logger.rs +++ b/automap/src/logger.rs @@ -1,6 +1,6 @@ // Copyright (c) 2019-2021, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use flexi_logger::{DeferredNow, LevelFilter, LogSpecBuilder, Logger, Record}; +use flexi_logger::{DeferredNow, FileSpec, LevelFilter, LogSpecBuilder, Logger, Record, WriteMode}; use lazy_static::lazy_static; use std::env::current_dir; use std::path::PathBuf; @@ -13,11 +13,14 @@ lazy_static! { pub fn initiate_logger() { let logger = Logger::with(LogSpecBuilder::new().default(LevelFilter::Info).build()) - .log_to_file() - .directory(WORKING_PATH.as_path()) + .log_to_file( + FileSpec::default() + .directory(WORKING_PATH.as_path()) + .suppress_timestamp(), + ) + .write_mode(WriteMode::BufferAndFlush) .format(brief_format) - .print_message() - .suppress_timestamp(); + .print_message(); logger.start().expect("Logging subsystem failed to start"); } diff --git a/automap/src/probe_researcher.rs b/automap/src/probe_researcher.rs index 053e8031e..c226f6419 100644 --- a/automap/src/probe_researcher.rs +++ b/automap/src/probe_researcher.rs @@ -179,7 +179,7 @@ pub fn request_probe( fn generate_nonce() -> u16 { let mut rnd = thread_rng(); - rnd.gen_range(1000, 9999) + rnd.gen_range(1000..=9999) } #[cfg(test)] diff --git a/dns_utility/Cargo.lock b/dns_utility/Cargo.lock index fcf776fd9..27d45febc 100644 --- a/dns_utility/Cargo.lock +++ b/dns_utility/Cargo.lock @@ -107,9 +107,9 @@ checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" @@ -215,12 +215,6 @@ dependencies = [ "iovec", ] -[[package]] -name = "cc" -version = "1.0.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff" - [[package]] name = "cfg-if" version = "0.1.10" @@ -279,9 +273,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.7.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", @@ -289,9 +283,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "crossbeam-channel" @@ -329,7 +323,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "cfg-if 0.1.10", "crossbeam-utils 0.7.2", "lazy_static", @@ -365,7 +359,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "cfg-if 0.1.10", "lazy_static", ] @@ -410,7 +404,7 @@ name = "dns_utility" version = "0.6.2" dependencies = [ "core-foundation", - "ipconfig 0.2.2", + "ipconfig 0.3.0", "libc", "masq_lib", "regex", @@ -680,7 +674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f7eadeaf4b52700de180d147c4805f199854600b36faa963d91114827b2ffc" dependencies = [ "error-chain", - "socket2", + "socket2 0.3.19", "widestring 0.2.2", "winapi 0.3.9", "winreg 0.5.1", @@ -688,14 +682,14 @@ dependencies = [ [[package]] name = "ipconfig" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" +checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" dependencies = [ - "socket2", - "widestring 0.4.3", + "socket2 0.4.7", + "widestring 0.5.1", "winapi 0.3.9", - "winreg 0.6.2", + "winreg 0.7.0", ] [[package]] @@ -737,9 +731,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.112" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" [[package]] name = "libsecp256k1" @@ -862,7 +856,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -871,7 +865,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -896,7 +890,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -954,15 +948,16 @@ dependencies = [ [[package]] name = "nix" -version = "0.23.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" dependencies = [ + "autocfg 1.1.0", "bitflags", - "cc", "cfg-if 1.0.0", "libc", "memoffset 0.6.5", + "pin-utils", ] [[package]] @@ -1072,6 +1067,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -1412,9 +1413,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.133" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" [[package]] name = "serde_derive" @@ -1497,6 +1498,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1563,9 +1574,9 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4bc0637a2b8c0b1a5145cca3e21b707865edc7e32285771536af1ade129468" +checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" dependencies = [ "bitflags", "core-foundation", @@ -1574,9 +1585,9 @@ dependencies = [ [[package]] name = "system-configuration-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269e271436d8e4bb2621c535a11fe03d5d012f74b19af72f80288f3a72f6180a" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ "core-foundation-sys", "libc", @@ -1878,7 +1889,7 @@ dependencies = [ "log 0.4.14", "rand 0.5.6", "smallvec", - "socket2", + "socket2 0.3.19", "tokio-executor", "tokio-io", "tokio-reactor", @@ -1902,7 +1913,7 @@ dependencies = [ "log 0.4.14", "rand 0.5.6", "smallvec", - "socket2", + "socket2 0.3.19", "tokio-executor", "tokio-io", "tokio-reactor", @@ -2075,9 +2086,9 @@ checksum = "7157704c2e12e3d2189c507b7482c52820a16dfa4465ba91add92f266667cadb" [[package]] name = "widestring" -version = "0.4.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" [[package]] name = "winapi" @@ -2124,9 +2135,9 @@ dependencies = [ [[package]] name = "winreg" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" dependencies = [ "winapi 0.3.9", ] diff --git a/dns_utility/Cargo.toml b/dns_utility/Cargo.toml index b1937a5f3..60c07b8ce 100644 --- a/dns_utility/Cargo.toml +++ b/dns_utility/Cargo.toml @@ -14,12 +14,12 @@ masq_lib = { path = "../masq_lib" } [target.'cfg(target_os = "macos")'.dependencies] -system-configuration = "0.4.0" -core-foundation = "0.7.0" +system-configuration = "0.5.0" +core-foundation = "0.9.3" [target.'cfg(target_os = "windows")'.dependencies] winreg = "0.10.1" -ipconfig = "0.2.2" +ipconfig = "0.3.0" [dev-dependencies] diff --git a/masq/Cargo.toml b/masq/Cargo.toml index 463517145..b7ab4b34f 100644 --- a/masq/Cargo.toml +++ b/masq/Cargo.toml @@ -14,7 +14,7 @@ workspace = "../node" time = {version = "0.3.11", features = [ "macros" ]} clap = "2.33.3" crossbeam-channel = "0.5.1" -itertools = "0.8.0" +itertools = "0.10.5" lazy_static = "1.4.0" linefeed = "0.6.0" masq_lib = { path = "../masq_lib" } @@ -23,7 +23,7 @@ websocket = {version = "0.26.2", default-features = false, features = ["sync"]} ctrlc = "3.2.1" [target.'cfg(not(target_os = "windows"))'.dependencies] -nix = "0.23.0" +nix = "0.25.0" [lib] name = "masq_cli_lib" diff --git a/masq_lib/Cargo.toml b/masq_lib/Cargo.toml index ac04b12b2..1abc984c7 100644 --- a/masq_lib/Cargo.toml +++ b/masq_lib/Cargo.toml @@ -14,6 +14,7 @@ time = {version = "0.3.11", features = [ "formatting" ]} clap = "2.33.3" const_format = "0.2.22" crossbeam-channel = "0.5.1" +ethereum-types = "0.9.2" itertools = "0.10.1" lazy_static = "1.4.0" log = "0.4.8" @@ -23,7 +24,6 @@ serde_derive = "1.0.133" serde_json = "1.0.74" tiny-hderive = "0.3.0" toml = "0.5.8" -ethereum-types = "0.9.0" websocket = {version = "0.26.2", default-features = false, features = ["sync"]} [features] @@ -31,7 +31,7 @@ no_test_share = [] log_recipient_test = [] [target.'cfg(not(target_os = "windows"))'.dependencies] -nix = "0.23.0" +nix = "0.25.0" [lib] name = "masq_lib" diff --git a/multinode_integration_tests/Cargo.toml b/multinode_integration_tests/Cargo.toml index 43eeae2a5..c6de06811 100644 --- a/multinode_integration_tests/Cargo.toml +++ b/multinode_integration_tests/Cargo.toml @@ -11,7 +11,7 @@ workspace = "../node" [dependencies] base64 = "0.13.0" crossbeam-channel = "0.5.1" -ethereum-types = "0.9.0" +ethereum-types = "0.9.2" ethsign-crypto = "0.2.1" futures = "0.1.31" itertools = "0.10.1" @@ -20,17 +20,17 @@ log = "0.4.14" masq_lib = { path = "../masq_lib" } native-tls = "0.2.8" node = { path = "../node", features = [ "expose_test_privates" ] } -pretty-hex = "0.2.1" +pretty-hex = "0.3.0" primitive-types = {version = "0.5.0", default-features = false, features = ["default", "rlp", "serde"] } regex = "1.5.4" -rusqlite = {version = "0.26.1", features = ["bundled"]} +rusqlite = {version = "0.28.0", features = ["bundled"]} rustc-hex = "2.1.0" serde = "1.0.130" serde_cbor = "0.11.2" serde_derive = "1.0.130" serde_json = "1.0" -sha1 = "0.6.0" -tiny-bip39 = "0.8.2" +sha1 = "0.6.1" +tiny-bip39 = "1.0.0" tiny-hderive = "0.3.0" uint = "0.9.1" web3 = {version = "0.11.0", default-features = false, features = ["http", "tls"]} diff --git a/node/Cargo.lock b/node/Cargo.lock index a853de615..26ebf1c69 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -14,7 +14,7 @@ dependencies = [ "crossbeam-channel 0.3.9", "failure", "fnv", - "futures", + "futures 0.1.31", "libc", "log 0.4.14", "parking_lot 0.7.1", @@ -121,11 +121,20 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "anyhow" -version = "1.0.52" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "arc-swap" @@ -145,6 +154,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + [[package]] name = "attohttpc" version = "0.16.3" @@ -176,24 +191,24 @@ checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "automap" version = "0.6.2" dependencies = [ "crossbeam-channel 0.5.1", - "flexi_logger 0.17.1", + "flexi_logger", "igd", "lazy_static", "local_ipaddress", "log 0.4.14", "masq_lib", "port_scanner", - "pretty-hex 0.1.1", - "rand 0.7.3", + "pretty-hex", + "rand 0.8.5", ] [[package]] @@ -266,7 +281,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" dependencies = [ "either", - "radium", + "radium 0.3.0", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium 0.7.0", + "tap", + "wyz", ] [[package]] @@ -276,7 +303,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.5.1", "constant_time_eq", ] @@ -301,6 +328,15 @@ dependencies = [ "generic-array 0.14.4", ] +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array 0.14.4", +] + [[package]] name = "block-cipher-trait" version = "0.6.2" @@ -337,6 +373,12 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" +[[package]] +name = "byte-slice-cast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e" + [[package]] name = "byte-tools" version = "0.3.1" @@ -403,7 +445,7 @@ version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ - "ansi_term", + "ansi_term 0.11.0", "atty", "bitflags", "strsim", @@ -436,7 +478,7 @@ version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", "unicode-xid 0.2.1", ] @@ -455,30 +497,14 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" -dependencies = [ - "core-foundation-sys 0.7.0", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ - "core-foundation-sys 0.8.3", + "core-foundation-sys", "libc", ] -[[package]] -name = "core-foundation-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" - [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -494,15 +520,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -dependencies = [ - "cfg-if 0.1.10", -] - [[package]] name = "crossbeam-channel" version = "0.3.9" @@ -550,7 +567,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "cfg-if 0.1.10", "crossbeam-utils 0.7.2", "lazy_static", @@ -599,7 +616,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "cfg-if 0.1.10", "lazy_static", ] @@ -620,6 +637,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.4", + "typenum", +] + [[package]] name = "crypto-mac" version = "0.7.0" @@ -670,6 +697,19 @@ dependencies = [ "libc", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if 1.0.0", + "hashbrown 0.12.3", + "lock_api 0.4.9", + "once_cell", + "parking_lot_core 0.9.3", +] + [[package]] name = "data-encoding" version = "2.3.0" @@ -695,7 +735,7 @@ checksum = "8d2d6daefd5f1d4b74a891a5d2ab7dccba028d423107c074232a0c5dc0d40a9e" dependencies = [ "data-encoding", "proc-macro-hack", - "syn 1.0.85", + "syn 1.0.100", ] [[package]] @@ -705,10 +745,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" dependencies = [ "convert_case", - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", "rustc_version 0.3.3", - "syn 1.0.85", + "syn 1.0.100", ] [[package]] @@ -729,6 +769,17 @@ dependencies = [ "generic-array 0.14.4", ] +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle 2.4.1", +] + [[package]] name = "dirs" version = "1.0.5" @@ -833,8 +884,8 @@ checksum = "71a6567e6fd35589fea0c63b94b4cf2e55573e413901bdbe60ab15cf0e25e5df" dependencies = [ "crunchy", "fixed-hash 0.6.1", - "impl-rlp", - "impl-serde 0.3.1", + "impl-rlp 0.2.1", + "impl-serde 0.3.2", "tiny-keccak 2.0.2", ] @@ -846,8 +897,8 @@ checksum = "473aecff686bd8e7b9db0165cbbb53562376b39bf35b427f0c60446a9e1634b0" dependencies = [ "ethbloom", "fixed-hash 0.6.1", - "impl-rlp", - "impl-serde 0.3.1", + "impl-rlp 0.2.1", + "impl-serde 0.3.2", "primitive-types 0.7.3", "uint 0.8.5", ] @@ -896,9 +947,9 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", - "syn 1.0.85", + "syn 1.0.100", "synstructure", ] @@ -954,48 +1005,32 @@ dependencies = [ ] [[package]] -name = "flate2" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da80be589a72651dcda34d8b35bcdc9b7254ad06325611074d9cc0fbb19f60ee" -dependencies = [ - "cfg-if 0.1.10", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] -name = "flexi_logger" -version = "0.15.12" +name = "fixed-hash" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaab3caedb4149800f91e8e4899f29cd9ddf3b569b04c365ca9334f92f7542bf" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ - "atty", - "chrono", - "flate2", - "glob", - "lazy_static", - "log 0.4.14", - "regex", - "thiserror", - "yansi", + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions 1.1.0", ] [[package]] name = "flexi_logger" -version = "0.17.1" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab94b6ac8eb69f1496a6993f26f785b5fd6d99b7416023eb2a6175c0b242b1" +checksum = "f4a12e3b5a8775259ee83ac38aea8cdf9c3a1667c02178d207378c0837808fa9" dependencies = [ + "ansi_term 0.12.1", "atty", - "chrono", "glob", "lazy_static", "log 0.4.14", "regex", + "rustversion", "thiserror", - "yansi", + "time 0.3.11", ] [[package]] @@ -1051,26 +1086,48 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" -version = "0.3.19" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.19" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-cpupool" @@ -1078,32 +1135,54 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" dependencies = [ - "futures", + "futures 0.1.31", "num_cpus", ] +[[package]] +name = "futures-executor" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" + [[package]] name = "futures-sink" -version = "0.3.19" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.19" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-util" -version = "0.3.19" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab 0.4.2", ] [[package]] @@ -1134,7 +1213,6 @@ dependencies = [ "cfg-if 0.1.10", "libc", "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -1169,7 +1247,7 @@ dependencies = [ "byteorder", "bytes 0.4.12", "fnv", - "futures", + "futures 0.1.31", "http 0.1.21", "indexmap", "log 0.4.14", @@ -1211,30 +1289,27 @@ checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash", ] [[package]] name = "hashlink" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" dependencies = [ - "hashbrown 0.11.2", + "hashbrown 0.12.3", ] [[package]] name = "heck" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" @@ -1271,6 +1346,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.5", +] + [[package]] name = "hmac-drbg" version = "0.2.0" @@ -1333,7 +1417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "http 0.1.21", "tokio-buf", ] @@ -1387,7 +1471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "futures-cpupool", "h2 0.1.26", "http 0.1.21", @@ -1441,7 +1525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "hyper 0.12.35", "native-tls", "tokio-io", @@ -1490,7 +1574,7 @@ checksum = "4c4e7ee8b51e541486d7040883fe1f00e2a9954bcc24fd155b7e4f03ed4b93dd" dependencies = [ "attohttpc", "log 0.4.14", - "rand 0.8.4", + "rand 0.8.5", "url 2.2.2", "xmltree", ] @@ -1501,7 +1585,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 1.3.5", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec 3.2.1", ] [[package]] @@ -1510,7 +1603,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f7a72f11830b52333f36e3b09a288333888bf54380fd0ac0790a3c31ab0f3c5" dependencies = [ - "rlp", + "rlp 0.4.6", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp 0.5.1", ] [[package]] @@ -1524,20 +1626,40 @@ dependencies = [ [[package]] name = "impl-serde" -version = "0.3.1" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47ca4d2b6931707a55fce5cf66aff80e2178c8b63bbb4ecb5695cbc870ddf6f" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" dependencies = [ "serde", ] +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2 1.0.43", + "quote 1.0.14", + "syn 1.0.100", +] + [[package]] name = "indexmap" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "hashbrown 0.9.1", ] @@ -1585,25 +1707,28 @@ dependencies = [ ] [[package]] -name = "ipnet" -version = "2.3.1" +name = "ipconfig" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" +dependencies = [ + "socket2 0.4.2", + "widestring 0.5.1", + "winapi 0.3.9", + "winreg 0.7.0", +] [[package]] -name = "itertools" -version = "0.8.2" +name = "ipnet" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" -dependencies = [ - "either", -] +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] @@ -1635,7 +1760,7 @@ version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0747307121ffb9703afd93afbd0fb4f854c38fb873f2c8b90e0e902f27c7b62" dependencies = [ - "futures", + "futures 0.1.31", "log 0.4.14", "serde", "serde_derive", @@ -1666,9 +1791,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" [[package]] name = "libsecp256k1" @@ -1699,7 +1824,7 @@ dependencies = [ "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", - "rand 0.8.4", + "rand 0.8.5", "serde", "sha2 0.9.8", "typenum", @@ -1747,9 +1872,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.23.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58" +checksum = "9f0455f2c1bc9a7caa792907026e469c1d91761fb0ea37cbb16427c77280cf35" dependencies = [ "cc", "pkg-config", @@ -1798,6 +1923,16 @@ dependencies = [ "scopeguard 1.1.0", ] +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg 1.1.0", + "scopeguard 1.1.0", +] + [[package]] name = "log" version = "0.3.9" @@ -1832,11 +1967,11 @@ dependencies = [ "clap", "crossbeam-channel 0.5.1", "ctrlc", - "itertools 0.8.2", + "itertools", "lazy_static", "linefeed", "masq_lib", - "nix 0.23.1", + "nix 0.25.0", "regex", "time 0.3.11", "websocket", @@ -1851,10 +1986,10 @@ dependencies = [ "const_format", "crossbeam-channel 0.5.1", "ethereum-types", - "itertools 0.10.3", + "itertools", "lazy_static", "log 0.4.14", - "nix 0.23.1", + "nix 0.25.0", "regex", "serde", "serde_derive", @@ -1895,7 +2030,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -1904,7 +2039,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -1935,7 +2070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" dependencies = [ "adler", - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -2026,14 +2161,14 @@ dependencies = [ "crossbeam-channel 0.5.1", "ethereum-types", "ethsign-crypto", - "futures", - "itertools 0.10.3", + "futures 0.1.31", + "itertools", "lazy_static", "log 0.4.14", "masq_lib", "native-tls", "node", - "pretty-hex 0.2.1", + "pretty-hex", "primitive-types 0.5.1", "regex", "reqwest", @@ -2112,6 +2247,20 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nix" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" +dependencies = [ + "autocfg 1.1.0", + "bitflags", + "cfg-if 1.0.0", + "libc", + "memoffset 0.6.5", + "pin-utils", +] + [[package]] name = "node" version = "0.6.2" @@ -2123,7 +2272,7 @@ dependencies = [ "base64 0.13.0", "bytes 0.4.12", "clap", - "core-foundation 0.7.0", + "core-foundation", "crossbeam-channel 0.5.1", "daemonize", "dirs 4.0.0", @@ -2132,13 +2281,13 @@ dependencies = [ "ethsign", "ethsign-crypto", "fdlimit", - "flexi_logger 0.15.12", - "futures", + "flexi_logger", + "futures 0.1.31", "heck", "http 0.2.5", "indoc", - "ipconfig 0.2.2", - "itertools 0.10.3", + "ipconfig 0.3.0", + "itertools", "jsonrpc-core", "lazy_static", "libc", @@ -2146,14 +2295,14 @@ dependencies = [ "log 0.4.14", "masq_lib", "native-tls", - "nix 0.23.1", + "nix 0.25.0", "openssl", "paste", - "pretty-hex 0.2.1", - "primitive-types 0.5.1", - "rand 0.8.4", + "pretty-hex", + "primitive-types 0.12.0", + "rand 0.8.5", "regex", - "rlp", + "rlp 0.5.1", "rpassword", "rusqlite", "rustc-hex", @@ -2209,7 +2358,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "num-traits", ] @@ -2219,7 +2368,7 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -2249,9 +2398,9 @@ checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "once_cell" -version = "1.9.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" [[package]] name = "opaque-debug" @@ -2300,7 +2449,7 @@ version = "0.9.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "cc", "libc", "openssl-src", @@ -2323,12 +2472,38 @@ version = "1.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c740e5fbcb6847058b40ac7e5574766c6388f585e184d769910fe0d3a2ca861" dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", + "arrayvec 0.5.1", + "bitvec 0.17.4", + "byte-slice-cast 0.3.5", "serde", ] +[[package]] +name = "parity-scale-codec" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" +dependencies = [ + "arrayvec 0.7.2", + "bitvec 1.0.1", + "byte-slice-cast 1.2.1", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.43", + "quote 1.0.14", + "syn 1.0.100", +] + [[package]] name = "parking_lot" version = "0.7.1" @@ -2360,6 +2535,16 @@ dependencies = [ "parking_lot_core 0.7.2", ] +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api 0.4.9", + "parking_lot_core 0.9.3", +] + [[package]] name = "parking_lot_core" version = "0.4.0" @@ -2402,6 +2587,19 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.2.10", + "smallvec 1.6.1", + "windows-sys", +] + [[package]] name = "paste" version = "1.0.6" @@ -2421,11 +2619,11 @@ dependencies = [ [[package]] name = "pbkdf2" -version = "0.4.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "crypto-mac 0.8.0", + "digest 0.10.5", ] [[package]] @@ -2502,9 +2700,9 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b95af56fee93df76d721d356ac1ca41fccf168bc448eb14049234df764ba3e76" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", - "syn 1.0.85", + "syn 1.0.100", ] [[package]] @@ -2539,15 +2737,9 @@ checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" [[package]] name = "pretty-hex" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be91bcc43e73799dc46a6c194a55e7aae1d86cc867c860fd4a436019af21bd8c" - -[[package]] -name = "pretty-hex" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" [[package]] name = "primitive-types" @@ -2556,8 +2748,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83ef7b3b965c0eadcb6838f34f827e1dfb2939bdd5ebd43f9647e009b12b0371" dependencies = [ "fixed-hash 0.4.0", - "impl-codec", - "impl-rlp", + "impl-codec 0.4.2", + "impl-rlp 0.2.1", "impl-serde 0.2.3", "uint 0.8.5", ] @@ -2569,12 +2761,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd39dcacf71411ba488570da7bbc89b717225e46478b30ba99b92db6b149809" dependencies = [ "fixed-hash 0.6.1", - "impl-codec", - "impl-rlp", - "impl-serde 0.3.1", + "impl-codec 0.4.2", + "impl-rlp 0.2.1", + "impl-serde 0.3.2", "uint 0.8.5", ] +[[package]] +name = "primitive-types" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cfd65aea0c5fa0bfcc7c9e7ca828c921ef778f43d325325ec84bda371bfa75a" +dependencies = [ + "fixed-hash 0.8.0", + "impl-codec 0.6.0", + "impl-rlp 0.3.0", + "impl-serde 0.4.0", + "uint 0.9.1", +] + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.43", + "quote 1.0.14", + "syn 1.0.100", + "version_check 0.9.2", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.43", + "quote 1.0.14", + "version_check 0.9.2", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -2592,11 +2832,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ - "unicode-xid 0.2.1", + "unicode-ident", ] [[package]] @@ -2620,7 +2860,7 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", ] [[package]] @@ -2629,6 +2869,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "radix_trie" version = "0.1.6" @@ -2687,14 +2933,13 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.3", - "rand_hc 0.3.1", ] [[package]] @@ -2778,15 +3023,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core 0.6.3", -] - [[package]] name = "rand_isaac" version = "0.1.1" @@ -2855,7 +3091,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "crossbeam-deque 0.8.1", "either", "rayon-core", @@ -2999,11 +3235,21 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rlp" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "999508abb0ae792aabed2460c45b89106d97fe4adac593bdaef433c2605847b5" +dependencies = [ + "bytes 1.1.0", + "rustc-hex", +] + [[package]] name = "rpassword" -version = "5.0.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +checksum = "26b763cb66df1c928432cc35053f8bd4cec3335d8559fc16010017d16b3c1680" dependencies = [ "libc", "winapi 0.3.9", @@ -3011,16 +3257,15 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.26.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7" +checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a" dependencies = [ "bitflags", "fallible-iterator", "fallible-streaming-iterator", "hashlink", "libsqlite3-sys", - "memchr", "smallvec 1.6.1", ] @@ -3072,6 +3317,12 @@ dependencies = [ "semver 0.11.0", ] +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + [[package]] name = "ryu" version = "1.0.5" @@ -3159,8 +3410,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" dependencies = [ "bitflags", - "core-foundation 0.9.2", - "core-foundation-sys 0.8.3", + "core-foundation", + "core-foundation-sys", "libc", "security-framework-sys", ] @@ -3171,7 +3422,7 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" dependencies = [ - "core-foundation-sys 0.8.3", + "core-foundation-sys", "libc", ] @@ -3210,9 +3461,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" dependencies = [ "serde_derive", ] @@ -3229,13 +3480,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", - "syn 1.0.85", + "syn 1.0.100", ] [[package]] @@ -3263,24 +3514,28 @@ dependencies = [ [[package]] name = "serial_test" -version = "0.5.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" +checksum = "92761393ee4dc3ff8f4af487bd58f4307c9329bbedea02cac0089ad9c411e153" dependencies = [ + "dashmap", + "futures 0.3.24", "lazy_static", - "parking_lot 0.10.2", + "log 0.4.14", + "parking_lot 0.12.1", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "0.5.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" +checksum = "4b6f5d1c3087fb119617cff2966fe3808a80e5eb59a8c1601d5994d66f4346a5" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro-error", + "proc-macro2 1.0.43", "quote 1.0.14", - "syn 1.0.85", + "syn 1.0.100", ] [[package]] @@ -3297,9 +3552,18 @@ dependencies = [ [[package]] name = "sha1" -version = "0.6.0" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" [[package]] name = "sha2" @@ -3326,6 +3590,17 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.5", +] + [[package]] name = "signal-hook-registry" version = "1.2.1" @@ -3492,13 +3767,13 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.85" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" +checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", - "unicode-xid 0.2.1", + "unicode-ident", ] [[package]] @@ -3507,20 +3782,20 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", - "syn 1.0.85", + "syn 1.0.100", "unicode-xid 0.2.1", ] [[package]] name = "sysinfo" -version = "0.21.2" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f88d66f3341b688163d3585037954ff276cf24a234d015b30025318a3e3449a" +checksum = "4ae2421f3e16b3afd4aa692d23b83d0ba42ee9b0081d5deeb7d21428d7195fb1" dependencies = [ "cfg-if 1.0.0", - "core-foundation-sys 0.8.3", + "core-foundation-sys", "libc", "ntapi", "once_cell", @@ -3530,25 +3805,31 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4bc0637a2b8c0b1a5145cca3e21b707865edc7e32285771536af1ade129468" +checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" dependencies = [ "bitflags", - "core-foundation 0.7.0", + "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269e271436d8e4bb2621c535a11fe03d5d012f74b19af72f80288f3a72f6180a" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "core-foundation-sys 0.7.0", + "core-foundation-sys", "libc", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.1.0" @@ -3587,22 +3868,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", - "syn 1.0.85", + "syn 1.0.100", ] [[package]] @@ -3636,17 +3917,17 @@ checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" [[package]] name = "tiny-bip39" -version = "0.8.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" dependencies = [ "anyhow", - "hmac 0.8.1", + "hmac 0.12.1", "once_cell", - "pbkdf2 0.4.0", - "rand 0.7.3", + "pbkdf2 0.11.0", + "rand 0.8.5", "rustc-hash", - "sha2 0.9.8", + "sha2 0.10.6", "thiserror", "unicode-normalization", "wasm-bindgen", @@ -3686,9 +3967,18 @@ dependencies = [ [[package]] name = "tinyvec" -version = "0.3.4" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" @@ -3697,7 +3987,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "mio 0.6.22", "num_cpus", "tokio-codec", @@ -3736,7 +4026,7 @@ checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" dependencies = [ "bytes 0.4.12", "either", - "futures", + "futures 0.1.31", ] [[package]] @@ -3746,7 +4036,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "tokio-io", ] @@ -3757,7 +4047,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "iovec", "log 0.4.14", "mio 0.6.22", @@ -3775,7 +4065,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ - "futures", + "futures 0.1.31", "tokio-executor", ] @@ -3786,7 +4076,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ "crossbeam-utils 0.7.2", - "futures", + "futures 0.1.31", ] [[package]] @@ -3795,7 +4085,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" dependencies = [ - "futures", + "futures 0.1.31", "tokio-io", "tokio-threadpool", ] @@ -3807,7 +4097,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "log 0.4.14", ] @@ -3828,7 +4118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ "crossbeam-utils 0.7.2", - "futures", + "futures 0.1.31", "lazy_static", "log 0.4.14", "mio 0.6.22", @@ -3846,7 +4136,7 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0c34c6e548f101053321cba3da7cbb87a610b85555884c41b07da2eb91aff12" dependencies = [ - "futures", + "futures 0.1.31", "libc", "mio 0.6.22", "mio-uds", @@ -3864,7 +4154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ "fnv", - "futures", + "futures 0.1.31", ] [[package]] @@ -3874,7 +4164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "iovec", "mio 0.6.22", "tokio-io", @@ -3890,7 +4180,7 @@ dependencies = [ "crossbeam-deque 0.7.3", "crossbeam-queue", "crossbeam-utils 0.7.2", - "futures", + "futures 0.1.31", "lazy_static", "log 0.4.14", "num_cpus", @@ -3904,7 +4194,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc" dependencies = [ - "futures", + "futures 0.1.31", "slab 0.3.0", ] @@ -3915,7 +4205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ "crossbeam-utils 0.7.2", - "futures", + "futures 0.1.31", "slab 0.4.2", "tokio-executor", ] @@ -3927,7 +4217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "log 0.4.14", "mio 0.6.22", "tokio-codec", @@ -3942,7 +4232,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "iovec", "libc", "log 0.4.14", @@ -4018,7 +4308,7 @@ dependencies = [ "data-encoding", "data-encoding-macro", "failure", - "futures", + "futures 0.1.31", "lazy_static", "log 0.4.14", "radix_trie", @@ -4037,7 +4327,7 @@ checksum = "0838272e89f1c693b4df38dc353412e389cf548ceed6f9fd1af5a8d6e0e7cf74" dependencies = [ "byteorder", "failure", - "futures", + "futures 0.1.31", "idna 0.1.5", "lazy_static", "log 0.4.14", @@ -4061,7 +4351,7 @@ checksum = "09144f0992b0870fa8d2972cc069cbf1e3c0fda64d1f3d45c4d68d0e0b52ad4e" dependencies = [ "byteorder", "failure", - "futures", + "futures 0.1.31", "idna 0.1.5", "lazy_static", "log 0.4.14", @@ -4086,7 +4376,7 @@ dependencies = [ "data-encoding", "enum-as-inner", "failure", - "futures", + "futures 0.1.31", "idna 0.2.0", "lazy_static", "log 0.4.14", @@ -4110,7 +4400,7 @@ checksum = "8a9f877f7a1ad821ab350505e1f1b146a4960402991787191d6d8cab2ce2de2c" dependencies = [ "cfg-if 0.1.10", "failure", - "futures", + "futures 0.1.31", "ipconfig 0.1.9", "lazy_static", "log 0.4.14", @@ -4129,7 +4419,7 @@ checksum = "cb1b3a41ee784f8da051cd342c6f42a3a75ee45818164acad867eac8f2f85332" dependencies = [ "cfg-if 0.1.10", "failure", - "futures", + "futures 0.1.31", "ipconfig 0.2.2", "lazy_static", "log 0.4.14", @@ -4157,9 +4447,9 @@ checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" [[package]] name = "typenum" -version = "1.12.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" @@ -4209,21 +4499,21 @@ dependencies = [ "matches", ] +[[package]] +name = "unicode-ident" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" + [[package]] name = "unicode-normalization" -version = "0.1.13" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" - [[package]] name = "unicode-width" version = "0.1.8" @@ -4316,7 +4606,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" dependencies = [ - "futures", + "futures 0.1.31", "log 0.4.14", "try-lock", ] @@ -4362,9 +4652,9 @@ dependencies = [ "bumpalo", "lazy_static", "log 0.4.14", - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", - "syn 1.0.85", + "syn 1.0.100", "wasm-bindgen-shared", ] @@ -4396,9 +4686,9 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", - "syn 1.0.85", + "syn 1.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4425,19 +4715,19 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a681e8d15deced7c510db88c59133d2eafa7b6298b6e91b545e2a3fed93b3fe" dependencies = [ - "arrayvec", + "arrayvec 0.5.1", "base64 0.12.3", "derive_more", "ethabi", "ethereum-types", - "futures", + "futures 0.1.31", "hyper 0.12.35", "hyper-tls 0.3.2", "jsonrpc-core", "log 0.4.14", "native-tls", "parking_lot 0.10.2", - "rlp", + "rlp 0.4.6", "rustc-hex", "secp256k1", "serde", @@ -4456,7 +4746,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "723abe6b75286edc51d8ecabb38a2353f62a9e9b0588998b59111474f1dcd637" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "hyper 0.10.16", "rand 0.6.5", "tokio-codec", @@ -4478,7 +4768,7 @@ dependencies = [ "bitflags", "byteorder", "bytes 0.4.12", - "futures", + "futures 0.1.31", "rand 0.6.5", "sha-1", "tokio-codec", @@ -4498,6 +4788,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" +[[package]] +name = "widestring" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" + [[package]] name = "wildmatch" version = "1.1.0" @@ -4538,6 +4834,49 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + [[package]] name = "winreg" version = "0.5.1" @@ -4584,6 +4923,15 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "wyz" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +dependencies = [ + "tap", +] + [[package]] name = "xml-rs" version = "0.8.4" @@ -4599,29 +4947,23 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "yansi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" - [[package]] name = "zeroize" -version = "1.4.3" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.2.2" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f1a51723ec88c66d5d1fe80c841f17f63587d6691901d66be9bec6c3b51f73" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.43", "quote 1.0.14", - "syn 1.0.85", + "syn 1.0.100", "synstructure", ] diff --git a/node/Cargo.toml b/node/Cargo.toml index 1a3b8f366..6ea7baf05 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -24,11 +24,11 @@ dirs = "4.0.0" ethabi = "12.0.0" ethsign = {version = "0.7.3", default-features = false, features = ["pure-rust"]} ethsign-crypto = "0.2.1" -ethereum-types = "0.9.0" +ethereum-types = "0.9.2" fdlimit = "0.2.1" -flexi_logger = { version = "0.15.12", features = [ "ziplogs" ] } +flexi_logger = "0.23.3" futures = "0.1.31" -heck = "0.3.3" +heck = "0.4.0" http = "0.2.5" indoc = "1.0.3" itertools = "0.10.1" @@ -37,22 +37,22 @@ libc = "0.2.107" libsecp256k1 = "0.7.0" log = "0.4.14" masq_lib = { path = "../masq_lib"} -pretty-hex = "0.2.1" -primitive-types = {version = "0.5.0", default-features = false, features = ["default", "rlp", "serde"]} +pretty-hex = "0.3.0" +primitive-types = {version = "0.12.0", default-features = false, features = ["default", "rlp", "serde"]} rand = {version = "0.8.4", features = ["getrandom", "small_rng"]} regex = "1.5.4" -rlp = "0.4.6" -rpassword = "5.0.1" -rusqlite = {version = "0.26.1", features = ["bundled"]} +rlp = "0.5.1" +rpassword = "7.0.0" +rusqlite = {version = "0.28.0", features = ["bundled"]} rustc-hex = "2.1.0" serde = "1.0.136" serde_derive = "1.0.136" serde_json = "1.0.79" serde_cbor = "0.11.2" -sha1 = "0.6.0" +sha1 = "0.6.1" sodiumoxide = "0.2.2" -sysinfo = "0.21.1" -tiny-bip39 = "0.8.2" +sysinfo = "0.26.2" +tiny-bip39 = "1.0.0" tiny-hderive = "0.3.0" tokio = "0.1.22" tokio-core = "0.1.18" @@ -66,25 +66,25 @@ secp256k1secrets = {package = "secp256k1", version = "0.17.2"} paste = "1.0.6" [target.'cfg(target_os = "macos")'.dependencies] -system-configuration = "0.4.0" -core-foundation = "0.7.0" +system-configuration = "0.5.0" +core-foundation = "0.9.3" [target.'cfg(not(target_os = "windows"))'.dependencies] daemonize = "0.4.1" -nix = "0.23.0" +nix = "0.25.0" openssl = {version = "0.10.38", features = ["vendored"]} [target.'cfg(target_os = "windows")'.dependencies] winreg = "0.10.1" -ipconfig = "0.2.2" +ipconfig = "0.3.0" [dev-dependencies] base58 = "0.2.0" -jsonrpc-core = "14.0.0" +jsonrpc-core = "14.0.5" native-tls = {version = "0.2.8", features = ["vendored"]} simple-server = "0.4.0" -serial_test_derive = "0.5.1" -serial_test = "0.5.1" +serial_test_derive = "0.9.0" +serial_test = "0.9.0" trust-dns-proto = "0.8.0" [[bin]] diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 297784c9c..8cc59a132 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -2,34 +2,35 @@ pub mod payable_dao; pub mod pending_payable_dao; pub mod receivable_dao; -pub mod tools; +pub mod scanners; +pub mod scanners_tools; #[cfg(test)] pub mod test_utils; use masq_lib::constants::SCAN_ERROR; +use std::cell::RefCell; -use masq_lib::messages::{ScanType, UiScanRequest, UiScanResponse}; +use masq_lib::messages::{ScanType, UiScanRequest}; use masq_lib::ui_gateway::{MessageBody, MessagePath}; -use crate::accountant::payable_dao::{Payable, PayableAccount, PayableDaoError, PayableDaoFactory}; +use crate::accountant::payable_dao::{Payable, PayableDaoError, PayableDaoFactory}; use crate::accountant::pending_payable_dao::{PendingPayableDao, PendingPayableDaoFactory}; -use crate::accountant::receivable_dao::{ - ReceivableAccount, ReceivableDaoError, ReceivableDaoFactory, -}; -use crate::accountant::tools::accountant_tools::{Scanner, Scanners, TransactionConfirmationTools}; -use crate::banned_dao::{BannedDao, BannedDaoFactory}; +use crate::accountant::receivable_dao::{ReceivableDaoError, ReceivableDaoFactory}; +use crate::accountant::scanners::{BeginScanError, NotifyLaterForScanners, Scanners}; +use crate::accountant::scanners_tools::common_tools::timestamp_as_string; +use crate::banned_dao::BannedDaoFactory; use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint, RetrieveTransactions}; use crate::blockchain::blockchain_interface::{BlockchainError, BlockchainTransaction}; use crate::bootstrapper::BootstrapperConfig; use crate::database::dao_utils::DaoFactoryReal; use crate::database::db_migrations::MigratorConfig; +use crate::sub_lib::accountant::FinancialStatistics; +use crate::sub_lib::accountant::ReportExitServiceProvidedMessage; use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; -use crate::sub_lib::accountant::{AccountantConfig, FinancialStatistics, PaymentThresholds}; -use crate::sub_lib::accountant::{AccountantSubs, ReportServicesConsumedMessage}; -use crate::sub_lib::accountant::{ - MessageIdGenerator, MessageIdGeneratorReal, ReportExitServiceProvidedMessage, -}; +use crate::sub_lib::accountant::ReportServicesConsumedMessage; +use crate::sub_lib::accountant::{AccountantSubs, ScanIntervals}; +use crate::sub_lib::accountant::{MessageIdGenerator, MessageIdGeneratorReal}; use crate::sub_lib::blockchain_bridge::ReportAccountsPayable; use crate::sub_lib::peer_actors::{BindMessage, StartMessage}; use crate::sub_lib::utils::{handle_ui_crash_request, NODE_MAILBOX_CAPACITY}; @@ -41,22 +42,19 @@ use actix::Context; use actix::Handler; use actix::Message; use actix::Recipient; -use itertools::Itertools; use masq_lib::crash_point::CrashPoint; use masq_lib::logger::Logger; use masq_lib::messages::UiFinancialsResponse; use masq_lib::messages::{FromMessageBody, ToMessageBody, UiFinancialsRequest}; use masq_lib::ui_gateway::MessageTarget::ClientId; use masq_lib::ui_gateway::{NodeFromUiMessage, NodeToUiMessage}; -use masq_lib::utils::{plus, ExpectValue}; +use masq_lib::utils::ExpectValue; use payable_dao::PayableDao; use receivable_dao::ReceivableDao; -#[cfg(test)] -use std::any::Any; use std::default::Default; -use std::ops::Add; use std::path::Path; -use std::time::{Duration, SystemTime}; +use std::rc::Rc; +use std::time::SystemTime; use web3::types::{TransactionReceipt, H256}; pub const CRASH_KEY: &str = "ACCOUNTANT"; @@ -64,23 +62,23 @@ pub const CRASH_KEY: &str = "ACCOUNTANT"; pub const DEFAULT_PENDING_TOO_LONG_SEC: u64 = 21_600; //6 hours pub struct Accountant { - config: AccountantConfig, + scan_intervals: ScanIntervals, + suppress_initial_scans_opt: Option, consuming_wallet: Option, - earning_wallet: Wallet, + earning_wallet: Rc, payable_dao: Box, receivable_dao: Box, pending_payable_dao: Box, - banned_dao: Box, crashable: bool, scanners: Scanners, - confirmation_tools: TransactionConfirmationTools, - financial_statistics: FinancialStatistics, - report_accounts_payable_sub: Option>, + notify_later: NotifyLaterForScanners, + financial_statistics: Rc>, + report_accounts_payable_sub_opt: Option>, retrieve_transactions_sub: Option>, + request_transaction_receipts_subs_opt: Option>, report_new_payments_sub: Option>, report_sent_payments_sub: Option>, ui_message_sub: Option>, - payable_threshold_tools: Box, message_id_generator: Box, logger: Logger, } @@ -144,7 +142,11 @@ impl Handler for Accountant { type Result = (); fn handle(&mut self, _msg: StartMessage, ctx: &mut Self::Context) -> Self::Result { - if self.config.suppress_initial_scans { + let suppress_initial_scans = self + .suppress_initial_scans_opt + .take() + .expect("Can't process StartMessage for Accountant."); + if suppress_initial_scans { info!( &self.logger, "Started with --scans off; declining to begin database and blockchain scans" @@ -171,7 +173,13 @@ impl Handler for Accountant { type Result = (); fn handle(&mut self, msg: ReceivedPayments, _ctx: &mut Self::Context) -> Self::Result { - self.handle_received_payments(msg); + if let Some(node_to_ui_msg) = self.scanners.receivable.finish_scan(msg, &self.logger) { + self.ui_message_sub + .as_ref() + .expect("UIGateway is not bound") + .try_send(node_to_ui_msg) + .expect("UIGateway is dead"); + } } } @@ -179,7 +187,13 @@ impl Handler for Accountant { type Result = (); fn handle(&mut self, msg: SentPayable, _ctx: &mut Self::Context) -> Self::Result { - self.handle_sent_payable(msg); + if let Some(node_to_ui_msg) = self.scanners.payable.finish_scan(msg, &self.logger) { + self.ui_message_sub + .as_ref() + .expect("UIGateway is not bound") + .try_send(node_to_ui_msg) + .expect("UIGateway is dead"); + } } } @@ -187,11 +201,14 @@ impl Handler for Accountant { type Result = (); fn handle(&mut self, msg: ScanForPayables, ctx: &mut Self::Context) -> Self::Result { - self.handle_scan_message( - self.scanners.payables.as_ref(), - msg.response_skeleton_opt, + self.handle_scan_for_payable_request(msg.response_skeleton_opt); + let _ = self.notify_later.scan_for_payable.notify_later( + ScanForPayables { + response_skeleton_opt: None, + }, + self.scan_intervals.payable_scan_interval, ctx, - ) + ); } } @@ -199,11 +216,14 @@ impl Handler for Accountant { type Result = (); fn handle(&mut self, msg: ScanForPendingPayables, ctx: &mut Self::Context) -> Self::Result { - self.handle_scan_message( - self.scanners.pending_payables.as_ref(), - msg.response_skeleton_opt, + self.handle_scan_for_pending_payable_request(msg.response_skeleton_opt); + let _ = self.notify_later.scan_for_pending_payable.notify_later( + ScanForPendingPayables { + response_skeleton_opt: None, // because scheduled scans don't respond + }, + self.scan_intervals.pending_payable_scan_interval, ctx, - ) + ); } } @@ -211,11 +231,14 @@ impl Handler for Accountant { type Result = (); fn handle(&mut self, msg: ScanForReceivables, ctx: &mut Self::Context) -> Self::Result { - self.handle_scan_message( - self.scanners.receivables.as_ref(), - msg.response_skeleton_opt, + self.handle_scan_for_receivables_request(msg.response_skeleton_opt); + let _ = self.notify_later.scan_for_receivable.notify_later( + ScanForReceivables { + response_skeleton_opt: None, // because scheduled scans don't respond + }, + self.scan_intervals.receivable_scan_interval, ctx, - ) + ); } } @@ -308,57 +331,17 @@ pub struct ReportTransactionReceipts { impl Handler for Accountant { type Result = (); - fn handle(&mut self, msg: ReportTransactionReceipts, ctx: &mut Self::Context) -> Self::Result { - debug!( - self.logger, - "Processing receipts for {} transactions", - msg.fingerprints_with_receipts.len() - ); - let statuses = self.handle_pending_transaction_with_its_receipt(&msg); - self.process_transaction_by_status(statuses, ctx); - if let Some(response_skeleton) = &msg.response_skeleton_opt { + fn handle(&mut self, msg: ReportTransactionReceipts, _ctx: &mut Self::Context) -> Self::Result { + if let Some(node_to_ui_msg) = self.scanners.pending_payable.finish_scan(msg, &self.logger) { self.ui_message_sub .as_ref() - .expect("UIGateway not bound") - .try_send(NodeToUiMessage { - target: ClientId(response_skeleton.client_id), - body: UiScanResponse {}.tmb(response_skeleton.context_id), - }) + .expect("UIGateway is not bound") + .try_send(node_to_ui_msg) .expect("UIGateway is dead"); } } } -#[derive(Debug, PartialEq, Eq, Message, Clone)] -pub struct CancelFailedPendingTransaction { - pub id: PendingPayableId, -} - -impl Handler for Accountant { - type Result = (); - - fn handle( - &mut self, - msg: CancelFailedPendingTransaction, - _ctx: &mut Self::Context, - ) -> Self::Result { - self.handle_cancel_pending_transaction(msg) - } -} - -#[derive(Debug, PartialEq, Eq, Message, Clone)] -pub struct ConfirmPendingTransaction { - pub pending_payable_fingerprint: PendingPayableFingerprint, -} - -impl Handler for Accountant { - type Result = (); - - fn handle(&mut self, msg: ConfirmPendingTransaction, _ctx: &mut Self::Context) -> Self::Result { - self.handle_confirm_pending_transaction(msg) - } -} - impl Handler for Accountant { type Result = (); fn handle(&mut self, msg: PendingPayableFingerprint, _ctx: &mut Self::Context) -> Self::Result { @@ -381,7 +364,7 @@ impl Handler for Accountant { client_id, context_id, }, - ); + ) } else { handle_ui_crash_request(msg, &self.logger, self.crashable, CRASH_KEY) } @@ -390,34 +373,54 @@ impl Handler for Accountant { impl Accountant { pub fn new( - config: &BootstrapperConfig, + config: &mut BootstrapperConfig, payable_dao_factory: Box, receivable_dao_factory: Box, pending_payable_dao_factory: Box, banned_dao_factory: Box, ) -> Accountant { + let payment_thresholds = Rc::new( + config + .payment_thresholds_opt + .take() + .expectv("Payment thresholds"), + ); + let scan_intervals = config.scan_intervals_opt.take().expectv("Scan Intervals"); + let when_pending_too_long_sec = config + .when_pending_too_long_opt + .take() + .expectv("When pending too long sec"); + + let earning_wallet = Rc::new(config.earning_wallet.clone()); + let financial_statistics = Rc::new(RefCell::new(FinancialStatistics::default())); Accountant { - config: *config - .accountant_config_opt - .as_ref() - .expectv("Accountant config"), + scan_intervals, + suppress_initial_scans_opt: config.suppress_initial_scans_opt, consuming_wallet: config.consuming_wallet_opt.clone(), - earning_wallet: config.earning_wallet.clone(), + earning_wallet: Rc::clone(&earning_wallet), payable_dao: payable_dao_factory.make(), receivable_dao: receivable_dao_factory.make(), pending_payable_dao: pending_payable_dao_factory.make(), - banned_dao: banned_dao_factory.make(), crashable: config.crash_point == CrashPoint::Message, - scanners: Scanners::default(), - financial_statistics: FinancialStatistics::default(), - report_accounts_payable_sub: None, + scanners: Scanners::new( + payable_dao_factory, + pending_payable_dao_factory, + receivable_dao_factory.make(), + banned_dao_factory.make(), + payment_thresholds, + Rc::clone(&earning_wallet), + when_pending_too_long_sec, + Rc::clone(&financial_statistics), + ), + notify_later: NotifyLaterForScanners::default(), + financial_statistics: Rc::clone(&financial_statistics), + report_accounts_payable_sub_opt: None, retrieve_transactions_sub: None, + request_transaction_receipts_subs_opt: None, report_new_payments_sub: None, report_sent_payments_sub: None, ui_message_sub: None, - confirmation_tools: TransactionConfirmationTools::default(), message_id_generator: Box::new(MessageIdGeneratorReal::default()), - payable_threshold_tools: Box::new(PayableExceedThresholdToolsReal::default()), logger: Logger::new("Accountant"), } } @@ -442,166 +445,6 @@ impl Accountant { DaoFactoryReal::new(data_directory, false, MigratorConfig::panic_on_migration()) } - fn handle_scan_message( - &self, - scanner: &dyn Scanner, - response_skeleton_opt: Option, - ctx: &mut Context, - ) { - scanner.scan(self, response_skeleton_opt); - scanner.notify_later_assertable(self, ctx) - } - - fn scan_for_payables(&self, response_skeleton_opt: Option) { - info!(self.logger, "Scanning for payables"); - - let all_non_pending_payables = self.payable_dao.non_pending_payables(); - debug!( - self.logger, - "{}", - Self::investigate_debt_extremes(&all_non_pending_payables) - ); - let qualified_payables = all_non_pending_payables - .into_iter() - .filter(|account| self.should_pay(account)) - .collect::>(); - info!( - self.logger, - "Chose {} qualified debts to pay", - qualified_payables.len() - ); - debug!( - self.logger, - "{}", - self.payables_debug_summary(&qualified_payables) - ); - if !qualified_payables.is_empty() { - self.report_accounts_payable_sub - .as_ref() - .expect("BlockchainBridge is unbound") - .try_send(ReportAccountsPayable { - accounts: qualified_payables, - response_skeleton_opt, - }) - .expect("BlockchainBridge is dead") - } - } - - fn scan_for_delinquencies(&self) { - info!(self.logger, "Scanning for delinquencies"); - let now = SystemTime::now(); - self.receivable_dao - .new_delinquencies(now, &self.config.payment_thresholds) - .into_iter() - .for_each(|account| { - self.banned_dao.ban(&account.wallet); - let (balance, age) = Self::balance_and_age(&account); - info!( - self.logger, - "Wallet {} (balance: {} MASQ, age: {} sec) banned for delinquency", - account.wallet, - balance, - age.as_secs() - ) - }); - self.receivable_dao - .paid_delinquencies(&self.config.payment_thresholds) - .into_iter() - .for_each(|account| { - self.banned_dao.unban(&account.wallet); - let (balance, age) = Self::balance_and_age(&account); - info!( - self.logger, - "Wallet {} (balance: {} MASQ, age: {} sec) is no longer delinquent: unbanned", - account.wallet, - balance, - age.as_secs() - ) - }); - } - - fn scan_for_received_payments(&self, response_skeleton_opt: Option) { - info!( - self.logger, - "Scanning for receivables to {}", self.earning_wallet - ); - self.retrieve_transactions_sub - .as_ref() - .expect("BlockchainBridge is unbound") - .try_send(RetrieveTransactions { - recipient: self.earning_wallet.clone(), - response_skeleton_opt, - }) - .expect("BlockchainBridge is dead"); - } - - fn scan_for_pending_payable(&self, response_skeleton_opt: Option) { - info!(self.logger, "Scanning for pending payable"); - let filtered_pending_payable = self.pending_payable_dao.return_all_fingerprints(); - if filtered_pending_payable.is_empty() { - debug!(self.logger, "No pending payable found during last scan") - } else { - debug!( - self.logger, - "Found {} pending payables to process", - filtered_pending_payable.len() - ); - self.confirmation_tools - .request_transaction_receipts_subs_opt - .as_ref() - .expect("BlockchainBridge is unbound") - .try_send(RequestTransactionReceipts { - pending_payable: filtered_pending_payable, - response_skeleton_opt, - }) - .expect("BlockchainBridge is dead"); - } - } - - fn balance_and_age(account: &ReceivableAccount) -> (String, Duration) { - let balance = format!("{}", (account.balance as f64) / 1_000_000_000.0); - let age = account - .last_received_timestamp - .elapsed() - .unwrap_or_else(|_| Duration::new(0, 0)); - (balance, age) - } - - fn should_pay(&self, payable: &PayableAccount) -> bool { - self.payable_exceeded_threshold(payable).is_some() - } - - fn payable_exceeded_threshold(&self, payable: &PayableAccount) -> Option { - // TODO: This calculation should be done in the database, if possible - let time_since_last_paid = SystemTime::now() - .duration_since(payable.last_paid_timestamp) - .expect("Internal error") - .as_secs(); - - if self.payable_threshold_tools.is_innocent_age( - time_since_last_paid, - self.config.payment_thresholds.maturity_threshold_sec as u64, - ) { - return None; - } - - if self.payable_threshold_tools.is_innocent_balance( - payable.balance, - self.config.payment_thresholds.permanent_debt_allowed_gwei, - ) { - return None; - } - - let threshold = self - .payable_threshold_tools - .calculate_payout_threshold(self.config.payment_thresholds, time_since_last_paid); - if payable.balance as f64 > threshold { - Some(threshold as u64) - } else { - None - } - } - fn record_service_provided( &self, service_rate: u64, @@ -678,95 +521,15 @@ impl Accountant { } } - //for debugging only - fn investigate_debt_extremes(all_non_pending_payables: &[PayableAccount]) -> String { - let now = SystemTime::now(); - if all_non_pending_payables.is_empty() { - "Payable scan found no debts".to_string() - } else { - struct PayableInfo { - balance: i64, - age: Duration, - } - let init = ( - PayableInfo { - balance: 0, - age: Duration::ZERO, - }, - PayableInfo { - balance: 0, - age: Duration::ZERO, - }, - ); - let (biggest, oldest) = all_non_pending_payables.iter().fold(init, |sofar, p| { - let (mut biggest, mut oldest) = sofar; - let p_age = now - .duration_since(p.last_paid_timestamp) - .expect("Payable time is corrupt"); - { - //look at a test if not understandable - let check_age_parameter_if_the_first_is_the_same = - || -> bool { p.balance == biggest.balance && p_age > biggest.age }; - - if p.balance > biggest.balance || check_age_parameter_if_the_first_is_the_same() - { - biggest = PayableInfo { - balance: p.balance, - age: p_age, - } - } - - let check_balance_parameter_if_the_first_is_the_same = - || -> bool { p_age == oldest.age && p.balance > oldest.balance }; - - if p_age > oldest.age || check_balance_parameter_if_the_first_is_the_same() { - oldest = PayableInfo { - balance: p.balance, - age: p_age, - } - } - } - (biggest, oldest) - }); - format!("Payable scan found {} debts; the biggest is {} owed for {}sec, the oldest is {} owed for {}sec", - all_non_pending_payables.len(), biggest.balance, biggest.age.as_secs(), - oldest.balance, oldest.age.as_secs()) - } - } - - fn payables_debug_summary(&self, qualified_payables: &[PayableAccount]) -> String { - let now = SystemTime::now(); - let list = qualified_payables - .iter() - .map(|payable| { - let p_age = now - .duration_since(payable.last_paid_timestamp) - .expect("Payable time is corrupt"); - let threshold = self - .payable_exceeded_threshold(payable) - .expect("Threshold suddenly changed!"); - format!( - "{} owed for {}sec exceeds threshold: {}; creditor: {}", - payable.balance, - p_age.as_secs(), - threshold, - payable.wallet - ) - }) - .join("\n"); - String::from("Paying qualified debts:\n").add(&list) - } - fn handle_bind_message(&mut self, msg: BindMessage) { - self.report_accounts_payable_sub = + self.report_accounts_payable_sub_opt = Some(msg.peer_actors.blockchain_bridge.report_accounts_payable); self.retrieve_transactions_sub = Some(msg.peer_actors.blockchain_bridge.retrieve_transactions); self.report_new_payments_sub = Some(msg.peer_actors.accountant.report_new_payments); self.report_sent_payments_sub = Some(msg.peer_actors.accountant.report_sent_payments); self.ui_message_sub = Some(msg.peer_actors.ui_gateway.node_to_ui_message_sub); - self.confirmation_tools - .request_transaction_receipts_subs_opt = Some( + self.request_transaction_receipts_subs_opt = Some( msg.peer_actors .blockchain_bridge .request_transaction_receipts, @@ -774,69 +537,6 @@ impl Accountant { info!(self.logger, "Accountant bound"); } - fn handle_received_payments(&mut self, msg: ReceivedPayments) { - if !msg.payments.is_empty() { - let total_newly_paid_receivable = msg - .payments - .iter() - .fold(0, |so_far, now| so_far + now.gwei_amount); - self.receivable_dao - .as_mut() - .more_money_received(msg.timestamp, msg.payments); - self.financial_statistics.total_paid_receivable += total_newly_paid_receivable; - } - if let Some(response_skeleton) = msg.response_skeleton_opt { - self.ui_message_sub - .as_ref() - .expect("UIGateway is not bound") - .try_send(NodeToUiMessage { - target: ClientId(response_skeleton.client_id), - body: UiScanResponse {}.tmb(response_skeleton.context_id), - }) - .expect("UIGateway is dead"); - } - } - - fn handle_sent_payable(&self, sent_payable: SentPayable) { - let (ok, err) = Self::separate_early_errors(&sent_payable, &self.logger); - debug!(self.logger, "We gathered these errors at sending transactions for payable: {:?}, out of the total of {} attempts", err, ok.len() + err.len()); - self.mark_pending_payable(ok); - if !err.is_empty() { - err.into_iter().for_each(|err| - if let Some(hash) = err.carries_transaction_hash(){ - self.discard_incomplete_transaction_with_a_failure(hash) - } else {debug!(self.logger,"Forgetting a transaction attempt that even did not reach the signing stage")}) - } - if let Some(response_skeleton) = &sent_payable.response_skeleton_opt { - self.ui_message_sub - .as_ref() - .expect("UIGateway is not bound") - .try_send(NodeToUiMessage { - target: ClientId(response_skeleton.client_id), - body: UiScanResponse {}.tmb(response_skeleton.context_id), - }) - .expect("UIGateway is dead"); - } - } - - fn discard_incomplete_transaction_with_a_failure(&self, hash: H256) { - if let Some(rowid) = self.pending_payable_dao.fingerprint_rowid(hash) { - debug!( - self.logger, - "Deleting an existing fingerprint for a failed transaction {:?}", hash - ); - if let Err(e) = self.pending_payable_dao.delete_fingerprint(rowid) { - panic!("Database unmaintainable; payable fingerprint deletion for transaction {:?} has stayed undone due to {:?}", hash,e) - } - }; - - warning!( - self.logger, - "Failed transaction with a hash '{:?}' but without the record - thrown out", - hash - ) - } - fn handle_report_routing_service_provided_message( &mut self, msg: ReportRoutingServiceProvidedMessage, @@ -918,10 +618,11 @@ impl Accountant { } fn handle_financials(&mut self, client_id: u64, context_id: u64) { + let financial_statistics = self.financial_statistics(); let total_unpaid_and_pending_payable = self.payable_dao.total(); - let total_paid_payable = self.financial_statistics.total_paid_payable; + let total_paid_payable = financial_statistics.total_paid_payable; let total_unpaid_receivable = self.receivable_dao.total(); - let total_paid_receivable = self.financial_statistics.total_paid_receivable; + let total_paid_receivable = financial_statistics.total_paid_receivable; let body = UiFinancialsResponse { total_unpaid_and_pending_payable, total_paid_payable, @@ -939,281 +640,162 @@ impl Accountant { .expect("UiGateway is dead"); } - fn handle_externally_triggered_scan( - &self, - _ctx: &mut Context, - scan_type: ScanType, - response_skeleton: ResponseSkeleton, - ) { - match scan_type { - ScanType::Payables => self.scanners.payables.scan(self, Some(response_skeleton)), - ScanType::Receivables => self - .scanners - .receivables - .scan(self, Some(response_skeleton)), - ScanType::PendingPayables => self - .scanners - .pending_payables - .scan(self, Some(response_skeleton)), - } - } - - fn handle_cancel_pending_transaction(&self, msg: CancelFailedPendingTransaction) { - match self - .pending_payable_dao - .mark_failure(msg.id.rowid) - { - Ok(_) => warning!( - self.logger, - "Broken transaction {:?} left with an error mark; you should take over the care of this transaction to make sure your debts will be paid because there is no automated process that can fix this without you", msg.id.hash), - Err(e) => panic!("Unsuccessful attempt for transaction {:?} to mark fatal error at payable fingerprint due to {:?}; database unreliable", msg.id.hash,e), - } - } - - fn handle_confirm_pending_transaction(&mut self, msg: ConfirmPendingTransaction) { - if let Err(e) = self - .payable_dao - .transaction_confirmed(&msg.pending_payable_fingerprint) - { - panic!( - "Was unable to uncheck pending payable '{:?}' after confirmation due to '{:?}'", - msg.pending_payable_fingerprint.hash, e - ) - } else { - self.financial_statistics.total_paid_payable += msg.pending_payable_fingerprint.amount; - debug!( - self.logger, - "Confirmation of transaction {:?}; record for payable was modified", - msg.pending_payable_fingerprint.hash - ); - if let Err(e) = self.pending_payable_dao.delete_fingerprint( - msg.pending_payable_fingerprint - .rowid_opt - .expectv("initialized rowid"), - ) { - panic!("Was unable to delete payable fingerprint '{:?}' after successful transaction due to '{:?}'",msg.pending_payable_fingerprint.hash,e) - } else { + fn handle_scan_for_payable_request(&mut self, response_skeleton_opt: Option) { + match self.scanners.payable.begin_scan( + SystemTime::now(), + response_skeleton_opt, + &self.logger, + ) { + Ok(message) => { + self.report_accounts_payable_sub_opt + .as_ref() + .expect("BlockchainBridge is unbound") + .try_send(message.clone()) + .expect("BlockchainBridge is dead"); + eprintln!("Message was sent to the blockchain bridge, {:?}", message); + } + Err(BeginScanError::CalledFromNullScanner) => { + if cfg!(test) { + eprintln!("Payable scan is disabled."); + } else { + panic!("Null Scanner shouldn't be running inside production code.") + } + } + Err(BeginScanError::NothingToProcess) => { + eprintln!("No payable found to process. The Scan was ended."); + // TODO: Do something better than just using eprintln + } + Err(BeginScanError::ScanAlreadyRunning(timestamp)) => { info!( - self.logger, - "Transaction {:?} has gone through the whole confirmation process succeeding", - msg.pending_payable_fingerprint.hash + &self.logger, + "Payable scan was already initiated at {}. \ + Hence, this scan request will be ignored.", + timestamp_as_string(×tamp) ) } } } - fn separate_early_errors( - sent_payments: &SentPayable, - logger: &Logger, - ) -> (Vec, Vec) { - sent_payments - .payable - .iter() - .fold((vec![],vec![]),|so_far,payment| { - match payment{ - Ok(payment_sent) => (plus(so_far.0,payment_sent.clone()),so_far.1), - Err(error) => { - logger.warning(|| match &error { - BlockchainError::TransactionFailed { .. } => format!("Encountered transaction error at this end: '{:?}'", error), - x => format!("Outbound transaction failure due to '{:?}'. Please check your blockchain service URL configuration.", x) - }); - (so_far.0,plus(so_far.1,error.clone())) - } - } - }) - } - - fn mark_pending_payable(&self, sent_payments: Vec) { - sent_payments - .into_iter() - .for_each(|payable| { - let rowid = match self.pending_payable_dao.fingerprint_rowid(payable.tx_hash) { - Some(rowid) => rowid, - None => panic!("Payable fingerprint for {:?} doesn't exist but should by now; system unreliable", payable.tx_hash) - }; - match self.payable_dao.as_ref().mark_pending_payable_rowid(&payable.to, rowid ) { - Ok(()) => (), - Err(e) => panic!("Was unable to create a mark in payables for a new pending payable '{:?}' due to '{:?}'", payable.tx_hash, e) + fn handle_scan_for_pending_payable_request( + &mut self, + response_skeleton_opt: Option, + ) { + match self.scanners.pending_payable.begin_scan( + SystemTime::now(), + response_skeleton_opt, + &self.logger, + ) { + Ok(message) => self + .request_transaction_receipts_subs_opt + .as_ref() + .expect("BlockchainBridge is unbound") + .try_send(message) + .expect("BlockchainBridge is dead"), + Err(BeginScanError::CalledFromNullScanner) => { + if cfg!(test) { + eprintln!("Pending payable scan is disabled."); + } else { + panic!("Null Scanner shouldn't be running inside production code.") } - debug!(self.logger, "Payable '{:?}' has been marked as pending in the payable table",payable.tx_hash) - }) + } + Err(BeginScanError::NothingToProcess) => { + eprintln!("No pending payable found to process. The Scan was ended."); + // TODO: Do something better than just using eprintln + } + Err(BeginScanError::ScanAlreadyRunning(timestamp)) => { + info!( + &self.logger, + "Pending Payable scan was already initiated at {}. \ + Hence, this scan request will be ignored.", + timestamp_as_string(×tamp) + ) + } + } } - fn handle_pending_transaction_with_its_receipt( - &self, - msg: &ReportTransactionReceipts, - ) -> Vec { - fn handle_none_receipt( - payable: &PendingPayableFingerprint, - logger: &Logger, - ) -> PendingTransactionStatus { - debug!(logger, - "DEBUG: Accountant: Interpreting a receipt for transaction '{:?}' but none was given; attempt {}, {}ms since sending", - payable.hash, payable.attempt_opt.expectv("initialized attempt"),elapsed_in_ms(payable.timestamp) - ); - PendingTransactionStatus::StillPending(PendingPayableId { - hash: payable.hash, - rowid: payable.rowid_opt.expectv("initialized rowid"), - }) - } - msg.fingerprints_with_receipts - .iter() - .map(|(receipt_opt, fingerprint)| match receipt_opt { - Some(receipt) => { - self.interpret_transaction_receipt(receipt, fingerprint, &self.logger) + fn handle_scan_for_receivables_request( + &mut self, + response_skeleton_opt: Option, + ) { + match self.scanners.receivable.begin_scan( + SystemTime::now(), + response_skeleton_opt, + &self.logger, + ) { + Ok(message) => self + .retrieve_transactions_sub + .as_ref() + .expect("BlockchainBridge is unbound") + .try_send(message) + .expect("BlockchainBridge is dead"), + Err(BeginScanError::CalledFromNullScanner) => { + if cfg!(test) { + eprintln!("Receivable scan is disabled."); + } else { + panic!("Null Scanner shouldn't be running inside production code.") } - None => handle_none_receipt(fingerprint, &self.logger), - }) - .collect() + } + Err(BeginScanError::NothingToProcess) => { + eprintln!("The Scan was ended."); + // TODO: Do something better than just using eprintln + } + Err(BeginScanError::ScanAlreadyRunning(timestamp)) => { + info!( + &self.logger, + "Receivable scan was already initiated at {}. \ + Hence, this scan request will be ignored.", + timestamp_as_string(×tamp) + ) + } + }; } - fn interpret_transaction_receipt( - &self, - receipt: &TransactionReceipt, - fingerprint: &PendingPayableFingerprint, - logger: &Logger, - ) -> PendingTransactionStatus { - fn handle_none_status( - fingerprint: &PendingPayableFingerprint, - max_pending_interval: u64, - logger: &Logger, - ) -> PendingTransactionStatus { - info!(logger,"Pending transaction '{:?}' couldn't be confirmed at attempt {} at {}ms after its sending",fingerprint.hash, fingerprint.attempt_opt.expectv("initialized attempt"), elapsed_in_ms(fingerprint.timestamp)); - let elapsed = fingerprint - .timestamp - .elapsed() - .expect("we should be older now"); - let transaction_id = PendingPayableId { - hash: fingerprint.hash, - rowid: fingerprint.rowid_opt.expectv("initialized rowid"), - }; - if max_pending_interval <= elapsed.as_secs() { - error!(logger,"Pending transaction '{:?}' has exceeded the maximum pending time ({}sec) and the confirmation process is going to be aborted now at the final attempt {}; \ - manual resolution is required from the user to complete the transaction.", fingerprint.hash, max_pending_interval, fingerprint.attempt_opt.expectv("initialized attempt")); - PendingTransactionStatus::Failure(transaction_id) - } else { - PendingTransactionStatus::StillPending(transaction_id) + fn handle_externally_triggered_scan( + &mut self, + _ctx: &mut Context, + scan_type: ScanType, + response_skeleton: ResponseSkeleton, + ) { + match scan_type { + ScanType::Payables => self.handle_scan_for_payable_request(Some(response_skeleton)), + ScanType::PendingPayables => { + self.handle_scan_for_pending_payable_request(Some(response_skeleton)); } - } - fn handle_status_with_success( - fingerprint: &PendingPayableFingerprint, - logger: &Logger, - ) -> PendingTransactionStatus { - info!( - logger, - "Transaction '{:?}' has been added to the blockchain; detected locally at attempt {} at {}ms after its sending", - fingerprint.hash, - fingerprint.attempt_opt.expectv("initialized attempt"), - elapsed_in_ms(fingerprint.timestamp) - ); - PendingTransactionStatus::Confirmed(fingerprint.clone()) - } - fn handle_status_with_failure( - fingerprint: &PendingPayableFingerprint, - logger: &Logger, - ) -> PendingTransactionStatus { - error!(logger,"Pending transaction '{:?}' announced as a failure, interpreting attempt {} after {}ms from the sending",fingerprint.hash,fingerprint.attempt_opt.expectv("initialized attempt"),elapsed_in_ms(fingerprint.timestamp)); - PendingTransactionStatus::Failure(fingerprint.into()) - } - match receipt.status{ - None => handle_none_status(fingerprint, self.config.when_pending_too_long_sec, logger), - Some(status_code) => - match status_code.as_u64(){ - 0 => handle_status_with_failure(fingerprint, logger), - 1 => handle_status_with_success(fingerprint, logger), - other => unreachable!("tx receipt for pending '{:?}' - tx status: code other than 0 or 1 shouldn't be possible, but was {}", fingerprint.hash, other) - } + ScanType::Receivables => { + self.handle_scan_for_receivables_request(Some(response_skeleton)) } + } } - fn update_payable_fingerprint(&self, pending_payable_id: PendingPayableId) { + fn handle_new_pending_payable_fingerprint(&self, msg: PendingPayableFingerprint) { match self .pending_payable_dao - .update_fingerprint(pending_payable_id.rowid) + .insert_new_fingerprint(msg.hash, msg.amount, msg.timestamp) { - Ok(_) => trace!( + Ok(_) => debug!( self.logger, - "Updated record for rowid: {} ", - pending_payable_id.rowid + "Processed a pending payable fingerprint for '{:?}'", msg.hash ), - Err(e) => panic!( - "Failure on updating payable fingerprint '{:?}' due to {:?}", - pending_payable_id.hash, e + Err(e) => error!( + self.logger, + "Failed to make a fingerprint for pending payable '{:?}' due to '{:?}'", + msg.hash, + e ), } } - fn process_transaction_by_status( - &self, - statuses: Vec, - ctx: &mut Context, - ) { - statuses.into_iter().for_each(|status| { - if let PendingTransactionStatus::StillPending(transaction_id) = status { - self.update_payable_fingerprint(transaction_id) - } else if let PendingTransactionStatus::Failure(transaction_id) = status { - self.order_cancel_failed_transaction(transaction_id, ctx) - } else if let PendingTransactionStatus::Confirmed(fingerprint) = status { - self.order_confirm_transaction(fingerprint, ctx) - } - }); + fn financial_statistics(&self) -> FinancialStatistics { + self.financial_statistics.as_ref().borrow().clone() } - - fn order_cancel_failed_transaction( - &self, - transaction_id: PendingPayableId, - ctx: &mut Context, - ) { - self.confirmation_tools - .notify_cancel_failed_transaction - .notify(CancelFailedPendingTransaction { id: transaction_id }, ctx) - } - - fn order_confirm_transaction( - &self, - pending_payable_fingerprint: PendingPayableFingerprint, - ctx: &mut Context, - ) { - self.confirmation_tools.notify_confirm_transaction.notify( - ConfirmPendingTransaction { - pending_payable_fingerprint, - }, - ctx, - ); - } - - fn handle_new_pending_payable_fingerprint(&self, msg: PendingPayableFingerprint) { - match self - .pending_payable_dao - .insert_new_fingerprint(msg.hash, msg.amount, msg.timestamp) - { - Ok(_) => debug!( - self.logger, - "Processed a pending payable fingerprint for '{:?}'", msg.hash - ), - Err(e) => error!( - self.logger, - "Failed to make a fingerprint for pending payable '{:?}' due to '{:?}'", - msg.hash, - e - ), - } - } -} +} pub fn unsigned_to_signed(unsigned: u64) -> Result { i64::try_from(unsigned).map_err(|_| unsigned) } -fn elapsed_in_ms(timestamp: SystemTime) -> u128 { - timestamp - .elapsed() - .expect("time calculation for elapsed failed") - .as_millis() -} - #[derive(Debug, PartialEq, Eq, Clone)] -enum PendingTransactionStatus { +pub enum PendingTransactionStatus { StillPending(PendingPayableId), //updates slightly the record, waits an interval and starts a new round Failure(PendingPayableId), //official tx failure Confirmed(PendingPayableFingerprint), //tx was fully processed and successful @@ -1236,52 +818,20 @@ impl From<&PendingPayableFingerprint> for PendingPayableId { } } -//TODO the data types should change with GH-497 (including signed => unsigned) -trait PayableExceedThresholdTools { - fn is_innocent_age(&self, age: u64, limit: u64) -> bool; - fn is_innocent_balance(&self, balance: i64, limit: i64) -> bool; - fn calculate_payout_threshold(&self, payment_thresholds: PaymentThresholds, x: u64) -> f64; - as_any_dcl!(); -} - -#[derive(Default)] -struct PayableExceedThresholdToolsReal {} - -impl PayableExceedThresholdTools for PayableExceedThresholdToolsReal { - fn is_innocent_age(&self, age: u64, limit: u64) -> bool { - age <= limit - } - - fn is_innocent_balance(&self, balance: i64, limit: i64) -> bool { - balance <= limit - } - - fn calculate_payout_threshold(&self, payment_thresholds: PaymentThresholds, x: u64) -> f64 { - let m = -((payment_thresholds.debt_threshold_gwei as f64 - - payment_thresholds.permanent_debt_allowed_gwei as f64) - / (payment_thresholds.threshold_interval_sec as f64 - - payment_thresholds.maturity_threshold_sec as f64)); - let b = payment_thresholds.debt_threshold_gwei as f64 - - m * payment_thresholds.maturity_threshold_sec as f64; - m * x as f64 + b - } - as_any_impl!(); -} - #[cfg(test)] mod tests { use super::*; - use std::cell::RefCell; - use std::ops::Sub; - use std::rc::Rc; + use std::any::TypeId; + use std::collections::HashMap; + use std::ops::{Add, Sub}; + use std::sync::Arc; use std::sync::Mutex; - use std::sync::{Arc, MutexGuard}; use std::time::Duration; - use std::time::SystemTime; use actix::{Arbiter, System}; use ethereum_types::{BigEndianHash, U64}; use ethsign_crypto::Keccak256; + use itertools::Itertools; use log::Level; use masq_lib::constants::SCAN_ERROR; use web3::types::U256; @@ -1291,118 +841,41 @@ mod tests { use masq_lib::test_utils::logging::TestLogHandler; use masq_lib::ui_gateway::{MessageBody, MessagePath, NodeFromUiMessage, NodeToUiMessage}; - use crate::accountant::payable_dao::PayableDaoError; + use crate::accountant::payable_dao::{PayableAccount, PayableDaoError}; use crate::accountant::pending_payable_dao::PendingPayableDaoError; - use crate::accountant::receivable_dao::ReceivableAccount; + use crate::accountant::scanners::{NullScanner, ScannerMock}; use crate::accountant::test_utils::{ - bc_from_ac_plus_earning_wallet, bc_from_ac_plus_wallets, make_pending_payable_fingerprint, - make_receivable_account, BannedDaoFactoryMock, MessageIdGeneratorMock, - PayableDaoFactoryMock, PayableDaoMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, - ReceivableDaoFactoryMock, ReceivableDaoMock, + bc_from_earning_wallet, bc_from_wallets, make_payables, BannedDaoFactoryMock, + MessageIdGeneratorMock, PayableDaoFactoryMock, PayableDaoMock, + PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoFactoryMock, + ReceivableDaoMock, }; use crate::accountant::test_utils::{AccountantBuilder, BannedDaoMock}; - use crate::accountant::tools::accountant_tools::{NullScanner, ReceivablesScanner}; use crate::accountant::Accountant; use crate::blockchain::blockchain_bridge::BlockchainBridge; use crate::blockchain::blockchain_interface::BlockchainError; use crate::blockchain::blockchain_interface::BlockchainTransaction; use crate::blockchain::test_utils::BlockchainInterfaceMock; use crate::blockchain::tool_wrappers::SendTransactionToolsWrapperNull; - use crate::bootstrapper::BootstrapperConfig; use crate::database::dao_utils::from_time_t; use crate::database::dao_utils::to_time_t; use crate::sub_lib::accountant::{ - ExitServiceConsumed, RoutingServiceConsumed, ScanIntervals, DEFAULT_PAYMENT_THRESHOLDS, + ExitServiceConsumed, PaymentThresholds, RoutingServiceConsumed, ScanIntervals, + DEFAULT_PAYMENT_THRESHOLDS, }; use crate::sub_lib::blockchain_bridge::ReportAccountsPayable; - use crate::sub_lib::utils::{NotifyHandleReal, NotifyLaterHandleReal}; + use crate::sub_lib::utils::NotifyLaterHandleReal; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::make_recorder; use crate::test_utils::recorder::peer_actors_builder; use crate::test_utils::recorder::Recorder; use crate::test_utils::unshared_test_utils::{ - make_accountant_config_null, make_populated_accountant_config_with_defaults, - prove_that_crash_request_handler_is_hooked_up, NotifyHandleMock, NotifyLaterHandleMock, - SystemKillerActor, + make_bc_with_defaults, prove_that_crash_request_handler_is_hooked_up, + NotifyLaterHandleMock, SystemKillerActor, }; use crate::test_utils::{make_paying_wallet, make_wallet}; use web3::types::{TransactionReceipt, H256}; - #[derive(Default)] - struct PayableThresholdToolsMock { - is_innocent_age_params: Arc>>, - is_innocent_age_results: RefCell>, - is_innocent_balance_params: Arc>>, - is_innocent_balance_results: RefCell>, - calculate_payout_threshold_params: Arc>>, - calculate_payout_threshold_results: RefCell>, - } - - impl PayableExceedThresholdTools for PayableThresholdToolsMock { - fn is_innocent_age(&self, age: u64, limit: u64) -> bool { - self.is_innocent_age_params - .lock() - .unwrap() - .push((age, limit)); - self.is_innocent_age_results.borrow_mut().remove(0) - } - - fn is_innocent_balance(&self, balance: i64, limit: i64) -> bool { - self.is_innocent_balance_params - .lock() - .unwrap() - .push((balance, limit)); - self.is_innocent_balance_results.borrow_mut().remove(0) - } - - fn calculate_payout_threshold(&self, payment_thresholds: PaymentThresholds, x: u64) -> f64 { - self.calculate_payout_threshold_params - .lock() - .unwrap() - .push((payment_thresholds, x)); - self.calculate_payout_threshold_results - .borrow_mut() - .remove(0) - } - } - - impl PayableThresholdToolsMock { - fn is_innocent_age_params(mut self, params: &Arc>>) -> Self { - self.is_innocent_age_params = params.clone(); - self - } - - fn is_innocent_age_result(self, result: bool) -> Self { - self.is_innocent_age_results.borrow_mut().push(result); - self - } - - fn is_innocent_balance_params(mut self, params: &Arc>>) -> Self { - self.is_innocent_balance_params = params.clone(); - self - } - - fn is_innocent_balance_result(self, result: bool) -> Self { - self.is_innocent_balance_results.borrow_mut().push(result); - self - } - - fn calculate_payout_threshold_params( - mut self, - params: &Arc>>, - ) -> Self { - self.calculate_payout_threshold_params = params.clone(); - self - } - - fn calculate_payout_threshold_result(self, result: f64) -> Self { - self.calculate_payout_threshold_results - .borrow_mut() - .push(result); - self - } - } - #[test] fn constants_have_correct_values() { assert_eq!(CRASH_KEY, "ACCOUNTANT"); @@ -1411,131 +884,123 @@ mod tests { #[test] fn new_calls_factories_properly() { - let mut config = BootstrapperConfig::new(); - config.accountant_config_opt = Some(make_accountant_config_null()); - let payable_dao_factory_called = Rc::new(RefCell::new(false)); - let payable_dao = PayableDaoMock::new(); - let payable_dao_factory = - PayableDaoFactoryMock::new(payable_dao).called(&payable_dao_factory_called); - let receivable_dao_factory_called = Rc::new(RefCell::new(false)); - let receivable_dao = ReceivableDaoMock::new(); - let receivable_dao_factory = - ReceivableDaoFactoryMock::new(receivable_dao).called(&receivable_dao_factory_called); - let pending_payable_dao_factory_called = Rc::new(RefCell::new(false)); - let pending_payable_dao = PendingPayableDaoMock::default(); - let pending_payable_dao_factory = PendingPayableDaoFactoryMock::new(pending_payable_dao) - .called(&pending_payable_dao_factory_called); - let banned_dao_factory_called = Rc::new(RefCell::new(false)); - let banned_dao = BannedDaoMock::new(); - let banned_dao_factory = - BannedDaoFactoryMock::new(banned_dao).called(&banned_dao_factory_called); + let mut config = make_bc_with_defaults(); + let payable_dao_factory_params_arc = Arc::new(Mutex::new(vec![])); + let pending_payable_dao_factory_params_arc = Arc::new(Mutex::new(vec![])); + let receivable_dao_factory_params_arc = Arc::new(Mutex::new(vec![])); + let banned_dao_factory_params_arc = Arc::new(Mutex::new(vec![])); + let payable_dao_factory = PayableDaoFactoryMock::new() + .make_params(&payable_dao_factory_params_arc) + .make_result(PayableDaoMock::new()) // For Accountant + .make_result(PayableDaoMock::new()) // For Payable Scanner + .make_result(PayableDaoMock::new()); // For PendingPayable Scanner + let pending_payable_dao_factory = PendingPayableDaoFactoryMock::new() + .make_params(&pending_payable_dao_factory_params_arc) + .make_result(PendingPayableDaoMock::new()) // For Accountant + .make_result(PendingPayableDaoMock::new()) // For Payable Scanner + .make_result(PendingPayableDaoMock::new()); // For PendingPayable Scanner + let receivable_dao_factory = ReceivableDaoFactoryMock::new() + .make_params(&receivable_dao_factory_params_arc) + .make_result(ReceivableDaoMock::new()) // For Accountant + .make_result(ReceivableDaoMock::new()); // For Receivable Scanner + let banned_dao_factory = BannedDaoFactoryMock::new() + .make_params(&banned_dao_factory_params_arc) + .make_result(BannedDaoMock::new()); // For Receivable Scanner let _ = Accountant::new( - &config, + &mut config, Box::new(payable_dao_factory), Box::new(receivable_dao_factory), Box::new(pending_payable_dao_factory), Box::new(banned_dao_factory), ); - assert_eq!(payable_dao_factory_called.as_ref(), &RefCell::new(true)); - assert_eq!(receivable_dao_factory_called.as_ref(), &RefCell::new(true)); assert_eq!( - pending_payable_dao_factory_called.as_ref(), - &RefCell::new(true) + *payable_dao_factory_params_arc.lock().unwrap(), + vec![(), (), ()] + ); + assert_eq!( + *pending_payable_dao_factory_params_arc.lock().unwrap(), + vec![(), (), ()] + ); + assert_eq!( + *receivable_dao_factory_params_arc.lock().unwrap(), + vec![(), ()] ); - assert_eq!(banned_dao_factory_called.as_ref(), &RefCell::new(true)); + assert_eq!(*banned_dao_factory_params_arc.lock().unwrap(), vec![()]); } #[test] fn accountant_have_proper_defaulted_values() { - let mut bootstrapper_config = BootstrapperConfig::new(); - bootstrapper_config.accountant_config_opt = - Some(make_populated_accountant_config_with_defaults()); - let payable_dao_factory = Box::new(PayableDaoFactoryMock::new(PayableDaoMock::new())); - let receivable_dao_factory = - Box::new(ReceivableDaoFactoryMock::new(ReceivableDaoMock::new())); - let pending_payable_dao_factory = Box::new(PendingPayableDaoFactoryMock::new( - PendingPayableDaoMock::default(), - )); - let banned_dao_factory = Box::new(BannedDaoFactoryMock::new(BannedDaoMock::new())); + let mut bootstrapper_config = make_bc_with_defaults(); + let payable_dao_factory = Box::new( + PayableDaoFactoryMock::new() + .make_result(PayableDaoMock::new()) // For Accountant + .make_result(PayableDaoMock::new()) // For Payable Scanner + .make_result(PayableDaoMock::new()), // For PendingPayable Scanner + ); + let pending_payable_dao_factory = Box::new( + PendingPayableDaoFactoryMock::new() + .make_result(PendingPayableDaoMock::new()) // For Accountant + .make_result(PendingPayableDaoMock::new()) // For Payable Scanner + .make_result(PendingPayableDaoMock::new()), // For PendingPayable Scanner + ); + let receivable_dao_factory = Box::new( + ReceivableDaoFactoryMock::new() + .make_result(ReceivableDaoMock::new()) // For Accountant + .make_result(ReceivableDaoMock::new()), // For Scanner + ); + let banned_dao_factory = + Box::new(BannedDaoFactoryMock::new().make_result(BannedDaoMock::new())); let result = Accountant::new( - &bootstrapper_config, + &mut bootstrapper_config, payable_dao_factory, receivable_dao_factory, pending_payable_dao_factory, banned_dao_factory, ); - let transaction_confirmation_tools = result.confirmation_tools; - transaction_confirmation_tools - .notify_confirm_transaction - .as_any() - .downcast_ref::>() - .unwrap(); - transaction_confirmation_tools - .notify_cancel_failed_transaction - .as_any() - .downcast_ref::>() - .unwrap(); - transaction_confirmation_tools - .notify_later_scan_for_pending_payable + let financial_statistics = result.financial_statistics(); + let notify_later = result.notify_later; + notify_later + .scan_for_pending_payable .as_any() .downcast_ref::>() .unwrap(); - transaction_confirmation_tools - .notify_later_scan_for_payable + notify_later + .scan_for_payable .as_any() .downcast_ref::>() .unwrap(); - transaction_confirmation_tools - .notify_later_scan_for_receivable - .as_any() - .downcast_ref::>() - .unwrap(); - //testing presence of real scanners, there is a different test covering them all - result - .scanners - .receivables - .as_any() - .downcast_ref::() - .unwrap(); - result - .payable_threshold_tools + notify_later + .scan_for_receivable .as_any() - .downcast_ref::() - .unwrap(); - assert_eq!(result.crashable, false); - assert_eq!(result.financial_statistics.total_paid_receivable, 0); - assert_eq!(result.financial_statistics.total_paid_payable, 0); + .downcast_ref::>(); result .message_id_generator .as_any() .downcast_ref::() .unwrap(); + assert_eq!(result.crashable, false); + assert_eq!(financial_statistics.total_paid_receivable, 0); + assert_eq!(financial_statistics.total_paid_payable, 0); } #[test] fn scan_receivables_request() { - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_millis(10_000), - receivable_scan_interval: Duration::from_millis(10_000), - pending_payable_scan_interval: Duration::from_secs(100), - }, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: true, - payment_thresholds: Default::default(), - }, - make_wallet("earning_wallet"), - ); + let mut config = bc_from_earning_wallet(make_wallet("earning_wallet")); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_millis(10_000), + receivable_scan_interval: Duration::from_millis(10_000), + pending_payable_scan_interval: Duration::from_secs(100), + }); let receivable_dao = ReceivableDaoMock::new() .new_delinquencies_result(vec![]) .paid_delinquencies_result(vec![]); let subject = AccountantBuilder::default() .bootstrapper_config(config) + .receivable_dao(ReceivableDaoMock::new()) .receivable_dao(receivable_dao) .build(); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); @@ -1564,7 +1029,7 @@ mod tests { recipient: make_wallet("earning_wallet"), response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, - context_id: 4321 + context_id: 4321, }), } ); @@ -1572,19 +1037,13 @@ mod tests { #[test] fn received_payments_with_response_skeleton_sends_response_to_ui_gateway() { - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_millis(10_000), - receivable_scan_interval: Duration::from_millis(10_000), - pending_payable_scan_interval: Duration::from_secs(100), - }, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: true, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - }, - make_wallet("earning_wallet"), - ); + let mut config = bc_from_earning_wallet(make_wallet("earning_wallet")); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_millis(10_000), + receivable_scan_interval: Duration::from_millis(10_000), + pending_payable_scan_interval: Duration::from_secs(100), + }); + config.suppress_initial_scans_opt = Some(true); let subject = AccountantBuilder::default() .bootstrapper_config(config) .build(); @@ -1618,19 +1077,7 @@ mod tests { #[test] fn scan_payables_request() { - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_millis(10_000), - receivable_scan_interval: Duration::from_millis(10_000), - pending_payable_scan_interval: Duration::from_secs(100), - }, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: true, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - }, - make_wallet("some_wallet_address"), - ); + let config = bc_from_earning_wallet(make_wallet("some_wallet_address")); let payable_account = PayableAccount { wallet: make_wallet("wallet"), balance: DEFAULT_PAYMENT_THRESHOLDS.debt_threshold_gwei + 1, @@ -1643,7 +1090,9 @@ mod tests { PayableDaoMock::new().non_pending_payables_result(vec![payable_account.clone()]); let subject = AccountantBuilder::default() .bootstrapper_config(config) - .payable_dao(payable_dao) + .payable_dao(PayableDaoMock::new()) // For Accountant + .payable_dao(payable_dao) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable Scanner .build(); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let subject_addr = subject.start(); @@ -1671,7 +1120,7 @@ mod tests { accounts: vec![payable_account], response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, - context_id: 4321 + context_id: 4321, }), } ); @@ -1679,19 +1128,7 @@ mod tests { #[test] fn sent_payable_with_response_skeleton_sends_scan_response_to_ui_gateway() { - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_millis(10_000), - receivable_scan_interval: Duration::from_millis(10_000), - pending_payable_scan_interval: Duration::from_secs(100), - }, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: true, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - }, - make_wallet("earning_wallet"), - ); + let config = bc_from_earning_wallet(make_wallet("earning_wallet")); let subject = AccountantBuilder::default() .bootstrapper_config(config) .build(); @@ -1725,19 +1162,13 @@ mod tests { #[test] fn scan_pending_payables_request() { - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_millis(10_000), - receivable_scan_interval: Duration::from_millis(10_000), - pending_payable_scan_interval: Duration::from_secs(100), - }, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: true, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - }, - make_wallet("some_wallet_address"), - ); + let mut config = bc_from_earning_wallet(make_wallet("some_wallet_address")); + config.suppress_initial_scans_opt = Some(true); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_millis(10_000), + receivable_scan_interval: Duration::from_millis(10_000), + pending_payable_scan_interval: Duration::from_secs(100), + }); let fingerprint = PendingPayableFingerprint { rowid_opt: Some(1234), timestamp: SystemTime::now(), @@ -1750,7 +1181,9 @@ mod tests { .return_all_fingerprints_result(vec![fingerprint.clone()]); let subject = AccountantBuilder::default() .bootstrapper_config(config) - .pending_payable_dao(pending_payable_dao) + .pending_payable_dao(PendingPayableDaoMock::new()) // For Accountant + .pending_payable_dao(PendingPayableDaoMock::new()) // For Payable Scanner + .pending_payable_dao(pending_payable_dao) // For PendingPayable Scanner .build(); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let subject_addr = subject.start(); @@ -1778,27 +1211,83 @@ mod tests { pending_payable: vec![fingerprint], response_skeleton_opt: Some(ResponseSkeleton { client_id: 1234, - context_id: 4321 + context_id: 4321, }), } ); } + #[test] + fn scan_request_from_ui_is_handled_in_case_the_scan_is_already_running() { + init_test_logging(); + let test_name = "scan_request_from_ui_is_handled_in_case_the_scan_is_already_running"; + let mut config = bc_from_earning_wallet(make_wallet("some_wallet_address")); + config.suppress_initial_scans_opt = Some(true); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_millis(10_000), + receivable_scan_interval: Duration::from_millis(10_000), + pending_payable_scan_interval: Duration::from_secs(100), + }); + let fingerprint = PendingPayableFingerprint { + rowid_opt: Some(1234), + timestamp: SystemTime::now(), + hash: Default::default(), + attempt_opt: Some(1), + amount: 1_000_000, + process_error: None, + }; + let pending_payable_dao = + PendingPayableDaoMock::default().return_all_fingerprints_result(vec![fingerprint]); + let mut subject = AccountantBuilder::default() + .bootstrapper_config(config) + .pending_payable_dao(PendingPayableDaoMock::new()) // For Accountant + .pending_payable_dao(PendingPayableDaoMock::new()) // For Payable Scanner + .pending_payable_dao(pending_payable_dao) // For PendingPayable Scanner + .build(); + subject.logger = Logger::new(test_name); + let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); + let subject_addr = subject.start(); + let system = System::new("test"); + let peer_actors = peer_actors_builder() + .blockchain_bridge(blockchain_bridge) + .build(); + subject_addr.try_send(BindMessage { peer_actors }).unwrap(); + let ui_message = NodeFromUiMessage { + client_id: 1234, + body: UiScanRequest { + scan_type: ScanType::PendingPayables, + } + .tmb(4321), + }; + subject_addr.try_send(ui_message).unwrap(); + let ui_message = NodeFromUiMessage { + client_id: 1234, + body: UiScanRequest { + scan_type: ScanType::PendingPayables, + } + .tmb(4321), + }; + + subject_addr.try_send(ui_message).unwrap(); + + System::current().stop(); + system.run(); + let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); + TestLogHandler::new().exists_log_containing(&format!( + "INFO: {}: Pending Payable scan was already initiated", + test_name + )); + assert_eq!(blockchain_bridge_recording.len(), 1); + } + #[test] fn report_transaction_receipts_with_response_skeleton_sends_scan_response_to_ui_gateway() { - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_millis(10_000), - receivable_scan_interval: Duration::from_millis(10_000), - pending_payable_scan_interval: Duration::from_secs(100), - }, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: true, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - }, - make_wallet("earning_wallet"), - ); + let mut config = bc_from_earning_wallet(make_wallet("earning_wallet")); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_millis(10_000), + receivable_scan_interval: Duration::from_millis(10_000), + pending_payable_scan_interval: Duration::from_secs(100), + }); let subject = AccountantBuilder::default() .bootstrapper_config(config) .build(); @@ -1846,12 +1335,13 @@ mod tests { .mark_pending_payable_rowid_result(Ok(())); let system = System::new("accountant_calls_payable_dao_to_mark_pending_payable"); let accountant = AccountantBuilder::default() - .bootstrapper_config(bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - make_wallet("some_wallet_address"), - )) - .payable_dao(payable_dao) - .pending_payable_dao(pending_payable_dao) + .bootstrapper_config(bc_from_earning_wallet(make_wallet("some_wallet_address"))) + .payable_dao(PayableDaoMock::new()) // For Accountant + .payable_dao(payable_dao) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable Scanner + .pending_payable_dao(PendingPayableDaoMock::new()) // For Accountant + .pending_payable_dao(pending_payable_dao) // For Payable Scanner + .pending_payable_dao(PendingPayableDaoMock::new()) // For PendingPayable Scanner .build(); let expected_payable = Payable::new( expected_wallet.clone(), @@ -1887,7 +1377,9 @@ mod tests { let system = System::new("sent payable failure without backup"); let pending_payable_dao = PendingPayableDaoMock::default().fingerprint_rowid_result(None); let accountant = AccountantBuilder::default() - .pending_payable_dao(pending_payable_dao) + .pending_payable_dao(PendingPayableDaoMock::new()) // For Accountant + .pending_payable_dao(pending_payable_dao) // For Payable Scanner + .pending_payable_dao(PendingPayableDaoMock::new()) // For PendingPayable Scanner .build(); let hash = H256::from_uint(&U256::from(12345)); let sent_payable = SentPayable { @@ -1917,7 +1409,7 @@ mod tests { hash )); log_handler.exists_log_containing( - r#"WARN: Accountant: Failed transaction with a hash '0x0000000000000000000000000000000000000000000000000000000000003039' but without the record - thrown out"#, + r#"WARN: Accountant: Failed transaction with a hash '0x0000…3039' but without the record - thrown out"#, ); } @@ -1941,8 +1433,12 @@ mod tests { .delete_fingerprint_params(&delete_fingerprint_params_arc) .delete_fingerprint_result(Ok(())); let subject = AccountantBuilder::default() - .payable_dao(payable_dao) - .pending_payable_dao(pending_payable_dao) + .payable_dao(PayableDaoMock::new()) // For Accountant + .payable_dao(payable_dao) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable + .pending_payable_dao(PendingPayableDaoMock::new()) // For Accountant + .pending_payable_dao(pending_payable_dao) // For Payable Scanner + .pending_payable_dao(PendingPayableDaoMock::new()) // For Scanner .build(); let wallet = make_wallet("blah"); let hash_tx_1 = H256::from_uint(&U256::from(5555)); @@ -1991,7 +1487,7 @@ mod tests { log_handler.exists_log_containing("WARN: Accountant: Encountered transaction error at this end: \ 'TransactionFailed { msg: \"Attempt failed\", hash_opt: Some(0x0000000000000000000000000000000000000000000000000000000000003039)"); log_handler.exists_log_containing( - "DEBUG: Accountant: Deleting an existing fingerprint for a failed transaction 0x0000000000000000000000000000000000000000000000000000000000003039", + "DEBUG: Accountant: Deleting an existing backup for a failed transaction 0x0000…3039", ); } @@ -1999,39 +1495,21 @@ mod tests { fn accountant_sends_report_accounts_payable_to_blockchain_bridge_when_qualified_payable_found() { let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); - let accounts = vec![ - PayableAccount { - wallet: make_wallet("blah"), - balance: DEFAULT_PAYMENT_THRESHOLDS.debt_threshold_gwei + 55, - last_paid_timestamp: from_time_t( - to_time_t(SystemTime::now()) - - DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec - - 5, - ), - pending_payable_opt: None, - }, - PayableAccount { - wallet: make_wallet("foo"), - balance: DEFAULT_PAYMENT_THRESHOLDS.debt_threshold_gwei + 66, - last_paid_timestamp: from_time_t( - to_time_t(SystemTime::now()) - - DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec - - 500, - ), - pending_payable_opt: None, - }, - ]; - let payable_dao = PayableDaoMock::new().non_pending_payables_result(accounts.clone()); + let now = SystemTime::now(); + let payment_thresholds = PaymentThresholds::default(); + let (qualified_payables, _, all_non_pending_payables) = + make_payables(now, &payment_thresholds); + let payable_dao = + PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); let system = System::new("report_accounts_payable forwarded to blockchain_bridge"); let mut subject = AccountantBuilder::default() - .bootstrapper_config(bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - make_wallet("some_wallet_address"), - )) - .payable_dao(payable_dao) + .bootstrapper_config(bc_from_earning_wallet(make_wallet("some_wallet_address"))) + .payable_dao(PayableDaoMock::new()) // For Accountant + .payable_dao(payable_dao) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable Scanner .build(); - subject.scanners.pending_payables = Box::new(NullScanner); - subject.scanners.receivables = Box::new(NullScanner); + subject.scanners.pending_payable = Box::new(NullScanner::new()); + subject.scanners.receivable = Box::new(NullScanner::new()); let accountant_addr = subject.start(); let accountant_subs = Accountant::make_subs_from(&accountant_addr); let peer_actors = peer_actors_builder() @@ -2045,18 +1523,13 @@ mod tests { system.run(); let blockchain_bridge_recorder = blockchain_bridge_recording_arc.lock().unwrap(); assert_eq!(blockchain_bridge_recorder.len(), 1); - let report_accounts_payables_msgs: Vec<&ReportAccountsPayable> = (0 - ..blockchain_bridge_recorder.len()) - .flat_map(|index| { - blockchain_bridge_recorder.get_record_opt::(index) - }) - .collect(); + let message = blockchain_bridge_recorder.get_record::(0); assert_eq!( - report_accounts_payables_msgs, - vec![&ReportAccountsPayable { - accounts, - response_skeleton_opt: None - }] + message, + &ReportAccountsPayable { + accounts: qualified_payables, + response_skeleton_opt: None, + } ); } @@ -2068,20 +1541,16 @@ mod tests { let system = System::new( "accountant_sends_a_request_to_blockchain_bridge_to_scan_for_received_payments", ); - let payable_dao = PayableDaoMock::new().non_pending_payables_result(vec![]); let receivable_dao = ReceivableDaoMock::new() .new_delinquencies_result(vec![]) .paid_delinquencies_result(vec![]); let mut subject = AccountantBuilder::default() - .bootstrapper_config(bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - earning_wallet.clone(), - )) - .payable_dao(payable_dao) - .receivable_dao(receivable_dao) + .bootstrapper_config(bc_from_earning_wallet(earning_wallet.clone())) + .receivable_dao(ReceivableDaoMock::new()) // For Accountant + .receivable_dao(receivable_dao) // For Scanner .build(); - subject.scanners.pending_payables = Box::new(NullScanner); - subject.scanners.payables = Box::new(NullScanner); + subject.scanners.pending_payable = Box::new(NullScanner::new()); + subject.scanners.payable = Box::new(NullScanner::new()); let accountant_addr = subject.start(); let accountant_subs = Accountant::make_subs_from(&accountant_addr); let peer_actors = peer_actors_builder() @@ -2121,15 +1590,16 @@ mod tests { gwei_amount: 10000, }; let more_money_received_params_arc = Arc::new(Mutex::new(vec![])); + let payable_dao = PayableDaoMock::new().non_pending_payables_result(vec![]); let receivable_dao = ReceivableDaoMock::new() .more_money_received_parameters(&more_money_received_params_arc) .more_money_received_result(Ok(())); let accountant = AccountantBuilder::default() - .bootstrapper_config(bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - earning_wallet.clone(), - )) - .payable_dao(PayableDaoMock::new().non_pending_payables_result(vec![])) + .bootstrapper_config(bc_from_earning_wallet(earning_wallet.clone())) + .payable_dao(payable_dao) + .payable_dao(PayableDaoMock::new()) + .payable_dao(PayableDaoMock::new()) + .receivable_dao(ReceivableDaoMock::new()) .receivable_dao(receivable_dao) .build(); let system = System::new("accountant_receives_new_payments_to_the_receivables_dao"); @@ -2155,43 +1625,35 @@ mod tests { #[test] fn accountant_scans_after_startup() { init_test_logging(); - let return_all_fingerprints_params_arc = Arc::new(Mutex::new(vec![])); - let non_pending_payables_params_arc = Arc::new(Mutex::new(vec![])); + let pending_payable_params_arc = Arc::new(Mutex::new(vec![])); + let payable_params_arc = Arc::new(Mutex::new(vec![])); let new_delinquencies_params_arc = Arc::new(Mutex::new(vec![])); let paid_delinquencies_params_arc = Arc::new(Mutex::new(vec![])); let (blockchain_bridge, _, _) = make_recorder(); + let earning_wallet = make_wallet("earning"); let system = System::new("accountant_scans_after_startup"); - let config = bc_from_ac_plus_wallets( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_secs(100), //making sure we cannot enter the first repeated scanning - receivable_scan_interval: Duration::from_secs(100), - pending_payable_scan_interval: Duration::from_millis(100), //except here, where we use it to stop the system - }, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: false, - }, - make_wallet("buy"), - make_wallet("hi"), - ); - let mut pending_payable_dao = PendingPayableDaoMock::default() - .return_all_fingerprints_params(&return_all_fingerprints_params_arc) + let config = bc_from_wallets(make_wallet("buy"), earning_wallet.clone()); + let payable_dao = PayableDaoMock::new() + .non_pending_payables_params(&payable_params_arc) + .non_pending_payables_result(vec![]); + let pending_payable_dao = PendingPayableDaoMock::default() + .return_all_fingerprints_params(&pending_payable_params_arc) .return_all_fingerprints_result(vec![]); - pending_payable_dao.have_return_all_fingerprints_shut_down_the_system = true; let receivable_dao = ReceivableDaoMock::new() .new_delinquencies_parameters(&new_delinquencies_params_arc) .new_delinquencies_result(vec![]) .paid_delinquencies_parameters(&paid_delinquencies_params_arc) .paid_delinquencies_result(vec![]); - let payable_dao = PayableDaoMock::new() - .non_pending_payables_params(&non_pending_payables_params_arc) - .non_pending_payables_result(vec![]); let subject = AccountantBuilder::default() .bootstrapper_config(config) - .payable_dao(payable_dao) - .receivable_dao(receivable_dao) - .pending_payable_dao(pending_payable_dao) + .payable_dao(PayableDaoMock::new()) // For Accountant + .payable_dao(payable_dao) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable Scanner + .pending_payable_dao(PendingPayableDaoMock::new()) // For Accountant + .pending_payable_dao(PendingPayableDaoMock::new()) // For Payable Scanner + .pending_payable_dao(pending_payable_dao) // For PendingPayable Scanner + .receivable_dao(ReceivableDaoMock::new()) // For Accountant + .receivable_dao(receivable_dao) // For Scanner .build(); let peer_actors = peer_actors_builder() .blockchain_bridge(blockchain_bridge) @@ -2202,129 +1664,78 @@ mod tests { send_start_message!(subject_subs); + System::current().stop(); system.run(); let tlh = TestLogHandler::new(); - tlh.await_log_containing("INFO: Accountant: Scanning for payables", 1000); + tlh.exists_log_containing("INFO: Accountant: Scanning for payables"); + tlh.exists_log_containing("INFO: Accountant: Scanning for pending payable"); tlh.exists_log_containing(&format!( "INFO: Accountant: Scanning for receivables to {}", - make_wallet("hi") + earning_wallet )); tlh.exists_log_containing("INFO: Accountant: Scanning for delinquencies"); - tlh.exists_log_containing("INFO: Accountant: Scanning for pending payable"); - //some more weak proofs but still good enough - //proof of calling a piece of scan_for_pending_payable - let return_all_fingerprints_params = return_all_fingerprints_params_arc.lock().unwrap(); - //the last ends this test calling System::current.stop() - assert_eq!(*return_all_fingerprints_params, vec![(), ()]); - //proof of calling a piece of scan_for_payable() - let non_pending_payables_params = non_pending_payables_params_arc.lock().unwrap(); - assert_eq!(*non_pending_payables_params, vec![()]); + let payable_params = payable_params_arc.lock().unwrap(); + let pending_payable_params = pending_payable_params_arc.lock().unwrap(); //proof of calling pieces of scan_for_delinquencies() let mut new_delinquencies_params = new_delinquencies_params_arc.lock().unwrap(); let (captured_timestamp, captured_curves) = new_delinquencies_params.remove(0); + let paid_delinquencies_params = paid_delinquencies_params_arc.lock().unwrap(); + assert_eq!(*payable_params, vec![()]); + assert_eq!(*pending_payable_params, vec![()]); assert!(new_delinquencies_params.is_empty()); assert!( captured_timestamp < SystemTime::now() && captured_timestamp >= from_time_t(to_time_t(SystemTime::now()) - 5) ); - assert_eq!(captured_curves, *DEFAULT_PAYMENT_THRESHOLDS); - let paid_delinquencies_params = paid_delinquencies_params_arc.lock().unwrap(); + assert_eq!(captured_curves, PaymentThresholds::default()); assert_eq!(paid_delinquencies_params.len(), 1); - assert_eq!(paid_delinquencies_params[0], *DEFAULT_PAYMENT_THRESHOLDS); + assert_eq!(paid_delinquencies_params[0], PaymentThresholds::default()); } #[test] fn periodical_scanning_for_receivables_and_delinquencies_works() { init_test_logging(); - let new_delinquencies_params_arc = Arc::new(Mutex::new(vec![])); - let ban_params_arc = Arc::new(Mutex::new(vec![])); + let test_name = "periodical_scanning_for_receivables_and_delinquencies_works"; + let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); let notify_later_receivable_params_arc = Arc::new(Mutex::new(vec![])); - let earning_wallet = make_wallet("earner3000"); - let wallet_to_be_banned = make_wallet("bad_luck"); - let (blockchain_bridge, _, blockchain_bridge_recording) = make_recorder(); - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_secs(100), - receivable_scan_interval: Duration::from_millis(99), - pending_payable_scan_interval: Duration::from_secs(100), - }, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: false, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - }, - earning_wallet.clone(), - ); - let new_delinquent_account = ReceivableAccount { - wallet: wallet_to_be_banned.clone(), - balance: 4567, - last_received_timestamp: from_time_t(200_000_000), - }; - let system = System::new("periodical_scanning_for_receivables_and_delinquencies_works"); - let banned_dao = BannedDaoMock::new().ban_parameters(&ban_params_arc); - let mut receivable_dao = ReceivableDaoMock::new() - .new_delinquencies_parameters(&new_delinquencies_params_arc) - //this is the immediate try, not with our interval - .new_delinquencies_result(vec![]) - //after the interval we actually process data - .new_delinquencies_result(vec![new_delinquent_account]) - .paid_delinquencies_result(vec![]) - .paid_delinquencies_result(vec![]) - .paid_delinquencies_result(vec![]); - receivable_dao.have_new_delinquencies_shutdown_the_system = true; + let system = System::new(test_name); + SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions + let receivable_scanner = ScannerMock::new() + .begin_scan_params(&begin_scan_params_arc) + .begin_scan_result(Err(BeginScanError::NothingToProcess)) + .begin_scan_result(Ok(RetrieveTransactions { + recipient: make_wallet("some_recipient"), + response_skeleton_opt: None, + })) + .stop_the_system(); + let mut config = make_bc_with_defaults(); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_secs(100), + receivable_scan_interval: Duration::from_millis(99), + pending_payable_scan_interval: Duration::from_secs(100), + }); let mut subject = AccountantBuilder::default() .bootstrapper_config(config) - .receivable_dao(receivable_dao) - .banned_dao(banned_dao) .build(); - subject.scanners.pending_payables = Box::new(NullScanner); - subject.scanners.payables = Box::new(NullScanner); - subject.confirmation_tools.notify_later_scan_for_receivable = Box::new( + subject.scanners.payable = Box::new(NullScanner::new()); // Skipping + subject.scanners.pending_payable = Box::new(NullScanner::new()); // Skipping + subject.scanners.receivable = Box::new(receivable_scanner); + subject.notify_later.scan_for_receivable = Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_receivable_params_arc) .permit_to_send_out(), ); - let peer_actors = peer_actors_builder() - .blockchain_bridge(blockchain_bridge) - .build(); - let subject_addr: Addr = subject.start(); + let subject_addr = subject.start(); let subject_subs = Accountant::make_subs_from(&subject_addr); + let peer_actors = peer_actors_builder().build(); send_bind_message!(subject_subs, peer_actors); send_start_message!(subject_subs); system.run(); - let retrieve_transactions_recording = blockchain_bridge_recording.lock().unwrap(); - assert_eq!(retrieve_transactions_recording.len(), 3); - let retrieve_transactions_msgs: Vec<&RetrieveTransactions> = (0 - ..retrieve_transactions_recording.len()) - .map(|index| retrieve_transactions_recording.get_record::(index)) - .collect(); - assert_eq!( - *retrieve_transactions_msgs, - vec![ - &RetrieveTransactions { - recipient: earning_wallet.clone(), - response_skeleton_opt: None, - }, - &RetrieveTransactions { - recipient: earning_wallet.clone(), - response_skeleton_opt: None, - }, - &RetrieveTransactions { - recipient: earning_wallet.clone(), - response_skeleton_opt: None, - } - ] - ); - //sadly I cannot effectively assert on the exact params - //they are a) real timestamp of now, b) constant payment_thresholds - //the Rust type system gives me enough support to be okay with counting occurrences - let new_delinquencies_params = new_delinquencies_params_arc.lock().unwrap(); - assert_eq!(new_delinquencies_params.len(), 3); //the third one is the signal to shut the system down - let ban_params = ban_params_arc.lock().unwrap(); - assert_eq!(*ban_params, vec![wallet_to_be_banned]); + let begin_scan_params = begin_scan_params_arc.lock().unwrap(); let notify_later_receivable_params = notify_later_receivable_params_arc.lock().unwrap(); + assert_eq!(begin_scan_params.len(), 2); assert_eq!( *notify_later_receivable_params, vec![ @@ -2340,92 +1751,53 @@ mod tests { }, Duration::from_millis(99) ), - ( - ScanForReceivables { - response_skeleton_opt: None - }, - Duration::from_millis(99) - ) ] ) } #[test] fn periodical_scanning_for_pending_payable_works() { - //in the very first round we scan without waiting but we cannot find any pending payable init_test_logging(); - let return_all_pending_payable_fingerprints_params_arc = Arc::new(Mutex::new(vec![])); + let test_name = "periodical_scanning_for_pending_payable_works"; + let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); let notify_later_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); - let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); - let system = - System::new("accountant_payable_scan_timer_triggers_scanning_for_pending_payable"); - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_secs(100), - receivable_scan_interval: Duration::from_secs(100), - pending_payable_scan_interval: Duration::from_millis(98), - }, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: false, - }, - make_wallet("hi"), - ); - // slightly above minimum balance, to the right of the curve (time intersection) - let pending_payable_fingerprint_record = PendingPayableFingerprint { - rowid_opt: Some(45454), - timestamp: SystemTime::now(), - hash: H256::from_uint(&U256::from(565)), - attempt_opt: Some(1), - amount: 4589, - process_error: None, - }; - let mut pending_payable_dao = PendingPayableDaoMock::default() - .return_all_fingerprints_params(&return_all_pending_payable_fingerprints_params_arc) - .return_all_fingerprints_result(vec![]) - .return_all_fingerprints_result(vec![pending_payable_fingerprint_record.clone()]); - pending_payable_dao.have_return_all_fingerprints_shut_down_the_system = true; - let peer_actors = peer_actors_builder() - .blockchain_bridge(blockchain_bridge) - .build(); + let system = System::new(test_name); + SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions + let pending_payable_scanner = ScannerMock::new() + .begin_scan_params(&begin_scan_params_arc) + .begin_scan_result(Err(BeginScanError::NothingToProcess)) + .begin_scan_result(Ok(RequestTransactionReceipts { + pending_payable: vec![], + response_skeleton_opt: None, + })) + .stop_the_system(); + let mut config = make_bc_with_defaults(); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_secs(100), + receivable_scan_interval: Duration::from_secs(100), + pending_payable_scan_interval: Duration::from_millis(98), + }); let mut subject = AccountantBuilder::default() .bootstrapper_config(config) - .pending_payable_dao(pending_payable_dao) .build(); - subject.scanners.receivables = Box::new(NullScanner); //skipping - subject.scanners.payables = Box::new(NullScanner); //skipping - subject - .confirmation_tools - .notify_later_scan_for_pending_payable = Box::new( + subject.scanners.payable = Box::new(NullScanner::new()); //skipping + subject.scanners.pending_payable = Box::new(pending_payable_scanner); + subject.scanners.receivable = Box::new(NullScanner::new()); //skipping + subject.notify_later.scan_for_pending_payable = Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_pending_payable_params_arc) .permit_to_send_out(), ); let subject_addr: Addr = subject.start(); let subject_subs = Accountant::make_subs_from(&subject_addr); + let peer_actors = peer_actors_builder().build(); send_bind_message!(subject_subs, peer_actors); send_start_message!(subject_subs); system.run(); - let return_all_pending_payable_fingerprints = - return_all_pending_payable_fingerprints_params_arc - .lock() - .unwrap(); - //the third attempt is the one where the queue is empty and System::current.stop() ends the cycle - assert_eq!(*return_all_pending_payable_fingerprints, vec![(), (), ()]); - let blockchain_bridge_recorder = blockchain_bridge_recording_arc.lock().unwrap(); - assert_eq!(blockchain_bridge_recorder.len(), 1); - let request_transaction_receipt_msg = - blockchain_bridge_recorder.get_record::(0); - assert_eq!( - request_transaction_receipt_msg, - &RequestTransactionReceipts { - pending_payable: vec![pending_payable_fingerprint_record], - response_skeleton_opt: None, - } - ); + let begin_scan_params = begin_scan_params_arc.lock().unwrap(); + assert_eq!(begin_scan_params.len(), 2); let notify_later_pending_payable_params = notify_later_pending_payable_params_arc.lock().unwrap(); assert_eq!( @@ -2443,88 +1815,56 @@ mod tests { }, Duration::from_millis(98) ), - ( - ScanForPendingPayables { - response_skeleton_opt: None - }, - Duration::from_millis(98) - ) ] ) } #[test] - fn accountant_payable_scan_timer_triggers_periodical_scanning_for_payables() { - //in the very first round we scan without waiting but we cannot find any payable records + fn periodical_scanning_for_payable_works() { init_test_logging(); - let non_pending_payables_params_arc = Arc::new(Mutex::new(vec![])); + let test_name = "periodical_scanning_for_payable_works"; + let begin_scan_params_arc = Arc::new(Mutex::new(vec![])); let notify_later_payables_params_arc = Arc::new(Mutex::new(vec![])); - let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); - let system = System::new("accountant_payable_scan_timer_triggers_scanning_for_payables"); - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_millis(97), - receivable_scan_interval: Duration::from_secs(100), - pending_payable_scan_interval: Duration::from_secs(100), - }, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: false, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - }, - make_wallet("hi"), - ); - let now = to_time_t(SystemTime::now()); - // slightly above minimum balance, to the right of the curve (time intersection) - let account = PayableAccount { - wallet: make_wallet("wallet"), - balance: DEFAULT_PAYMENT_THRESHOLDS.debt_threshold_gwei + 5, - last_paid_timestamp: from_time_t( - now - DEFAULT_PAYMENT_THRESHOLDS.threshold_interval_sec - 10, - ), - pending_payable_opt: None, - }; - let mut payable_dao = PayableDaoMock::new() - .non_pending_payables_params(&non_pending_payables_params_arc) - .non_pending_payables_result(vec![]) - .non_pending_payables_result(vec![account.clone()]); - payable_dao.have_non_pending_payables_shut_down_the_system = true; - let peer_actors = peer_actors_builder() - .blockchain_bridge(blockchain_bridge) - .build(); + let system = System::new(test_name); + SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions + let payable_scanner = ScannerMock::new() + .begin_scan_params(&begin_scan_params_arc) + .begin_scan_result(Err(BeginScanError::NothingToProcess)) + .begin_scan_result(Ok(ReportAccountsPayable { + accounts: vec![], + response_skeleton_opt: None, + })) + .stop_the_system(); + let mut config = bc_from_earning_wallet(make_wallet("hi")); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_millis(97), + receivable_scan_interval: Duration::from_secs(100), // We'll never run this scanner + pending_payable_scan_interval: Duration::from_secs(100), // We'll never run this scanner + }); let mut subject = AccountantBuilder::default() .bootstrapper_config(config) - .payable_dao(payable_dao) .build(); - subject.scanners.pending_payables = Box::new(NullScanner); //skipping - subject.scanners.receivables = Box::new(NullScanner); //skipping - subject.confirmation_tools.notify_later_scan_for_payable = Box::new( + subject.logger = Logger::new(test_name); + subject.scanners.payable = Box::new(payable_scanner); + subject.scanners.pending_payable = Box::new(NullScanner::new()); //skipping + subject.scanners.receivable = Box::new(NullScanner::new()); //skipping + subject.notify_later.scan_for_payable = Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_payables_params_arc) .permit_to_send_out(), ); let subject_addr = subject.start(); let subject_subs = Accountant::make_subs_from(&subject_addr); + let peer_actors = peer_actors_builder().build(); send_bind_message!(subject_subs, peer_actors); send_start_message!(subject_subs); system.run(); - let non_pending_payables_params = non_pending_payables_params_arc.lock().unwrap(); - //the third attempt is the one where the queue is empty and System::current.stop() ends the cycle - assert_eq!(*non_pending_payables_params, vec![(), (), ()]); - let blockchain_bridge_recorder = blockchain_bridge_recording_arc.lock().unwrap(); - assert_eq!(blockchain_bridge_recorder.len(), 1); - let report_accounts_payables_msg = - blockchain_bridge_recorder.get_record::(0); - assert_eq!( - report_accounts_payables_msg, - &ReportAccountsPayable { - accounts: vec![account], - response_skeleton_opt: None, - } - ); + //the second attempt is the one where the queue is empty and System::current.stop() ends the cycle + let begin_scan_params = begin_scan_params_arc.lock().unwrap(); let notify_later_payables_params = notify_later_payables_params_arc.lock().unwrap(); + assert_eq!(*begin_scan_params, vec![(), ()]); assert_eq!( *notify_later_payables_params, vec![ @@ -2540,12 +1880,6 @@ mod tests { }, Duration::from_millis(97) ), - ( - ScanForPayables { - response_skeleton_opt: None - }, - Duration::from_millis(97) - ) ] ) } @@ -2554,26 +1888,23 @@ mod tests { fn start_message_triggers_no_scans_in_suppress_mode() { init_test_logging(); let system = System::new("start_message_triggers_no_scans_in_suppress_mode"); - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_millis(1), - receivable_scan_interval: Duration::from_millis(1), - pending_payable_scan_interval: Duration::from_secs(100), - }, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: true, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - }, - make_wallet("hi"), - ); + let mut config = bc_from_earning_wallet(make_wallet("hi")); + config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_millis(1), + receivable_scan_interval: Duration::from_millis(1), + pending_payable_scan_interval: Duration::from_secs(100), + }); + config.suppress_initial_scans_opt = Some(true); let payable_dao = PayableDaoMock::new(); // No payables: demanding one would cause a panic let receivable_dao = ReceivableDaoMock::new(); // No delinquencies: demanding one would cause a panic let peer_actors = peer_actors_builder().build(); let subject = AccountantBuilder::default() .bootstrapper_config(config) - .payable_dao(payable_dao) - .receivable_dao(receivable_dao) + .payable_dao(payable_dao) // For Accountant + .payable_dao(PayableDaoMock::new()) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable Scanner + .receivable_dao(receivable_dao) // For Accountant + .receivable_dao(ReceivableDaoMock::new()) // For Scanner .build(); let subject_addr = subject.start(); let subject_subs = Accountant::make_subs_from(&subject_addr); @@ -2592,9 +1923,6 @@ mod tests { #[test] fn scan_for_payables_message_does_not_trigger_payment_for_balances_below_the_curve() { init_test_logging(); - let accountant_config = make_populated_accountant_config_with_defaults(); - let config = bc_from_ac_plus_earning_wallet(accountant_config, make_wallet("mine")); - let now = to_time_t(SystemTime::now()); let payment_thresholds = PaymentThresholds { threshold_interval_sec: 2_592_000, debt_threshold_gwei: 1_000_000_000, @@ -2603,6 +1931,9 @@ mod tests { permanent_debt_allowed_gwei: 10_000_000, unban_below_gwei: 10_000_000, }; + let config = bc_from_earning_wallet(make_wallet("mine")); + let now = to_time_t(SystemTime::now()); + let accounts = vec![ // below minimum balance, to the right of time intersection (inside buffer zone) PayableAccount { @@ -2644,12 +1975,16 @@ mod tests { blockchain_bridge_addr.recipient::(); let mut subject = AccountantBuilder::default() .bootstrapper_config(config) - .payable_dao(payable_dao) + .payable_dao(PayableDaoMock::new()) // For Accountant + .payable_dao(payable_dao) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable Scanner .build(); - subject.report_accounts_payable_sub = Some(report_accounts_payable_sub); - subject.config.payment_thresholds = payment_thresholds; + subject.report_accounts_payable_sub_opt = Some(report_accounts_payable_sub); - subject.scan_for_payables(None); + let _result = subject + .scanners + .payable + .begin_scan(SystemTime::now(), None, &subject.logger); System::current().stop_with_code(0); system.run(); @@ -2660,19 +1995,12 @@ mod tests { #[test] fn scan_for_payable_message_triggers_payment_for_balances_over_the_curve() { init_test_logging(); - let config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - pending_payable_scan_interval: Duration::from_secs(50_000), - payable_scan_interval: Duration::from_millis(100), - receivable_scan_interval: Duration::from_secs(50_000), - }, - payment_thresholds: DEFAULT_PAYMENT_THRESHOLDS.clone(), - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: false, - }, - make_wallet("mine"), - ); + let mut config = bc_from_earning_wallet(make_wallet("mine")); + config.scan_intervals_opt = Some(ScanIntervals { + pending_payable_scan_interval: Duration::from_secs(50_000), + payable_scan_interval: Duration::from_millis(100), + receivable_scan_interval: Duration::from_secs(50_000), + }); let now = to_time_t(SystemTime::now()); let accounts = vec![ // slightly above minimum balance, to the right of the curve (time intersection) @@ -2694,11 +2022,12 @@ mod tests { pending_payable_opt: None, }, ]; - let mut payable_dao = PayableDaoMock::default() - .non_pending_payables_result(accounts.clone()) - .non_pending_payables_result(vec![]); - payable_dao.have_non_pending_payables_shut_down_the_system = true; + let payable_dao = PayableDaoMock::default().non_pending_payables_result(accounts.clone()); let (blockchain_bridge, _, blockchain_bridge_recordings_arc) = make_recorder(); + let mut expected_messages_by_type = HashMap::new(); + expected_messages_by_type.insert(TypeId::of::(), 1); + let blockchain_bridge = blockchain_bridge + .stop_after_messages_and_start_system_killer(expected_messages_by_type); let system = System::new("scan_for_payable_message_triggers_payment_for_balances_over_the_curve"); let peer_actors = peer_actors_builder() @@ -2706,10 +2035,12 @@ mod tests { .build(); let mut subject = AccountantBuilder::default() .bootstrapper_config(config) - .payable_dao(payable_dao) + .payable_dao(PayableDaoMock::new()) // For Accountant + .payable_dao(payable_dao) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable Scanner .build(); - subject.scanners.pending_payables = Box::new(NullScanner); - subject.scanners.receivables = Box::new(NullScanner); + subject.scanners.pending_payable = Box::new(NullScanner::new()); + subject.scanners.receivable = Box::new(NullScanner::new()); let subject_addr = subject.start(); let accountant_subs = Accountant::make_subs_from(&subject_addr); send_bind_message!(accountant_subs, peer_actors); @@ -2718,91 +2049,66 @@ mod tests { system.run(); let blockchain_bridge_recordings = blockchain_bridge_recordings_arc.lock().unwrap(); + let message = blockchain_bridge_recordings.get_record::(0); assert_eq!( - blockchain_bridge_recordings.get_record::(0), + message, &ReportAccountsPayable { accounts, - response_skeleton_opt: None + response_skeleton_opt: None, } ); } #[test] - fn scan_for_delinquencies_triggers_bans_and_unbans() { + fn accountant_doesn_t_starts_another_scan_in_case_it_receives_the_message_and_the_scanner_is_running( + ) { init_test_logging(); - let accountant_config = make_populated_accountant_config_with_defaults(); - let config = bc_from_ac_plus_earning_wallet(accountant_config, make_wallet("mine")); - let newly_banned_1 = make_receivable_account(1234, true); - let newly_banned_2 = make_receivable_account(2345, true); - let newly_unbanned_1 = make_receivable_account(3456, false); - let newly_unbanned_2 = make_receivable_account(4567, false); - let payable_dao = PayableDaoMock::new().non_pending_payables_result(vec![]); - let new_delinquencies_parameters_arc = Arc::new(Mutex::new(vec![])); - let paid_delinquencies_parameters_arc = Arc::new(Mutex::new(vec![])); - let receivable_dao = ReceivableDaoMock::new() - .new_delinquencies_parameters(&new_delinquencies_parameters_arc) - .new_delinquencies_result(vec![newly_banned_1.clone(), newly_banned_2.clone()]) - .paid_delinquencies_parameters(&paid_delinquencies_parameters_arc) - .paid_delinquencies_result(vec![newly_unbanned_1.clone(), newly_unbanned_2.clone()]); - let ban_parameters_arc = Arc::new(Mutex::new(vec![])); - let unban_parameters_arc = Arc::new(Mutex::new(vec![])); - let banned_dao = BannedDaoMock::new() - .ban_list_result(vec![]) - .ban_parameters(&ban_parameters_arc) - .unban_parameters(&unban_parameters_arc); - let subject = AccountantBuilder::default() + let test_name = "accountant_doesn_t_starts_another_scan_in_case_it_receives_the_message_and_the_scanner_is_running"; + let payable_dao = PayableDaoMock::default(); + let (blockchain_bridge, _, blockchain_bridge_recording) = make_recorder(); + let report_accounts_payable_sub = blockchain_bridge.start().recipient(); + let now = + to_time_t(SystemTime::now()) - DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec - 1; + let payable_account = PayableAccount { + wallet: make_wallet("scan_for_payables"), + balance: DEFAULT_PAYMENT_THRESHOLDS.debt_threshold_gwei + 1, + last_paid_timestamp: from_time_t(now), + pending_payable_opt: None, + }; + let mut payable_dao = + payable_dao.non_pending_payables_result(vec![payable_account.clone()]); + payable_dao.have_non_pending_payables_shut_down_the_system = true; + let config = bc_from_earning_wallet(make_wallet("mine")); + let system = System::new(test_name); + let mut subject = AccountantBuilder::default() + .payable_dao(PayableDaoMock::new()) // For Accountant + .payable_dao(payable_dao) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable Scanner .bootstrapper_config(config) - .payable_dao(payable_dao) - .receivable_dao(receivable_dao) - .banned_dao(banned_dao) - .build(); - - subject.scan_for_delinquencies(); - - let new_delinquencies_parameters: MutexGuard> = - new_delinquencies_parameters_arc.lock().unwrap(); - assert_eq!( - DEFAULT_PAYMENT_THRESHOLDS.clone(), - new_delinquencies_parameters[0].1 - ); - let paid_delinquencies_parameters: MutexGuard> = - paid_delinquencies_parameters_arc.lock().unwrap(); - assert_eq!( - DEFAULT_PAYMENT_THRESHOLDS.clone(), - paid_delinquencies_parameters[0] - ); - let ban_parameters = ban_parameters_arc.lock().unwrap(); - assert!(ban_parameters.contains(&newly_banned_1.wallet)); - assert!(ban_parameters.contains(&newly_banned_2.wallet)); - assert_eq!(2, ban_parameters.len()); - let unban_parameters = unban_parameters_arc.lock().unwrap(); - assert!(unban_parameters.contains(&newly_unbanned_1.wallet)); - assert!(unban_parameters.contains(&newly_unbanned_2.wallet)); - assert_eq!(2, unban_parameters.len()); - let tlh = TestLogHandler::new(); - tlh.exists_log_matching("INFO: Accountant: Wallet 0x00000000000000000077616c6c65743132333464 \\(balance: 1234 MASQ, age: \\d+ sec\\) banned for delinquency"); - tlh.exists_log_matching("INFO: Accountant: Wallet 0x00000000000000000077616c6c65743233343564 \\(balance: 2345 MASQ, age: \\d+ sec\\) banned for delinquency"); - tlh.exists_log_matching("INFO: Accountant: Wallet 0x00000000000000000077616c6c6574333435366e \\(balance: 3456 MASQ, age: \\d+ sec\\) is no longer delinquent: unbanned"); - tlh.exists_log_matching("INFO: Accountant: Wallet 0x00000000000000000077616c6c6574343536376e \\(balance: 4567 MASQ, age: \\d+ sec\\) is no longer delinquent: unbanned"); - } - - #[test] - fn scan_for_pending_payable_found_no_pending_payable() { - init_test_logging(); - let return_all_backup_records_params_arc = Arc::new(Mutex::new(vec![])); - let pending_payable_dao = PendingPayableDaoMock::default() - .return_all_fingerprints_params(&return_all_backup_records_params_arc) - .return_all_fingerprints_result(vec![]); - let subject = AccountantBuilder::default() - .pending_payable_dao(pending_payable_dao) .build(); + subject.report_accounts_payable_sub_opt = Some(report_accounts_payable_sub); + subject.scan_intervals.payable_scan_interval = Duration::from_millis(10); + subject.logger = Logger::new(test_name); + let addr = subject.start(); + addr.try_send(ScanForPayables { + response_skeleton_opt: None, + }) + .unwrap(); - let _ = subject.scan_for_pending_payable(None); + addr.try_send(ScanForPayables { + response_skeleton_opt: None, + }) + .unwrap(); - let return_all_backup_records_params = return_all_backup_records_params_arc.lock().unwrap(); - assert_eq!(*return_all_backup_records_params, vec![()]); - TestLogHandler::new() - .exists_log_containing("DEBUG: Accountant: No pending payable found during last scan"); + System::current().stop(); + system.run(); + let recording = blockchain_bridge_recording.lock().unwrap(); + let messages_received = recording.len(); + assert_eq!(messages_received, 0); + TestLogHandler::new().exists_log_containing(&format!( + "INFO: {}: Payable scan was already initiated", + test_name + )); } #[test] @@ -2830,19 +2136,16 @@ mod tests { payable_fingerprint_1.clone(), payable_fingerprint_2.clone(), ]); - let config = bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - make_wallet("mine"), - ); + let config = bc_from_earning_wallet(make_wallet("mine")); let system = System::new("pending payable scan"); let mut subject = AccountantBuilder::default() - .pending_payable_dao(pending_payable_dao) + .pending_payable_dao(PendingPayableDaoMock::new()) // For Accountant + .pending_payable_dao(PendingPayableDaoMock::new()) // For Payable Scanner + .pending_payable_dao(pending_payable_dao) // For PendiingPayable Scanner .bootstrapper_config(config) .build(); let blockchain_bridge_addr = blockchain_bridge.start(); - subject - .confirmation_tools - .request_transaction_receipts_subs_opt = Some(blockchain_bridge_addr.recipient()); + subject.request_transaction_receipts_subs_opt = Some(blockchain_bridge_addr.recipient()); let account_addr = subject.start(); let _ = account_addr @@ -2872,9 +2175,7 @@ mod tests { fn report_routing_service_provided_message_is_received() { init_test_logging(); let now = SystemTime::now(); - let mut bootstrapper_config = BootstrapperConfig::default(); - bootstrapper_config.accountant_config_opt = Some(make_accountant_config_null()); - bootstrapper_config.earning_wallet = make_wallet("hi"); + let bootstrapper_config = bc_from_earning_wallet(make_wallet("hi")); let more_money_receivable_parameters_arc = Arc::new(Mutex::new(vec![])); let payable_dao_mock = PayableDaoMock::new().non_pending_payables_result(vec![]); let receivable_dao_mock = ReceivableDaoMock::new() @@ -2883,7 +2184,10 @@ mod tests { let subject = AccountantBuilder::default() .bootstrapper_config(bootstrapper_config) .payable_dao(payable_dao_mock) + .payable_dao(PayableDaoMock::new()) + .payable_dao(PayableDaoMock::new()) .receivable_dao(receivable_dao_mock) + .receivable_dao(ReceivableDaoMock::new()) .build(); let system = System::new("report_routing_service_message_is_received"); let subject_addr: Addr = subject.start(); @@ -2921,19 +2225,18 @@ mod tests { fn report_routing_service_provided_message_is_received_from_our_consuming_wallet() { init_test_logging(); let consuming_wallet = make_wallet("our consuming wallet"); - let config = bc_from_ac_plus_wallets( - make_populated_accountant_config_with_defaults(), - consuming_wallet.clone(), - make_wallet("our earning wallet"), - ); + let config = bc_from_wallets(consuming_wallet.clone(), make_wallet("our earning wallet")); let more_money_receivable_parameters_arc = Arc::new(Mutex::new(vec![])); let payable_dao_mock = PayableDaoMock::new().non_pending_payables_result(vec![]); let receivable_dao_mock = ReceivableDaoMock::new() .more_money_receivable_parameters(&more_money_receivable_parameters_arc); let subject = AccountantBuilder::default() .bootstrapper_config(config) - .receivable_dao(receivable_dao_mock) .payable_dao(payable_dao_mock) + .payable_dao(PayableDaoMock::new()) + .payable_dao(PayableDaoMock::new()) + .receivable_dao(receivable_dao_mock) + .receivable_dao(ReceivableDaoMock::new()) .build(); let system = System::new("report_routing_service_message_is_received"); let subject_addr: Addr = subject.start(); @@ -2970,10 +2273,7 @@ mod tests { fn report_routing_service_provided_message_is_received_from_our_earning_wallet() { init_test_logging(); let earning_wallet = make_wallet("our earning wallet"); - let config = bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - earning_wallet.clone(), - ); + let config = bc_from_earning_wallet(earning_wallet.clone()); let more_money_receivable_parameters_arc = Arc::new(Mutex::new(vec![])); let payable_dao_mock = PayableDaoMock::new().non_pending_payables_result(vec![]); let receivable_dao_mock = ReceivableDaoMock::new() @@ -2981,7 +2281,10 @@ mod tests { let subject = AccountantBuilder::default() .bootstrapper_config(config) .payable_dao(payable_dao_mock) + .payable_dao(PayableDaoMock::new()) + .payable_dao(PayableDaoMock::new()) .receivable_dao(receivable_dao_mock) + .receivable_dao(ReceivableDaoMock::new()) .build(); let system = System::new("report_routing_service_message_is_received"); let subject_addr: Addr = subject.start(); @@ -3018,10 +2321,7 @@ mod tests { fn report_exit_service_provided_message_is_received() { init_test_logging(); let now = SystemTime::now(); - let config = bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - make_wallet("hi"), - ); + let config = bc_from_earning_wallet(make_wallet("hi")); let more_money_receivable_parameters_arc = Arc::new(Mutex::new(vec![])); let payable_dao_mock = PayableDaoMock::new().non_pending_payables_result(vec![]); let receivable_dao_mock = ReceivableDaoMock::new() @@ -3029,8 +2329,11 @@ mod tests { .more_money_receivable_result(Ok(())); let subject = AccountantBuilder::default() .bootstrapper_config(config) - .receivable_dao(receivable_dao_mock) .payable_dao(payable_dao_mock) + .payable_dao(PayableDaoMock::new()) + .payable_dao(PayableDaoMock::new()) + .receivable_dao(receivable_dao_mock) + .receivable_dao(ReceivableDaoMock::new()) .build(); let system = System::new("report_exit_service_provided_message_is_received"); let subject_addr: Addr = subject.start(); @@ -3068,11 +2371,7 @@ mod tests { fn report_exit_service_provided_message_is_received_from_our_consuming_wallet() { init_test_logging(); let consuming_wallet = make_wallet("my consuming wallet"); - let config = bc_from_ac_plus_wallets( - make_accountant_config_null(), - consuming_wallet.clone(), - make_wallet("my earning wallet"), - ); + let config = bc_from_wallets(consuming_wallet.clone(), make_wallet("my earning wallet")); let more_money_receivable_parameters_arc = Arc::new(Mutex::new(vec![])); let payable_dao_mock = PayableDaoMock::new().non_pending_payables_result(vec![]); let receivable_dao_mock = ReceivableDaoMock::new() @@ -3080,7 +2379,10 @@ mod tests { let subject = AccountantBuilder::default() .bootstrapper_config(config) .payable_dao(payable_dao_mock) + .payable_dao(PayableDaoMock::new()) + .payable_dao(PayableDaoMock::new()) .receivable_dao(receivable_dao_mock) + .receivable_dao(ReceivableDaoMock::new()) .build(); let system = System::new("report_exit_service_provided_message_is_received"); let subject_addr: Addr = subject.start(); @@ -3117,16 +2419,18 @@ mod tests { fn report_exit_service_provided_message_is_received_from_our_earning_wallet() { init_test_logging(); let earning_wallet = make_wallet("my earning wallet"); - let config = - bc_from_ac_plus_earning_wallet(make_accountant_config_null(), earning_wallet.clone()); + let config = bc_from_earning_wallet(earning_wallet.clone()); let more_money_receivable_parameters_arc = Arc::new(Mutex::new(vec![])); let payable_dao_mock = PayableDaoMock::new().non_pending_payables_result(vec![]); let receivable_dao_mock = ReceivableDaoMock::new() .more_money_receivable_parameters(&more_money_receivable_parameters_arc); let subject = AccountantBuilder::default() .bootstrapper_config(config) - .payable_dao(payable_dao_mock) - .receivable_dao(receivable_dao_mock) + .payable_dao(payable_dao_mock) // For Accountant + .payable_dao(PayableDaoMock::new()) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable Scanner + .receivable_dao(receivable_dao_mock) // For Accountant + .receivable_dao(ReceivableDaoMock::new()) // For Scanner .build(); let system = System::new("report_exit_service_provided_message_is_received"); let subject_addr: Addr = subject.start(); @@ -3162,10 +2466,7 @@ mod tests { #[test] fn report_services_consumed_message_is_received() { init_test_logging(); - let config = bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - make_wallet("hi"), - ); + let config = make_bc_with_defaults(); let more_money_payable_params_arc = Arc::new(Mutex::new(vec![])); let payable_dao_mock = PayableDaoMock::new() .more_money_payable_params(more_money_payable_params_arc.clone()) @@ -3175,6 +2476,8 @@ mod tests { let mut subject = AccountantBuilder::default() .bootstrapper_config(config) .payable_dao(payable_dao_mock) + .payable_dao(PayableDaoMock::new()) + .payable_dao(PayableDaoMock::new()) .build(); subject.message_id_generator = Box::new(MessageIdGeneratorMock::default().id_result(123)); let system = System::new("report_services_consumed_message_is_received"); @@ -3264,6 +2567,8 @@ mod tests { let subject = AccountantBuilder::default() .bootstrapper_config(config) .payable_dao(payable_dao_mock) + .payable_dao(PayableDaoMock::new()) + .payable_dao(PayableDaoMock::new()) .build(); let system = System::new("test"); let subject_addr: Addr = subject.start(); @@ -3284,11 +2589,7 @@ mod tests { fn routing_service_consumed_is_reported_for_our_consuming_wallet() { init_test_logging(); let consuming_wallet = make_wallet("the consuming wallet"); - let config = bc_from_ac_plus_wallets( - make_populated_accountant_config_with_defaults(), - consuming_wallet.clone(), - make_wallet("the earning wallet"), - ); + let config = bc_from_wallets(consuming_wallet.clone(), make_wallet("the earning wallet")); let foreign_wallet = make_wallet("exit wallet"); let timestamp = SystemTime::now(); let report_message = ReportServicesConsumedMessage { @@ -3331,10 +2632,7 @@ mod tests { let earning_wallet = make_wallet("routing_service_consumed_is_reported_for_our_earning_wallet"); let foreign_wallet = make_wallet("exit wallet"); - let config = bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - earning_wallet.clone(), - ); + let config = bc_from_earning_wallet(earning_wallet.clone()); let timestamp = SystemTime::now(); let report_message = ReportServicesConsumedMessage { timestamp, @@ -3375,11 +2673,7 @@ mod tests { init_test_logging(); let consuming_wallet = make_wallet("exit_service_consumed_is_reported_for_our_consuming_wallet"); - let config = bc_from_ac_plus_wallets( - make_accountant_config_null(), - consuming_wallet.clone(), - make_wallet("own earning wallet"), - ); + let config = bc_from_wallets(consuming_wallet.clone(), make_wallet("own earning wallet")); let report_message = ReportServicesConsumedMessage { timestamp: SystemTime::now(), exit: ExitServiceConsumed { @@ -3409,8 +2703,7 @@ mod tests { fn exit_service_consumed_is_reported_for_our_earning_wallet() { init_test_logging(); let earning_wallet = make_wallet("own earning wallet"); - let config = - bc_from_ac_plus_earning_wallet(make_accountant_config_null(), earning_wallet.clone()); + let config = bc_from_earning_wallet(earning_wallet.clone()); let report_message = ReportServicesConsumedMessage { timestamp: SystemTime::now(), exit: ExitServiceConsumed { @@ -3451,6 +2744,7 @@ mod tests { )); let subject = AccountantBuilder::default() .receivable_dao(receivable_dao) + .receivable_dao(ReceivableDaoMock::new()) .build(); let _ = subject.record_service_provided(i64::MAX as u64, 1, SystemTime::now(), 2, &wallet); @@ -3464,6 +2758,7 @@ mod tests { .more_money_receivable_result(Err(ReceivableDaoError::SignConversion(1234))); let subject = AccountantBuilder::default() .receivable_dao(receivable_dao) + .receivable_dao(ReceivableDaoMock::new()) .build(); subject.record_service_provided(i64::MAX as u64, 1, SystemTime::now(), 2, &wallet); @@ -3483,6 +2778,8 @@ mod tests { .more_money_payable_result(Err(PayableDaoError::SignConversion(1234))); let subject = AccountantBuilder::default() .payable_dao(payable_dao) + .payable_dao(PayableDaoMock::new()) + .payable_dao(PayableDaoMock::new()) .build(); let service_rate = i64::MAX as u64; @@ -3491,7 +2788,7 @@ mod tests { TestLogHandler::new().exists_log_containing(&format!( "ERROR: Accountant: Overflow error recording consumed services from {}: total charge {}, service rate {}, byte rate 1, payload size 2. Skipping", wallet, - i64::MAX as u64 +1*2, + i64::MAX as u64 + 1 * 2, i64::MAX as u64 )); } @@ -3509,41 +2806,20 @@ mod tests { )); let subject = AccountantBuilder::default() .payable_dao(payable_dao) + .payable_dao(PayableDaoMock::new()) + .payable_dao(PayableDaoMock::new()) .build(); let _ = subject.record_service_consumed(i64::MAX as u64, 1, SystemTime::now(), 2, &wallet); } - #[test] - #[should_panic( - expected = "Was unable to create a mark in payables for a new pending payable '0x000000000000000000000000000000000000000000000000000000000000007b' due to 'SignConversion(9999999999999)'" - )] - fn handle_sent_payable_fails_to_make_a_mark_in_payables_and_so_panics() { - let payable = Payable::new( - make_wallet("blah"), - 6789, - H256::from_uint(&U256::from(123)), - SystemTime::now(), - ); - let payable_dao = PayableDaoMock::new() - .mark_pending_payable_rowid_result(Err(PayableDaoError::SignConversion(9999999999999))); - let pending_payable_dao = - PendingPayableDaoMock::default().fingerprint_rowid_result(Some(7879)); - let subject = AccountantBuilder::default() - .payable_dao(payable_dao) - .pending_payable_dao(pending_payable_dao) - .build(); - - let _ = subject.mark_pending_payable(vec![payable]); - } - #[test] #[should_panic( expected = "Database unmaintainable; payable fingerprint deletion for transaction 0x000000000000000000000000000000000000000000000000000000000000007b \ has stayed undone due to RecordDeletion(\"we slept over, sorry\")" )] - fn handle_sent_payable_dealing_with_failed_payment_fails_to_delete_the_existing_pending_payable_fingerprint_and_panics( - ) { + fn accountant_panics_in_case_it_receives_an_error_from_scanner_while_handling_sent_payable_msg() + { let rowid = 4; let hash = H256::from_uint(&U256::from(123)); let sent_payable = SentPayable { @@ -3559,429 +2835,59 @@ mod tests { .delete_fingerprint_result(Err(PendingPayableDaoError::RecordDeletion( "we slept over, sorry".to_string(), ))); + let system = System::new("test"); let subject = AccountantBuilder::default() - .pending_payable_dao(pending_payable_dao) - .build(); - - let _ = subject.handle_sent_payable(sent_payable); - } - - #[test] - fn handle_sent_payable_receives_two_payments_one_incorrect_and_one_correct() { - //the two failures differ in the logged messages - init_test_logging(); - let fingerprint_rowid_params_arc = Arc::new(Mutex::new(vec![])); - let now_system = SystemTime::now(); - let payable_1 = Err(BlockchainError::InvalidResponse); - let payable_2_rowid = 126; - let payable_hash_2 = H256::from_uint(&U256::from(166)); - let payable_2 = Payable::new(make_wallet("booga"), 6789, payable_hash_2, now_system); - let payable_3 = Err(BlockchainError::TransactionFailed { - msg: "closing hours, sorry".to_string(), - hash_opt: None, - }); - let sent_payable = SentPayable { - timestamp: SystemTime::now(), - payable: vec![payable_1, Ok(payable_2.clone()), payable_3], - response_skeleton_opt: None, - }; - let pending_payable_dao = PendingPayableDaoMock::default() - .fingerprint_rowid_params(&fingerprint_rowid_params_arc) - .fingerprint_rowid_result(Some(payable_2_rowid)); - let subject = AccountantBuilder::default() - .payable_dao(PayableDaoMock::new().mark_pending_payable_rowid_result(Ok(()))) - .pending_payable_dao(pending_payable_dao) + .pending_payable_dao(PendingPayableDaoMock::new()) // For Accountant + .pending_payable_dao(pending_payable_dao) // For Payable Scanner + .pending_payable_dao(PendingPayableDaoMock::new()) // For PendingPayable Scanner .build(); + let addr = subject.start(); - subject.handle_sent_payable(sent_payable); + let _ = addr.try_send(sent_payable); - let fingerprint_rowid_params = fingerprint_rowid_params_arc.lock().unwrap(); - assert_eq!(*fingerprint_rowid_params, vec![payable_hash_2]); //we know the other two errors are associated with an initiated transaction having a backup - let log_handler = TestLogHandler::new(); - log_handler.exists_log_containing("WARN: Accountant: Outbound transaction failure due to 'InvalidResponse'. Please check your blockchain service URL configuration."); - log_handler.exists_log_containing("DEBUG: Accountant: Payable '0x00000000000000000000000000000000000000000000000000000000000000a6' has been marked as pending in the payable table"); - log_handler.exists_log_containing("WARN: Accountant: Encountered transaction error at this end: 'TransactionFailed { msg: \"closing hours, sorry\", hash_opt: None }'"); - log_handler.exists_log_containing("DEBUG: Accountant: Forgetting a transaction attempt that even did not reach the signing stage"); + System::current().stop(); + assert_eq!(system.run(), 0); } #[test] #[should_panic( - expected = "Payable fingerprint for 0x0000000000000000000000000000000000000000000000000000000000000315 doesn't exist but should by now; system unreliable" + expected = "panic message (processed with: node_lib::sub_lib::utils::crash_request_analyzer)" )] - fn handle_sent_payable_receives_proper_payment_but_fingerprint_not_found_so_it_panics() { - init_test_logging(); - let now_system = SystemTime::now(); - let payment_hash = H256::from_uint(&U256::from(789)); - let payment = Payable::new(make_wallet("booga"), 6789, payment_hash, now_system); - let pending_payable_dao = PendingPayableDaoMock::default().fingerprint_rowid_result(None); - let subject = AccountantBuilder::default() - .payable_dao(PayableDaoMock::new().mark_pending_payable_rowid_result(Ok(()))) - .pending_payable_dao(pending_payable_dao) + fn accountant_can_be_crashed_properly_but_not_improperly() { + let mut config = make_bc_with_defaults(); + config.crash_point = CrashPoint::Message; + let accountant = AccountantBuilder::default() + .bootstrapper_config(config) .build(); - let _ = subject.mark_pending_payable(vec![payment]); + prove_that_crash_request_handler_is_hooked_up(accountant, CRASH_KEY); } #[test] - fn handle_confirm_transaction_works() { + fn pending_transaction_is_registered_and_monitored_until_it_gets_confirmed_or_canceled() { init_test_logging(); + let mark_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); let transaction_confirmed_params_arc = Arc::new(Mutex::new(vec![])); - let delete_pending_payable_fingerprint_params_arc = Arc::new(Mutex::new(vec![])); - let payable_dao = PayableDaoMock::default() - .transaction_confirmed_params(&transaction_confirmed_params_arc) - .transaction_confirmed_result(Ok(())); - let pending_payable_dao = PendingPayableDaoMock::default() - .delete_fingerprint_params(&delete_pending_payable_fingerprint_params_arc) - .delete_fingerprint_result(Ok(())); - let mut subject = AccountantBuilder::default() - .payable_dao(payable_dao) - .pending_payable_dao(pending_payable_dao) - .build(); - let tx_hash = H256::from("sometransactionhash".keccak256()); - let amount = 4567; - let timestamp_from_time_of_payment = from_time_t(200_000_000); - let rowid = 2; - let pending_payable_fingerprint = PendingPayableFingerprint { - rowid_opt: Some(rowid), - timestamp: timestamp_from_time_of_payment, - hash: tx_hash, - attempt_opt: Some(1), - amount, - process_error: None, - }; - - let _ = subject.handle_confirm_pending_transaction(ConfirmPendingTransaction { - pending_payable_fingerprint: pending_payable_fingerprint.clone(), - }); - - let transaction_confirmed_params = transaction_confirmed_params_arc.lock().unwrap(); - assert_eq!( - *transaction_confirmed_params, - vec![pending_payable_fingerprint] - ); - let delete_pending_payable_fingerprint_params = - delete_pending_payable_fingerprint_params_arc - .lock() - .unwrap(); - assert_eq!(*delete_pending_payable_fingerprint_params, vec![rowid]); - let log_handler = TestLogHandler::new(); - log_handler.exists_log_containing("DEBUG: Accountant: Confirmation of transaction 0x051aae12b9595ccaa43c2eabfd5b86347c37fa0988167165b0b17b23fcaa8c19; record for payable was modified"); - log_handler.exists_log_containing("INFO: Accountant: Transaction 0x051aae12b9595ccaa43c2eabfd5b86347c37fa0988167165b0b17b23fcaa8c19 has gone through the whole confirmation process succeeding"); - } - - #[test] - #[should_panic( - expected = "Was unable to uncheck pending payable '0x0000000000000000000000000000000000000000000000000000000000000315' after confirmation due to 'RusqliteError(\"record change not successful\")" - )] - fn handle_confirm_pending_transaction_panics_on_unchecking_payable_table() { - init_test_logging(); - let hash = H256::from_uint(&U256::from(789)); - let rowid = 3; - let payable_dao = PayableDaoMock::new().transaction_confirmed_result(Err( - PayableDaoError::RusqliteError("record change not successful".to_string()), + let get_transaction_receipt_params_arc = Arc::new(Mutex::new(vec![])); + let return_all_fingerprints_params_arc = Arc::new(Mutex::new(vec![])); + let non_pending_payables_params_arc = Arc::new(Mutex::new(vec![])); + let insert_fingerprint_params_arc = Arc::new(Mutex::new(vec![])); + let update_fingerprint_params_arc = Arc::new(Mutex::new(vec![])); + let mark_failure_params_arc = Arc::new(Mutex::new(vec![])); + let delete_record_params_arc = Arc::new(Mutex::new(vec![])); + let notify_later_scan_for_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); + let notify_later_scan_for_pending_payable_arc_cloned = + notify_later_scan_for_pending_payable_params_arc.clone(); //because it moves into a closure + let pending_tx_hash_1 = H256::from_uint(&U256::from(123)); + let pending_tx_hash_2 = H256::from_uint(&U256::from(567)); + let rowid_for_account_1 = 3; + let rowid_for_account_2 = 5; + let now = SystemTime::now(); + let past_payable_timestamp_1 = now.sub(Duration::from_secs( + (DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec + 555) as u64, )); - let mut subject = AccountantBuilder::default() - .payable_dao(payable_dao) - .build(); - let mut payment = make_pending_payable_fingerprint(); - payment.rowid_opt = Some(rowid); - payment.hash = hash; - let msg = ConfirmPendingTransaction { - pending_payable_fingerprint: payment.clone(), - }; - - let _ = subject.handle_confirm_pending_transaction(msg); - } - - #[test] - #[should_panic( - expected = "Was unable to delete payable fingerprint '0x0000000000000000000000000000000000000000000000000000000000000315' after successful transaction due to 'RecordDeletion(\"the database is fooling around with us\")'" - )] - fn handle_confirm_pending_transaction_panics_on_deleting_pending_payable_fingerprint() { - init_test_logging(); - let hash = H256::from_uint(&U256::from(789)); - let rowid = 3; - let payable_dao = PayableDaoMock::new().transaction_confirmed_result(Ok(())); - let pending_payable_dao = PendingPayableDaoMock::default().delete_fingerprint_result(Err( - PendingPayableDaoError::RecordDeletion( - "the database is fooling around with us".to_string(), - ), - )); - let mut subject = AccountantBuilder::default() - .payable_dao(payable_dao) - .pending_payable_dao(pending_payable_dao) - .build(); - let mut pending_payable_fingerprint = make_pending_payable_fingerprint(); - pending_payable_fingerprint.rowid_opt = Some(rowid); - pending_payable_fingerprint.hash = hash; - let msg = ConfirmPendingTransaction { - pending_payable_fingerprint: pending_payable_fingerprint.clone(), - }; - - let _ = subject.handle_confirm_pending_transaction(msg); - } - - #[test] - fn handle_cancel_pending_transaction_works() { - init_test_logging(); - let mark_failure_params_arc = Arc::new(Mutex::new(vec![])); - let pending_payable_dao = PendingPayableDaoMock::default() - .mark_failure_params(&mark_failure_params_arc) - .mark_failure_result(Ok(())); - let subject = AccountantBuilder::default() - .pending_payable_dao(pending_payable_dao) - .build(); - let tx_hash = H256::from("sometransactionhash".keccak256()); - let rowid = 2; - let transaction_id = PendingPayableId { - hash: tx_hash, - rowid, - }; - - let _ = subject.handle_cancel_pending_transaction(CancelFailedPendingTransaction { - id: transaction_id, - }); - - let mark_failure_params = mark_failure_params_arc.lock().unwrap(); - assert_eq!(*mark_failure_params, vec![rowid]); - TestLogHandler::new().exists_log_containing( - "WARN: Accountant: Broken transaction 0x051aae12b9595ccaa43c2eabfd5b86347c37fa0988167165b0b17b23fcaa8c19 left with an error mark; you should take over \ - the care of this transaction to make sure your debts will be paid because there is no automated process that can fix this without you", - ); - } - - #[test] - #[should_panic( - expected = "Unsuccessful attempt for transaction 0x051aae12b9595ccaa43c2eabfd5b86347c37fa0988167165b0b17b23fcaa8c19 to mark fatal error at payable fingerprint due to UpdateFailed(\"no no no\")" - )] - fn handle_cancel_pending_transaction_panics_on_its_inability_to_mark_failure() { - let payable_dao = PayableDaoMock::default().transaction_canceled_result(Ok(())); - let pending_payable_dao = PendingPayableDaoMock::default().mark_failure_result(Err( - PendingPayableDaoError::UpdateFailed("no no no".to_string()), - )); - let subject = AccountantBuilder::default() - .payable_dao(payable_dao) - .pending_payable_dao(pending_payable_dao) - .build(); - let rowid = 2; - let hash = H256::from("sometransactionhash".keccak256()); - - let _ = subject.handle_cancel_pending_transaction(CancelFailedPendingTransaction { - id: PendingPayableId { hash, rowid }, - }); - } - - #[test] - #[should_panic( - expected = "panic message (processed with: node_lib::sub_lib::utils::crash_request_analyzer)" - )] - fn accountant_can_be_crashed_properly_but_not_improperly() { - let mut config = BootstrapperConfig::default(); - config.crash_point = CrashPoint::Message; - config.accountant_config_opt = Some(make_accountant_config_null()); - let accountant = AccountantBuilder::default() - .bootstrapper_config(config) - .build(); - - prove_that_crash_request_handler_is_hooked_up(accountant, CRASH_KEY); - } - - #[test] - fn investigate_debt_extremes_picks_the_most_relevant_records() { - let now = to_time_t(SystemTime::now()); - let same_amount_significance = 2_000_000; - let same_age_significance = from_time_t(now - 30000); - let payables = &[ - PayableAccount { - wallet: make_wallet("wallet0"), - balance: same_amount_significance, - last_paid_timestamp: from_time_t(now - 5000), - pending_payable_opt: None, - }, - //this debt is more significant because beside being high in amount it's also older, so should be prioritized and picked - PayableAccount { - wallet: make_wallet("wallet1"), - balance: same_amount_significance, - last_paid_timestamp: from_time_t(now - 10000), - pending_payable_opt: None, - }, - //similarly these two wallets have debts equally old but the second has a bigger balance and should be chosen - PayableAccount { - wallet: make_wallet("wallet3"), - balance: 100, - last_paid_timestamp: same_age_significance, - pending_payable_opt: None, - }, - PayableAccount { - wallet: make_wallet("wallet2"), - balance: 330, - last_paid_timestamp: same_age_significance, - pending_payable_opt: None, - }, - ]; - - let result = Accountant::investigate_debt_extremes(payables); - - assert_eq!(result,"Payable scan found 4 debts; the biggest is 2000000 owed for 10000sec, the oldest is 330 owed for 30000sec") - } - - #[test] - fn payables_debug_summary_prints_pretty_summary() { - let now = to_time_t(SystemTime::now()); - let payment_thresholds = PaymentThresholds { - threshold_interval_sec: 2_592_000, - debt_threshold_gwei: 1_000_000_000, - payment_grace_period_sec: 86_400, - maturity_threshold_sec: 86_400, - permanent_debt_allowed_gwei: 10_000_000, - unban_below_gwei: 10_000_000, - }; - let qualified_payables = &[ - PayableAccount { - wallet: make_wallet("wallet0"), - balance: payment_thresholds.permanent_debt_allowed_gwei + 1000, - last_paid_timestamp: from_time_t( - now - payment_thresholds.threshold_interval_sec - 1234, - ), - pending_payable_opt: None, - }, - PayableAccount { - wallet: make_wallet("wallet1"), - balance: payment_thresholds.permanent_debt_allowed_gwei + 1, - last_paid_timestamp: from_time_t( - now - payment_thresholds.threshold_interval_sec - 1, - ), - pending_payable_opt: None, - }, - ]; - let mut config = BootstrapperConfig::default(); - config.accountant_config_opt = Some(make_populated_accountant_config_with_defaults()); - let mut subject = AccountantBuilder::default() - .bootstrapper_config(config) - .build(); - subject.config.payment_thresholds = payment_thresholds; - - let result = subject.payables_debug_summary(qualified_payables); - - assert_eq!(result, - "Paying qualified debts:\n\ - 10001000 owed for 2593234sec exceeds threshold: 9512428; creditor: 0x0000000000000000000000000077616c6c657430\n\ - 10000001 owed for 2592001sec exceeds threshold: 9999604; creditor: 0x0000000000000000000000000077616c6c657431" - ) - } - - #[test] - fn threshold_calculation_depends_on_user_defined_payment_thresholds() { - let safe_age_params_arc = Arc::new(Mutex::new(vec![])); - let safe_balance_params_arc = Arc::new(Mutex::new(vec![])); - let calculate_payable_threshold_params_arc = Arc::new(Mutex::new(vec![])); - let balance = 5555; - let how_far_in_past = Duration::from_secs(1111 + 1); - let last_paid_timestamp = SystemTime::now().sub(how_far_in_past); - let payable_account = PayableAccount { - wallet: make_wallet("hi"), - balance, - last_paid_timestamp, - pending_payable_opt: None, - }; - let custom_payment_thresholds = PaymentThresholds { - maturity_threshold_sec: 1111, - payment_grace_period_sec: 2222, - permanent_debt_allowed_gwei: 3333, - debt_threshold_gwei: 4444, - threshold_interval_sec: 5555, - unban_below_gwei: 3333, - }; - let mut bootstrapper_config = BootstrapperConfig::default(); - bootstrapper_config.accountant_config_opt = Some(AccountantConfig { - scan_intervals: Default::default(), - payment_thresholds: custom_payment_thresholds, - suppress_initial_scans: false, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - }); - let payable_thresholds_tools = PayableThresholdToolsMock::default() - .is_innocent_age_params(&safe_age_params_arc) - .is_innocent_age_result( - how_far_in_past.as_secs() - <= custom_payment_thresholds.maturity_threshold_sec as u64, - ) - .is_innocent_balance_params(&safe_balance_params_arc) - .is_innocent_balance_result( - balance <= custom_payment_thresholds.permanent_debt_allowed_gwei, - ) - .calculate_payout_threshold_params(&calculate_payable_threshold_params_arc) - .calculate_payout_threshold_result(4567.0); //made up value - let mut subject = AccountantBuilder::default() - .bootstrapper_config(bootstrapper_config) - .build(); - subject.payable_threshold_tools = Box::new(payable_thresholds_tools); - - let result = subject.payable_exceeded_threshold(&payable_account); - - assert_eq!(result, Some(4567)); - let mut safe_age_params = safe_age_params_arc.lock().unwrap(); - let safe_age_single_params = safe_age_params.remove(0); - assert_eq!(*safe_age_params, vec![]); - let (time_elapsed, curve_derived_time) = safe_age_single_params; - assert!( - (how_far_in_past.as_secs() - 3) < time_elapsed - && time_elapsed < (how_far_in_past.as_secs() + 3) - ); - assert_eq!( - curve_derived_time, - custom_payment_thresholds.maturity_threshold_sec as u64 - ); - let safe_balance_params = safe_balance_params_arc.lock().unwrap(); - assert_eq!( - *safe_balance_params, - vec![( - payable_account.balance, - custom_payment_thresholds.permanent_debt_allowed_gwei - )] - ); - let mut calculate_payable_curves_params = - calculate_payable_threshold_params_arc.lock().unwrap(); - let calculate_payable_curves_single_params = calculate_payable_curves_params.remove(0); - assert_eq!(*calculate_payable_curves_params, vec![]); - let (payment_thresholds, time_elapsed) = calculate_payable_curves_single_params; - assert!( - (how_far_in_past.as_secs() - 3) < time_elapsed - && time_elapsed < (how_far_in_past.as_secs() + 3) - ); - assert_eq!(payment_thresholds, custom_payment_thresholds) - } - - #[test] - fn pending_transaction_is_registered_and_monitored_until_it_gets_confirmed_or_canceled() { - init_test_logging(); - let mark_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); - let transaction_confirmed_params_arc = Arc::new(Mutex::new(vec![])); - let get_transaction_receipt_params_arc = Arc::new(Mutex::new(vec![])); - let return_all_fingerprints_params_arc = Arc::new(Mutex::new(vec![])); - let non_pending_payables_params_arc = Arc::new(Mutex::new(vec![])); - let insert_fingerprint_params_arc = Arc::new(Mutex::new(vec![])); - let update_fingerprint_params_arc = Arc::new(Mutex::new(vec![])); - let mark_failure_params_arc = Arc::new(Mutex::new(vec![])); - let delete_record_params_arc = Arc::new(Mutex::new(vec![])); - let notify_later_scan_for_pending_payable_params_arc = Arc::new(Mutex::new(vec![])); - let notify_later_scan_for_pending_payable_arc_cloned = - notify_later_scan_for_pending_payable_params_arc.clone(); //because it moves into a closure - let notify_cancel_failed_transaction_params_arc = Arc::new(Mutex::new(vec![])); - let notify_cancel_failed_transaction_params_arc_cloned = - notify_cancel_failed_transaction_params_arc.clone(); //because it moves into a closure - let notify_confirm_transaction_params_arc = Arc::new(Mutex::new(vec![])); - let notify_confirm_transaction_params_arc_cloned = - notify_confirm_transaction_params_arc.clone(); //because it moves into a closure - let pending_tx_hash_1 = H256::from_uint(&U256::from(123)); - let pending_tx_hash_2 = H256::from_uint(&U256::from(567)); - let rowid_for_account_1 = 3; - let rowid_for_account_2 = 5; - let now = SystemTime::now(); - let past_payable_timestamp_1 = now.sub(Duration::from_secs( - (DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec + 555) as u64, - )); - let past_payable_timestamp_2 = now.sub(Duration::from_secs( - (DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec + 50) as u64, + let past_payable_timestamp_2 = now.sub(Duration::from_secs( + (DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec + 50) as u64, )); let this_payable_timestamp_1 = now; let this_payable_timestamp_2 = now.add(Duration::from_millis(50)); @@ -4037,29 +2943,23 @@ mod tests { pending_payable_opt: None, }; let pending_payable_scan_interval = 200; //should be slightly less than 1/5 of the time until shutting the system - let payable_dao = PayableDaoMock::new() - .non_pending_payables_params(&non_pending_payables_params_arc) - .non_pending_payables_result(vec![account_1, account_2]) + let payable_dao_for_accountant = PayableDaoMock::new(); + let payable_dao_for_payable_scanner = PayableDaoMock::new() .mark_pending_payable_rowid_params(&mark_pending_payable_params_arc) .mark_pending_payable_rowid_result(Ok(())) .mark_pending_payable_rowid_result(Ok(())) + .non_pending_payables_params(&non_pending_payables_params_arc) + .non_pending_payables_result(vec![account_1, account_2]); + let payable_dao_for_pending_payable_scanner = PayableDaoMock::new() .transaction_confirmed_params(&transaction_confirmed_params_arc) .transaction_confirmed_result(Ok(())); - let bootstrapper_config = bc_from_ac_plus_earning_wallet( - AccountantConfig { - scan_intervals: ScanIntervals { - payable_scan_interval: Duration::from_secs(1_000_000), //we don't care about this scan - receivable_scan_interval: Duration::from_secs(1_000_000), //we don't care about this scan - pending_payable_scan_interval: Duration::from_millis( - pending_payable_scan_interval, - ), - }, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - suppress_initial_scans: false, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - }, - make_wallet("some_wallet_address"), - ); + + let mut bootstrapper_config = bc_from_earning_wallet(make_wallet("some_wallet_address")); + bootstrapper_config.scan_intervals_opt = Some(ScanIntervals { + payable_scan_interval: Duration::from_secs(1_000_000), //we don't care about this scan + receivable_scan_interval: Duration::from_secs(1_000_000), //we don't care about this scan + pending_payable_scan_interval: Duration::from_millis(pending_payable_scan_interval), + }); let fingerprint_1_first_round = PendingPayableFingerprint { rowid_opt: Some(rowid_for_account_1), timestamp: this_payable_timestamp_1, @@ -4096,7 +2996,14 @@ mod tests { attempt_opt: Some(4), ..fingerprint_2_first_round.clone() }; - let mut pending_payable_dao = PendingPayableDaoMock::default() + let pending_payable_dao_for_accountant = PendingPayableDaoMock::default(); + let pending_payable_dao_for_payable_scanner = PendingPayableDaoMock::default() + .fingerprint_rowid_result(Some(rowid_for_account_1)) + .fingerprint_rowid_result(Some(rowid_for_account_2)) + .insert_fingerprint_params(&insert_fingerprint_params_arc) + .insert_fingerprint_result(Ok(())) + .insert_fingerprint_result(Ok(())); + let mut pending_payable_dao_for_pending_payable_scanner = PendingPayableDaoMock::new() .return_all_fingerprints_params(&return_all_fingerprints_params_arc) .return_all_fingerprints_result(vec![]) .return_all_fingerprints_result(vec![ @@ -4112,11 +3019,6 @@ mod tests { fingerprint_2_third_round, ]) .return_all_fingerprints_result(vec![fingerprint_2_fourth_round.clone()]) - .insert_fingerprint_params(&insert_fingerprint_params_arc) - .insert_fingerprint_result(Ok(())) - .insert_fingerprint_result(Ok(())) - .fingerprint_rowid_result(Some(rowid_for_account_1)) - .fingerprint_rowid_result(Some(rowid_for_account_2)) .update_fingerprint_params(&update_fingerprint_params_arc) .update_fingerprint_results(Ok(())) .update_fingerprint_results(Ok(())) @@ -4130,31 +3032,25 @@ mod tests { .delete_fingerprint_params(&delete_record_params_arc) //this is used during confirmation of the successful one .delete_fingerprint_result(Ok(())); - pending_payable_dao.have_return_all_fingerprints_shut_down_the_system = true; + pending_payable_dao_for_pending_payable_scanner + .have_return_all_fingerprints_shut_down_the_system = true; let accountant_addr = Arbiter::builder() .stop_system_on_panic(true) .start(move |_| { let mut subject = AccountantBuilder::default() .bootstrapper_config(bootstrapper_config) - .payable_dao(payable_dao) - .pending_payable_dao(pending_payable_dao) + .payable_dao(payable_dao_for_accountant) + .payable_dao(payable_dao_for_payable_scanner) + .payable_dao(payable_dao_for_pending_payable_scanner) + .pending_payable_dao(pending_payable_dao_for_accountant) + .pending_payable_dao(pending_payable_dao_for_payable_scanner) + .pending_payable_dao(pending_payable_dao_for_pending_payable_scanner) .build(); - subject.scanners.receivables = Box::new(NullScanner); + subject.scanners.receivable = Box::new(NullScanner::new()); let notify_later_half_mock = NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_scan_for_pending_payable_arc_cloned) .permit_to_send_out(); - subject - .confirmation_tools - .notify_later_scan_for_pending_payable = Box::new(notify_later_half_mock); - let notify_half_mock = NotifyHandleMock::default() - .notify_params(¬ify_cancel_failed_transaction_params_arc_cloned) - .permit_to_send_out(); - subject.confirmation_tools.notify_cancel_failed_transaction = - Box::new(notify_half_mock); - let notify_half_mock = NotifyHandleMock::default() - .notify_params(¬ify_confirm_transaction_params_arc_cloned) - .permit_to_send_out(); - subject.confirmation_tools.notify_confirm_transaction = Box::new(notify_half_mock); + subject.notify_later.scan_for_pending_payable = Box::new(notify_later_half_mock); subject }); let mut peer_actors = peer_actors_builder().build(); @@ -4196,7 +3092,7 @@ mod tests { pending_tx_hash_2, pending_tx_hash_1, pending_tx_hash_2, - pending_tx_hash_2 + pending_tx_hash_2, ] ); let update_backup_after_cycle_params = update_fingerprint_params_arc.lock().unwrap(); @@ -4207,7 +3103,7 @@ mod tests { rowid_for_account_2, rowid_for_account_1, rowid_for_account_2, - rowid_for_account_2 + rowid_for_account_2, ] ); let mark_failure_params = mark_failure_params_arc.lock().unwrap(); @@ -4243,233 +3139,14 @@ mod tests { expected_scan_pending_payable_msg_and_interval, ] ); - let mut notify_confirm_transaction_params = - notify_confirm_transaction_params_arc.lock().unwrap(); - let actual_confirmed_payable: ConfirmPendingTransaction = - notify_confirm_transaction_params.remove(0); - assert!(notify_confirm_transaction_params.is_empty()); - let expected_confirmed_payable = ConfirmPendingTransaction { - pending_payable_fingerprint: fingerprint_2_fourth_round, - }; - assert_eq!(actual_confirmed_payable, expected_confirmed_payable); let log_handler = TestLogHandler::new(); log_handler.exists_log_containing( - "WARN: Accountant: Broken transaction 0x000000000000000000000000000000000000000000000000000000000000007b left with an error mark; you should take over the care of this transaction to make sure your debts will be paid because there \ + "WARN: Accountant: Broken transaction 0x0000…007b left with an error mark; you should take over the care of this transaction to make sure your debts will be paid because there \ is no automated process that can fix this without you"); - log_handler.exists_log_matching("INFO: Accountant: Transaction '0x0000000000000000000000000000000000000000000000000000000000000237' has been added to the blockchain; detected locally at attempt 4 at \\d{2,}ms after its sending"); + log_handler.exists_log_matching("INFO: Accountant: Transaction '0x0000…0237' has been added to the blockchain; detected locally at attempt 4 at \\d{2,}ms after its sending"); log_handler.exists_log_containing("INFO: Accountant: Transaction 0x0000000000000000000000000000000000000000000000000000000000000237 has gone through the whole confirmation process succeeding"); } - #[test] - fn handle_pending_tx_handles_none_returned_for_transaction_receipt() { - init_test_logging(); - let subject = AccountantBuilder::default().build(); - let tx_receipt_opt = None; - let rowid = 455; - let hash = H256::from_uint(&U256::from(2323)); - let fingerprint = PendingPayableFingerprint { - rowid_opt: Some(rowid), - timestamp: SystemTime::now().sub(Duration::from_millis(10000)), - hash, - attempt_opt: Some(3), - amount: 111, - process_error: None, - }; - let msg = ReportTransactionReceipts { - fingerprints_with_receipts: vec![(tx_receipt_opt, fingerprint.clone())], - response_skeleton_opt: None, - }; - - let result = subject.handle_pending_transaction_with_its_receipt(&msg); - - assert_eq!( - result, - vec![PendingTransactionStatus::StillPending(PendingPayableId { - hash, - rowid - })] - ); - TestLogHandler::new().exists_log_matching("DEBUG: Accountant: Interpreting a receipt for transaction '0x0000000000000000000000000000000000000000000000000000000000000913' but none was given; attempt 3, 100\\d\\dms since sending"); - } - - #[test] - fn accountant_receives_reported_transaction_receipts_and_processes_them_all() { - let notify_handle_params_arc = Arc::new(Mutex::new(vec![])); - let mut subject = AccountantBuilder::default().build(); - subject.confirmation_tools.notify_confirm_transaction = - Box::new(NotifyHandleMock::default().notify_params(¬ify_handle_params_arc)); - let subject_addr = subject.start(); - let transaction_hash_1 = H256::from_uint(&U256::from(4545)); - let mut transaction_receipt_1 = TransactionReceipt::default(); - transaction_receipt_1.transaction_hash = transaction_hash_1; - transaction_receipt_1.status = Some(U64::from(1)); //success - let fingerprint_1 = PendingPayableFingerprint { - rowid_opt: Some(5), - timestamp: from_time_t(200_000_000), - hash: transaction_hash_1, - attempt_opt: Some(2), - amount: 444, - process_error: None, - }; - let transaction_hash_2 = H256::from_uint(&U256::from(3333333)); - let mut transaction_receipt_2 = TransactionReceipt::default(); - transaction_receipt_2.transaction_hash = transaction_hash_2; - transaction_receipt_2.status = Some(U64::from(1)); //success - let fingerprint_2 = PendingPayableFingerprint { - rowid_opt: Some(10), - timestamp: from_time_t(199_780_000), - hash: Default::default(), - attempt_opt: Some(15), - amount: 1212, - process_error: None, - }; - let msg = ReportTransactionReceipts { - fingerprints_with_receipts: vec![ - (Some(transaction_receipt_1), fingerprint_1.clone()), - (Some(transaction_receipt_2), fingerprint_2.clone()), - ], - response_skeleton_opt: None, - }; - - let _ = subject_addr.try_send(msg).unwrap(); - - let system = System::new("processing reported receipts"); - System::current().stop(); - system.run(); - let notify_handle_params = notify_handle_params_arc.lock().unwrap(); - assert_eq!( - *notify_handle_params, - vec![ - ConfirmPendingTransaction { - pending_payable_fingerprint: fingerprint_1 - }, - ConfirmPendingTransaction { - pending_payable_fingerprint: fingerprint_2 - } - ] - ); - } - - #[test] - fn interpret_transaction_receipt_when_transaction_status_is_a_failure() { - init_test_logging(); - let subject = AccountantBuilder::default().build(); - let mut tx_receipt = TransactionReceipt::default(); - tx_receipt.status = Some(U64::from(0)); //failure - let hash = H256::from_uint(&U256::from(4567)); - let fingerprint = PendingPayableFingerprint { - rowid_opt: Some(777777), - timestamp: SystemTime::now().sub(Duration::from_millis(150000)), - hash, - attempt_opt: Some(5), - amount: 2222, - process_error: None, - }; - - let result = subject.interpret_transaction_receipt( - &tx_receipt, - &fingerprint, - &Logger::new("receipt_check_logger"), - ); - - assert_eq!( - result, - PendingTransactionStatus::Failure(PendingPayableId { - hash, - rowid: 777777 - }) - ); - TestLogHandler::new().exists_log_matching("ERROR: receipt_check_logger: Pending \ - transaction '0x00000000000000000000000000000000000000000000000000000000000011d7' announced as a failure, interpreting attempt 5 after 1500\\d\\dms from the sending"); - } - - #[test] - fn interpret_transaction_receipt_when_transaction_status_is_none_and_within_waiting_interval() { - init_test_logging(); - let hash = H256::from_uint(&U256::from(567)); - let rowid = 466; - let tx_receipt = TransactionReceipt::default(); //status defaulted to None - let when_sent = SystemTime::now().sub(Duration::from_millis(100)); - let subject = AccountantBuilder::default().build(); - let fingerprint = PendingPayableFingerprint { - rowid_opt: Some(rowid), - timestamp: when_sent, - hash, - attempt_opt: Some(1), - amount: 123, - process_error: None, - }; - - let result = subject.interpret_transaction_receipt( - &tx_receipt, - &fingerprint, - &Logger::new("none_within_waiting"), - ); - - assert_eq!( - result, - PendingTransactionStatus::StillPending(PendingPayableId { hash, rowid }) - ); - TestLogHandler::new().exists_log_containing( - "INFO: none_within_waiting: Pending \ - transaction '0x0000000000000000000000000000000000000000000000000000000000000237' couldn't be confirmed at attempt 1 at ", - ); - } - - #[test] - fn interpret_transaction_receipt_when_transaction_status_is_none_and_outside_waiting_interval() - { - init_test_logging(); - let hash = H256::from_uint(&U256::from(567)); - let rowid = 466; - let tx_receipt = TransactionReceipt::default(); //status defaulted to None - let when_sent = - SystemTime::now().sub(Duration::from_secs(DEFAULT_PENDING_TOO_LONG_SEC + 5)); //old transaction - let subject = AccountantBuilder::default().build(); - let fingerprint = PendingPayableFingerprint { - rowid_opt: Some(rowid), - timestamp: when_sent, - hash, - attempt_opt: Some(10), - amount: 123, - process_error: None, - }; - - let result = subject.interpret_transaction_receipt( - &tx_receipt, - &fingerprint, - &Logger::new("receipt_check_logger"), - ); - - assert_eq!( - result, - PendingTransactionStatus::Failure(PendingPayableId { hash, rowid }) - ); - TestLogHandler::new().exists_log_containing( - "ERROR: receipt_check_logger: Pending transaction '0x0000000000000000000000000000000000000000000000000000000000000237' has exceeded the maximum \ - pending time (21600sec) and the confirmation process is going to be aborted now at the final attempt 10; manual resolution is required from the user to \ - complete the transaction", - ); - } - - #[test] - #[should_panic( - expected = "tx receipt for pending '0x000000000000000000000000000000000000000000000000000000000000007b' - tx status: code other than 0 or 1 shouldn't be possible, but was 456" - )] - fn interpret_transaction_receipt_panics_at_undefined_status_code() { - let mut tx_receipt = TransactionReceipt::default(); - tx_receipt.status = Some(U64::from(456)); - let mut fingerprint = make_pending_payable_fingerprint(); - fingerprint.hash = H256::from_uint(&U256::from(123)); - let subject = AccountantBuilder::default().build(); - - let _ = subject.interpret_transaction_receipt( - &tx_receipt, - &fingerprint, - &Logger::new("receipt_check_logger"), - ); - } - #[test] fn accountant_handles_pending_payable_fingerprint() { init_test_logging(); @@ -4478,7 +3155,9 @@ mod tests { .insert_fingerprint_params(&insert_fingerprint_params_arc) .insert_fingerprint_result(Ok(())); let subject = AccountantBuilder::default() - .pending_payable_dao(pending_payment_dao) + .pending_payable_dao(pending_payment_dao) // For Accountant + .pending_payable_dao(PendingPayableDaoMock::new()) // For Payable Scanner + .pending_payable_dao(PendingPayableDaoMock::new()) // For PendingPayable Scanner .build(); let accountant_addr = subject.start(); let tx_hash = H256::from_uint(&U256::from(55)); @@ -4526,6 +3205,8 @@ mod tests { let transaction_hash = H256::from_uint(&U256::from(456)); let subject = AccountantBuilder::default() .pending_payable_dao(pending_payable_dao) + .pending_payable_dao(PendingPayableDaoMock::new()) + .pending_payable_dao(PendingPayableDaoMock::new()) .build(); let timestamp_secs = 150_000_000; let fingerprint = PendingPayableFingerprint { @@ -4547,65 +3228,6 @@ mod tests { TestLogHandler::new().exists_log_containing("ERROR: Accountant: Failed to make a fingerprint for pending payable '0x00000000000000000000000000000000000000000000000000000000000001c8' due to 'InsertionFailed(\"Crashed\")'"); } - #[test] - fn separate_early_errors_works() { - let payable_ok = Payable { - to: make_wallet("blah"), - amount: 5555, - timestamp: SystemTime::now(), - tx_hash: Default::default(), - }; - let error = BlockchainError::SignedValueConversion(666); - let sent_payable = SentPayable { - timestamp: SystemTime::now(), - payable: vec![Ok(payable_ok.clone()), Err(error.clone())], - response_skeleton_opt: None, - }; - - let (ok, err) = Accountant::separate_early_errors(&sent_payable, &Logger::new("test")); - - assert_eq!(ok, vec![payable_ok]); - assert_eq!(err, vec![error]) - } - - #[test] - fn update_payable_fingerprint_happy_path() { - let update_after_cycle_params_arc = Arc::new(Mutex::new(vec![])); - let hash = H256::from_uint(&U256::from(444888)); - let rowid = 3456; - let pending_payable_dao = PendingPayableDaoMock::default() - .update_fingerprint_params(&update_after_cycle_params_arc) - .update_fingerprint_results(Ok(())); - let subject = AccountantBuilder::default() - .pending_payable_dao(pending_payable_dao) - .build(); - let transaction_id = PendingPayableId { hash, rowid }; - - let _ = subject.update_payable_fingerprint(transaction_id); - - let update_after_cycle_params = update_after_cycle_params_arc.lock().unwrap(); - assert_eq!(*update_after_cycle_params, vec![rowid]) - } - - #[test] - #[should_panic( - expected = "Failure on updating payable fingerprint '0x000000000000000000000000000000000000000000000000000000000006c9d8' \ - due to UpdateFailed(\"yeah, bad\")" - )] - fn update_payable_fingerprint_sad_path() { - let hash = H256::from_uint(&U256::from(444888)); - let rowid = 3456; - let pending_payable_dao = PendingPayableDaoMock::default().update_fingerprint_results(Err( - PendingPayableDaoError::UpdateFailed("yeah, bad".to_string()), - )); - let subject = AccountantBuilder::default() - .pending_payable_dao(pending_payable_dao) - .build(); - let transaction_id = PendingPayableId { hash, rowid }; - - let _ = subject.update_payable_fingerprint(transaction_id); - } - #[test] fn handles_scan_error() { let (ui_gateway, _, ui_gateway_recording_arc) = make_recorder(); @@ -4639,8 +3261,8 @@ mod tests { payload: Err(( SCAN_ERROR, "Payables scan failed: 'My tummy hurts'".to_string() - )) - } + )), + }, } ); } @@ -4650,16 +3272,18 @@ mod tests { let payable_dao = PayableDaoMock::new().total_result(23456789); let receivable_dao = ReceivableDaoMock::new().total_result(98765432); let system = System::new("test"); - let mut subject = AccountantBuilder::default() - .bootstrapper_config(bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - make_wallet("some_wallet_address"), - )) - .receivable_dao(receivable_dao) - .payable_dao(payable_dao) + let subject = AccountantBuilder::default() + .bootstrapper_config(make_bc_with_defaults()) + .payable_dao(payable_dao) // For Accountant + .payable_dao(PayableDaoMock::new()) // For Payable Scanner + .payable_dao(PayableDaoMock::new()) // For PendingPayable Scanner + .receivable_dao(receivable_dao) // For Accountant + .receivable_dao(ReceivableDaoMock::new()) // For Scanner .build(); - subject.financial_statistics.total_paid_payable = 123456; - subject.financial_statistics.total_paid_receivable = 334455; + let mut financial_statistics = subject.financial_statistics(); + financial_statistics.total_paid_payable = 123456; + financial_statistics.total_paid_receivable = 334455; + subject.financial_statistics.replace(financial_statistics); let (ui_gateway, _, ui_gateway_recording_arc) = make_recorder(); let subject_addr = subject.start(); let peer_actors = peer_actors_builder().ui_gateway(ui_gateway).build(); @@ -4684,83 +3308,11 @@ mod tests { total_unpaid_and_pending_payable: 23456789, total_paid_payable: 123456, total_unpaid_receivable: 98765432, - total_paid_receivable: 334455 + total_paid_receivable: 334455, } ); } - #[test] - fn total_paid_payable_rises_with_each_bill_paid() { - let transaction_confirmed_params_arc = Arc::new(Mutex::new(vec![])); - let fingerprint = PendingPayableFingerprint { - rowid_opt: Some(5), - timestamp: from_time_t(189_999_888), - hash: H256::from_uint(&U256::from(56789)), - attempt_opt: Some(1), - amount: 5478, - process_error: None, - }; - let mut pending_payable_dao = - PendingPayableDaoMock::default().delete_fingerprint_result(Ok(())); - let payable_dao = PayableDaoMock::default() - .transaction_confirmed_params(&transaction_confirmed_params_arc) - .transaction_confirmed_result(Ok(())) - .transaction_confirmed_result(Ok(())); - pending_payable_dao.have_return_all_fingerprints_shut_down_the_system = true; - let mut subject = AccountantBuilder::default() - .pending_payable_dao(pending_payable_dao) - .payable_dao(payable_dao) - .build(); - subject.financial_statistics.total_paid_payable += 1111; - let msg = ConfirmPendingTransaction { - pending_payable_fingerprint: fingerprint.clone(), - }; - - subject.handle_confirm_pending_transaction(msg); - - assert_eq!(subject.financial_statistics.total_paid_payable, 1111 + 5478); - let transaction_confirmed_params = transaction_confirmed_params_arc.lock().unwrap(); - assert_eq!(*transaction_confirmed_params, vec![fingerprint]) - } - - #[test] - fn total_paid_receivable_rises_with_each_bill_paid() { - let more_money_received_params_arc = Arc::new(Mutex::new(vec![])); - let receivable_dao = ReceivableDaoMock::new() - .more_money_received_parameters(&more_money_received_params_arc) - .more_money_receivable_result(Ok(())); - let mut subject = AccountantBuilder::default() - .receivable_dao(receivable_dao) - .build(); - subject.financial_statistics.total_paid_receivable += 2222; - let receivables = vec![ - BlockchainTransaction { - block_number: 4578910, - from: make_wallet("wallet_1"), - gwei_amount: 45780, - }, - BlockchainTransaction { - block_number: 4569898, - from: make_wallet("wallet_2"), - gwei_amount: 33345, - }, - ]; - let now = SystemTime::now(); - - subject.handle_received_payments(ReceivedPayments { - timestamp: now, - payments: receivables.clone(), - response_skeleton_opt: None, - }); - - assert_eq!( - subject.financial_statistics.total_paid_receivable, - 2222 + 45780 + 33345 - ); - let more_money_received_params = more_money_received_params_arc.lock().unwrap(); - assert_eq!(*more_money_received_params, vec![(now, receivables)]); - } - #[test] #[cfg(not(feature = "no_test_share"))] fn msg_id_generates_numbers_only_if_debug_log_enabled() { diff --git a/node/src/accountant/scanners.rs b/node/src/accountant/scanners.rs new file mode 100644 index 000000000..43886e4c8 --- /dev/null +++ b/node/src/accountant/scanners.rs @@ -0,0 +1,1848 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::accountant::payable_dao::{Payable, PayableDao, PayableDaoFactory}; +use crate::accountant::pending_payable_dao::{PendingPayableDao, PendingPayableDaoFactory}; +use crate::accountant::receivable_dao::ReceivableDao; +use crate::accountant::scanners_tools::payable_scanner_tools::{ + investigate_debt_extremes, qualified_payables_and_summary, separate_early_errors, +}; +use crate::accountant::scanners_tools::pending_payable_scanner_tools::{ + elapsed_in_ms, handle_none_status, handle_status_with_failure, handle_status_with_success, +}; +use crate::accountant::scanners_tools::receivable_scanner_tools::balance_and_age; +use crate::accountant::{ + Accountant, ReceivedPayments, ReportTransactionReceipts, RequestTransactionReceipts, + ResponseSkeleton, ScanForPayables, ScanForPendingPayables, ScanForReceivables, SentPayable, +}; +use crate::accountant::{PendingPayableId, PendingTransactionStatus, ReportAccountsPayable}; +use crate::banned_dao::BannedDao; +use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint, RetrieveTransactions}; +use crate::blockchain::blockchain_interface::BlockchainError; +use crate::sub_lib::accountant::{FinancialStatistics, PaymentThresholds}; +use crate::sub_lib::utils::NotifyLaterHandle; +use crate::sub_lib::wallet::Wallet; +use actix::{Message, System}; +use masq_lib::logger::Logger; +use masq_lib::messages::{ToMessageBody, UiScanResponse}; +use masq_lib::ui_gateway::{MessageTarget, NodeToUiMessage}; +use masq_lib::utils::ExpectValue; +#[cfg(test)] +use std::any::Any; +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::{Arc, Mutex}; +use std::time::SystemTime; +use web3::types::TransactionReceipt; + +pub struct Scanners { + pub payable: Box>, + pub pending_payable: Box>, + pub receivable: Box>, +} + +impl Scanners { + #[allow(clippy::too_many_arguments)] + pub fn new( + payable_dao_factory: Box, + pending_payable_dao_factory: Box, + receivable_dao: Box, + banned_dao: Box, + payment_thresholds: Rc, + earning_wallet: Rc, + when_pending_too_long_sec: u64, + financial_statistics: Rc>, + ) -> Self { + Scanners { + payable: Box::new(PayableScanner::new( + payable_dao_factory.make(), + pending_payable_dao_factory.make(), + Rc::clone(&payment_thresholds), + )), + pending_payable: Box::new(PendingPayableScanner::new( + payable_dao_factory.make(), + pending_payable_dao_factory.make(), + Rc::clone(&payment_thresholds), + when_pending_too_long_sec, + Rc::clone(&financial_statistics), + )), + receivable: Box::new(ReceivableScanner::new( + receivable_dao, + banned_dao, + Rc::clone(&payment_thresholds), + earning_wallet, + financial_statistics, + )), + } + } +} + +pub trait Scanner +where + BeginMessage: Message, + EndMessage: Message, +{ + fn begin_scan( + &mut self, + timestamp: SystemTime, + response_skeleton_opt: Option, + logger: &Logger, + ) -> Result; + fn finish_scan(&mut self, message: EndMessage, logger: &Logger) -> Option; + fn scan_started_at(&self) -> Option; + fn mark_as_started(&mut self, timestamp: SystemTime); + fn mark_as_ended(&mut self, logger: &Logger); + as_any_dcl!(); +} + +#[derive(Debug, PartialEq, Eq)] +pub enum BeginScanError { + NothingToProcess, + ScanAlreadyRunning(SystemTime), + CalledFromNullScanner, // Exclusive for tests +} + +pub struct ScannerCommon { + initiated_at_opt: Option, + pub payment_thresholds: Rc, +} + +impl ScannerCommon { + fn new(payment_thresholds: Rc) -> Self { + Self { + initiated_at_opt: None, + payment_thresholds, + } + } +} + +pub struct PayableScanner { + pub common: ScannerCommon, + pub payable_dao: Box, + pub pending_payable_dao: Box, +} + +impl Scanner for PayableScanner { + fn begin_scan( + &mut self, + timestamp: SystemTime, + response_skeleton_opt: Option, + logger: &Logger, + ) -> Result { + if let Some(timestamp) = self.scan_started_at() { + return Err(BeginScanError::ScanAlreadyRunning(timestamp)); + } + self.mark_as_started(timestamp); + info!(logger, "Scanning for payables"); + let all_non_pending_payables = self.payable_dao.non_pending_payables(); + debug!( + logger, + "{}", + investigate_debt_extremes(timestamp, &all_non_pending_payables) + ); + let (qualified_payables, summary) = qualified_payables_and_summary( + timestamp, + all_non_pending_payables, + self.common.payment_thresholds.as_ref(), + ); + info!( + logger, + "Chose {} qualified debts to pay", + qualified_payables.len() + ); + debug!(logger, "{}", summary); + match qualified_payables.is_empty() { + true => { + self.mark_as_ended(logger); + Err(BeginScanError::NothingToProcess) + } + false => Ok(ReportAccountsPayable { + accounts: qualified_payables, + response_skeleton_opt, + }), + } + } + + fn finish_scan(&mut self, message: SentPayable, logger: &Logger) -> Option { + let (sent_payables, blockchain_errors) = separate_early_errors(&message, logger); + debug!( + logger, + "We gathered these errors at sending transactions for payable: {:?}, out of the \ + total of {} attempts", + blockchain_errors, + sent_payables.len() + blockchain_errors.len() + ); + + self.handle_sent_payables(sent_payables, logger); + self.handle_blockchain_errors(blockchain_errors, logger); + + self.mark_as_ended(logger); + message + .response_skeleton_opt + .map(|response_skeleton| NodeToUiMessage { + target: MessageTarget::ClientId(response_skeleton.client_id), + body: UiScanResponse {}.tmb(response_skeleton.context_id), + }) + } + + fn scan_started_at(&self) -> Option { + self.common.initiated_at_opt + } + + fn mark_as_started(&mut self, timestamp: SystemTime) { + self.common.initiated_at_opt = Some(timestamp); + } + + fn mark_as_ended(&mut self, logger: &Logger) { + match self.scan_started_at() { + Some(timestamp) => { + let elapsed_time = SystemTime::now() + .duration_since(timestamp) + .expect("Unable to calculate elapsed time for the scan.") + .as_millis(); + info!(logger, "The Payable scan ended in {elapsed_time}ms."); + self.common.initiated_at_opt = None; + } + None => error!( + logger, + "The scan_finished() was called for Payable scanner but timestamp was not found" + ), + }; + } + + as_any_impl!(); +} + +impl PayableScanner { + pub fn new( + payable_dao: Box, + pending_payable_dao: Box, + payment_thresholds: Rc, + ) -> Self { + Self { + common: ScannerCommon::new(payment_thresholds), + payable_dao, + pending_payable_dao, + } + } + + fn handle_sent_payables(&self, sent_payables: Vec, logger: &Logger) { + for payable in sent_payables { + if let Some(rowid) = self.pending_payable_dao.fingerprint_rowid(payable.tx_hash) { + if let Err(e) = self + .payable_dao + .as_ref() + .mark_pending_payable_rowid(&payable.to, rowid) + { + panic!( + "Was unable to create a mark in payables for a new pending payable \ + '{}' due to '{:?}'", + payable.tx_hash, e + ); + } + } else { + panic!( + "Payable fingerprint for {} doesn't exist but should by now; \ + system unreliable", + payable.tx_hash + ); + }; + + debug!( + logger, + "Payable '{}' has been marked as pending in the payable table", payable.tx_hash + ) + } + } + + fn handle_blockchain_errors(&self, blockchain_errors: Vec, logger: &Logger) { + for blockchain_error in blockchain_errors { + if let Some(hash) = blockchain_error.carries_transaction_hash() { + if let Some(rowid) = self.pending_payable_dao.fingerprint_rowid(hash) { + debug!( + logger, + "Deleting an existing backup for a failed transaction {}", hash + ); + if let Err(e) = self.pending_payable_dao.delete_fingerprint(rowid) { + panic!( + "Database unmaintainable; payable fingerprint deletion for \ + transaction {:?} has stayed undone due to {:?}", + hash, e + ); + }; + }; + + warning!( + logger, + "Failed transaction with a hash '{}' but without the record - thrown out", + hash + ) + } else { + debug!( + logger, + "Forgetting a transaction attempt that even did not reach the signing stage" + ) + }; + } + } +} + +pub struct PendingPayableScanner { + pub common: ScannerCommon, + pub payable_dao: Box, + pub pending_payable_dao: Box, + pub when_pending_too_long_sec: u64, + pub financial_statistics: Rc>, +} + +impl Scanner for PendingPayableScanner { + fn begin_scan( + &mut self, + timestamp: SystemTime, + response_skeleton_opt: Option, + logger: &Logger, + ) -> Result { + if let Some(timestamp) = self.scan_started_at() { + return Err(BeginScanError::ScanAlreadyRunning(timestamp)); + } + self.mark_as_started(timestamp); + info!(logger, "Scanning for pending payable"); + let filtered_pending_payable = self.pending_payable_dao.return_all_fingerprints(); + match filtered_pending_payable.is_empty() { + true => { + debug!( + logger, + "Pending payable scan ended. No pending payable found." + ); + self.mark_as_ended(logger); + Err(BeginScanError::NothingToProcess) + } + false => { + debug!( + logger, + "Found {} pending payables to process", + filtered_pending_payable.len() + ); + Ok(RequestTransactionReceipts { + pending_payable: filtered_pending_payable, + response_skeleton_opt, + }) + } + } + } + + fn finish_scan( + &mut self, + message: ReportTransactionReceipts, + logger: &Logger, + ) -> Option { + // TODO: Make accountant to handle empty vector. Maybe log it as an error. + debug!( + logger, + "Processing receipts for {} transactions", + message.fingerprints_with_receipts.len() + ); + let statuses = self.handle_pending_transaction_with_its_receipt(&message, logger); + self.process_transaction_by_status(statuses, logger); + + self.mark_as_ended(logger); + message + .response_skeleton_opt + .map(|response_skeleton| NodeToUiMessage { + target: MessageTarget::ClientId(response_skeleton.client_id), + body: UiScanResponse {}.tmb(response_skeleton.context_id), + }) + } + + fn scan_started_at(&self) -> Option { + self.common.initiated_at_opt + } + + fn mark_as_started(&mut self, timestamp: SystemTime) { + self.common.initiated_at_opt = Some(timestamp); + } + + fn mark_as_ended(&mut self, logger: &Logger) { + match self.scan_started_at() { + Some(timestamp) => { + let elapsed_time = SystemTime::now() + .duration_since(timestamp) + .expect("Unable to calculate elapsed time for the scan.") + .as_millis(); + info!( + logger, + "The Pending Payable scan ended in {elapsed_time}ms." + ); + self.common.initiated_at_opt = None; + } + None => error!(logger, "The scan_finished() was called for Pending Payable scanner but timestamp was not found"), + }; + } + + as_any_impl!(); +} + +impl PendingPayableScanner { + pub fn new( + payable_dao: Box, + pending_payable_dao: Box, + payment_thresholds: Rc, + when_pending_too_long_sec: u64, + financial_statistics: Rc>, + ) -> Self { + Self { + common: ScannerCommon::new(payment_thresholds), + payable_dao, + pending_payable_dao, + when_pending_too_long_sec, + financial_statistics, + } + } + + pub(crate) fn handle_pending_transaction_with_its_receipt( + &self, + msg: &ReportTransactionReceipts, + logger: &Logger, + ) -> Vec { + msg.fingerprints_with_receipts + .iter() + .map(|(receipt_opt, fingerprint)| match receipt_opt { + Some(receipt) => self.interpret_transaction_receipt(receipt, fingerprint, logger), + None => { + debug!( + logger, + "Interpreting a receipt for transaction '{}' but none was given; \ + attempt {}, {}ms since sending", + fingerprint.hash, + fingerprint.attempt_opt.expectv("initialized attempt"), + elapsed_in_ms(fingerprint.timestamp) + ); + PendingTransactionStatus::StillPending(PendingPayableId { + hash: fingerprint.hash, + rowid: fingerprint.rowid_opt.expectv("initialized rowid"), + }) + } + }) + .collect() + } + + pub fn interpret_transaction_receipt( + &self, + receipt: &TransactionReceipt, + fingerprint: &PendingPayableFingerprint, + logger: &Logger, + ) -> PendingTransactionStatus { + match receipt.status { + None => handle_none_status(fingerprint, self.when_pending_too_long_sec, logger), + Some(status_code) => + match status_code.as_u64() { + 0 => handle_status_with_failure(fingerprint, logger), + 1 => handle_status_with_success(fingerprint, logger), + other => unreachable!("tx receipt for pending '{}' - tx status: code other than 0 or 1 shouldn't be possible, but was {}", fingerprint.hash, other) + } + } + } + + fn process_transaction_by_status( + &mut self, + statuses: Vec, + logger: &Logger, + ) { + for status in statuses { + match status { + PendingTransactionStatus::StillPending(transaction_id) => { + self.update_payable_fingerprint(transaction_id, logger); + } + PendingTransactionStatus::Failure(transaction_id) => { + self.order_cancel_failed_transaction(transaction_id, logger); + } + PendingTransactionStatus::Confirmed(fingerprint) => { + self.order_confirm_transaction(fingerprint, logger); + } + } + } + } + + pub(crate) fn update_payable_fingerprint( + &self, + pending_payable_id: PendingPayableId, + logger: &Logger, + ) { + if let Err(e) = self + .pending_payable_dao + .update_fingerprint(pending_payable_id.rowid) + { + panic!( + "Failure on updating payable fingerprint '{:?}' due to {:?}", + pending_payable_id.hash, e + ); + } else { + trace!( + logger, + "Updated record for rowid: {} ", + pending_payable_id.rowid + ); + } + } + + pub fn order_cancel_failed_transaction( + &self, + transaction_id: PendingPayableId, + logger: &Logger, + ) { + if let Err(e) = self.pending_payable_dao.mark_failure(transaction_id.rowid) { + panic!( + "Unsuccessful attempt for transaction {} to mark fatal error at payable \ + fingerprint due to {:?}; database unreliable", + transaction_id.hash, e + ) + } else { + warning!( + logger, + "Broken transaction {} left with an error mark; you should take over the care \ + of this transaction to make sure your debts will be paid because there is no \ + automated process that can fix this without you", transaction_id.hash + ); + } + } + + pub fn order_confirm_transaction( + &mut self, + pending_payable_fingerprint: PendingPayableFingerprint, + logger: &Logger, + ) { + let hash = pending_payable_fingerprint.hash; + let amount = pending_payable_fingerprint.amount; + let rowid = pending_payable_fingerprint + .rowid_opt + .expectv("initialized rowid"); + + if let Err(e) = self + .payable_dao + .transaction_confirmed(&pending_payable_fingerprint) + { + panic!( + "Was unable to uncheck pending payable '{}' after confirmation due to '{:?}'", + hash, e + ); + } else { + let mut financial_statistics = self.financial_statistics.as_ref().borrow().clone(); + financial_statistics.total_paid_payable += amount; + self.financial_statistics.replace(financial_statistics); + debug!( + logger, + "Confirmation of transaction {}; record for payable was modified", hash + ); + if let Err(e) = self.pending_payable_dao.delete_fingerprint(rowid) { + panic!( + "Was unable to delete payable fingerprint '{}' after successful transaction \ + due to '{:?}'", + hash, e + ); + } else { + info!( + logger, + "Transaction {:?} has gone through the whole confirmation process succeeding", + hash + ); + } + } + } + + pub fn financial_statistics(&self) -> FinancialStatistics { + self.financial_statistics.as_ref().borrow().clone() + } +} + +pub struct ReceivableScanner { + pub common: ScannerCommon, + pub receivable_dao: Box, + pub banned_dao: Box, + pub earning_wallet: Rc, + pub financial_statistics: Rc>, +} + +impl Scanner for ReceivableScanner { + fn begin_scan( + &mut self, + timestamp: SystemTime, + response_skeleton_opt: Option, + logger: &Logger, + ) -> Result { + if let Some(timestamp) = self.scan_started_at() { + return Err(BeginScanError::ScanAlreadyRunning(timestamp)); + } + self.mark_as_started(timestamp); + info!( + logger, + "Scanning for receivables to {}", self.earning_wallet + ); + self.scan_for_delinquencies(timestamp, logger); + + Ok(RetrieveTransactions { + recipient: self.earning_wallet.as_ref().clone(), + response_skeleton_opt, + }) + } + + fn finish_scan( + &mut self, + message: ReceivedPayments, + logger: &Logger, + ) -> Option { + if message.payments.is_empty() { + warning!( + logger, + "Handling received payments we got zero payments but expected some, \ + skipping database operations" + ) + } else { + let total_newly_paid_receivable = message + .payments + .iter() + .fold(0, |so_far, now| so_far + now.gwei_amount); + self.receivable_dao + .as_mut() + .more_money_received(message.timestamp, message.payments); + let mut financial_statistics = self.financial_statistics(); + financial_statistics.total_paid_receivable += total_newly_paid_receivable; + self.financial_statistics.replace(financial_statistics); + } + + self.mark_as_ended(logger); + message + .response_skeleton_opt + .map(|response_skeleton| NodeToUiMessage { + target: MessageTarget::ClientId(response_skeleton.client_id), + body: UiScanResponse {}.tmb(response_skeleton.context_id), + }) + } + + fn scan_started_at(&self) -> Option { + self.common.initiated_at_opt + } + + fn mark_as_started(&mut self, timestamp: SystemTime) { + self.common.initiated_at_opt = Some(timestamp); + } + + fn mark_as_ended(&mut self, logger: &Logger) { + match self.scan_started_at() { + Some(timestamp) => { + let elapsed_time = SystemTime::now() + .duration_since(timestamp) + .expect("Unable to calculate elapsed time for the scan.") + .as_millis(); + info!(logger, "The Receivable scan ended in {elapsed_time}ms."); + self.common.initiated_at_opt = None; + } + None => error!( + logger, + "The scan_finished() was called for Receivable scanner but timestamp was not found" + ), + }; + } + + as_any_impl!(); +} + +impl ReceivableScanner { + pub fn new( + receivable_dao: Box, + banned_dao: Box, + payment_thresholds: Rc, + earning_wallet: Rc, + financial_statistics: Rc>, + ) -> Self { + Self { + common: ScannerCommon::new(payment_thresholds), + earning_wallet, + receivable_dao, + banned_dao, + financial_statistics, + } + } + + pub fn scan_for_delinquencies(&self, timestamp: SystemTime, logger: &Logger) { + info!(logger, "Scanning for delinquencies"); + self.ban_nodes(timestamp, logger); + self.unban_nodes(timestamp, logger); + } + + pub fn ban_nodes(&self, timestamp: SystemTime, logger: &Logger) { + self.receivable_dao + .new_delinquencies(timestamp, self.common.payment_thresholds.as_ref()) + .into_iter() + .for_each(|account| { + self.banned_dao.ban(&account.wallet); + let (balance, age) = balance_and_age(timestamp, &account); + info!( + logger, + "Wallet {} (balance: {} MASQ, age: {} sec) banned for delinquency", + account.wallet, + balance, + age.as_secs() + ) + }); + } + + pub fn unban_nodes(&self, timestamp: SystemTime, logger: &Logger) { + self.receivable_dao + .paid_delinquencies(self.common.payment_thresholds.as_ref()) + .into_iter() + .for_each(|account| { + self.banned_dao.unban(&account.wallet); + let (balance, age) = balance_and_age(timestamp, &account); + info!( + logger, + "Wallet {} (balance: {} MASQ, age: {} sec) is no longer delinquent: unbanned", + account.wallet, + balance, + age.as_secs() + ) + }); + } + + pub fn financial_statistics(&self) -> FinancialStatistics { + self.financial_statistics.as_ref().borrow().clone() + } +} + +pub struct NullScanner {} + +impl Scanner for NullScanner +where + BeginMessage: Message, + EndMessage: Message, +{ + fn begin_scan( + &mut self, + _timestamp: SystemTime, + _response_skeleton_opt: Option, + _logger: &Logger, + ) -> Result { + Err(BeginScanError::CalledFromNullScanner) + } + + fn finish_scan(&mut self, _message: EndMessage, _logger: &Logger) -> Option { + panic!("Called from NullScanner"); + } + + fn scan_started_at(&self) -> Option { + panic!("Called from NullScanner"); + } + + fn mark_as_started(&mut self, _timestamp: SystemTime) { + panic!("Called from NullScanner"); + } + + fn mark_as_ended(&mut self, _logger: &Logger) { + panic!("Called from NullScanner"); + } + + as_any_impl!(); +} + +impl Default for NullScanner { + fn default() -> Self { + Self::new() + } +} + +impl NullScanner { + pub fn new() -> Self { + Self {} + } +} + +pub struct ScannerMock { + begin_scan_params: Arc>>, + begin_scan_results: RefCell>>, + end_scan_params: Arc>>, + end_scan_results: RefCell>>, + stop_system_after_last_message: RefCell, +} + +impl Scanner + for ScannerMock +where + BeginMessage: Message, + EndMessage: Message, +{ + fn begin_scan( + &mut self, + _timestamp: SystemTime, + _response_skeleton_opt: Option, + _logger: &Logger, + ) -> Result { + self.begin_scan_params.lock().unwrap().push(()); + if self.is_allowed_to_stop_the_system() && self.is_last_message() { + System::current().stop(); + } + self.begin_scan_results.borrow_mut().remove(0) + } + + fn finish_scan(&mut self, message: EndMessage, _logger: &Logger) -> Option { + self.end_scan_params.lock().unwrap().push(message); + if self.is_allowed_to_stop_the_system() && self.is_last_message() { + System::current().stop(); + } + self.end_scan_results.borrow_mut().remove(0) + } + + fn scan_started_at(&self) -> Option { + unimplemented!() + } + + fn mark_as_started(&mut self, _timestamp: SystemTime) { + unimplemented!() + } + + fn mark_as_ended(&mut self, _logger: &Logger) { + unimplemented!() + } +} + +impl Default for ScannerMock { + fn default() -> Self { + Self::new() + } +} + +impl ScannerMock { + pub fn new() -> Self { + Self { + begin_scan_params: Arc::new(Mutex::new(vec![])), + begin_scan_results: RefCell::new(vec![]), + end_scan_params: Arc::new(Mutex::new(vec![])), + end_scan_results: RefCell::new(vec![]), + stop_system_after_last_message: RefCell::new(false), + } + } + + pub fn begin_scan_params(mut self, params: &Arc>>) -> Self { + self.begin_scan_params = params.clone(); + self + } + + pub fn begin_scan_result(self, result: Result) -> Self { + self.begin_scan_results.borrow_mut().push(result); + self + } + + pub fn stop_the_system(self) -> Self { + self.stop_system_after_last_message.replace(true); + self + } + + pub fn is_allowed_to_stop_the_system(&self) -> bool { + *self.stop_system_after_last_message.borrow() + } + + pub fn is_last_message(&self) -> bool { + self.is_last_message_from_begin_scan() || self.is_last_message_from_end_scan() + } + + pub fn is_last_message_from_begin_scan(&self) -> bool { + self.begin_scan_results.borrow().len() == 1 && self.end_scan_results.borrow().is_empty() + } + + pub fn is_last_message_from_end_scan(&self) -> bool { + self.end_scan_results.borrow().len() == 1 && self.begin_scan_results.borrow().is_empty() + } +} + +#[derive(Default)] +pub struct NotifyLaterForScanners { + pub scan_for_pending_payable: Box>, + pub scan_for_payable: Box>, + pub scan_for_receivable: Box>, +} + +#[cfg(test)] +mod tests { + use crate::accountant::scanners::{ + BeginScanError, PayableScanner, PendingPayableScanner, ReceivableScanner, Scanner, Scanners, + }; + use crate::accountant::test_utils::{ + make_custom_payment_thresholds, make_payables, make_pending_payable_fingerprint, + make_receivable_account, BannedDaoMock, PayableDaoFactoryMock, PayableDaoMock, + PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoMock, + }; + use crate::accountant::{ + PendingPayableId, PendingTransactionStatus, ReceivedPayments, ReportTransactionReceipts, + RequestTransactionReceipts, SentPayable, DEFAULT_PENDING_TOO_LONG_SEC, + }; + use crate::blockchain::blockchain_bridge::{PendingPayableFingerprint, RetrieveTransactions}; + use std::cell::RefCell; + use std::ops::Sub; + + use crate::accountant::payable_dao::{Payable, PayableDaoError}; + use crate::accountant::pending_payable_dao::PendingPayableDaoError; + use crate::blockchain::blockchain_interface::{BlockchainError, BlockchainTransaction}; + use crate::database::dao_utils::from_time_t; + use crate::sub_lib::accountant::{FinancialStatistics, PaymentThresholds}; + use crate::sub_lib::blockchain_bridge::ReportAccountsPayable; + use crate::test_utils::make_wallet; + use ethereum_types::{BigEndianHash, U64}; + use ethsign_crypto::Keccak256; + use masq_lib::logger::Logger; + use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + use std::rc::Rc; + use std::sync::{Arc, Mutex, MutexGuard}; + use std::time::{Duration, SystemTime}; + use web3::types::{TransactionReceipt, H256, U256}; + + #[test] + fn scanners_struct_can_be_constructed_with_the_respective_scanners() { + let payment_thresholds = Rc::new(PaymentThresholds::default()); + let payable_dao_factory = PayableDaoFactoryMock::new() + .make_result(PayableDaoMock::new()) + .make_result(PayableDaoMock::new()); + let pending_payable_dao_factory = PendingPayableDaoFactoryMock::new() + .make_result(PendingPayableDaoMock::new()) + .make_result(PendingPayableDaoMock::new()); + let scanners = Scanners::new( + Box::new(payable_dao_factory), + Box::new(pending_payable_dao_factory), + Box::new(ReceivableDaoMock::new()), + Box::new(BannedDaoMock::new()), + Rc::clone(&payment_thresholds), + Rc::new(make_wallet("earning")), + 0, + Rc::new(RefCell::new(FinancialStatistics::default())), + ); + + scanners + .payable + .as_any() + .downcast_ref::() + .unwrap(); + scanners + .pending_payable + .as_any() + .downcast_ref::() + .unwrap(); + scanners + .receivable + .as_any() + .downcast_ref::() + .unwrap(); + } + + #[test] + fn payable_scanner_can_initiate_a_scan() { + init_test_logging(); + let test_name = "payable_scanner_can_initiate_a_scan"; + let now = SystemTime::now(); + let (qualified_payable_accounts, _, all_non_pending_payables) = + make_payables(now, &PaymentThresholds::default()); + let len_of_qualified_payables = qualified_payable_accounts.len(); + let payable_dao = + PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); + + let mut subject = PayableScanner::default().payable_dao(payable_dao); + + let result = subject.begin_scan(now, None, &Logger::new(test_name)); + + let timestamp = subject.scan_started_at(); + assert_eq!(timestamp, Some(now)); + assert_eq!( + result, + Ok(ReportAccountsPayable { + accounts: qualified_payable_accounts.clone(), + response_skeleton_opt: None, + }) + ); + TestLogHandler::new().assert_logs_match_in_order(vec![ + &format!("INFO: {test_name}: Scanning for payables"), + &format!("INFO: {test_name}: Chose {len_of_qualified_payables} qualified debts to pay"), + ]) + } + + #[test] + fn payable_scanner_throws_error_when_a_scan_is_already_running() { + let now = SystemTime::now(); + let (_, _, all_non_pending_payables) = make_payables(now, &PaymentThresholds::default()); + let payable_dao = + PayableDaoMock::new().non_pending_payables_result(all_non_pending_payables); + let mut subject = PayableScanner::default().payable_dao(payable_dao); + let _result = subject.begin_scan(now, None, &Logger::new("test")); + + let run_again_result = subject.begin_scan(SystemTime::now(), None, &Logger::new("test")); + + let is_scan_running = subject.scan_started_at().is_some(); + assert_eq!(is_scan_running, true); + assert_eq!( + run_again_result, + Err(BeginScanError::ScanAlreadyRunning(now)) + ); + } + + #[test] + fn payable_scanner_throws_error_in_case_no_qualified_payable_is_found() { + init_test_logging(); + let test_name = "payable_scanner_throws_error_in_case_no_qualified_payable_is_found"; + let now = SystemTime::now(); + let (_, unqualified_payable_accounts, _) = + make_payables(now, &PaymentThresholds::default()); + let payable_dao = + PayableDaoMock::new().non_pending_payables_result(unqualified_payable_accounts); + + let mut subject = PayableScanner::default().payable_dao(payable_dao); + + let result = subject.begin_scan(now, None, &Logger::new(test_name)); + + let is_scan_running = subject.scan_started_at().is_some(); + assert_eq!(is_scan_running, false); + assert_eq!(result, Err(BeginScanError::NothingToProcess)); + TestLogHandler::new().assert_logs_match_in_order(vec![ + &format!("INFO: {test_name}: Scanning for payables"), + "Chose 0 qualified debts to pay", + ]); + } + + #[test] + #[should_panic( + expected = "Payable fingerprint for 0x0000…0315 doesn't exist but should by now; system unreliable" + )] + fn payable_scanner_throws_error_when_fingerprint_is_not_found() { + let now = SystemTime::now(); + let payment_hash = H256::from_uint(&U256::from(789)); + let payable = Payable::new(make_wallet("booga"), 6789, payment_hash, now); + let payable_dao = PayableDaoMock::new().mark_pending_payable_rowid_result(Ok(())); + let pending_payable_dao = PendingPayableDaoMock::default().fingerprint_rowid_result(None); + let mut subject = PayableScanner::default() + .payable_dao(payable_dao) + .pending_payable_dao(pending_payable_dao); + let sent_payable = SentPayable { + timestamp: now, + payable: vec![Ok(payable)], + response_skeleton_opt: None, + }; + + let _ = subject.finish_scan(sent_payable, &Logger::new("test")); + } + + #[test] + #[should_panic( + expected = "Database unmaintainable; payable fingerprint deletion for transaction \ + 0x000000000000000000000000000000000000000000000000000000000000007b has stayed \ + undone due to RecordDeletion(\"we slept over, sorry\")" + )] + fn payable_scanner_panics_when_failed_payment_fails_to_delete_the_existing_pending_payable_fingerprint( + ) { + let rowid = 4; + let hash = H256::from_uint(&U256::from(123)); + let sent_payable = SentPayable { + timestamp: SystemTime::now(), + payable: vec![Err(BlockchainError::TransactionFailed { + msg: "blah".to_string(), + hash_opt: Some(hash), + })], + response_skeleton_opt: None, + }; + let pending_payable_dao = PendingPayableDaoMock::default() + .fingerprint_rowid_result(Some(rowid)) + .delete_fingerprint_result(Err(PendingPayableDaoError::RecordDeletion( + "we slept over, sorry".to_string(), + ))); + let mut subject = PayableScanner::default().pending_payable_dao(pending_payable_dao); + + let _ = subject.finish_scan(sent_payable, &Logger::new("test")); + } + + #[test] + #[should_panic( + expected = "Was unable to create a mark in payables for a new pending payable '0x0000…007b' \ + due to 'SignConversion(9999999999999)'" + )] + fn payable_scanner_panics_when_it_fails_to_make_a_mark_in_payables() { + let payable = Payable::new( + make_wallet("blah"), + 6789, + H256::from_uint(&U256::from(123)), + SystemTime::now(), + ); + let payable_dao = PayableDaoMock::new() + .mark_pending_payable_rowid_result(Err(PayableDaoError::SignConversion(9999999999999))); + let pending_payable_dao = + PendingPayableDaoMock::default().fingerprint_rowid_result(Some(7879)); + let mut subject = PayableScanner::default() + .payable_dao(payable_dao) + .pending_payable_dao(pending_payable_dao); + let sent_payable = SentPayable { + timestamp: SystemTime::now(), + payable: vec![Ok(payable)], + response_skeleton_opt: None, + }; + + let _ = subject.finish_scan(sent_payable, &Logger::new("test")); + } + + #[test] + fn payable_scanner_handles_sent_payable_message() { + //the two failures differ in the logged messages + init_test_logging(); + let test_name = "payable_scanner_handles_sent_payable_message"; + let fingerprint_rowid_params_arc = Arc::new(Mutex::new(vec![])); + let now = SystemTime::now(); + let payable_1 = Err(BlockchainError::InvalidResponse); + let payable_2_rowid = 126; + let payable_2_hash = H256::from_uint(&U256::from(166)); + let payable_2 = Payable::new(make_wallet("booga"), 6789, payable_2_hash, now); + let payable_3 = Err(BlockchainError::TransactionFailed { + msg: "closing hours, sorry".to_string(), + hash_opt: None, + }); + let sent_payable = SentPayable { + timestamp: SystemTime::now(), + payable: vec![payable_1, Ok(payable_2.clone()), payable_3], + response_skeleton_opt: None, + }; + let payable_dao = PayableDaoMock::new().mark_pending_payable_rowid_result(Ok(())); + let pending_payable_dao = PendingPayableDaoMock::default() + .fingerprint_rowid_params(&fingerprint_rowid_params_arc) + .fingerprint_rowid_result(Some(payable_2_rowid)); + let mut subject = PayableScanner::default() + .payable_dao(payable_dao) + .pending_payable_dao(pending_payable_dao); + subject.mark_as_started(SystemTime::now()); + + let message_opt = subject.finish_scan(sent_payable, &Logger::new(test_name)); + + let is_scan_running = subject.scan_started_at().is_some(); + let fingerprint_rowid_params = fingerprint_rowid_params_arc.lock().unwrap(); + assert_eq!(message_opt, None); + assert_eq!(is_scan_running, false); + //we know the other two errors are associated with an initiated transaction having a backup + assert_eq!(*fingerprint_rowid_params, vec![payable_2_hash]); + let log_handler = TestLogHandler::new(); + log_handler.assert_logs_contain_in_order(vec![ + &format!( + "WARN: {test_name}: Outbound transaction failure due to 'InvalidResponse'. \ + Please check your blockchain service URL configuration." + ), + &format!( + "WARN: {test_name}: Encountered transaction error at this end: 'TransactionFailed \ + {{ msg: \"closing hours, sorry\", hash_opt: None }}'" + ), + &format!( + "DEBUG: {test_name}: Payable '0x0000…00a6' has been marked as pending in the payable table" + ), + &format!( + "DEBUG: {test_name}: Forgetting a transaction attempt that even did not reach the signing stage" + ), + ]); + log_handler.exists_log_matching( + "INFO: payable_scanner_handles_sent_payable_message: The Payable scan ended in \\d+ms.", + ); + } + + #[test] + fn pending_payable_scanner_can_initiate_a_scan() { + init_test_logging(); + let test_name = "pending_payable_scanner_can_initiate_a_scan"; + let now = SystemTime::now(); + let fingerprints = vec![PendingPayableFingerprint { + rowid_opt: Some(1234), + timestamp: SystemTime::now(), + hash: Default::default(), + attempt_opt: Some(1), + amount: 1_000_000, + process_error: None, + }]; + let no_of_pending_payables = fingerprints.len(); + let pending_payable_dao = + PendingPayableDaoMock::new().return_all_fingerprints_result(fingerprints.clone()); + let mut pending_payable_scanner = + PendingPayableScanner::default().pending_payable_dao(pending_payable_dao); + + let result = pending_payable_scanner.begin_scan(now, None, &Logger::new(test_name)); + + let is_scan_running = pending_payable_scanner.scan_started_at().is_some(); + assert_eq!(is_scan_running, true); + assert_eq!( + result, + Ok(RequestTransactionReceipts { + pending_payable: fingerprints, + response_skeleton_opt: None + }) + ); + TestLogHandler::new().assert_logs_match_in_order(vec![ + &format!("INFO: {test_name}: Scanning for pending payable"), + &format!( + "DEBUG: {test_name}: Found {no_of_pending_payables} pending payables to process" + ), + ]) + } + + #[test] + fn pending_payable_scanner_throws_error_in_case_scan_is_already_running() { + let now = SystemTime::now(); + let pending_payable_dao = + PendingPayableDaoMock::new().return_all_fingerprints_result(vec![ + PendingPayableFingerprint { + rowid_opt: Some(1234), + timestamp: SystemTime::now(), + hash: Default::default(), + attempt_opt: Some(1), + amount: 1_000_000, + process_error: None, + }, + ]); + let mut subject = PendingPayableScanner::default().pending_payable_dao(pending_payable_dao); + let _ = subject.begin_scan(now, None, &Logger::new("test")); + + let result = subject.begin_scan(SystemTime::now(), None, &Logger::new("test")); + + let is_scan_running = subject.scan_started_at().is_some(); + assert_eq!(is_scan_running, true); + assert_eq!(result, Err(BeginScanError::ScanAlreadyRunning(now))); + } + + #[test] + fn pending_payable_scanner_throws_an_error_when_no_fingerprint_is_found() { + init_test_logging(); + let test_name = "pending_payable_scanner_throws_an_error_when_no_fingerprint_is_found"; + let now = SystemTime::now(); + let pending_payable_dao = + PendingPayableDaoMock::new().return_all_fingerprints_result(vec![]); + let mut pending_payable_scanner = + PendingPayableScanner::default().pending_payable_dao(pending_payable_dao); + + let result = pending_payable_scanner.begin_scan(now, None, &Logger::new(test_name)); + + let is_scan_running = pending_payable_scanner.scan_started_at().is_some(); + assert_eq!(result, Err(BeginScanError::NothingToProcess)); + assert_eq!(is_scan_running, false); + TestLogHandler::new().assert_logs_match_in_order(vec![ + &format!("INFO: {test_name}: Scanning for pending payable"), + &format!("DEBUG: {test_name}: Pending payable scan ended. No pending payable found."), + ]) + } + + #[test] + fn interpret_transaction_receipt_when_transaction_status_is_none_and_outside_waiting_interval() + { + init_test_logging(); + let test_name = "interpret_transaction_receipt_when_transaction_status_is_none_and_outside_waiting_interval"; + let hash = H256::from_uint(&U256::from(567)); + let rowid = 466; + let tx_receipt = TransactionReceipt::default(); //status defaulted to None + let when_sent = + SystemTime::now().sub(Duration::from_secs(DEFAULT_PENDING_TOO_LONG_SEC + 5)); //old transaction + let subject = PendingPayableScanner::default(); + let fingerprint = PendingPayableFingerprint { + rowid_opt: Some(rowid), + timestamp: when_sent, + hash, + attempt_opt: Some(10), + amount: 123, + process_error: None, + }; + + let result = subject.interpret_transaction_receipt( + &tx_receipt, + &fingerprint, + &Logger::new(test_name), + ); + + assert_eq!( + result, + PendingTransactionStatus::Failure(PendingPayableId { hash, rowid }) + ); + TestLogHandler::new().exists_log_containing(&format!( + "ERROR: {test_name}: Pending transaction '0x0000…0237' has exceeded the maximum \ + pending time (21600sec) and the confirmation process is going to be aborted now \ + at the final attempt 10; manual resolution is required from the user to complete \ + the transaction" + )); + } + + #[test] + fn interpret_transaction_receipt_when_transaction_status_is_none_and_within_waiting_interval() { + init_test_logging(); + let test_name = "interpret_transaction_receipt_when_transaction_status_is_none_and_within_waiting_interval"; + let subject = PendingPayableScanner::default(); + let hash = H256::from_uint(&U256::from(567)); + let rowid = 466; + let tx_receipt = TransactionReceipt::default(); //status defaulted to None + let when_sent = SystemTime::now().sub(Duration::from_millis(100)); + let fingerprint = PendingPayableFingerprint { + rowid_opt: Some(rowid), + timestamp: when_sent, + hash, + attempt_opt: Some(1), + amount: 123, + process_error: None, + }; + + let result = subject.interpret_transaction_receipt( + &tx_receipt, + &fingerprint, + &Logger::new(test_name), + ); + + assert_eq!( + result, + PendingTransactionStatus::StillPending(PendingPayableId { hash, rowid }) + ); + TestLogHandler::new().exists_log_containing(&format!( + "INFO: {test_name}: Pending \ + transaction '0x0000…0237' couldn't be confirmed at attempt 1 at ", + )); + } + + #[test] + #[should_panic( + expected = "tx receipt for pending '0x0000…007b' - tx status: code other than 0 or 1 shouldn't be possible, but was 456" + )] + fn interpret_transaction_receipt_panics_at_undefined_status_code() { + let mut tx_receipt = TransactionReceipt::default(); + tx_receipt.status = Some(U64::from(456)); + let mut fingerprint = make_pending_payable_fingerprint(); + fingerprint.hash = H256::from_uint(&U256::from(123)); + let subject = PendingPayableScanner::default(); + + let _ = + subject.interpret_transaction_receipt(&tx_receipt, &fingerprint, &Logger::new("test")); + } + + #[test] + fn interpret_transaction_receipt_when_transaction_status_is_a_failure() { + init_test_logging(); + let test_name = "interpret_transaction_receipt_when_transaction_status_is_a_failure"; + let subject = PendingPayableScanner::default(); + let mut tx_receipt = TransactionReceipt::default(); + tx_receipt.status = Some(U64::from(0)); //failure + let hash = H256::from_uint(&U256::from(4567)); + let fingerprint = PendingPayableFingerprint { + rowid_opt: Some(777777), + timestamp: SystemTime::now().sub(Duration::from_millis(150000)), + hash, + attempt_opt: Some(5), + amount: 2222, + process_error: None, + }; + + let result = subject.interpret_transaction_receipt( + &tx_receipt, + &fingerprint, + &Logger::new(test_name), + ); + + assert_eq!( + result, + PendingTransactionStatus::Failure(PendingPayableId { + hash, + rowid: 777777, + }) + ); + TestLogHandler::new().exists_log_matching(&format!( + "ERROR: {test_name}: Pending transaction '0x0000…11d7' announced as a failure, \ + interpreting attempt 5 after 1500\\d\\dms from the sending" + )); + } + + #[test] + fn handle_pending_tx_handles_none_returned_for_transaction_receipt() { + init_test_logging(); + let test_name = "handle_pending_tx_handles_none_returned_for_transaction_receipt"; + let subject = PendingPayableScanner::default(); + let tx_receipt_opt = None; + let rowid = 455; + let hash = H256::from_uint(&U256::from(2323)); + let fingerprint = PendingPayableFingerprint { + rowid_opt: Some(rowid), + timestamp: SystemTime::now().sub(Duration::from_millis(10000)), + hash, + attempt_opt: Some(3), + amount: 111, + process_error: None, + }; + let msg = ReportTransactionReceipts { + fingerprints_with_receipts: vec![(tx_receipt_opt, fingerprint.clone())], + response_skeleton_opt: None, + }; + + let result = + subject.handle_pending_transaction_with_its_receipt(&msg, &Logger::new(test_name)); + + assert_eq!( + result, + vec![PendingTransactionStatus::StillPending(PendingPayableId { + hash, + rowid, + })] + ); + TestLogHandler::new().exists_log_matching(&format!( + "DEBUG: {test_name}: Interpreting a receipt for transaction '0x0000…0913' \ + but none was given; attempt 3, 100\\d\\dms since sending" + )); + } + + #[test] + fn update_payable_fingerprint_happy_path() { + let update_after_cycle_params_arc = Arc::new(Mutex::new(vec![])); + let hash = H256::from_uint(&U256::from(444888)); + let rowid = 3456; + let pending_payable_dao = PendingPayableDaoMock::default() + .update_fingerprint_params(&update_after_cycle_params_arc) + .update_fingerprint_results(Ok(())); + let subject = PendingPayableScanner::default().pending_payable_dao(pending_payable_dao); + let transaction_id = PendingPayableId { hash, rowid }; + + subject.update_payable_fingerprint(transaction_id, &Logger::new("test")); + + let update_after_cycle_params = update_after_cycle_params_arc.lock().unwrap(); + assert_eq!(*update_after_cycle_params, vec![rowid]) + } + + #[test] + #[should_panic(expected = "Failure on updating payable fingerprint \ + '0x000000000000000000000000000000000000000000000000000000000006c9d8' \ + due to UpdateFailed(\"yeah, bad\")")] + fn update_payable_fingerprint_sad_path() { + let hash = H256::from_uint(&U256::from(444888)); + let rowid = 3456; + let pending_payable_dao = PendingPayableDaoMock::default().update_fingerprint_results(Err( + PendingPayableDaoError::UpdateFailed("yeah, bad".to_string()), + )); + let subject = PendingPayableScanner::default().pending_payable_dao(pending_payable_dao); + let transaction_id = PendingPayableId { hash, rowid }; + + subject.update_payable_fingerprint(transaction_id, &Logger::new("test")); + } + + #[test] + fn order_cancel_pending_transaction_works() { + init_test_logging(); + let test_name = "order_cancel_pending_transaction_works"; + let mark_failure_params_arc = Arc::new(Mutex::new(vec![])); + let pending_payable_dao = PendingPayableDaoMock::default() + .mark_failure_params(&mark_failure_params_arc) + .mark_failure_result(Ok(())); + let subject = PendingPayableScanner::default().pending_payable_dao(pending_payable_dao); + let tx_hash = H256::from("sometransactionhash".keccak256()); + let rowid = 2; + let transaction_id = PendingPayableId { + hash: tx_hash, + rowid, + }; + + subject.order_cancel_failed_transaction(transaction_id, &Logger::new(test_name)); + + let mark_failure_params = mark_failure_params_arc.lock().unwrap(); + assert_eq!(*mark_failure_params, vec![rowid]); + TestLogHandler::new().exists_log_containing(&format!( + "WARN: {test_name}: Broken transaction 0x051a…8c19 left with an error \ + mark; you should take over the care of this transaction to make sure your debts will \ + be paid because there is no automated process that can fix this without you", + )); + } + + #[test] + #[should_panic( + expected = "Unsuccessful attempt for transaction 0x051a…8c19 to mark fatal error at payable \ + fingerprint due to UpdateFailed(\"no no no\"); database unreliable" + )] + fn order_cancel_pending_transaction_panics_when_it_fails_to_mark_failure() { + let payable_dao = PayableDaoMock::default().transaction_canceled_result(Ok(())); + let pending_payable_dao = PendingPayableDaoMock::default().mark_failure_result(Err( + PendingPayableDaoError::UpdateFailed("no no no".to_string()), + )); + let subject = PendingPayableScanner::default() + .payable_dao(payable_dao) + .pending_payable_dao(pending_payable_dao); + let rowid = 2; + let hash = H256::from("sometransactionhash".keccak256()); + let transaction_id = PendingPayableId { hash, rowid }; + + subject.order_cancel_failed_transaction(transaction_id, &Logger::new("test")); + } + + #[test] + #[should_panic( + expected = "Was unable to delete payable fingerprint '0x0000…0315' after successful \ + transaction due to 'RecordDeletion(\"the database is fooling around with us\")'" + )] + fn handle_confirm_pending_transaction_panics_while_deleting_pending_payable_fingerprint() { + let hash = H256::from_uint(&U256::from(789)); + let rowid = 3; + let payable_dao = PayableDaoMock::new().transaction_confirmed_result(Ok(())); + let pending_payable_dao = PendingPayableDaoMock::default().delete_fingerprint_result(Err( + PendingPayableDaoError::RecordDeletion( + "the database is fooling around with us".to_string(), + ), + )); + let mut subject = PendingPayableScanner::default() + .payable_dao(payable_dao) + .pending_payable_dao(pending_payable_dao); + let mut pending_payable_fingerprint = make_pending_payable_fingerprint(); + pending_payable_fingerprint.rowid_opt = Some(rowid); + pending_payable_fingerprint.hash = hash; + + subject.order_confirm_transaction(pending_payable_fingerprint, &Logger::new("test")); + } + + #[test] + fn order_confirm_transaction_works() { + init_test_logging(); + let test_name = "order_confirm_transaction_works"; + let transaction_confirmed_params_arc = Arc::new(Mutex::new(vec![])); + let delete_pending_payable_fingerprint_params_arc = Arc::new(Mutex::new(vec![])); + let payable_dao = PayableDaoMock::default() + .transaction_confirmed_params(&transaction_confirmed_params_arc) + .transaction_confirmed_result(Ok(())); + let pending_payable_dao = PendingPayableDaoMock::default() + .delete_fingerprint_params(&delete_pending_payable_fingerprint_params_arc) + .delete_fingerprint_result(Ok(())); + let mut subject = PendingPayableScanner::default() + .payable_dao(payable_dao) + .pending_payable_dao(pending_payable_dao); + let tx_hash = H256::from("sometransactionhash".keccak256()); + let amount = 4567; + let timestamp_from_time_of_payment = from_time_t(200_000_000); + let rowid = 2; + let pending_payable_fingerprint = PendingPayableFingerprint { + rowid_opt: Some(rowid), + timestamp: timestamp_from_time_of_payment, + hash: tx_hash, + attempt_opt: Some(1), + amount, + process_error: None, + }; + + subject.order_confirm_transaction( + pending_payable_fingerprint.clone(), + &Logger::new(test_name), + ); + + let transaction_confirmed_params = transaction_confirmed_params_arc.lock().unwrap(); + let delete_pending_payable_fingerprint_params = + delete_pending_payable_fingerprint_params_arc + .lock() + .unwrap(); + assert_eq!( + *transaction_confirmed_params, + vec![pending_payable_fingerprint] + ); + assert_eq!(*delete_pending_payable_fingerprint_params, vec![rowid]); + TestLogHandler::new().assert_logs_contain_in_order(vec![ + &format!( + "DEBUG: {test_name}: Confirmation of transaction 0x051a…8c19; \ + record for payable was modified" + ), + &format!( + "INFO: {test_name}: Transaction \ + 0x051aae12b9595ccaa43c2eabfd5b86347c37fa0988167165b0b17b23fcaa8c19 \ + has gone through the whole confirmation process succeeding" + ), + ]); + } + + #[test] + fn total_paid_payable_rises_with_each_bill_paid() { + let test_name = "total_paid_payable_rises_with_each_bill_paid"; + let transaction_confirmed_params_arc = Arc::new(Mutex::new(vec![])); + let fingerprint = PendingPayableFingerprint { + rowid_opt: Some(5), + timestamp: from_time_t(189_999_888), + hash: H256::from_uint(&U256::from(56789)), + attempt_opt: Some(1), + amount: 5478, + process_error: None, + }; + let payable_dao = PayableDaoMock::default() + .transaction_confirmed_params(&transaction_confirmed_params_arc) + .transaction_confirmed_result(Ok(())) + .transaction_confirmed_result(Ok(())); + let pending_payable_dao = + PendingPayableDaoMock::default().delete_fingerprint_result(Ok(())); + let mut subject = PendingPayableScanner::default() + .payable_dao(payable_dao) + .pending_payable_dao(pending_payable_dao); + let mut financial_statistics = subject.financial_statistics(); + financial_statistics.total_paid_payable += 1111; + subject.financial_statistics.replace(financial_statistics); + + subject.order_confirm_transaction(fingerprint.clone(), &Logger::new(test_name)); + + let total_paid_payable = subject.financial_statistics().total_paid_payable; + let transaction_confirmed_params = transaction_confirmed_params_arc.lock().unwrap(); + assert_eq!(total_paid_payable, 1111 + 5478); + assert_eq!(*transaction_confirmed_params, vec![fingerprint]) + } + + #[test] + #[should_panic( + expected = "Was unable to uncheck pending payable '0x0000…0315' after confirmation due to \ + 'RusqliteError(\"record change not successful\")'" + )] + fn order_confirm_transaction_panics_on_unchecking_payable_table() { + let hash = H256::from_uint(&U256::from(789)); + let rowid = 3; + let payable_dao = PayableDaoMock::new().transaction_confirmed_result(Err( + PayableDaoError::RusqliteError("record change not successful".to_string()), + )); + let mut subject = PendingPayableScanner::default().payable_dao(payable_dao); + let mut fingerprint = make_pending_payable_fingerprint(); + fingerprint.rowid_opt = Some(rowid); + fingerprint.hash = hash; + + subject.order_confirm_transaction(fingerprint, &Logger::new("test")); + } + + #[test] + fn pending_payable_scanner_handles_report_transaction_receipts_message() { + init_test_logging(); + let test_name = "pending_payable_scanner_handles_report_transaction_receipts_message"; + let transaction_confirmed_params_arc = Arc::new(Mutex::new(vec![])); + let payable_dao = PayableDaoMock::new() + .transaction_confirmed_params(&transaction_confirmed_params_arc) + .transaction_confirmed_result(Ok(())) + .transaction_confirmed_result(Ok(())); + let pending_payable_dao = PendingPayableDaoMock::new() + .delete_fingerprint_result(Ok(())) + .delete_fingerprint_result(Ok(())); + let mut subject = PendingPayableScanner::default() + .payable_dao(payable_dao) + .pending_payable_dao(pending_payable_dao); + let transaction_hash_1 = H256::from_uint(&U256::from(4545)); + let mut transaction_receipt_1 = TransactionReceipt::default(); + transaction_receipt_1.transaction_hash = transaction_hash_1; + transaction_receipt_1.status = Some(U64::from(1)); //success + let fingerprint_1 = PendingPayableFingerprint { + rowid_opt: Some(5), + timestamp: from_time_t(200_000_000), + hash: transaction_hash_1, + attempt_opt: Some(2), + amount: 444, + process_error: None, + }; + let transaction_hash_2 = H256::from_uint(&U256::from(1234)); + let mut transaction_receipt_2 = TransactionReceipt::default(); + transaction_receipt_2.transaction_hash = transaction_hash_2; + transaction_receipt_2.status = Some(U64::from(1)); //success + let fingerprint_2 = PendingPayableFingerprint { + rowid_opt: Some(10), + timestamp: from_time_t(199_780_000), + hash: transaction_hash_2, + attempt_opt: Some(15), + amount: 1212, + process_error: None, + }; + let msg = ReportTransactionReceipts { + fingerprints_with_receipts: vec![ + (Some(transaction_receipt_1), fingerprint_1.clone()), + (Some(transaction_receipt_2), fingerprint_2.clone()), + ], + response_skeleton_opt: None, + }; + subject.mark_as_started(SystemTime::now()); + + let message_opt = subject.finish_scan(msg, &Logger::new(test_name)); + + let transaction_confirmed_params = transaction_confirmed_params_arc.lock().unwrap(); + assert_eq!(message_opt, None); + assert_eq!( + *transaction_confirmed_params, + vec![fingerprint_1, fingerprint_2] + ); + assert_eq!(subject.scan_started_at(), None); + TestLogHandler::new().assert_logs_match_in_order(vec![ + &format!( + "INFO: {}: Transaction {:?} has gone through the whole confirmation process succeeding", + test_name, transaction_hash_1 + ), + &format!( + "INFO: {}: Transaction {:?} has gone through the whole confirmation process succeeding", + test_name, transaction_hash_2 + ), + "INFO: pending_payable_scanner_handles_report_transaction_receipts_message: The \ + Pending Payable scan ended in \\d+ms.", + + ]); + } + + #[test] + fn receivable_scanner_can_initiate_a_scan() { + init_test_logging(); + let test_name = "receivable_scanner_can_initiate_a_scan"; + let now = SystemTime::now(); + let receivable_dao = ReceivableDaoMock::new() + .new_delinquencies_result(vec![]) + .paid_delinquencies_result(vec![]); + let earning_wallet = make_wallet("earning"); + let mut receivable_scanner = ReceivableScanner::default() + .receivable_dao(receivable_dao) + .earning_wallet(earning_wallet.clone()); + + let result = receivable_scanner.begin_scan(now, None, &Logger::new(test_name)); + + let is_scan_running = receivable_scanner.scan_started_at().is_some(); + assert_eq!(is_scan_running, true); + assert_eq!( + result, + Ok(RetrieveTransactions { + recipient: earning_wallet.clone(), + response_skeleton_opt: None + }) + ); + TestLogHandler::new().exists_log_containing(&format!( + "INFO: {test_name}: Scanning for receivables to {earning_wallet}" + )); + } + + #[test] + fn receivable_scanner_throws_error_in_case_scan_is_already_running() { + let now = SystemTime::now(); + let receivable_dao = ReceivableDaoMock::new() + .new_delinquencies_result(vec![]) + .paid_delinquencies_result(vec![]); + let earning_wallet = make_wallet("earning"); + let mut receivable_scanner = ReceivableScanner::default() + .receivable_dao(receivable_dao) + .earning_wallet(earning_wallet.clone()); + let _ = receivable_scanner.begin_scan(now, None, &Logger::new("test")); + + let result = receivable_scanner.begin_scan(SystemTime::now(), None, &Logger::new("test")); + + let is_scan_running = receivable_scanner.scan_started_at().is_some(); + assert_eq!(is_scan_running, true); + assert_eq!(result, Err(BeginScanError::ScanAlreadyRunning(now))); + } + + #[test] + fn receivable_scanner_scans_for_delinquencies() { + init_test_logging(); + let newly_banned_1 = make_receivable_account(1234, true); + let newly_banned_2 = make_receivable_account(2345, true); + let newly_unbanned_1 = make_receivable_account(3456, false); + let newly_unbanned_2 = make_receivable_account(4567, false); + let new_delinquencies_parameters_arc = Arc::new(Mutex::new(vec![])); + let paid_delinquencies_parameters_arc = Arc::new(Mutex::new(vec![])); + let receivable_dao = ReceivableDaoMock::new() + .new_delinquencies_parameters(&new_delinquencies_parameters_arc) + .new_delinquencies_result(vec![newly_banned_1.clone(), newly_banned_2.clone()]) + .paid_delinquencies_parameters(&paid_delinquencies_parameters_arc) + .paid_delinquencies_result(vec![newly_unbanned_1.clone(), newly_unbanned_2.clone()]); + let ban_parameters_arc = Arc::new(Mutex::new(vec![])); + let unban_parameters_arc = Arc::new(Mutex::new(vec![])); + let payment_thresholds = make_custom_payment_thresholds(); + let banned_dao = BannedDaoMock::new() + .ban_list_result(vec![]) + .ban_parameters(&ban_parameters_arc) + .unban_parameters(&unban_parameters_arc); + let mut receivable_scanner = ReceivableScanner::default() + .receivable_dao(receivable_dao) + .banned_dao(banned_dao) + .payment_thresholds(payment_thresholds.clone()); + + let _result = receivable_scanner.begin_scan( + SystemTime::now(), + None, + &Logger::new("DELINQUENCY_TEST"), + ); + + let new_delinquencies_parameters: MutexGuard> = + new_delinquencies_parameters_arc.lock().unwrap(); + assert_eq!( + payment_thresholds.clone(), + new_delinquencies_parameters[0].1 + ); + let paid_delinquencies_parameters: MutexGuard> = + paid_delinquencies_parameters_arc.lock().unwrap(); + assert_eq!(payment_thresholds, paid_delinquencies_parameters[0]); + let ban_parameters = ban_parameters_arc.lock().unwrap(); + assert!(ban_parameters.contains(&newly_banned_1.wallet)); + assert!(ban_parameters.contains(&newly_banned_2.wallet)); + assert_eq!(2, ban_parameters.len()); + let unban_parameters = unban_parameters_arc.lock().unwrap(); + assert!(unban_parameters.contains(&newly_unbanned_1.wallet)); + assert!(unban_parameters.contains(&newly_unbanned_2.wallet)); + assert_eq!(2, unban_parameters.len()); + let tlh = TestLogHandler::new(); + tlh.exists_log_matching( + "INFO: DELINQUENCY_TEST: Wallet 0x00000000000000000077616c6c65743132333464 \ + \\(balance: 1234 MASQ, age: \\d+ sec\\) banned for delinquency", + ); + tlh.exists_log_matching( + "INFO: DELINQUENCY_TEST: Wallet 0x00000000000000000077616c6c65743233343564 \ + \\(balance: 2345 MASQ, age: \\d+ sec\\) banned for delinquency", + ); + tlh.exists_log_matching( + "INFO: DELINQUENCY_TEST: Wallet 0x00000000000000000077616c6c6574333435366e \ + \\(balance: 3456 MASQ, age: \\d+ sec\\) is no longer delinquent: unbanned", + ); + tlh.exists_log_matching( + "INFO: DELINQUENCY_TEST: Wallet 0x00000000000000000077616c6c6574343536376e \ + \\(balance: 4567 MASQ, age: \\d+ sec\\) is no longer delinquent: unbanned", + ); + } + + #[test] + fn receivable_scanner_aborts_scan_if_no_payments_were_supplied() { + init_test_logging(); + let test_name = "receivable_scanner_aborts_scan_if_no_payments_were_supplied"; + let mut subject = ReceivableScanner::default(); + let msg = ReceivedPayments { + timestamp: SystemTime::now(), + payments: vec![], + response_skeleton_opt: None, + }; + + let message_opt = subject.finish_scan(msg, &Logger::new(test_name)); + + assert_eq!(message_opt, None); + TestLogHandler::new().exists_log_containing(&format!( + "WARN: {test_name}: Handling received payments we got zero payments but \ + expected some, skipping database operations" + )); + } + + #[test] + fn receivable_scanner_handles_received_payments_message() { + init_test_logging(); + let test_name = "receivable_scanner_handles_received_payments_message"; + let now = SystemTime::now(); + let more_money_received_params_arc = Arc::new(Mutex::new(vec![])); + let receivable_dao = ReceivableDaoMock::new() + .more_money_received_parameters(&more_money_received_params_arc) + .more_money_receivable_result(Ok(())); + let mut subject = ReceivableScanner::default().receivable_dao(receivable_dao); + let mut financial_statistics = subject.financial_statistics(); + financial_statistics.total_paid_receivable += 2222; + subject.financial_statistics.replace(financial_statistics); + let receivables = vec![ + BlockchainTransaction { + block_number: 4578910, + from: make_wallet("wallet_1"), + gwei_amount: 45780, + }, + BlockchainTransaction { + block_number: 4569898, + from: make_wallet("wallet_2"), + gwei_amount: 33345, + }, + ]; + let msg = ReceivedPayments { + timestamp: now, + payments: receivables.clone(), + response_skeleton_opt: None, + }; + subject.mark_as_started(SystemTime::now()); + + let message_opt = subject.finish_scan(msg, &Logger::new(test_name)); + + let total_paid_receivable = subject.financial_statistics().total_paid_receivable; + let more_money_received_params = more_money_received_params_arc.lock().unwrap(); + assert_eq!(message_opt, None); + assert_eq!(subject.scan_started_at(), None); + assert_eq!(total_paid_receivable, 2222 + 45780 + 33345); + assert_eq!(*more_money_received_params, vec![(now, receivables)]); + TestLogHandler::new().exists_log_matching( + "INFO: receivable_scanner_handles_received_payments_message: The Receivable scan ended in \\d+ms.", + ); + } +} diff --git a/node/src/accountant/scanners_tools.rs b/node/src/accountant/scanners_tools.rs new file mode 100644 index 000000000..fb15fbbc3 --- /dev/null +++ b/node/src/accountant/scanners_tools.rs @@ -0,0 +1,539 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub(crate) mod payable_scanner_tools { + use crate::accountant::payable_dao::{Payable, PayableAccount}; + use crate::accountant::SentPayable; + use crate::blockchain::blockchain_interface::BlockchainError; + use crate::sub_lib::accountant::PaymentThresholds; + use masq_lib::logger::Logger; + use masq_lib::utils::plus; + use std::time::SystemTime; + + //for debugging only + pub(crate) fn investigate_debt_extremes( + timestamp: SystemTime, + all_non_pending_payables: &[PayableAccount], + ) -> String { + if all_non_pending_payables.is_empty() { + return "Payable scan found no debts".to_string(); + } + #[derive(Clone, Copy, Default)] + struct PayableInfo { + balance: i64, + age: u64, + } + + fn bigger(payable_1: PayableInfo, payable_2: PayableInfo) -> PayableInfo { + #[allow(clippy::comparison_chain)] + if payable_1.balance > payable_2.balance { + payable_1 + } else if payable_2.balance > payable_1.balance { + payable_2 + } else { + if payable_1.age != payable_2.age { + return older(payable_1, payable_2); + } + payable_1 + } + } + + fn older(payable_1: PayableInfo, payable_2: PayableInfo) -> PayableInfo { + #[allow(clippy::comparison_chain)] + if payable_1.age > payable_2.age { + payable_1 + } else if payable_2.age > payable_1.age { + payable_2 + } else { + if payable_1.balance != payable_2.balance { + return bigger(payable_1, payable_2); + } + payable_1 + } + } + + let init = (PayableInfo::default(), PayableInfo::default()); + let (biggest, oldest) = all_non_pending_payables + .iter() + .map(|payable| PayableInfo { + balance: payable.balance, + age: payable_time_diff(timestamp, payable), + }) + .fold(init, |so_far, payable| { + let (mut biggest, mut oldest) = so_far; + + biggest = bigger(biggest, payable); + oldest = older(oldest, payable); + + (biggest, oldest) + }); + format!("Payable scan found {} debts; the biggest is {} owed for {}sec, the oldest is {} owed for {}sec", + all_non_pending_payables.len(), biggest.balance, biggest.age, + oldest.balance, oldest.age) + } + + pub(crate) fn is_payable_qualified( + time: SystemTime, + payable: &PayableAccount, + payment_thresholds: &PaymentThresholds, + ) -> Option { + // TODO: This calculation should be done in the database, if possible + let maturity_time_limit = payment_thresholds.maturity_threshold_sec as u64; + let permanent_allowed_debt = payment_thresholds.permanent_debt_allowed_gwei; + let time_since_last_paid = payable_time_diff(time, payable); + let payable_balance = payable.balance; + + if time_since_last_paid <= maturity_time_limit { + return None; + } + + if payable_balance <= permanent_allowed_debt { + return None; + } + + let payout_threshold = calculate_payout_threshold(time_since_last_paid, payment_thresholds); + if payable_balance as f64 <= payout_threshold { + return None; + } + + Some(payout_threshold as u64) + } + + pub(crate) fn payable_time_diff(time: SystemTime, payable: &PayableAccount) -> u64 { + time.duration_since(payable.last_paid_timestamp) + .expect("Payable time is corrupt") + .as_secs() + } + + pub(crate) fn calculate_payout_threshold( + x: u64, + payment_thresholds: &PaymentThresholds, + ) -> f64 { + let m = -((payment_thresholds.debt_threshold_gwei as f64 + - payment_thresholds.permanent_debt_allowed_gwei as f64) + / (payment_thresholds.threshold_interval_sec as f64 + - payment_thresholds.maturity_threshold_sec as f64)); + let b = payment_thresholds.debt_threshold_gwei as f64 + - m * payment_thresholds.maturity_threshold_sec as f64; + m * x as f64 + b + } + + pub(crate) fn exceeded_summary( + time: SystemTime, + payable: &PayableAccount, + threshold: u64, + ) -> String { + format!( + "{} owed for {}sec exceeds threshold: {}; creditor: {}\n", + payable.balance, + payable_time_diff(time, payable), + threshold, + payable.wallet.clone(), + ) + } + + pub(crate) fn qualified_payables_and_summary( + time: SystemTime, + non_pending_payables: Vec, + payment_thresholds: &PaymentThresholds, + ) -> (Vec, String) { + let mut qualified_summary = String::from("Paying qualified debts:\n"); + let mut qualified_payables: Vec = vec![]; + + for payable in non_pending_payables { + if let Some(threshold) = is_payable_qualified(time, &payable, payment_thresholds) { + let payable_summary = exceeded_summary(time, &payable, threshold); + qualified_summary.push_str(&payable_summary); + qualified_payables.push(payable); + } + } + + let summary = match qualified_payables.is_empty() { + true => String::from("No Qualified Payables found."), + false => qualified_summary, + }; + + (qualified_payables, summary) + } + + pub(crate) fn separate_early_errors( + sent_payments: &SentPayable, + logger: &Logger, + ) -> (Vec, Vec) { + sent_payments + .payable + .iter() + .fold((vec![], vec![]), |so_far, payment| { + match payment { + Ok(payment_sent) => (plus(so_far.0, payment_sent.clone()), so_far.1), + Err(error) => { + logger.warning(|| match &error { + BlockchainError::TransactionFailed { .. } => format!("Encountered transaction error at this end: '{:?}'", error), + x => format!("Outbound transaction failure due to '{:?}'. Please check your blockchain service URL configuration.", x) + }); + (so_far.0, plus(so_far.1, error.clone())) + } + } + }) + } +} + +pub(crate) mod pending_payable_scanner_tools { + use crate::accountant::{PendingPayableId, PendingTransactionStatus}; + use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; + use masq_lib::logger::Logger; + use masq_lib::utils::ExpectValue; + use std::time::SystemTime; + + pub fn elapsed_in_ms(timestamp: SystemTime) -> u128 { + timestamp + .elapsed() + .expect("time calculation for elapsed failed") + .as_millis() + } + + pub fn handle_none_status( + fingerprint: &PendingPayableFingerprint, + max_pending_interval: u64, + logger: &Logger, + ) -> PendingTransactionStatus { + info!( + logger, + "Pending transaction '{}' couldn't be confirmed at attempt \ + {} at {}ms after its sending", + fingerprint.hash, + fingerprint.attempt_opt.expectv("initialized attempt"), + elapsed_in_ms(fingerprint.timestamp) + ); + let elapsed = fingerprint + .timestamp + .elapsed() + .expect("we should be older now"); + let transaction_id = PendingPayableId { + hash: fingerprint.hash, + rowid: fingerprint.rowid_opt.expectv("initialized rowid"), + }; + if max_pending_interval <= elapsed.as_secs() { + error!( + logger, + "Pending transaction '{}' has exceeded the maximum pending time \ + ({}sec) and the confirmation process is going to be aborted now \ + at the final attempt {}; manual resolution is required from the \ + user to complete the transaction.", + fingerprint.hash, + max_pending_interval, + fingerprint.attempt_opt.expectv("initialized attempt") + ); + PendingTransactionStatus::Failure(transaction_id) + } else { + PendingTransactionStatus::StillPending(transaction_id) + } + } + + pub fn handle_status_with_success( + fingerprint: &PendingPayableFingerprint, + logger: &Logger, + ) -> PendingTransactionStatus { + info!( + logger, + "Transaction '{}' has been added to the blockchain; detected locally at attempt \ + {} at {}ms after its sending", + fingerprint.hash, + fingerprint.attempt_opt.expectv("initialized attempt"), + elapsed_in_ms(fingerprint.timestamp) + ); + PendingTransactionStatus::Confirmed(fingerprint.clone()) + } + + pub fn handle_status_with_failure( + fingerprint: &PendingPayableFingerprint, + logger: &Logger, + ) -> PendingTransactionStatus { + error!( + logger, + "Pending transaction '{}' announced as a failure, interpreting attempt \ + {} after {}ms from the sending", + fingerprint.hash, + fingerprint.attempt_opt.expectv("initialized attempt"), + elapsed_in_ms(fingerprint.timestamp) + ); + PendingTransactionStatus::Failure(fingerprint.into()) + } +} + +pub(crate) mod receivable_scanner_tools { + use crate::accountant::receivable_dao::ReceivableAccount; + use std::time::{Duration, SystemTime}; + + pub(crate) fn balance_and_age( + time: SystemTime, + account: &ReceivableAccount, + ) -> (String, Duration) { + let balance = format!("{}", (account.balance as f64) / 1_000_000_000.0); + let age = time + .duration_since(account.last_received_timestamp) + .unwrap_or_else(|_| Duration::new(0, 0)); + (balance, age) + } +} + +pub mod common_tools { + use std::time::SystemTime; + use time::format_description::parse; + use time::OffsetDateTime; + + const TIME_FORMATTING_STRING: &str = + "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]"; + + pub fn timestamp_as_string(timestamp: &SystemTime) -> String { + let offset_date_time = OffsetDateTime::from(*timestamp); + offset_date_time + .format(&parse(TIME_FORMATTING_STRING).unwrap()) + .unwrap() + } +} + +#[cfg(test)] +mod tests { + use crate::accountant::payable_dao::{Payable, PayableAccount}; + use crate::accountant::receivable_dao::ReceivableAccount; + use crate::accountant::scanners_tools::payable_scanner_tools::{ + calculate_payout_threshold, exceeded_summary, investigate_debt_extremes, + is_payable_qualified, payable_time_diff, qualified_payables_and_summary, + separate_early_errors, + }; + use crate::accountant::scanners_tools::receivable_scanner_tools::balance_and_age; + use crate::accountant::SentPayable; + use crate::blockchain::blockchain_interface::BlockchainError; + use crate::database::dao_utils::{from_time_t, to_time_t}; + use crate::sub_lib::accountant::PaymentThresholds; + use crate::test_utils::make_wallet; + use masq_lib::logger::Logger; + use std::rc::Rc; + use std::time::SystemTime; + + #[test] + fn payable_generated_before_maturity_time_limit_is_marked_unqualified() { + let now = SystemTime::now(); + let payment_thresholds = PaymentThresholds::default(); + let qualified_debt = payment_thresholds.permanent_debt_allowed_gwei + 1; + let unqualified_time = to_time_t(now) - payment_thresholds.maturity_threshold_sec + 1; + let unqualified_payable_account = PayableAccount { + wallet: make_wallet("wallet0"), + balance: qualified_debt, + last_paid_timestamp: from_time_t(unqualified_time), + pending_payable_opt: None, + }; + + let result = is_payable_qualified(now, &unqualified_payable_account, &payment_thresholds); + + assert_eq!(result, None); + } + + #[test] + fn payable_with_low_debt_is_marked_unqualified() { + let now = SystemTime::now(); + let payment_thresholds = PaymentThresholds::default(); + let unqualified_debt = payment_thresholds.permanent_debt_allowed_gwei - 1; + let qualified_time = to_time_t(now) - payment_thresholds.maturity_threshold_sec - 1; + let unqualified_payable_account = PayableAccount { + wallet: make_wallet("wallet0"), + balance: unqualified_debt, + last_paid_timestamp: from_time_t(qualified_time), + pending_payable_opt: None, + }; + + let result = is_payable_qualified(now, &unqualified_payable_account, &payment_thresholds); + + assert_eq!(result, None); + } + + #[test] + fn payable_with_low_payout_threshold_is_marked_unqualified() { + let now = SystemTime::now(); + let payment_thresholds = PaymentThresholds::default(); + let debt = payment_thresholds.permanent_debt_allowed_gwei + 1; + let time = to_time_t(now) - payment_thresholds.maturity_threshold_sec - 1; + let unqualified_payable_account = PayableAccount { + wallet: make_wallet("wallet0"), + balance: debt, + last_paid_timestamp: from_time_t(time), + pending_payable_opt: None, + }; + + let result = is_payable_qualified(now, &unqualified_payable_account, &payment_thresholds); + + assert_eq!(result, None); + } + + #[test] + fn payable_above_threshold_values_is_marked_qualified_and_returns_threshold() { + let now = SystemTime::now(); + let payment_thresholds = PaymentThresholds::default(); + let debt = payment_thresholds.permanent_debt_allowed_gwei + 1_000_000_000; + let time = to_time_t(now) - payment_thresholds.maturity_threshold_sec - 1; + let payment_thresholds_rc = Rc::new(payment_thresholds); + let qualified_payable = PayableAccount { + wallet: make_wallet("wallet0"), + balance: debt, + last_paid_timestamp: from_time_t(time), + pending_payable_opt: None, + }; + let threshold = calculate_payout_threshold( + payable_time_diff(now, &qualified_payable), + &payment_thresholds_rc, + ); + eprintln!("Threshold: {}, Debt: {}", threshold, debt); + + let result = is_payable_qualified(now, &qualified_payable, &payment_thresholds_rc); + + assert_eq!(result, Some(threshold as u64)); + } + + #[test] + fn qualified_payables_can_be_filtered_out_from_non_pending_payables_along_with_their_summary() { + let now = SystemTime::now(); + let payment_thresholds = PaymentThresholds::default(); + let unqualified_payable_accounts = vec![PayableAccount { + wallet: make_wallet("wallet1"), + balance: payment_thresholds.permanent_debt_allowed_gwei + 1, + last_paid_timestamp: from_time_t( + to_time_t(now) - payment_thresholds.maturity_threshold_sec + 1, + ), + pending_payable_opt: None, + }]; + let qualified_payable_accounts = vec![ + PayableAccount { + wallet: make_wallet("wallet2"), + balance: payment_thresholds.permanent_debt_allowed_gwei + 1_000_000_000, + last_paid_timestamp: from_time_t( + to_time_t(now) - payment_thresholds.maturity_threshold_sec - 1, + ), + pending_payable_opt: None, + }, + PayableAccount { + wallet: make_wallet("wallet3"), + balance: payment_thresholds.permanent_debt_allowed_gwei + 1_200_000_000, + last_paid_timestamp: from_time_t( + to_time_t(now) - payment_thresholds.maturity_threshold_sec - 100, + ), + pending_payable_opt: None, + }, + ]; + let mut all_non_pending_payables = Vec::new(); + all_non_pending_payables.extend(qualified_payable_accounts.clone()); + all_non_pending_payables.extend(unqualified_payable_accounts.clone()); + + let (qualified_payables, summary) = + qualified_payables_and_summary(now, all_non_pending_payables, &payment_thresholds); + + let mut expected_summary = String::from("Paying qualified debts:\n"); + for payable in qualified_payable_accounts.iter() { + expected_summary.push_str(&exceeded_summary( + now, + &payable, + calculate_payout_threshold(payable_time_diff(now, &payable), &payment_thresholds) + as u64, + )) + } + + assert_eq!(qualified_payables, qualified_payable_accounts); + assert_eq!(summary, expected_summary); + } + + #[test] + fn returns_empty_array_and_summary_when_no_qualified_payables_are_found() { + let now = SystemTime::now(); + let payment_thresholds = PaymentThresholds::default(); + let unqualified_payable_accounts = vec![PayableAccount { + wallet: make_wallet("wallet1"), + balance: payment_thresholds.permanent_debt_allowed_gwei + 1, + last_paid_timestamp: from_time_t( + to_time_t(now) - payment_thresholds.maturity_threshold_sec + 1, + ), + pending_payable_opt: None, + }]; + + let (qualified_payables, summary) = + qualified_payables_and_summary(now, unqualified_payable_accounts, &payment_thresholds); + + assert_eq!(qualified_payables, vec![]); + assert_eq!(summary, String::from("No Qualified Payables found.")); + } + + #[test] + fn investigate_debt_extremes_picks_the_most_relevant_records() { + let now = SystemTime::now(); + let now_t = to_time_t(now); + let same_amount_significance = 2_000_000; + let same_age_significance = from_time_t(now_t - 30000); + let payables = &[ + PayableAccount { + wallet: make_wallet("wallet0"), + balance: same_amount_significance, + last_paid_timestamp: from_time_t(now_t - 5000), + pending_payable_opt: None, + }, + //this debt is more significant because beside being high in amount it's also older, so should be prioritized and picked + PayableAccount { + wallet: make_wallet("wallet1"), + balance: same_amount_significance, + last_paid_timestamp: from_time_t(now_t - 10000), + pending_payable_opt: None, + }, + //similarly these two wallets have debts equally old but the second has a bigger balance and should be chosen + PayableAccount { + wallet: make_wallet("wallet3"), + balance: 100, + last_paid_timestamp: same_age_significance, + pending_payable_opt: None, + }, + PayableAccount { + wallet: make_wallet("wallet2"), + balance: 330, + last_paid_timestamp: same_age_significance, + pending_payable_opt: None, + }, + ]; + + let result = investigate_debt_extremes(now, payables); + + assert_eq!(result, "Payable scan found 4 debts; the biggest is 2000000 owed for 10000sec, the oldest is 330 owed for 30000sec") + } + + #[test] + fn balance_and_age_is_calculated_as_expected() { + let now = SystemTime::now(); + let offset = 1000; + let receivable_account = ReceivableAccount { + wallet: make_wallet("wallet0"), + balance: 10_000_000_000, + last_received_timestamp: from_time_t(to_time_t(now) - offset), + }; + + let (balance, age) = balance_and_age(now, &receivable_account); + + assert_eq!(balance, "10"); + assert_eq!(age.as_secs(), offset as u64); + } + + #[test] + fn separate_early_errors_works() { + let payable_ok = Payable { + to: make_wallet("blah"), + amount: 5555, + timestamp: SystemTime::now(), + tx_hash: Default::default(), + }; + let error = BlockchainError::SignedValueConversion(666); + let sent_payable = SentPayable { + timestamp: SystemTime::now(), + payable: vec![Ok(payable_ok.clone()), Err(error.clone())], + response_skeleton_opt: None, + }; + + let (ok, err) = separate_early_errors(&sent_payable, &Logger::new("test")); + + assert_eq!(ok, vec![payable_ok]); + assert_eq!(err, vec![error]) + } +} diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 33255d35b..0b96bcee7 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -11,7 +11,8 @@ use crate::accountant::pending_payable_dao::{ use crate::accountant::receivable_dao::{ ReceivableAccount, ReceivableDao, ReceivableDaoError, ReceivableDaoFactory, }; -use crate::accountant::{Accountant, PendingPayableId}; +use crate::accountant::scanners::{PayableScanner, PendingPayableScanner, ReceivableScanner}; +use crate::accountant::{Accountant, PendingPayableId, DEFAULT_PENDING_TOO_LONG_SEC}; use crate::banned_dao::{BannedDao, BannedDaoFactory}; use crate::blockchain::blockchain_bridge::PendingPayableFingerprint; use crate::blockchain::blockchain_interface::BlockchainTransaction; @@ -20,10 +21,11 @@ use crate::database::dao_utils; use crate::database::dao_utils::{from_time_t, to_time_t}; use crate::db_config::config_dao::{ConfigDao, ConfigDaoFactory}; use crate::db_config::mocks::ConfigDaoMock; -use crate::sub_lib::accountant::{AccountantConfig, MessageIdGenerator, PaymentThresholds}; +use crate::sub_lib::accountant::FinancialStatistics; +use crate::sub_lib::accountant::{MessageIdGenerator, PaymentThresholds}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; -use crate::test_utils::unshared_test_utils::make_populated_accountant_config_with_defaults; +use crate::test_utils::unshared_test_utils::make_bc_with_defaults; use actix::System; use ethereum_types::{BigEndianHash, H256, U256}; use rusqlite::{Connection, Error, OptionalExtension}; @@ -70,10 +72,10 @@ pub fn make_payable_account_with_recipient_and_balance_and_timestamp_opt( pub struct AccountantBuilder { config: Option, - payable_dao_factory: Option>, - receivable_dao_factory: Option>, - pending_payable_dao_factory: Option>, - banned_dao_factory: Option>, + payable_dao_factory: Option, + receivable_dao_factory: Option, + pending_payable_dao_factory: Option, + banned_dao_factory: Option, config_dao_factory: Option>, } @@ -97,24 +99,55 @@ impl AccountantBuilder { } pub fn payable_dao(mut self, payable_dao: PayableDaoMock) -> Self { - self.payable_dao_factory = Some(Box::new(PayableDaoFactoryMock::new(payable_dao))); + match self.payable_dao_factory { + None => { + self.payable_dao_factory = + Some(PayableDaoFactoryMock::new().make_result(payable_dao)) + } + Some(payable_dao_factory) => { + self.payable_dao_factory = Some(payable_dao_factory.make_result(payable_dao)) + } + } self } pub fn receivable_dao(mut self, receivable_dao: ReceivableDaoMock) -> Self { - self.receivable_dao_factory = Some(Box::new(ReceivableDaoFactoryMock::new(receivable_dao))); + match self.receivable_dao_factory { + None => { + self.receivable_dao_factory = + Some(ReceivableDaoFactoryMock::new().make_result(receivable_dao)) + } + Some(receivable_dao_factory) => { + self.receivable_dao_factory = + Some(receivable_dao_factory.make_result(receivable_dao)) + } + } self } pub fn pending_payable_dao(mut self, pending_payable_dao: PendingPayableDaoMock) -> Self { - self.pending_payable_dao_factory = Some(Box::new(PendingPayableDaoFactoryMock::new( - pending_payable_dao, - ))); + match self.pending_payable_dao_factory { + None => { + self.pending_payable_dao_factory = + Some(PendingPayableDaoFactoryMock::new().make_result(pending_payable_dao)) + } + Some(pending_payable_dao_factory) => { + self.pending_payable_dao_factory = + Some(pending_payable_dao_factory.make_result(pending_payable_dao)) + } + } self } pub fn banned_dao(mut self, banned_dao: BannedDaoMock) -> Self { - self.banned_dao_factory = Some(Box::new(BannedDaoFactoryMock::new(banned_dao))); + match self.banned_dao_factory { + None => { + self.banned_dao_factory = Some(BannedDaoFactoryMock::new().make_result(banned_dao)) + } + Some(banned_dao_factory) => { + self.banned_dao_factory = Some(banned_dao_factory.make_result(banned_dao)) + } + } self } @@ -124,110 +157,140 @@ impl AccountantBuilder { } pub fn build(self) -> Accountant { - let config = self.config.unwrap_or({ - let mut config = BootstrapperConfig::default(); - config.accountant_config_opt = Some(make_populated_accountant_config_with_defaults()); - config - }); - let payable_dao_factory = self - .payable_dao_factory - .unwrap_or(Box::new(PayableDaoFactoryMock::new(PayableDaoMock::new()))); - let receivable_dao_factory = - self.receivable_dao_factory - .unwrap_or(Box::new(ReceivableDaoFactoryMock::new( - ReceivableDaoMock::new(), - ))); - let pending_payable_dao_factory = self.pending_payable_dao_factory.unwrap_or(Box::new( - PendingPayableDaoFactoryMock::new(PendingPayableDaoMock::default()), - )); + let mut config = self.config.unwrap_or(make_bc_with_defaults()); + let payable_dao_factory = self.payable_dao_factory.unwrap_or( + PayableDaoFactoryMock::new() + .make_result(PayableDaoMock::new()) + .make_result(PayableDaoMock::new()) + .make_result(PayableDaoMock::new()), + ); + let receivable_dao_factory = self.receivable_dao_factory.unwrap_or( + ReceivableDaoFactoryMock::new() + .make_result(ReceivableDaoMock::new()) + .make_result(ReceivableDaoMock::new()), + ); + let pending_payable_dao_factory = self.pending_payable_dao_factory.unwrap_or( + PendingPayableDaoFactoryMock::new() + .make_result(PendingPayableDaoMock::new()) + .make_result(PendingPayableDaoMock::new()) + .make_result(PendingPayableDaoMock::new()), + ); let banned_dao_factory = self .banned_dao_factory - .unwrap_or(Box::new(BannedDaoFactoryMock::new(BannedDaoMock::new()))); + .unwrap_or(BannedDaoFactoryMock::new().make_result(BannedDaoMock::new())); let accountant = Accountant::new( - &config, - payable_dao_factory, - receivable_dao_factory, - pending_payable_dao_factory, - banned_dao_factory, + &mut config, + Box::new(payable_dao_factory), + Box::new(receivable_dao_factory), + Box::new(pending_payable_dao_factory), + Box::new(banned_dao_factory), ); accountant } } pub struct PayableDaoFactoryMock { - called: Rc>, - mock: RefCell>, + make_params: Arc>>, + make_results: RefCell>>, } impl PayableDaoFactory for PayableDaoFactoryMock { fn make(&self) -> Box { - *self.called.borrow_mut() = true; - Box::new(self.mock.borrow_mut().remove(0)) + if self.make_results.borrow().len() == 0 { + panic!( + "PayableDao Missing. This problem mostly occurs when PayableDao is only supplied for Accountant and not for the Scanner while building Accountant." + ) + }; + self.make_params.lock().unwrap().push(()); + self.make_results.borrow_mut().remove(0) } } impl PayableDaoFactoryMock { - pub fn new(mock: PayableDaoMock) -> Self { + pub fn new() -> Self { Self { - called: Rc::new(RefCell::new(false)), - mock: RefCell::new(vec![mock]), + make_params: Arc::new(Mutex::new(vec![])), + make_results: RefCell::new(vec![]), } } - pub fn called(mut self, called: &Rc>) -> Self { - self.called = called.clone(); + pub fn make_params(mut self, params: &Arc>>) -> Self { + self.make_params = params.clone(); + self + } + + pub fn make_result(self, result: PayableDaoMock) -> Self { + self.make_results.borrow_mut().push(Box::new(result)); self } } pub struct ReceivableDaoFactoryMock { - called: Rc>, - mock: RefCell>, + make_params: Arc>>, + make_results: RefCell>>, } impl ReceivableDaoFactory for ReceivableDaoFactoryMock { fn make(&self) -> Box { - *self.called.borrow_mut() = true; - Box::new(self.mock.borrow_mut().remove(0)) + if self.make_results.borrow().len() == 0 { + panic!( + "ReceivableDao Missing. This problem mostly occurs when ReceivableDao is only supplied for Accountant and not for the Scanner while building Accountant." + ) + }; + self.make_params.lock().unwrap().push(()); + self.make_results.borrow_mut().remove(0) } } impl ReceivableDaoFactoryMock { - pub fn new(mock: ReceivableDaoMock) -> Self { + pub fn new() -> Self { Self { - called: Rc::new(RefCell::new(false)), - mock: RefCell::new(vec![mock]), + make_params: Arc::new(Mutex::new(vec![])), + make_results: RefCell::new(vec![]), } } - pub fn called(mut self, called: &Rc>) -> Self { - self.called = called.clone(); + pub fn make_params(mut self, params: &Arc>>) -> Self { + self.make_params = params.clone(); + self + } + + pub fn make_result(self, result: ReceivableDaoMock) -> Self { + self.make_results.borrow_mut().push(Box::new(result)); self } } pub struct BannedDaoFactoryMock { - called: Rc>, - mock: RefCell>, + make_params: Arc>>, + make_results: RefCell>>, } impl BannedDaoFactory for BannedDaoFactoryMock { fn make(&self) -> Box { - *self.called.borrow_mut() = true; - Box::new(self.mock.borrow_mut().take().unwrap()) + if self.make_results.borrow().len() == 0 { + panic!("BannedDao Missing.") + }; + self.make_params.lock().unwrap().push(()); + self.make_results.borrow_mut().remove(0) } } impl BannedDaoFactoryMock { - pub fn new(mock: BannedDaoMock) -> Self { + pub fn new() -> Self { Self { - called: Rc::new(RefCell::new(false)), - mock: RefCell::new(Some(mock)), + make_params: Arc::new(Mutex::new(vec![])), + make_results: RefCell::new(vec![]), } } - pub fn called(mut self, called: &Rc>) -> Self { - self.called = called.clone(); + pub fn make_params(mut self, params: &Arc>>) -> Self { + self.make_params = params.clone(); + self + } + + pub fn make_result(self, result: BannedDaoMock) -> Self { + self.make_results.borrow_mut().push(Box::new(result)); self } } @@ -638,23 +701,14 @@ impl BannedDaoMock { } } -pub fn bc_from_ac_plus_earning_wallet( - ac: AccountantConfig, - earning_wallet: Wallet, -) -> BootstrapperConfig { - let mut bc = BootstrapperConfig::new(); - bc.accountant_config_opt = Some(ac); +pub fn bc_from_earning_wallet(earning_wallet: Wallet) -> BootstrapperConfig { + let mut bc = make_bc_with_defaults(); bc.earning_wallet = earning_wallet; bc } -pub fn bc_from_ac_plus_wallets( - ac: AccountantConfig, - consuming_wallet: Wallet, - earning_wallet: Wallet, -) -> BootstrapperConfig { - let mut bc = BootstrapperConfig::new(); - bc.accountant_config_opt = Some(ac); +pub fn bc_from_wallets(consuming_wallet: Wallet, earning_wallet: Wallet) -> BootstrapperConfig { + let mut bc = make_bc_with_defaults(); bc.consuming_wallet_opt = Some(consuming_wallet); bc.earning_wallet = earning_wallet; bc @@ -727,6 +781,10 @@ impl PendingPayableDao for PendingPayableDaoMock { } impl PendingPayableDaoMock { + pub fn new() -> Self { + PendingPayableDaoMock::default() + } + pub fn fingerprint_rowid_params(mut self, params: &Arc>>) -> Self { self.fingerprint_rowid_params = params.clone(); self @@ -794,29 +852,130 @@ impl PendingPayableDaoMock { } pub struct PendingPayableDaoFactoryMock { - called: Rc>, - mock: RefCell>, + make_params: Arc>>, + make_results: RefCell>>, } impl PendingPayableDaoFactory for PendingPayableDaoFactoryMock { fn make(&self) -> Box { - *self.called.borrow_mut() = true; - Box::new(self.mock.borrow_mut().remove(0)) + if self.make_results.borrow().len() == 0 { + panic!( + "PendingPayableDao Missing. This problem mostly occurs when PendingPayableDao is only supplied for Accountant and not for the Scanner while building Accountant." + ) + }; + self.make_params.lock().unwrap().push(()); + self.make_results.borrow_mut().remove(0) } } impl PendingPayableDaoFactoryMock { - pub fn new(mock: PendingPayableDaoMock) -> Self { + pub fn new() -> Self { Self { - called: Rc::new(RefCell::new(false)), - mock: RefCell::new(vec![mock]), + make_params: Arc::new(Mutex::new(vec![])), + make_results: RefCell::new(vec![]), } } - pub fn called(mut self, called: &Rc>) -> Self { - self.called = called.clone(); + pub fn make_params(mut self, params: &Arc>>) -> Self { + self.make_params = params.clone(); + self + } + + pub fn make_result(self, result: PendingPayableDaoMock) -> Self { + self.make_results.borrow_mut().push(Box::new(result)); + self + } +} + +impl Default for PayableScanner { + fn default() -> Self { + PayableScanner::new( + Box::new(PayableDaoMock::new()), + Box::new(PendingPayableDaoMock::new()), + Rc::new(PaymentThresholds::default()), + ) + } +} + +impl PayableScanner { + pub fn payable_dao(mut self, payable_dao: PayableDaoMock) -> Self { + self.payable_dao = Box::new(payable_dao); + self + } + + pub fn pending_payable_dao(mut self, pending_payable_dao: PendingPayableDaoMock) -> Self { + self.pending_payable_dao = Box::new(pending_payable_dao); + self + } +} + +impl Default for PendingPayableScanner { + fn default() -> Self { + PendingPayableScanner::new( + Box::new(PayableDaoMock::new()), + Box::new(PendingPayableDaoMock::new()), + Rc::new(PaymentThresholds::default()), + DEFAULT_PENDING_TOO_LONG_SEC, + Rc::new(RefCell::new(FinancialStatistics::default())), + ) + } +} + +impl PendingPayableScanner { + pub fn payable_dao(mut self, payable_dao: PayableDaoMock) -> Self { + self.payable_dao = Box::new(payable_dao); + self + } + + pub fn pending_payable_dao(mut self, pending_payable_dao: PendingPayableDaoMock) -> Self { + self.pending_payable_dao = Box::new(pending_payable_dao); + self + } +} + +impl Default for ReceivableScanner { + fn default() -> Self { + ReceivableScanner::new( + Box::new(ReceivableDaoMock::new()), + Box::new(BannedDaoMock::new()), + Rc::new(PaymentThresholds::default()), + Rc::new(make_wallet("earning")), + Rc::new(RefCell::new(FinancialStatistics::default())), + ) + } +} + +impl ReceivableScanner { + pub fn receivable_dao(mut self, receivable_dao: ReceivableDaoMock) -> Self { + self.receivable_dao = Box::new(receivable_dao); + self + } + + pub fn banned_dao(mut self, banned_dao: BannedDaoMock) -> Self { + self.banned_dao = Box::new(banned_dao); self } + + pub fn payment_thresholds(mut self, payment_thresholds: PaymentThresholds) -> Self { + self.common.payment_thresholds = Rc::new(payment_thresholds); + self + } + + pub fn earning_wallet(mut self, earning_wallet: Wallet) -> Self { + self.earning_wallet = Rc::new(earning_wallet); + self + } +} + +pub fn make_custom_payment_thresholds() -> PaymentThresholds { + PaymentThresholds { + threshold_interval_sec: 2_592_000, + debt_threshold_gwei: 1_000_000_000, + payment_grace_period_sec: 86_400, + maturity_threshold_sec: 86_400, + permanent_debt_allowed_gwei: 10_000_000, + unban_below_gwei: 10_000_000, + } } pub fn make_pending_payable_fingerprint() -> PendingPayableFingerprint { @@ -830,6 +989,52 @@ pub fn make_pending_payable_fingerprint() -> PendingPayableFingerprint { } } +pub fn make_payables( + now: SystemTime, + payment_thresholds: &PaymentThresholds, +) -> ( + Vec, + Vec, + Vec, +) { + let unqualified_payable_accounts = vec![PayableAccount { + wallet: make_wallet("wallet1"), + balance: payment_thresholds.permanent_debt_allowed_gwei + 1, + last_paid_timestamp: from_time_t( + to_time_t(now) - payment_thresholds.maturity_threshold_sec + 1, + ), + pending_payable_opt: None, + }]; + let qualified_payable_accounts = vec![ + PayableAccount { + wallet: make_wallet("wallet2"), + balance: payment_thresholds.permanent_debt_allowed_gwei + 1_000_000_000, + last_paid_timestamp: from_time_t( + to_time_t(now) - payment_thresholds.maturity_threshold_sec - 1, + ), + pending_payable_opt: None, + }, + PayableAccount { + wallet: make_wallet("wallet3"), + balance: payment_thresholds.permanent_debt_allowed_gwei + 1_200_000_000, + last_paid_timestamp: from_time_t( + to_time_t(now) - payment_thresholds.maturity_threshold_sec - 100, + ), + pending_payable_opt: None, + }, + ]; + + let mut all_non_pending_payables = Vec::new(); + all_non_pending_payables.extend(qualified_payable_accounts.clone()); + all_non_pending_payables.extend(unqualified_payable_accounts.clone()); + + ( + qualified_payable_accounts, + unqualified_payable_accounts, + all_non_pending_payables, + ) +} + #[derive(Default)] pub struct MessageIdGeneratorMock { ids: RefCell>, diff --git a/node/src/accountant/tools.rs b/node/src/accountant/tools.rs deleted file mode 100644 index 399d9e536..000000000 --- a/node/src/accountant/tools.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. - -pub(in crate::accountant) mod accountant_tools { - use crate::accountant::{ - Accountant, CancelFailedPendingTransaction, ConfirmPendingTransaction, - RequestTransactionReceipts, ResponseSkeleton, ScanForPayables, ScanForPendingPayables, - ScanForReceivables, - }; - use crate::sub_lib::utils::{NotifyHandle, NotifyLaterHandle}; - use actix::{Context, Recipient}; - #[cfg(test)] - use std::any::Any; - - pub struct Scanners { - pub pending_payables: Box, - pub payables: Box, - pub receivables: Box, - } - - impl Default for Scanners { - fn default() -> Self { - Scanners { - pending_payables: Box::new(PendingPayablesScanner), - payables: Box::new(PayablesScanner), - receivables: Box::new(ReceivablesScanner), - } - } - } - - pub trait Scanner { - fn scan(&self, accountant: &Accountant, response_skeleton_opt: Option); - fn notify_later_assertable(&self, accountant: &Accountant, ctx: &mut Context); - as_any_dcl!(); - } - - #[derive(Debug, PartialEq, Eq)] - pub struct PendingPayablesScanner; - - impl Scanner for PendingPayablesScanner { - fn scan(&self, accountant: &Accountant, response_skeleton_opt: Option) { - accountant.scan_for_pending_payable(response_skeleton_opt) - } - fn notify_later_assertable(&self, accountant: &Accountant, ctx: &mut Context) { - let _ = accountant - .confirmation_tools - .notify_later_scan_for_pending_payable - .notify_later( - ScanForPendingPayables { - response_skeleton_opt: None, // because scheduled scans don't respond - }, - accountant - .config - .scan_intervals - .pending_payable_scan_interval, - ctx, - ); - } - as_any_impl!(); - } - - #[derive(Debug, PartialEq, Eq)] - pub struct PayablesScanner; - - impl Scanner for PayablesScanner { - fn scan(&self, accountant: &Accountant, response_skeleton_opt: Option) { - accountant.scan_for_payables(response_skeleton_opt) - } - - fn notify_later_assertable(&self, accountant: &Accountant, ctx: &mut Context) { - let _ = accountant - .confirmation_tools - .notify_later_scan_for_payable - .notify_later( - ScanForPayables { - response_skeleton_opt: None, - }, - accountant.config.scan_intervals.payable_scan_interval, - ctx, - ); - } - - as_any_impl!(); - } - - #[derive(Debug, PartialEq, Eq)] - pub struct ReceivablesScanner; - - impl Scanner for ReceivablesScanner { - fn scan(&self, accountant: &Accountant, response_skeleton_opt: Option) { - // TODO: Figure out how to combine the results of these two into a single response to the UI - accountant.scan_for_received_payments(response_skeleton_opt); - accountant.scan_for_delinquencies() - } - - fn notify_later_assertable(&self, accountant: &Accountant, ctx: &mut Context) { - let _ = accountant - .confirmation_tools - .notify_later_scan_for_receivable - .notify_later( - ScanForReceivables { - response_skeleton_opt: None, - }, - accountant.config.scan_intervals.receivable_scan_interval, - ctx, - ); - } - - as_any_impl!(); - } - - //this is for turning off a certain scanner in testing to prevent it make "noise" - #[derive(Debug, PartialEq, Eq)] - pub struct NullScanner; - - impl Scanner for NullScanner { - fn scan(&self, _accountant: &Accountant, _response_skeleton_opt: Option) { - } - fn notify_later_assertable( - &self, - _accountant: &Accountant, - _ctx: &mut Context, - ) { - } - as_any_impl!(); - } - - #[derive(Default)] - pub struct TransactionConfirmationTools { - pub notify_later_scan_for_pending_payable: - Box>, - pub notify_later_scan_for_payable: Box>, - pub notify_later_scan_for_receivable: - Box>, - pub notify_confirm_transaction: - Box>, - pub notify_cancel_failed_transaction: - Box>, - pub request_transaction_receipts_subs_opt: Option>, - } -} - -#[cfg(test)] -mod tests { - use crate::accountant::tools::accountant_tools::{ - PayablesScanner, PendingPayablesScanner, ReceivablesScanner, Scanners, - }; - - #[test] - fn scanners_are_properly_defaulted() { - let subject = Scanners::default(); - - assert_eq!( - subject.pending_payables.as_any().downcast_ref(), - Some(&PendingPayablesScanner) - ); - assert_eq!( - subject.payables.as_any().downcast_ref(), - Some(&PayablesScanner) - ); - assert_eq!( - subject.receivables.as_any().downcast_ref(), - Some(&ReceivablesScanner) - ) - } -} diff --git a/node/src/actor_system_factory.rs b/node/src/actor_system_factory.rs index a8d1b0b36..68357e878 100644 --- a/node/src/actor_system_factory.rs +++ b/node/src/actor_system_factory.rs @@ -150,9 +150,10 @@ impl ActorSystemFactoryTools for ActorSystemFactoryToolsReal { }); let blockchain_bridge_subs = actor_factory.make_and_start_blockchain_bridge(&config); let neighborhood_subs = actor_factory.make_and_start_neighborhood(cryptdes.main, &config); + let data_directory = config.data_directory.clone(); let accountant_subs = actor_factory.make_and_start_accountant( - &config, - &config.data_directory.clone(), + &mut config.clone(), + &data_directory, &db_initializer, &BannedCacheLoaderReal {}, ); @@ -358,7 +359,7 @@ pub trait ActorFactory { ) -> NeighborhoodSubs; fn make_and_start_accountant( &self, - config: &BootstrapperConfig, + config: &mut BootstrapperConfig, data_directory: &Path, db_initializer: &dyn DbInitializer, banned_cache_loader: &dyn BannedCacheLoader, @@ -437,12 +438,12 @@ impl ActorFactory for ActorFactoryReal { fn make_and_start_accountant( &self, - config: &BootstrapperConfig, + config: &mut BootstrapperConfig, data_directory: &Path, db_initializer: &dyn DbInitializer, banned_cache_loader: &dyn BannedCacheLoader, ) -> AccountantSubs { - let cloned_config = config.clone(); + let mut cloned_config = config.clone(); let payable_dao_factory = Accountant::dao_factory(data_directory); let receivable_dao_factory = Accountant::dao_factory(data_directory); let pending_payable_dao_factory = Accountant::dao_factory(data_directory); @@ -456,7 +457,7 @@ impl ActorFactory for ActorFactoryReal { let arbiter = Arbiter::builder().stop_system_on_panic(true); let addr: Addr = arbiter.start(move |_| { Accountant::new( - &cloned_config, + &mut cloned_config, Box::new(payable_dao_factory), Box::new(receivable_dao_factory), Box::new(pending_payable_dao_factory), @@ -599,12 +600,14 @@ impl LogRecipientSetter for LogRecipientSetterReal { #[cfg(test)] mod tests { use super::*; + use crate::accountant::DEFAULT_PENDING_TOO_LONG_SEC; use crate::bootstrapper::{Bootstrapper, RealUser}; use crate::database::connection_wrapper::ConnectionWrapper; use crate::node_test_utils::{ make_stream_handler_pool_subs_from, make_stream_handler_pool_subs_from_recorder, start_recorder_refcell_opt, }; + use crate::sub_lib::accountant::PaymentThresholds; use crate::sub_lib::blockchain_bridge::BlockchainBridgeConfig; use crate::sub_lib::cryptde::{PlainData, PublicKey}; use crate::sub_lib::cryptde_null::CryptDENull; @@ -627,7 +630,7 @@ mod tests { }; use crate::test_utils::recorder::{make_recorder, Recorder}; use crate::test_utils::unshared_test_utils::{ - make_populated_accountant_config_with_defaults, ArbitraryIdStamp, SystemKillerActor, + make_scan_intervals_with_defaults, ArbitraryIdStamp, SystemKillerActor, }; use crate::test_utils::{alias_cryptde, rate_pack}; use crate::test_utils::{main_cryptde, make_cryptde_pair}; @@ -853,7 +856,7 @@ mod tests { fn make_and_start_accountant( &self, - config: &BootstrapperConfig, + config: &mut BootstrapperConfig, data_directory: &Path, _db_initializer: &dyn DbInitializer, _banned_cache_loader: &dyn BannedCacheLoader, @@ -1026,7 +1029,8 @@ mod tests { log_level: LevelFilter::Off, crash_point: CrashPoint::None, dns_servers: vec![], - accountant_config_opt: Some(make_populated_accountant_config_with_defaults()), + scan_intervals_opt: Some(make_scan_intervals_with_defaults()), + suppress_initial_scans_opt: Some(false), clandestine_discriminator_factories: Vec::new(), ui_gateway_config: UiGatewayConfig { ui_port: 5335 }, blockchain_bridge_config: BlockchainBridgeConfig { @@ -1052,6 +1056,8 @@ mod tests { rate_pack(100), ), }, + payment_thresholds_opt: Some(PaymentThresholds::default()), + when_pending_too_long_opt: Some(DEFAULT_PENDING_TOO_LONG_SEC), }; let persistent_config = PersistentConfigurationMock::default().chain_name_result("eth-ropsten".to_string()); @@ -1096,7 +1102,8 @@ mod tests { log_level: LevelFilter::Off, crash_point: CrashPoint::None, dns_servers: vec![], - accountant_config_opt: None, + scan_intervals_opt: None, + suppress_initial_scans_opt: None, clandestine_discriminator_factories: Vec::new(), ui_gateway_config: UiGatewayConfig { ui_port: 5335 }, blockchain_bridge_config: BlockchainBridgeConfig { @@ -1122,6 +1129,8 @@ mod tests { rate_pack(100), ), }, + payment_thresholds_opt: Default::default(), + when_pending_too_long_opt: None }; let add_mapping_params_arc = Arc::new(Mutex::new(vec![])); let mut subject = make_subject_with_null_setter(); @@ -1392,7 +1401,8 @@ mod tests { log_level: LevelFilter::Off, crash_point: CrashPoint::None, dns_servers: vec![], - accountant_config_opt: None, + scan_intervals_opt: None, + suppress_initial_scans_opt: None, clandestine_discriminator_factories: Vec::new(), ui_gateway_config: UiGatewayConfig { ui_port: 5335 }, blockchain_bridge_config: BlockchainBridgeConfig { @@ -1414,6 +1424,8 @@ mod tests { neighborhood_config: NeighborhoodConfig { mode: NeighborhoodMode::ConsumeOnly(vec![]), }, + payment_thresholds_opt: Default::default(), + when_pending_too_long_opt: None }; let system = System::new("MASQNode"); let mut subject = make_subject_with_null_setter(); @@ -1574,7 +1586,8 @@ mod tests { log_level: LevelFilter::Off, crash_point: CrashPoint::None, dns_servers: vec![], - accountant_config_opt: None, + scan_intervals_opt: None, + suppress_initial_scans_opt: None, clandestine_discriminator_factories: Vec::new(), ui_gateway_config: UiGatewayConfig { ui_port: 5335 }, blockchain_bridge_config: BlockchainBridgeConfig { @@ -1600,6 +1613,8 @@ mod tests { ), }, node_descriptor: Default::default(), + payment_thresholds_opt: Default::default(), + when_pending_too_long_opt: None, }; let subject = make_subject_with_null_setter(); let system = System::new("MASQNode"); diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index fb2892168..7d430e0cb 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -323,21 +323,19 @@ impl BlockchainBridge { _ => so_far, }); let (vector_of_results, error_opt) = short_circuit_result; - if !vector_of_results.is_empty() { - let pairs = vector_of_results - .into_iter() - .zip(msg.pending_payable.iter().cloned()) - .collect_vec(); - self.payment_confirmation - .report_transaction_receipts_sub_opt - .as_ref() - .expect("Accountant is unbound") - .try_send(ReportTransactionReceipts { - fingerprints_with_receipts: pairs, - response_skeleton_opt: msg.response_skeleton_opt, - }) - .expect("Accountant is dead"); - } + let pairs = vector_of_results + .into_iter() + .zip(msg.pending_payable.iter().cloned()) + .collect_vec(); + self.payment_confirmation + .report_transaction_receipts_sub_opt + .as_ref() + .expect("Accountant is unbound") + .try_send(ReportTransactionReceipts { + fingerprints_with_receipts: pairs, + response_skeleton_opt: msg.response_skeleton_opt, + }) + .expect("Accountant is dead"); if let Some((e, hash)) = error_opt { return Err (format! ( "Aborting scanning; request of a transaction receipt for '{:?}' failed due to '{:?}'", @@ -987,6 +985,41 @@ mod tests { for '0x000000000000000000000000000000000000000000000000000000000001348d' failed due to 'QueryFailed(\"bad bad bad\")'"); } + #[test] + fn blockchain_bridge_can_return_report_transaction_receipts_with_an_empty_vector() { + let (accountant, _, accountant_recording) = make_recorder(); + let recipient = accountant.start().recipient(); + let mut subject = BlockchainBridge::new( + Box::new(BlockchainInterfaceClandestine::new(Chain::Dev)), + Box::new(PersistentConfigurationMock::default()), + false, + Some(Wallet::new("mine")), + ); + subject + .payment_confirmation + .report_transaction_receipts_sub_opt = Some(recipient); + let msg = RequestTransactionReceipts { + pending_payable: vec![], + response_skeleton_opt: None, + }; + let system = System::new( + "blockchain_bridge_can_return_report_transaction_receipts_with_an_empty_vector", + ); + + let _ = subject.handle_request_transaction_receipts(&msg); + + System::current().stop(); + system.run(); + let recording = accountant_recording.lock().unwrap(); + assert_eq!( + recording.get_record::(0), + &ReportTransactionReceipts { + fingerprints_with_receipts: vec![], + response_skeleton_opt: None + } + ) + } + #[test] fn handle_request_transaction_receipts_short_circuits_on_failure_of_the_first_payment_and_it_does_not_send_any_message_just_aborts_and_logs( ) { diff --git a/node/src/bootstrapper.rs b/node/src/bootstrapper.rs index 47b0ad42d..0c6ca2227 100644 --- a/node/src/bootstrapper.rs +++ b/node/src/bootstrapper.rs @@ -21,7 +21,7 @@ use crate::node_configurator::{initialize_database, DirsWrapper, NodeConfigurato use crate::privilege_drop::{IdWrapper, IdWrapperReal}; use crate::server_initializer::LoggerInitializerWrapper; use crate::sub_lib::accountant; -use crate::sub_lib::accountant::AccountantConfig; +use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals}; use crate::sub_lib::blockchain_bridge::BlockchainBridgeConfig; use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::cryptde_null::CryptDENull; @@ -325,7 +325,9 @@ pub struct BootstrapperConfig { // These fields can be set while privileged without penalty pub log_level: LevelFilter, pub dns_servers: Vec, - pub accountant_config_opt: Option, + pub scan_intervals_opt: Option, + pub suppress_initial_scans_opt: Option, + pub when_pending_too_long_opt: Option, pub crash_point: CrashPoint, pub clandestine_discriminator_factories: Vec>, pub ui_gateway_config: UiGatewayConfig, @@ -337,6 +339,7 @@ pub struct BootstrapperConfig { pub alias_cryptde_null_opt: Option, pub mapping_protocol_opt: Option, pub real_user: RealUser, + pub payment_thresholds_opt: Option, // These fields must be set without privilege: otherwise the database will be created as root pub db_password_opt: Option, @@ -358,7 +361,8 @@ impl BootstrapperConfig { // These fields can be set while privileged without penalty log_level: LevelFilter::Off, dns_servers: vec![], - accountant_config_opt: Default::default(), + scan_intervals_opt: None, + suppress_initial_scans_opt: None, crash_point: CrashPoint::None, clandestine_discriminator_factories: vec![], ui_gateway_config: UiGatewayConfig { @@ -376,6 +380,7 @@ impl BootstrapperConfig { alias_cryptde_null_opt: None, mapping_protocol_opt: None, real_user: RealUser::new(None, None, None), + payment_thresholds_opt: Default::default(), // These fields must be set without privilege: otherwise the database will be created as root db_password_opt: None, @@ -385,6 +390,7 @@ impl BootstrapperConfig { neighborhood_config: NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, }, + when_pending_too_long_opt: None, } } @@ -398,7 +404,10 @@ impl BootstrapperConfig { self.earning_wallet = unprivileged.earning_wallet; self.consuming_wallet_opt = unprivileged.consuming_wallet_opt; self.db_password_opt = unprivileged.db_password_opt; - self.accountant_config_opt = unprivileged.accountant_config_opt; + self.scan_intervals_opt = unprivileged.scan_intervals_opt; + self.suppress_initial_scans_opt = unprivileged.suppress_initial_scans_opt; + self.payment_thresholds_opt = unprivileged.payment_thresholds_opt; + self.when_pending_too_long_opt = unprivileged.when_pending_too_long_opt; } pub fn exit_service_rate(&self) -> u64 { @@ -689,6 +698,7 @@ impl Bootstrapper { #[cfg(test)] mod tests { + use crate::accountant::DEFAULT_PENDING_TOO_LONG_SEC; use crate::actor_system_factory::{ActorFactory, ActorSystemFactory}; use crate::bootstrapper::{ main_cryptde_ref, Bootstrapper, BootstrapperConfig, EnvironmentWrapper, PortConfiguration, @@ -724,7 +734,7 @@ mod tests { use crate::test_utils::tokio_wrapper_mocks::ReadHalfWrapperMock; use crate::test_utils::tokio_wrapper_mocks::WriteHalfWrapperMock; use crate::test_utils::unshared_test_utils::{ - make_populated_accountant_config_with_defaults, make_simplified_multi_config, + make_scan_intervals_with_defaults, make_simplified_multi_config, }; use crate::test_utils::{assert_contains, rate_pack}; use crate::test_utils::{main_cryptde, make_wallet}; @@ -1222,8 +1232,9 @@ mod tests { unprivileged_config.earning_wallet = earning_wallet.clone(); unprivileged_config.consuming_wallet_opt = consuming_wallet_opt.clone(); unprivileged_config.db_password_opt = db_password_opt.clone(); - unprivileged_config.accountant_config_opt = - Some(make_populated_accountant_config_with_defaults()); + unprivileged_config.scan_intervals_opt = Some(make_scan_intervals_with_defaults()); + unprivileged_config.suppress_initial_scans_opt = Some(false); + unprivileged_config.when_pending_too_long_opt = Some(DEFAULT_PENDING_TOO_LONG_SEC); privileged_config.merge_unprivileged(unprivileged_config); @@ -1244,8 +1255,13 @@ mod tests { assert_eq!(privileged_config.consuming_wallet_opt, consuming_wallet_opt); assert_eq!(privileged_config.db_password_opt, db_password_opt); assert_eq!( - privileged_config.accountant_config_opt, - Some(make_populated_accountant_config_with_defaults()) + privileged_config.scan_intervals_opt, + Some(make_scan_intervals_with_defaults()) + ); + assert_eq!(privileged_config.suppress_initial_scans_opt, Some(false)); + assert_eq!( + privileged_config.when_pending_too_long_opt, + Some(DEFAULT_PENDING_TOO_LONG_SEC) ); //some values from the privileged config assert_eq!(privileged_config.log_level, Off); diff --git a/node/src/daemon/launch_verifier.rs b/node/src/daemon/launch_verifier.rs index f0a4125c9..056768755 100644 --- a/node/src/daemon/launch_verifier.rs +++ b/node/src/daemon/launch_verifier.rs @@ -10,7 +10,7 @@ use std::cell::RefCell; use std::net::TcpStream; use std::thread; use std::time::Duration; -use sysinfo::{ProcessExt, ProcessStatus, Signal, SystemExt}; +use sysinfo::{Pid, PidExt, ProcessExt, ProcessStatus, SystemExt}; use websocket::client::ParseError; use websocket::sync::Client; use websocket::{ClientBuilder, OwnedMessage, WebSocketResult}; @@ -50,7 +50,7 @@ impl VerifierTools for VerifierToolsReal { fn process_is_running(&self, process_id: u32) -> bool { let system = Self::system(); - let process_info_opt = system.process(Self::convert_pid(process_id)); + let process_info_opt = system.process(Pid::from_u32(process_id)); match process_info_opt { None => false, Some(process) => { @@ -61,8 +61,8 @@ impl VerifierTools for VerifierToolsReal { } fn kill_process(&self, process_id: u32) { - if let Some(process) = Self::system().process(Self::convert_pid(process_id)) { - if !process.kill(Signal::Term) && !process.kill(Signal::Kill) { + if let Some(process) = Self::system().process(Pid::from_u32(process_id)) { + if !process.kill() { error!( self.logger, "Process {} could be neither terminated nor killed", process_id @@ -96,16 +96,6 @@ impl VerifierToolsReal { system } - #[cfg(not(target_os = "windows"))] - fn convert_pid(process_id: u32) -> i32 { - process_id as i32 - } - - #[cfg(target_os = "windows")] - fn convert_pid(process_id: u32) -> usize { - process_id as usize - } - #[cfg(target_os = "linux")] fn is_alive(process_status: ProcessStatus) -> bool { !matches!(process_status, ProcessStatus::Dead | ProcessStatus::Zombie) diff --git a/node/src/daemon/setup_reporter.rs b/node/src/daemon/setup_reporter.rs index 1174c6ac2..065e9632e 100644 --- a/node/src/daemon/setup_reporter.rs +++ b/node/src/daemon/setup_reporter.rs @@ -19,7 +19,8 @@ use crate::node_configurator::unprivileged_parse_args_configuration::{ use crate::node_configurator::{ data_directory_from_context, determine_config_file_path, DirsWrapper, DirsWrapperReal, }; -use crate::sub_lib::accountant::{DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS}; +use crate::sub_lib::accountant::PaymentThresholds as PaymentThresholdsFromAccountant; +use crate::sub_lib::accountant::DEFAULT_SCAN_INTERVALS; use crate::sub_lib::neighborhood::NodeDescriptor; use crate::sub_lib::neighborhood::{NeighborhoodMode as NeighborhoodModeEnum, DEFAULT_RATE_PACK}; use crate::sub_lib::utils::make_new_multi_config; @@ -864,7 +865,10 @@ impl ValueRetriever for PaymentThresholds { _db_password_opt: &Option, ) -> Option<(String, UiSetupResponseValueStatus)> { let pc_value = pc.payment_thresholds().expectv("payment-thresholds"); - payment_thresholds_rate_pack_and_scan_intervals(pc_value, *DEFAULT_PAYMENT_THRESHOLDS) + payment_thresholds_rate_pack_and_scan_intervals( + pc_value, + PaymentThresholdsFromAccountant::default(), + ) } fn is_required(&self, _params: &SetupCluster) -> bool { @@ -928,7 +932,7 @@ fn payment_thresholds_rate_pack_and_scan_intervals( default: T, ) -> Option<(String, UiSetupResponseValueStatus)> where - T: PartialEq + Display + Copy, + T: PartialEq + Display + Clone, { if persistent_config_value == default { Some((default.to_string(), Default)) @@ -1042,6 +1046,9 @@ mod tests { }; use crate::node_configurator::{DirsWrapper, DirsWrapperReal}; use crate::node_test_utils::DirsWrapperMock; + use crate::sub_lib::accountant::{ + PaymentThresholds as PaymentThresholdsFromAccountant, DEFAULT_PAYMENT_THRESHOLDS, + }; use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::wallet::Wallet; @@ -3113,7 +3120,7 @@ mod tests { #[test] fn payment_thresholds_computed_default_persistent_config_unequal_to_default() { - let mut payment_thresholds = *DEFAULT_PAYMENT_THRESHOLDS; + let mut payment_thresholds = PaymentThresholdsFromAccountant::default(); payment_thresholds.maturity_threshold_sec += 12; payment_thresholds.unban_below_gwei -= 11; payment_thresholds.debt_threshold_gwei += 1111; @@ -3151,14 +3158,16 @@ mod tests { pc_method_result_setter: &C, ) where C: Fn(PersistentConfigurationMock, T) -> PersistentConfigurationMock, - T: Display + PartialEq + Copy, + T: Display + PartialEq + Clone, { let mut bootstrapper_config = BootstrapperConfig::new(); //the rate_pack within the mode setting does not determine the result, so I just set a nonsense bootstrapper_config.neighborhood_config.mode = NeighborhoodModeEnum::OriginateOnly(vec![], rate_pack(0)); - let persistent_config = - pc_method_result_setter(PersistentConfigurationMock::new(), persistent_config_value); + let persistent_config = pc_method_result_setter( + PersistentConfigurationMock::new(), + persistent_config_value.clone(), + ); let result = subject.computed_default(&bootstrapper_config, &persistent_config, &None); diff --git a/node/src/database/config_dumper.rs b/node/src/database/config_dumper.rs index f26efe306..c22e759ec 100644 --- a/node/src/database/config_dumper.rs +++ b/node/src/database/config_dumper.rs @@ -20,7 +20,7 @@ use crate::sub_lib::cryptde_real::CryptDEReal; use crate::sub_lib::neighborhood::NodeDescriptor; use crate::sub_lib::utils::make_new_multi_config; use clap::value_t; -use heck::MixedCase; +use heck::ToLowerCamelCase; use masq_lib::blockchains::chains::Chain; use masq_lib::command::StdStreams; use masq_lib::multi_config::make_arg_matches_accesible; @@ -66,7 +66,7 @@ fn configuration_to_json( ) -> String { let mut map = Map::new(); configuration.into_iter().for_each(|record| { - let json_name = record.name.to_mixed_case(); + let json_name = record.name.to_lower_camel_case(); let value_opt = match (&record.value_opt, record.encrypted, &password_opt) { (None, _, _) => None, (Some(value), false, _) => Some(value.to_string()), diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index ab30645e5..7ff277dca 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -4,9 +4,7 @@ use crate::accountant::DEFAULT_PENDING_TOO_LONG_SEC; use crate::blockchain::bip32::Bip32ECKeyProvider; use crate::bootstrapper::BootstrapperConfig; use crate::db_config::persistent_configuration::{PersistentConfigError, PersistentConfiguration}; -use crate::sub_lib::accountant::{ - AccountantConfig, PaymentThresholds, ScanIntervals, DEFAULT_EARNING_WALLET, -}; +use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals, DEFAULT_EARNING_WALLET}; use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::cryptde_real::CryptDEReal; @@ -479,30 +477,29 @@ fn configure_accountant_config( config: &mut BootstrapperConfig, persist_config: &mut dyn PersistentConfiguration, ) -> Result<(), ConfiguratorError> { + let payment_thresholds = process_combined_params( + "payment-thresholds", + multi_config, + persist_config, + |str: &str| PaymentThresholds::try_from(str), + |pc: &dyn PersistentConfiguration| pc.payment_thresholds(), + |pc: &mut dyn PersistentConfiguration, curves| pc.set_payment_thresholds(curves), + )?; + let scan_intervals = process_combined_params( + "scan-intervals", + multi_config, + persist_config, + |str: &str| ScanIntervals::try_from(str), + |pc: &dyn PersistentConfiguration| pc.scan_intervals(), + |pc: &mut dyn PersistentConfiguration, intervals| pc.set_scan_intervals(intervals), + )?; let suppress_initial_scans = value_m!(multi_config, "scans", String).unwrap_or_else(|| "on".to_string()) == *"off"; - - let accountant_config = AccountantConfig { - scan_intervals: process_combined_params( - "scan-intervals", - multi_config, - persist_config, - |str: &str| ScanIntervals::try_from(str), - |pc: &dyn PersistentConfiguration| pc.scan_intervals(), - |pc: &mut dyn PersistentConfiguration, intervals| pc.set_scan_intervals(intervals), - )?, - payment_thresholds: process_combined_params( - "payment-thresholds", - multi_config, - persist_config, - |str: &str| PaymentThresholds::try_from(str), - |pc: &dyn PersistentConfiguration| pc.payment_thresholds(), - |pc: &mut dyn PersistentConfiguration, curves| pc.set_payment_thresholds(curves), - )?, - suppress_initial_scans, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - }; - config.accountant_config_opt = Some(accountant_config); + let when_pending_too_long = DEFAULT_PENDING_TOO_LONG_SEC; + config.payment_thresholds_opt = Some(payment_thresholds); + config.scan_intervals_opt = Some(scan_intervals); + config.suppress_initial_scans_opt = Some(suppress_initial_scans); + config.when_pending_too_long_opt = Some(when_pending_too_long); Ok(()) } @@ -1738,25 +1735,29 @@ mod tests { ) .unwrap(); - let actual_accountant_config = config.accountant_config_opt.unwrap(); - let expected_accountant_config = AccountantConfig { - scan_intervals: ScanIntervals { - pending_payable_scan_interval: Duration::from_secs(180), - payable_scan_interval: Duration::from_secs(150), - receivable_scan_interval: Duration::from_secs(130), - }, - payment_thresholds: PaymentThresholds { - threshold_interval_sec: 1000, - debt_threshold_gwei: 10000, - payment_grace_period_sec: 1000, - maturity_threshold_sec: 10000, - permanent_debt_allowed_gwei: 20000, - unban_below_gwei: 20000, - }, - suppress_initial_scans: false, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, + let expected_scan_intervals = ScanIntervals { + pending_payable_scan_interval: Duration::from_secs(180), + payable_scan_interval: Duration::from_secs(150), + receivable_scan_interval: Duration::from_secs(130), + }; + let expected_payment_thresholds = PaymentThresholds { + threshold_interval_sec: 1000, + debt_threshold_gwei: 10000, + payment_grace_period_sec: 1000, + maturity_threshold_sec: 10000, + permanent_debt_allowed_gwei: 20000, + unban_below_gwei: 20000, }; - assert_eq!(actual_accountant_config, expected_accountant_config); + assert_eq!( + config.payment_thresholds_opt, + Some(expected_payment_thresholds) + ); + assert_eq!(config.scan_intervals_opt, Some(expected_scan_intervals)); + assert_eq!(config.suppress_initial_scans_opt, Some(false)); + assert_eq!( + config.when_pending_too_long_opt, + Some(DEFAULT_PENDING_TOO_LONG_SEC) + ); let set_scan_intervals_params = set_scan_intervals_params_arc.lock().unwrap(); assert_eq!(*set_scan_intervals_params, vec!["180|150|130".to_string()]); let set_payment_thresholds_params = set_payment_thresholds_params_arc.lock().unwrap(); @@ -1806,25 +1807,34 @@ mod tests { ) .unwrap(); - let actual_accountant_config = config.accountant_config_opt.unwrap(); - let expected_accountant_config = AccountantConfig { - scan_intervals: ScanIntervals { - pending_payable_scan_interval: Duration::from_secs(180), - payable_scan_interval: Duration::from_secs(150), - receivable_scan_interval: Duration::from_secs(130), - }, - payment_thresholds: PaymentThresholds { - threshold_interval_sec: 1000, - debt_threshold_gwei: 100000, - payment_grace_period_sec: 1000, - maturity_threshold_sec: 1000, - permanent_debt_allowed_gwei: 20000, - unban_below_gwei: 20000, - }, - suppress_initial_scans: false, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, + let expected_payment_thresholds = PaymentThresholds { + threshold_interval_sec: 1000, + debt_threshold_gwei: 100000, + payment_grace_period_sec: 1000, + maturity_threshold_sec: 1000, + permanent_debt_allowed_gwei: 20000, + unban_below_gwei: 20000, + }; + let expected_scan_intervals = ScanIntervals { + pending_payable_scan_interval: Duration::from_secs(180), + payable_scan_interval: Duration::from_secs(150), + receivable_scan_interval: Duration::from_secs(130), }; - assert_eq!(actual_accountant_config, expected_accountant_config); + let expected_suppress_initial_scans = false; + let expected_when_pending_too_long_sec = DEFAULT_PENDING_TOO_LONG_SEC; + assert_eq!( + config.payment_thresholds_opt, + Some(expected_payment_thresholds) + ); + assert_eq!(config.scan_intervals_opt, Some(expected_scan_intervals)); + assert_eq!( + config.suppress_initial_scans_opt, + Some(expected_suppress_initial_scans) + ); + assert_eq!( + config.when_pending_too_long_opt, + Some(expected_when_pending_too_long_sec) + ); //no prepared results for the setter methods, that is they were uncalled } @@ -2356,13 +2366,7 @@ mod tests { ) .unwrap(); - assert_eq!( - bootstrapper_config - .accountant_config_opt - .unwrap() - .suppress_initial_scans, - true - ); + assert_eq!(bootstrapper_config.suppress_initial_scans_opt, Some(true)); } #[test] @@ -2383,13 +2387,7 @@ mod tests { ) .unwrap(); - assert_eq!( - bootstrapper_config - .accountant_config_opt - .unwrap() - .suppress_initial_scans, - false - ); + assert_eq!(bootstrapper_config.suppress_initial_scans_opt, Some(false)); } #[test] @@ -2410,13 +2408,7 @@ mod tests { ) .unwrap(); - assert_eq!( - bootstrapper_config - .accountant_config_opt - .unwrap() - .suppress_initial_scans, - false - ); + assert_eq!(bootstrapper_config.suppress_initial_scans_opt, Some(false)); } fn make_persistent_config( diff --git a/node/src/server_initializer.rs b/node/src/server_initializer.rs index 5d6a87c51..e88aa14f1 100644 --- a/node/src/server_initializer.rs +++ b/node/src/server_initializer.rs @@ -9,7 +9,8 @@ use crate::run_modes_factories::{RunModeResult, ServerInitializer}; use crate::sub_lib::socket_server::ConfiguredByPrivilege; use backtrace::Backtrace; use flexi_logger::{ - Cleanup, Criterion, DeferredNow, Duplicate, LevelFilter, LogSpecBuilder, Logger, Naming, Record, + Cleanup, Criterion, DeferredNow, Duplicate, FileSpec, LevelFilter, LogSpecBuilder, Logger, + Naming, Record, WriteMode, }; use futures::try_ready; use lazy_static::lazy_static; @@ -163,19 +164,27 @@ impl LoggerInitializerWrapper for LoggerInitializerWrapperReal { .module("mio", LevelFilter::Off) .build(), ) - .log_to_file() - .directory(file_path.clone()) + .log_to_file( + FileSpec::default() + .directory(file_path.clone()) + .suppress_timestamp(), + ) + .write_mode(WriteMode::BufferAndFlush) .print_message() .duplicate_to_stderr(Duplicate::Info) - .suppress_timestamp() .format(format_function) .rotate( Criterion::Size(100_000_000), Naming::Numbers, - Cleanup::KeepZipFiles(50), + Cleanup::KeepLogFiles(50), ); if let Some(discriminant) = discriminant_opt { - logger = logger.discriminant(discriminant); + logger = logger.log_to_file( + FileSpec::default() + .directory(file_path.clone()) + .discriminant(discriminant) + .suppress_timestamp(), + ); } logger.start().expect("Logging subsystem failed to start"); let privilege_dropper = PrivilegeDropperReal::new(); diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 965a1a828..0a5c38122 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -36,7 +36,7 @@ lazy_static! { } //please, alphabetical order -#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)] +#[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct PaymentThresholds { pub debt_threshold_gwei: i64, pub maturity_threshold_sec: i64, @@ -46,6 +46,12 @@ pub struct PaymentThresholds { pub unban_below_gwei: i64, } +impl Default for PaymentThresholds { + fn default() -> Self { + *DEFAULT_PAYMENT_THRESHOLDS + } +} + //this code is used in tests in Accountant impl PaymentThresholds { pub fn sugg_and_grace(&self, now: i64) -> i64 { @@ -64,14 +70,6 @@ pub struct ScanIntervals { pub receivable_scan_interval: Duration, } -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct AccountantConfig { - pub scan_intervals: ScanIntervals, - pub payment_thresholds: PaymentThresholds, - pub suppress_initial_scans: bool, - pub when_pending_too_long_sec: u64, -} - #[derive(Clone)] pub struct AccountantSubs { pub bind: Recipient, diff --git a/node/src/sub_lib/combined_parameters.rs b/node/src/sub_lib/combined_parameters.rs index 4d71805bd..ed88f290f 100644 --- a/node/src/sub_lib/combined_parameters.rs +++ b/node/src/sub_lib/combined_parameters.rs @@ -290,9 +290,9 @@ fn unreachable() -> ! { #[cfg(test)] mod tests { use super::*; - use crate::sub_lib::accountant::{DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS}; use crate::sub_lib::combined_parameters::CombinedParamsDataTypes::U128; use crate::sub_lib::neighborhood::DEFAULT_RATE_PACK; + use crate::test_utils::unshared_test_utils::make_scan_intervals_with_defaults; use std::panic::catch_unwind; #[test] @@ -425,7 +425,7 @@ mod tests { let panic_2 = catch_unwind(|| { let _: &[(&str, CombinedParamsDataTypes)] = - (&CombinedParams::PaymentThresholds(Some(*DEFAULT_PAYMENT_THRESHOLDS))).into(); + (&CombinedParams::PaymentThresholds(Some(PaymentThresholds::default()))).into(); }) .unwrap_err(); let panic_2_msg = panic_2.downcast_ref::().unwrap(); @@ -434,13 +434,13 @@ mod tests { panic_2_msg, &format!( "should be called only on uninitialized object, not: PaymentThresholds(Some({:?}))", - *DEFAULT_PAYMENT_THRESHOLDS + PaymentThresholds::default() ) ); let panic_3 = catch_unwind(|| { let _: &[(&str, CombinedParamsDataTypes)] = - (&CombinedParams::ScanIntervals(Some(*DEFAULT_SCAN_INTERVALS))).into(); + (&CombinedParams::ScanIntervals(Some(make_scan_intervals_with_defaults()))).into(); }) .unwrap_err(); let panic_3_msg = panic_3.downcast_ref::().unwrap(); @@ -449,7 +449,7 @@ mod tests { panic_3_msg, &format!( "should be called only on uninitialized object, not: ScanIntervals(Some({:?}))", - *DEFAULT_SCAN_INTERVALS + make_scan_intervals_with_defaults() ) ); } @@ -471,7 +471,7 @@ mod tests { ); let panic_2 = catch_unwind(|| { - (&CombinedParams::PaymentThresholds(Some(*DEFAULT_PAYMENT_THRESHOLDS))) + (&CombinedParams::PaymentThresholds(Some(PaymentThresholds::default()))) .initiate_objects(HashMap::new()); }) .unwrap_err(); @@ -481,12 +481,12 @@ mod tests { panic_2_msg, &format!( "should be called only on uninitialized object, not: PaymentThresholds(Some({:?}))", - *DEFAULT_PAYMENT_THRESHOLDS + PaymentThresholds::default() ) ); let panic_3 = catch_unwind(|| { - (&CombinedParams::ScanIntervals(Some(*DEFAULT_SCAN_INTERVALS))) + (&CombinedParams::ScanIntervals(Some(make_scan_intervals_with_defaults()))) .initiate_objects(HashMap::new()); }) .unwrap_err(); @@ -496,7 +496,7 @@ mod tests { panic_3_msg, &format!( "should be called only on uninitialized object, not: ScanIntervals(Some({:?}))", - *DEFAULT_SCAN_INTERVALS + make_scan_intervals_with_defaults() ) ); } diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 256f66985..b01478433 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -513,13 +513,12 @@ pub struct TestRawTransaction { pub mod unshared_test_utils { use crate::accountant::DEFAULT_PENDING_TOO_LONG_SEC; use crate::apps::app_node; + use crate::bootstrapper::BootstrapperConfig; use crate::daemon::{ChannelFactory, DaemonBindMessage}; use crate::db_config::config_dao_null::ConfigDaoNull; use crate::db_config::persistent_configuration::PersistentConfigurationReal; use crate::node_test_utils::DirsWrapperMock; - use crate::sub_lib::accountant::{ - AccountantConfig, DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS, - }; + use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals, DEFAULT_SCAN_INTERVALS}; use crate::sub_lib::neighborhood::{ConnectionProgressMessage, DEFAULT_RATE_PACK}; use crate::sub_lib::utils::{ NLSpawnHandleHolder, NLSpawnHandleHolderReal, NotifyHandle, NotifyLaterHandle, @@ -598,30 +597,25 @@ pub mod unshared_test_utils { persistent_config_mock: PersistentConfigurationMock, ) -> PersistentConfigurationMock { persistent_config_mock - .payment_thresholds_result(Ok(*DEFAULT_PAYMENT_THRESHOLDS)) - .scan_intervals_result(Ok(*DEFAULT_SCAN_INTERVALS)) + .payment_thresholds_result(Ok(PaymentThresholds::default())) + .scan_intervals_result(Ok(make_scan_intervals_with_defaults())) } pub fn make_persistent_config_real_with_config_dao_null() -> PersistentConfigurationReal { PersistentConfigurationReal::new(Box::new(ConfigDaoNull::default())) } - pub fn make_populated_accountant_config_with_defaults() -> AccountantConfig { - AccountantConfig { - scan_intervals: *DEFAULT_SCAN_INTERVALS, - payment_thresholds: *DEFAULT_PAYMENT_THRESHOLDS, - when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, - suppress_initial_scans: false, - } + pub fn make_bc_with_defaults() -> BootstrapperConfig { + let mut config = BootstrapperConfig::new(); + config.scan_intervals_opt = Some(make_scan_intervals_with_defaults()); + config.suppress_initial_scans_opt = Some(false); + config.when_pending_too_long_opt = Some(DEFAULT_PENDING_TOO_LONG_SEC); + config.payment_thresholds_opt = Some(PaymentThresholds::default()); + config } - pub fn make_accountant_config_null() -> AccountantConfig { - AccountantConfig { - scan_intervals: Default::default(), - payment_thresholds: Default::default(), - when_pending_too_long_sec: Default::default(), - suppress_initial_scans: false, - } + pub fn make_scan_intervals_with_defaults() -> ScanIntervals { + DEFAULT_SCAN_INTERVALS.clone() } pub fn make_recipient_and_recording_arc( diff --git a/port_exposer/Cargo.lock b/port_exposer/Cargo.lock index f48e4922e..5e2b9be97 100644 --- a/port_exposer/Cargo.lock +++ b/port_exposer/Cargo.lock @@ -2,13 +2,37 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "default-net" -version = "0.8.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c02564d247dfbb3dae21f141da9053e83d1b5efb0018081a2ced47dfed3f97" +checksum = "05e70d471b0ba4e722c85651b3bb04b6880dfdb1224a43ade80c1295314db646" dependencies = [ "libc", + "memalloc", + "system-configuration", "windows", ] @@ -18,6 +42,12 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +[[package]] +name = "memalloc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1" + [[package]] name = "port_exposer" version = "0.6.2" @@ -25,11 +55,32 @@ dependencies = [ "default-net", ] +[[package]] +name = "system-configuration" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "windows" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac7fef12f4b59cd0a29339406cc9203ab44e440ddff6b3f5a41455349fa9cf3" +checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0" dependencies = [ "windows_aarch64_msvc", "windows_i686_gnu", @@ -40,30 +91,30 @@ dependencies = [ [[package]] name = "windows_aarch64_msvc" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b" +checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" [[package]] name = "windows_i686_gnu" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58" +checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" [[package]] name = "windows_i686_msvc" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4" +checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" [[package]] name = "windows_x86_64_gnu" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354" +checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" [[package]] name = "windows_x86_64_msvc" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561" +checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" diff --git a/port_exposer/Cargo.toml b/port_exposer/Cargo.toml index 38392d01f..c75b22e77 100644 --- a/port_exposer/Cargo.toml +++ b/port_exposer/Cargo.toml @@ -8,7 +8,7 @@ description = "Does port-forwarding from meta-address 0.0.0.0 to the loopback in edition = "2021" [dependencies] -default-net = "0.8.1" +default-net = "0.11.0" [[bin]] name = "port_exposer"