diff --git a/Cargo.lock b/Cargo.lock index e25a7c0c76..b3e708d4a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,15 @@ dependencies = [ "generic-array 0.14.4", ] +[[package]] +name = "aead" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "922b33332f54fc0ad13fa3e514601e8d30fb54e1f3eadc36643f6526db645621" +dependencies = [ + "generic-array 0.14.4", +] + [[package]] name = "aes" version = "0.6.0" @@ -37,17 +46,43 @@ dependencies = [ "cipher 0.2.5", ] +[[package]] +name = "aes" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "495ee669413bfbe9e8cace80f4d3d78e6d8c8d99579f97fb93bde351b185f2d4" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.3.0", + "cpufeatures", + "opaque-debug 0.3.0", +] + [[package]] name = "aes-gcm" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" dependencies = [ - "aead", - "aes", + "aead 0.3.2", + "aes 0.6.0", "cipher 0.2.5", - "ctr", - "ghash", + "ctr 0.6.0", + "ghash 0.3.1", + "subtle 2.4.0", +] + +[[package]] +name = "aes-gcm" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f" +dependencies = [ + "aead 0.4.1", + "aes 0.7.4", + "cipher 0.3.0", + "ctr 0.7.0", + "ghash 0.4.2", "subtle 2.4.0", ] @@ -155,18 +190,18 @@ checksum = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] name = "async-trait" -version = "0.1.42" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" +checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -315,13 +350,14 @@ dependencies = [ ] [[package]] -name = "blake2-rfc" -version = "0.2.18" +name = "blake2" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +checksum = "10a5720225ef5daecf08657f23791354e1685a8c91a4c60c7f3d3b2892f978f4" dependencies = [ - "arrayvec 0.4.12", - "constant_time_eq", + "crypto-mac 0.8.0", + "digest 0.9.0", + "opaque-debug 0.3.0", ] [[package]] @@ -467,7 +503,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" dependencies = [ - "rustc_version", + "rustc_version 0.2.3", ] [[package]] @@ -484,7 +520,7 @@ dependencies = [ "quote 1.0.9", "serde 1.0.126", "serde_json", - "syn 1.0.60", + "syn 1.0.67", "tempfile", "toml 0.5.8", ] @@ -525,15 +561,20 @@ dependencies = [ "cfg-if 1.0.0", "cipher 0.3.0", "cpufeatures", + "zeroize", ] [[package]] -name = "chacha20-poly1305-aead" -version = "0.1.2" +name = "chacha20poly1305" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77d2058ba29594f69c75e8a9018e0485e3914ca5084e3613cd64529042f5423b" +checksum = "1580317203210c517b6d44794abfbe600698276db18127e37ad3e69bf5e848e5" dependencies = [ - "constant_time_eq", + "aead 0.4.1", + "chacha20", + "cipher 0.3.0", + "poly1305", + "zeroize", ] [[package]] @@ -944,6 +985,15 @@ dependencies = [ "cipher 0.2.5", ] +[[package]] +name = "ctr" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481" +dependencies = [ + "cipher 0.3.0", +] + [[package]] name = "curl-sys" version = "0.4.40+curl-7.75.0" @@ -1030,7 +1080,7 @@ checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -1118,7 +1168,7 @@ dependencies = [ "heck", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -1356,7 +1406,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -1546,7 +1596,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" dependencies = [ "opaque-debug 0.3.0", - "polyval", + "polyval 0.4.5", +] + +[[package]] +name = "ghash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbd60caa311237d508927dbba7594b483db3ef05faa55172fcf89b1bcda7853" +dependencies = [ + "opaque-debug 0.3.0", + "polyval 0.5.1", ] [[package]] @@ -1655,9 +1715,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.3.5" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" +checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" [[package]] name = "httpdate" @@ -2113,7 +2173,7 @@ dependencies = [ "migrations_internals", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -2254,7 +2314,7 @@ dependencies = [ "proc-macro-error", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", "synstructure", ] @@ -2616,6 +2676,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + [[package]] name = "petgraph" version = "0.5.1" @@ -2652,7 +2721,7 @@ checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -2663,7 +2732,7 @@ checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -2690,6 +2759,17 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +[[package]] +name = "poly1305" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe800695325da85083cd23b56826fccb2e2dc29b218e7811a6f33bc93f414be" +dependencies = [ + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash", +] + [[package]] name = "polyval" version = "0.4.5" @@ -2701,6 +2781,18 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "polyval" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e597450cbf209787f0e6de80bf3795c6b2356a380ee87837b545aded8dbc1823" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -2725,7 +2817,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", "version_check 0.9.2", ] @@ -2808,7 +2900,7 @@ dependencies = [ "itertools", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3234,7 +3326,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", ] [[package]] @@ -3276,7 +3377,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54a50e29610a5be68d4a586a5cce3bfb572ed2c2a74227e4168444b7bf4e5235" dependencies = [ "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3361,7 +3462,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.2", ] [[package]] @@ -3370,6 +3480,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "0.8.23" @@ -3426,7 +3545,7 @@ checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3448,7 +3567,7 @@ checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3496,6 +3615,19 @@ dependencies = [ "opaque-debug 0.2.3", ] +[[package]] +name = "sha2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "sha3" version = "0.8.2" @@ -3570,17 +3702,17 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "snow" -version = "0.6.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb767eee7d257ba202f0b9b08673bc13b22281632ef45267b19f13100accd2f" +checksum = "6142f7c25e94f6fd25a32c3348ec230df9109b463f59c8c7acc4bd34936babb7" dependencies = [ - "arrayref", - "blake2-rfc", - "chacha20-poly1305-aead", - "rand 0.7.3", - "rand_core 0.5.1", - "rustc_version", - "sha2", + "aes-gcm 0.9.2", + "blake2 0.9.1", + "chacha20poly1305", + "rand 0.8.3", + "rand_core 0.6.2", + "rustc_version 0.3.3", + "sha2 0.9.5", "subtle 2.4.0", "x25519-dalek", ] @@ -3644,7 +3776,7 @@ dependencies = [ "proc-macro-error", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3662,7 +3794,7 @@ dependencies = [ "heck", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3674,7 +3806,7 @@ dependencies = [ "heck", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3686,7 +3818,7 @@ dependencies = [ "heck", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3731,9 +3863,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.60" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +checksum = "6498a9efc342871f91cc2d0d694c674368b4ceb40f62b65a7a08c3792935e702" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", @@ -3757,7 +3889,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", "unicode-xid 0.2.1", ] @@ -3871,7 +4003,7 @@ dependencies = [ "prost-build", "serde 1.0.126", "serde_json", - "sha2", + "sha2 0.8.2", "structopt", "tari_storage", "tari_test_utils", @@ -3897,7 +4029,7 @@ dependencies = [ "anyhow", "async-trait", "bitflags 1.2.1", - "blake2", + "blake2 0.8.1", "bytes 0.5.6", "chrono", "cidr", @@ -3987,7 +4119,7 @@ dependencies = [ "proc-macro2 1.0.24", "prost", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", "tari_comms", "tari_test_utils", "tokio", @@ -3999,6 +4131,7 @@ dependencies = [ name = "tari_console_wallet" version = "0.8.11" dependencies = [ + "bitflags 1.2.1", "chrono", "chrono-english", "crossterm", @@ -4035,7 +4168,7 @@ version = "0.8.11" dependencies = [ "bincode", "bitflags 1.2.1", - "blake2", + "blake2 0.8.1", "bytes 0.4.12", "chrono", "config", @@ -4082,10 +4215,10 @@ dependencies = [ [[package]] name = "tari_crypto" version = "0.9.0" -source = "git+ssh://git@github.com/tari-project/tari-crypto.git?branch=main#1620db4dd428dfb67179e8c6dd3bb1a6d04b67a9" +source = "git+ssh://git@github.com/tari-project/tari-crypto.git?branch=main#45fba2160694ac19f51d6233fd5465da8a1614ee" dependencies = [ "base64 0.10.1", - "blake2", + "blake2 0.8.1", "blake3", "cbindgen", "clear_on_drop", @@ -4098,7 +4231,7 @@ dependencies = [ "rmp-serde", "serde 1.0.126", "serde_json", - "sha2", + "sha2 0.8.2", "sha3 0.9.1", "tari_bulletproofs", "tari_utilities", @@ -4109,7 +4242,7 @@ dependencies = [ name = "tari_infra_derive" version = "0.8.11" dependencies = [ - "blake2", + "blake2 0.8.1", "proc-macro2 0.4.30", "quote 0.6.13", "syn 0.15.44", @@ -4124,7 +4257,7 @@ dependencies = [ "serde 1.0.126", "serde_derive", "serde_json", - "sha2", + "sha2 0.8.2", "tari_crypto", "thiserror", ] @@ -4195,7 +4328,7 @@ name = "tari_mmr" version = "0.8.11" dependencies = [ "bincode", - "blake2", + "blake2 0.8.1", "criterion", "croaring", "digest 0.8.1", @@ -4254,6 +4387,7 @@ name = "tari_service_framework" version = "0.8.11" dependencies = [ "anyhow", + "async-trait", "futures 0.3.12", "futures-test", "log 0.4.14", @@ -4326,9 +4460,9 @@ dependencies = [ name = "tari_wallet" version = "0.8.11" dependencies = [ - "aes-gcm", + "aes-gcm 0.8.0", "bincode", - "blake2", + "blake2 0.8.1", "chrono", "crossbeam-channel 0.3.9", "diesel", @@ -4453,7 +4587,7 @@ checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -4552,7 +4686,7 @@ checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -4661,7 +4795,7 @@ dependencies = [ "proc-macro2 1.0.24", "prost-build", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -4877,7 +5011,7 @@ checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -5041,6 +5175,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "uint" version = "0.9.0" @@ -5265,7 +5405,7 @@ dependencies = [ "log 0.4.14", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", "wasm-bindgen-shared", ] @@ -5299,7 +5439,7 @@ checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5403,11 +5543,11 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "0.6.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637ff90c9540fa3073bb577e65033069e4bae7c79d49d74aa3ffdf5342a53217" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" dependencies = [ - "curve25519-dalek 2.1.2", + "curve25519-dalek 3.1.0", "rand_core 0.5.1", "zeroize", ] @@ -5452,6 +5592,6 @@ checksum = "c3f369ddb18862aba61aa49bf31e74d29f0f162dec753063200e1dc084345d16" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.60", + "syn 1.0.67", "synstructure", ] diff --git a/README.md b/README.md index c08ea00578..0d45463cf1 100644 --- a/README.md +++ b/README.md @@ -504,7 +504,7 @@ and performing mining: 2021-02-26 11:28:19.687855700 [tari_mining_node::miner] INFO Mining thread 2 stopped 2021-02-26 11:28:19.688251200 [tari_mining_node] INFO Miner 2 found block header BlockHeader { hash: [...], version: 1, height: 8493, prev_hash: [...], timestamp: Some(Timestamp { seconds: 1614331698, nanos: 0 }), output_mr: [...], - range_proof_mr: [...], total_kernel_offset: [...], nonce: 8415580256943728281, pow: Some(ProofOfWork { pow_algo: 2, + witness_mr: [...], total_kernel_offset: [...], nonce: 8415580256943728281, pow: Some(ProofOfWork { pow_algo: 2, pow_data: [] }), kernel_mmr_size: 24983, output_mmr_size: 125474 } with difficulty 7316856839 ``` diff --git a/RFC/src/Glossary.md b/RFC/src/Glossary.md index 7c82ecfecd..be42264961 100644 --- a/RFC/src/Glossary.md +++ b/RFC/src/Glossary.md @@ -4,27 +4,29 @@ Below are a list of terms and their definitions that are used throughout the Tar glossary to disambiguate ideas, and work towards a [ubiquitous language](https://blog.carbonfive.com/2016/10/04/ubiquitous-language-the-joy-of-naming/) for this project. - ## Archive node [archivenode]: #archive-node "a full history node" -This is a full history [base node]. It will keep a complete history of every transaction ever received and it will not implement pruning. +This is a full history [base node]. It will keep a complete history of every transaction ever received and it will not +implement pruning. ## AssetCollateral [AssetCollateral]: #assetcollateral -The amount of tari coin that a [Validator Node] must put up on the [base layer] in order to become part of an asset [committee]. +The amount of tari coin that a [Validator Node] must put up on the [base layer] in order to become part of an asset +[committee]. ## Asset Issuer [Asset Issuer]: #asset-issuer "An entity that creates digital assets on the Tari DAN" -An entity that creates digital assets on the Tari DAN. The Asset Issuer will specify the parameters of the contract template -that defines the rules that govern the asset and the number and nature of its constituent tokens on issuance. The Asset Issuer -will, generally, be the initial owner of the tokens. +An entity that creates digital assets on the Tari DAN. The Asset Issuer will specify the parameters of the contract +template that defines the rules that govern the asset and the number and nature of its constituent tokens on issuance. +The Asset Issuer will, generally, be the initial owner of the tokens. ## Bad Actor -[Bad Actor]: #bad-actor "A participant that acts maliciously or negligently to the detriment of the network or another participant" +[Bad Actor]: #bad-actor "A participant that acts maliciously or negligently to the detriment of the network or another +participant" A participant that acts maliciously or negligently to the detriment of the network or another participant. @@ -37,7 +39,8 @@ the emission of new Tari, for securing and managing [Tari coin] transfers. ## Base Node -[base node]: #base-node "A full Tari node running on the base layer, validating and propagating Tari coin transactions and blocks" +[base node]: #base-node "A full Tari node running on the base layer, validating and propagating Tari coin transactions +and blocks" A full Tari node running on the base layer. It's primary role is validating and propagating [Tari coin] transactions and blocks to the rest of the network. @@ -73,7 +76,8 @@ blockchain from that point on. ## Blockchain state [blockchainstate]: #blockchain-state "This is a snapshot of how the blockchain looks" -The complete state of the blockchain at a specific block height. This means a pruned [utxo] set, a complete set of kernels and headers up to that block height from the genesis block. +The complete state of the blockchain at a specific block height. This means a pruned [utxo] set, a complete set of +kernels and headers up to that block height from the genesis block. ## BroadcastStrategy @@ -99,20 +103,24 @@ awarded to the miner that performed the Proof of Work for the block. ## Committee [Committee]: #committee "A group of validator nodes that are responsible for managing a specific Digital Asset" -A group of [Validator Node]s that are responsible for managing the state of a specific [Digital Asset]. A committee is selected -during asset issuance and can be updated at [Checkpoint]s. +A group of [Validator Node]s that are responsible for managing the state of a specific [Digital Asset]. A committee is +selected during asset issuance and can be updated at [Checkpoint]s. ## CommitteeSelectionStrategy -[CommitteeSelectionStrategy]: #committeeselectionstrategy "A strategy for an Asset Issuer to select candidates for the committee from the available registered Validator Nodes who responded to the nomination call for that asset" -A strategy for an Asset Issuer to select candidates for the committee from the available registered Validator Nodes who responded to the nomination call for that asset. +[CommitteeSelectionStrategy]: #committeeselectionstrategy "A strategy for an Asset Issuer to select candidates for the +committee from the available registered Validator Nodes who responded to the nomination call for that asset" + +A strategy for an Asset Issuer to select candidates for the committee from the available registered Validator Nodes who +responded to the nomination call for that asset. ## ConsensusStrategy -[ConsensusStrategy]: #consensusstrategy "The approach that will be taken for a committee to reach consensus on instructions" +[ConsensusStrategy]: #consensusstrategy "The approach that will be taken for a committee to reach consensus on +instructions" -The approach that will be taken for a committee to reach consensus on the validity of instructions that are performed on a -given Digital Asset. +The approach that will be taken for a committee to reach consensus on the validity of instructions that are performed +on a given Digital Asset. ## Commitment @@ -124,27 +132,36 @@ value or statement after they have committed to it. ## Communication Node -[Communication Node]: #communication-node "A communication node that is responsible for maintaining the Tari communication network" +[Communication Node]: #communication-node "A communication node that is responsible for maintaining the Tari +communication network" -A Communication Node is either a Validator Node or Base Node that is part of the Tari communication network. It maintains the network and is responsible for forwarding and propagating joining requests, discovery requests and data messages on the communication network. +A Communication Node is either a Validator Node or Base Node that is part of the Tari communication network. It +maintains the network and is responsible for forwarding and propagating joining requests, discovery requests and data +messages on the communication network. ## Communication Client -[Communication Client]: #communication-client "A communication client that makes use of the Tari communication network, but does not maintain it" +[Communication Client]: #communication-client "A communication client that makes use of the Tari communication network, +but does not maintain it" -A Communication Client is a Wallet or Asset Manager that makes use of the Tari communication network to send joining and discovery requests. A Communication Client does not maintain the communication network and is not responsible for forwarding or propagating any requests or data messages. +A Communication Client is a Wallet or Asset Manager that makes use of the Tari communication network to send joining and +discovery requests. A Communication Client does not maintain the communication network and is not responsible for +forwarding or propagating any requests or data messages. ## Creator Nomination Mode -[creator nomination mode]: #creator-nomination-mode "An asset runs in creator nomination mode when _every_ validator node in a validator committee is a [Trusted Node] that was directly nominated by the AI." +[creator nomination mode]: #creator-nomination-mode "An asset runs in creator nomination mode when _every_ validator +node in a validator committee is a [Trusted Node] that was directly nominated by the AI." -An asset runs in creator nomination mode when _every_ validator node in a validator committee is a [Trusted Node] that was directly nominated by the [Asset Issuer]. +An asset runs in creator nomination mode when _every_ validator node in a validator committee is a [Trusted Node] that +was directly nominated by the [Asset Issuer]. ## Current head [currenthead]: #current-head "The last valid block of the longest chain" -The last [block] of the base layer that represents the latest valid block. This [block] must be from the longest proof-of-work chain to be the current head. +The last [block] of the base layer that represents the latest valid block. This [block] must be from the longest +proof-of-work chain to be the current head. ## Digital asset @@ -152,8 +169,8 @@ The last [block] of the base layer that represents the latest valid block. This asset issuers on the Tari 2nd layer' Digital assets (DAs) are the sets or collections of native digital tokens (both fungible and non-fungible) that are -created by [asset issuer]s on the Tari 2nd layer. For example, a promoter might create a DA for a music concert event. The - event is the digital asset, and the tickets for the event are digital asset [tokens]. +created by [asset issuer]s on the Tari 2nd layer. For example, a promoter might create a DA for a music concert event. +The event is the digital asset, and the tickets for the event are digital asset [tokens]. ## Digital Asset Network @@ -166,8 +183,8 @@ interactions (defined in [instruction]s) are processed and validated by [Validat ## DigitalAssetTemplate [DigitalAssetTemplate]: #digitalassettemplate "A set of non-turing complete contract types supported by the DAN" -A DigitalAssetTemplate is one of a set of contract types supported by the DAN. These contracts are non-turing complete and consist of -rigid rule-sets with parameters that can be set by Asset Issuers. +A DigitalAssetTemplate is one of a set of contract types supported by the DAN. These contracts are non-turing complete +and consist of rigid rule-sets with parameters that can be set by Asset Issuers. ## Digital asset tokens @@ -182,7 +199,8 @@ asset. Depending on the DA created, tokens can represent tickets, in-game items, ## Hashed Time Locked Contract [htlc]: #hashed-time-locked-contract 'or just, "HTLC".' -A time locked contract that only pays out after a certain criteria has been met or refunds the originator if a certain period has expired. +A time locked contract that only pays out after a certain criteria has been met or refunds the originator if a certain +period has expired. ## Emission schedule @@ -203,8 +221,9 @@ client applications and are relayed by the DAN to the [validator node]s that are ## Mempool [mempool]: #mempool "A memory pool for unconfirmed transactions on the base layer" -The mempool consists of the transaction pool, pending pool, orphan pool and reorg pool, and is responsible for managing unconfirmed transactions that have not yet been included in the -longest proof-of-work chain. Miners usually draw verified transactions from the mempool to build up transaction [block]s. +The mempool consists of the transaction pool, pending pool, orphan pool and reorg pool, and is responsible for managing +unconfirmed transactions that have not yet been included in the longest proof-of-work chain. Miners usually draw +verified transactions from the mempool to build up transaction [block]s. ## Mimblewimble @@ -218,7 +237,8 @@ anonymous author and has since been refined by several authors, including Andrew ## Mining Server [mining server]: #mining-server -A Mining Server is responsible for constructing new blocks by bundling transactions from the [mempool] of a connected [Base Node]. It also distributes Proof-of-Work tasks to Mining Workers and verifies PoW solutions. +A Mining Server is responsible for constructing new blocks by bundling transactions from the [mempool] of a connected +[Base Node]. It also distributes Proof-of-Work tasks to Mining Workers and verifies PoW solutions. ## Mining Worker @@ -230,7 +250,9 @@ A Mining Worker is responsible for performing Proof-of-Work tasks received from ## Multisig [multisig]: #multisig -Multi-signatures (Multisigs) are also known as N-of-M signatures, this means that a minimum of N number of the M peers need to agree before a transaction can be spent. N and M can be equal; which is a special case and is often referred to as an N-of-N Multisig. +Multi-signatures (Multisigs) are also known as N-of-M signatures, this means that a minimum of N number of the M peers +need to agree before a transaction can be spent. N and M can be equal; which is a special case and is often referred to +as an N-of-N Multisig. [TLU musig]() @@ -246,23 +268,29 @@ from the public identification key of a [communication node] or [communication c ## Orphan Pool [orphan pool]: #orphan-pool "A pool in the Mempool for unconfirmed transactions that attempt to spend non-existent UTXOs" -The orphan pool is part of the [mempool] and manages all [transaction]s that have been verified but attempt to spend [UTXO]s that do not exist or haven't been created yet. +The orphan pool is part of the [mempool] and manages all [transaction]s that have been verified but attempt to spend +[UTXO]s that do not exist or haven't been created yet. ## Pending Pool [pending pool]: #pending-pool "A pool in the Mempool for unconfirmed transactions with time-lock restrictions" -The pending pool is part of the [mempool] and manages all [transaction]s that have a time-lock restriction on when it can be processed or attempts to spend [UTXO]s with time-locks. +The pending pool is part of the [mempool] and manages all [transaction]s that have a time-lock restriction on when it +can be processed or attempts to spend [UTXO]s with time-locks. ## Pruning horizon [pruninghorizon]: #pruning-horizon "Block height at which pruning will commence" -This is a local setting for each node to help reduce syncing time and bandwidth. This is the number of blocks from the chain tip beyond which a chain will be pruned. +This is a local setting for each node to help reduce syncing time and bandwidth. This is the number of blocks from the +chain tip beyond which a chain will be pruned. ## Public Nomination Mode [public nomination mode]: #public-nomination-mode -An asset runs in public nomination mode when the [Asset Issuer] broadcasts a call for nominations to the network and VNs from the network nominate themselves as candidates to become members of the [committee] for the asset. The [Asset Issuer] will then employ the [CommitteeSelectionStrategy] to select the committee from the list of available candidates. +An asset runs in public nomination mode when the [Asset Issuer] broadcasts a call for nominations to the network and VNs +from the network nominate themselves as candidates to become members of the [committee] for the asset. The +[Asset Issuer] will then employ the [CommitteeSelectionStrategy] to select the committee from the list of available +candidates. ## Range proof [range proof]: #range-proof @@ -272,7 +300,8 @@ A mathematical demonstration that a value inside a [commitment] (i.e. it is hidd ## Registration Deposit -[Registration Deposit]: #registration-deposit "An amount of tari coin that is locked up on the base layer when a [Validator Node] is registered" +[Registration Deposit]: #registration-deposit "An amount of tari coin that is locked up on the base layer when a +[Validator Node] is registered" An amount of tari coin that is locked up on the base layer when a [Validator Node] is registered. In order to make Sybil attacks expensive and to provide an authorative base layer registry of [validator node]s they will need to lock up a @@ -289,7 +318,35 @@ minimum period has elapsed. ## Reorg Pool [reorg pool]: #reorg-pool "A backup pool in the Mempool for unconfirmed transactions that have been included in blocks" -The reorg pool is part of the [mempool] and stores all [transaction]s that have recently been included in blocks in case a blockchain reorganization occurs and the transactions need to be restored to the [transaction pool]. +The reorg pool is part of the [mempool] and stores all [transaction]s that have recently been included in blocks in case +a blockchain reorganization occurs and the transactions need to be restored to the [transaction pool]. + + +## Script Keypair +[script key]: #script-keypair + +The script private - public keypair, \\((k\_{Si}\\),\\(K\_{Si})\\), is used in [TariScript] to unlock and execute the +script associated with an output. Afterwards the execution stack must contain exactly one value that must be equal to +the script public key. + + +## Script Offset +[script offset]: #script-offset + +The script offset provides a proof that every script public key \\( K\_{Si} \\) and sender offset public key +\\( K\_{Oi} \\) provided for the a transaction's inputs and outputs are correct. + +## Sender Metadata Signature +[sender metadata signature]: #sender-metadata-signature + +The sender metadata signature is used to sign the metadata of the UTXO with the [sender offset] private key +\\( k_{Oi} \\) and this stops malleability of the UTXO metadata. + +## Sender Offset Keypair +[sender offset]: #sender-offset-keypair + +The sender offset private - public keypair, (\\( k\_{Oi} \\),\\( K\_{Oi} \\)), is used by the sender of an output to +lock all its metadata by virtue of a [sender metadata signature]. ## Spending Key @@ -327,6 +384,14 @@ network conditions etc. The base layer token. Tari coins are released according to the [emission schedule] on the Tari [base layer] [blockchain] in [coinbase transaction]s. + +## TariScript +[TariScript]: #tariscript "The Tari scripting system for transactions" + +Tari uses a scripting system for transactions, not unlike [Bitcoin's scripting system](https://en.bitcoin.it/wiki/Script), +called TariScript. It is also simple, stack-based, processed from left to right, not Turing-complete, with no loops. It +is a list of instructions linked in a non malleable way to each output, specifying its conditions of spending. + ## Transaction [transaction]: #transaction "Base layer tari coin transfers." @@ -337,7 +402,8 @@ transfer of [Tari coin]s. ## Transaction Pool [transaction pool]: #transaction-pool "A pool in the Mempool for valid and verified unconfirmed transactions" -The transaction pool is part of the [mempool] and manages all [transaction]s that have been verified, that spend valid [UTXO]s and don't have any time-lock restrictions. +The transaction pool is part of the [mempool] and manages all [transaction]s that have been verified, that spend valid +[UTXO]s and don't have any time-lock restrictions. ## Trusted Node @@ -349,7 +415,8 @@ A permissioned Validator Node nominated by an Asset Issuer that will form part o ## Token Wallet [token wallet]: #token-wallet "An Asset Manager Wallet for Tari Assets and Tokens" -A Tari Token Wallet is responsible for managing [Digital asset]s and [Tokens], and for constructing and negotiating [instruction]s for transferring and receiving Assets and Tokens on the [Digital Asset Network]. +A Tari Token Wallet is responsible for managing [Digital asset]s and [Tokens], and for constructing and negotiating +[instruction]s for transferring and receiving Assets and Tokens on the [Digital Asset Network]. ## Unspent transaction outputs @@ -384,8 +451,8 @@ Transactions or blocks are `unvalidated` when first received by a [Base Node]. A [wallet]: #wallet "A Wallet for Tari coins" [Registration Deposit]: #registration-deposit - -A Tari Wallet is responsible for managing key pairs, and for constructing and negotiating [transaction]s for transferring and receiving [tari coin]s on the [Base Layer]. +A Tari Wallet is responsible for managing key pairs, and for constructing and negotiating [transaction]s for +transferring and receiving [tari coin]s on the [Base Layer]. # Disclaimer diff --git a/RFC/src/RFC-0201_TariScript.md b/RFC/src/RFC-0201_TariScript.md index 8650dcba24..4e18201c61 100644 --- a/RFC/src/RFC-0201_TariScript.md +++ b/RFC/src/RFC-0201_TariScript.md @@ -49,14 +49,14 @@ technological merits of the potential system outlined herein. ## Goals -This Request for Comment (RFC) presents a proposal for introducing Tari Script into the Tari base layer protocol. Tari +This Request for Comment (RFC) presents a proposal for introducing [TariScript] into the Tari base layer protocol. Tari Script aims to provide a general mechanism for enabling further extensions such as side chains, the DAN, one-sided payments and atomic swaps. ## Related Requests for Comment * [RFC-0200: Base Layer Extensions](BaseLayerExtensions.md) -* [RFC-0202: Tari Script Opcodes](RFC-0202_TariScriptOpcodes.md) +* [RFC-0202: TariScript Opcodes](RFC-0202_TariScriptOpcodes.md) * [RFC-0300: The Tari Digital Assets Network](RFC-0300_DAN.md) @@ -82,7 +82,7 @@ Extensions to Mimblewimble have been proposed for most of these features, for ex proposal for LiteCoin ([LIP-004]), this project's [HTLC RFC](RFC-0230_HTLC.md) and the pegging proposals for the Clacks side-chain. -Some smart contract features are possible, or partly possible in vanilla Mimblewimble using [Scriptless script], such as +Some smart contract features are possible, or partly possible in vanilla [Mimblewimble] using [Scriptless script], such as * Atomic swaps * Hash time-locked contracts @@ -93,7 +93,7 @@ current Tari and Mimblewimble protocol. ## Scripting on Mimblewimble -To the author's knowledge, none of existing Mimblewimble projects have employed a scripting language, nor are there +To the author's knowledge, none of existing [Mimblewimble] projects have employed a scripting language, nor are there ambitions to do so. [Grin](https://github.com/mimblewimble/grin) styles itself as a "Minimal implementation of the Mimblewimble protocol", @@ -112,7 +112,7 @@ to be no plans to include general scripting into the protocol. ### Scriptless scripts -[Scriptless script] is a wonderfully elegant technology and inclusion of Tari Script does not preclude the use of +[Scriptless script] is a wonderfully elegant technology and inclusion of [TariScript] does not preclude the use of Scriptless script in Tari. However, scriptless scripts have some disadvantages: * They are often difficult to reason about, with the result that the development of features based on scriptless scripts @@ -122,9 +122,9 @@ Scriptless script in Tari. However, scriptless scripts have some disadvantages: * Every feature must be written and implemented separately using the specific and specialised protocol designed for that feature. That is, it cannot be used as a dynamic scripting framework on a running blockchain. -## Tari Script - a brief motivation +## TariScript - a brief motivation -The essential idea of Tari Script is as follows: +The essential idea of [TariScript] is as follows: Given a standard Tari UTXO, we add _additional restrictions_ on whether that UTXO can be included as a valid input in a transaction. @@ -136,7 +136,7 @@ requirement of having range proofs attached to UTXOs, which require that the val This argument is independent of the nature of the additional restrictions. Specifically, if these restrictions are manifested as a script that provides additional constraints over whether a UTXO may be spent, the same arguments apply. -This means that in a very hand-wavy sort of way, there ought to be no reason that Tari Script is not workable. +This means that in a very hand-wavy sort of way, there ought to be no reason that TariScript is not workable. Note that range proofs can be discarded after a UTXO is spent. This entails that the global security guarantees of Mimblewimble are not that every transaction in history was valid from an inflation perspective, but that the net effect @@ -150,7 +150,7 @@ chain synchronisation in pruned mode. But if there was a steady inflation bug due to invalid range proofs making it into the blockchain, a pruned mode sync would still detect that _something_ was awry, because the global coin supply balance acts as another check. -With Tari Script, once the script has been pruned away, and then there is a re-org to an earlier point on the chain, +With TariScript, once the script has been pruned away, and then there is a re-org to an earlier point on the chain, then there's no way to ensure that the script was honoured unless you run an archival node. This is broadly in keeping with the Mimblewimble security guarantees that, in pruned-mode synchronisation, individual @@ -158,7 +158,7 @@ transactions are not necessarily verified during chain synchronisation. However, the guarantee that no additional coins are created or destroyed remains intact. -Put another way, the blockchain relies on the network _at the time_ to enforce the Tari Script spending rules. +Put another way, the blockchain relies on the network _at the time_ to enforce the TariScript spending rules. This means that the scheme may be susceptible to certain _horizon attacks_. Incidentally, a single honest archival node would be able to detect any fraud on the same chain and provide a simple @@ -181,23 +181,23 @@ The next section discusses the specific proposals for achieving these requiremen Please refer to [Notation](#notation), which provides important pre-knowledge for the remainder of the report. -At a high level, Tari Script works as follows: +At a high level, TariScript works as follows: * The spending _script_ \\((\script)\\) is recorded in the transaction UTXO. -* UTXOs also define a new, _script offset public key_ \\((K\_{O})\\). +* UTXOs also define a new, _[sender offset] public key_ \\((K\_{O})\\). * After the _script_ \\((\script)\\) is executed, the execution stack must contain exactly one value that will be interpreted as the - _script public key_ \\((K\_{S})\\). One can prove ownership of a UTXO by demonstrating knowledge of both the commitment _blinding - factor_ \\((k\\)), _and_ the _script private key_ \\((k_\{S})\\). + _[script public key]_ \\((K\_{S})\\). One can prove ownership of a UTXO by demonstrating knowledge of both the commitment _blinding + factor_ \\((k\\)), _and_ the _[script private key]_ \\((k_\{S})\\). * The _script private key_ \\((k_\{S})\\), commitment _blinding factor_ \\((k)\\) and commitment _value_ \\((v)\\) signs the _script input data_ \\((\input)\\). -* The _script offset private keys_ \\((k\_{O})\\) and _script private keys_ \\((k\_{S})\\) are used in conjunction to create a _script +* The _sender offset private keys_ \\((k\_{O})\\) and _script private keys_ \\((k\_{S})\\) are used in conjunction to create a _script offset_ \\((\so)\\), which are used in the consensus balance to prevent a number of attacks. ### UTXO data commitments -The script, as well as other UTXO metadata, such as the output features are signed for with the script offset key to -prevent malleability. As we will describe later, the notion of a script offset is introduced to prevent cut-through -and forces the preservation of these commitments until they are recorded into the blockchain. +The script, as well as other UTXO metadata, such as the output features are signed for with the [sender offset] private +key to prevent malleability. As we will describe later, the notion of a [script offset] is introduced to prevent +cut-through and forces the preservation of these commitments until they are recorded into the blockchain. There are two changes to the protocol data structures that must be made to allow this scheme to work. @@ -219,9 +219,9 @@ pub struct TransactionOutput { } ``` -_Note:_ Currently, the output features are actually malleable. Tari Script fixes this. +_Note:_ Currently, the output features are actually malleable. TariScript fixes this. -Under Tari Script, this definition changes to accommodate the script and the script offset public keys: +Under TariScript, this definition changes to accommodate the script and the [sender offset] public keys: ```rust,ignore pub struct TransactionOutput { @@ -233,9 +233,9 @@ pub struct TransactionOutput { proof: RangeProof, /// The serialised script script: Vec, - /// The script offset pubkey, K_O - script_offset_public_key: PublicKey - /// UTXO signature with the script offset private key, k_O + /// The sender offset pubkey, K_O + sender_offset_public_key: PublicKey + /// UTXO signature with the sender offset private key, k_O sender_metadata_signature : Signature } ``` @@ -249,7 +249,7 @@ C_i = v_i \cdot H + k_i \cdot G \tag{1} $$ -The sender signature signs the metadata of the UTXO with the script offset private key \\( k_{Oi} \\) and this stops +The [sender metadata signature] signs the metadata of the UTXO with the sender offset private key \\( k_{Oi} \\) and this stops malleability of the UTXO metadata. $$ @@ -263,7 +263,7 @@ $$ Note that: * The UTXO has a positive value `v` like any normal UTXO. * The script and the output features can no longer be changed by the miner or any other party. Once mined, the owner can - also no longer change the script or output features without invalidating the meta data signature. + also no longer change the script or output features without invalidating the sender meta data signature. * We provide the complete script on the output. ### Transaction input changes @@ -294,18 +294,18 @@ pub struct TransactionInput { script: Vec, /// The script input data, if any input_data: Vec, - /// Signature signing the script, input data, public script key and the homomorphic commitment with a combination - /// of the homomorphic commitment private values (amount and blinding factor) and the private script key. + /// Signature signing the script, input data, [script public key] and the homomorphic commitment with a combination + /// of the homomorphic commitment private values (amount and blinding factor) and the [script private key]. script_signature: CommitmentSignature, - /// The script offset pubkey, K_O - script_offset_public_key: PublicKey + /// The sender offset pubkey, K_O + sender_offset_public_key: PublicKey } ``` The `script_signature` is an aggregated Schnorr signature signed with a combination of the homomorphic commitment -private values \\( (v\_i \\, , \\, k\_i )\\) and private script key \\(k\_{Si}\\) to prove ownership of thereof, see +private values \\( (v\_i \\, , \\, k\_i )\\) and [script private key] \\(k\_{Si}\\) to prove ownership of thereof, see [Signature on Commitment values] by F. Zhang et. al. and [Commitment Signature] by G. Yu. It signs the script, the -script input, public script key and the commitment: +script input, [script public key] and the commitment: $$ \begin{aligned} @@ -332,14 +332,16 @@ a_{Si} \cdot H + b_{Si} \cdot G = R_{Si} + (C_i+K_{Si})e \tag{5} $$ -This signature ensures that only the owner can provide the input data \\(\input_i\\) to the TransactionInput. - +The script public key \\(K\_{Si}\\) needed for the script signature verification is not stored with the TransactionInput, +but obtained by executing the script with the provided input data. Because this signature is signed with the script +private key \\(k\_{Si}\\), it ensures that only the owner can provide the input data \\(\input_i\\) to the +TransactionInput. ### Script Offset -For every transaction an accompanying script offset \\( \so \\) needs to be provided. This is there to prove that every -public script key \\( K\_{Sj} \\) and every public script offset key \\( K\_{Oi} \\) supplied with the UTXOs are the -correct ones. The sender will know and provide script offset private keys \\(k_{Oi} \\) and script private keys +For every transaction an accompanying [script offset] \\( \so \\) needs to be provided. This is there to prove that every +script public key \\( K\_{Sj} \\) and every sender offset public key \\( K\_{Oi} \\) supplied with the UTXOs are the +correct ones. The sender will know and provide sender offset private keys \\(k_{Oi} \\) and script private keys \\(k_{Si} \\); these are combined to create the script offset \\( \so \\), which is calculated as follows: $$ @@ -370,7 +372,7 @@ pub struct Transaction { } ``` -All script offsets (\\(\so\\)) from (6) contained in a block is summed together to create a total script offset (8) +All script offsets (\\(\so\\)) from (6) contained in a block is summed together to create a total [script offset] (8) so that algorithm (6) still holds for a block. $$ @@ -392,7 +394,7 @@ $$ As can be seen all information required to verify (8) is contained in a block's inputs and outputs. One important distinction to make is that the Coinbase output in a coinbase transaction does not count towards the script offset. This is because the Coinbase UTXO already has special rules accompanying it and it has no input, thus we cannot generate a -script offset \\( \so \\). The coinbase output can allow any script \\(\script_i\\) and script offset public key +script offset \\( \so \\). The coinbase output can allow any script \\(\script_i\\) and sender offset public key \\( K\_{Oi} \\) as long as it does not break any of the rules in [RFC 120](RFC-0120_Consensus.md) and the script is honored at spend. If the coinbase is used as in input, it is treated exactly the same as any other input. @@ -410,16 +412,16 @@ pub struct BlockHeader { This notion of the script offset \\(\so\\) means that the no third party can remove any input or output from a transaction or the block, as that will invalidate the script offset balance equation, either (7) or (9) depending on whether the scope is a transaction or block. It is important to know that this also stops -[cut‑through](#cut-through) so that we can verify all spent UTXO scripts. Because the private script key and private -script offset key is not publicly known, its impossible to create a new script offset. +[cut‑through](#cut-through) so that we can verify all spent UTXO scripts. Because the script private key and +sender offset private key is not publicly known, its impossible to create a new script offset. Certain scripts may allow more than one valid set of input data. Users might be led to believe that this will allow a third party to change the script keypair \\((k\_{Si}\\),\\(K\_{Si})\\). If an attacker can change the \\(K\_{Si}\\) keys of the input then he can take control of the \\(K\_{Oi}\\) as well, allowing the attacker to change the metadata of -the UTXO including the script. But as shown in [Script Offset security](#script-offset-security), this is not possible. +the UTXO including the script. But as shown in [Script offset security](#script-offset-security), this is not possible. If equation (7) or (9) balances then we know that every included input and output in the transaction or block has its -correct public script key and public script offset key. Signatures (2) & (3) are checked independently from script +correct script public key and sender offset public key. Signatures (2) & (3) are checked independently from script offset verification (7) and (9), and looked at in isolation those could verify correctly but can still be signed by fake keys. When doing verification in (7) and (9) you know that the signatures and the message/metadata signed by the private keys can be trusted. @@ -430,12 +432,12 @@ The Mimblewimble balance for blocks and transactions stays the same. In addition to the changes given above, there are consensus rule changes for transaction and block validation. -For every valid transaction or block, +Verify that for every valid transaction or block: -1. Check the sender signature \\(s\_{Mi}\\) is valid for every output. +1. The [sender metadata signature] \\(s\_{Mi}\\) is valid for every output. 2. The script executes successfully using the given input script data. -3. The result of the script is a valid public key, \\( K\_S \\). -4. The script signature, \\( s\_{Si} \\) is valid for every input. +3. The result of the script is a valid script public key, \\( K\_S \\). +4. The script signature, \\( s\_{Si} \\), is valid for every input. 5. The script offset is valid for every transaction and block. ## Examples @@ -457,7 +459,7 @@ To spend \\( C\_a \\), she provides: * A valid script signature, \\( (a_{Sa}, b_{Sa}, R_{Sa}) \\) as per (3),(4) proving that she owns the commitment \\( C\_a \\), knows the private key, \\( k_{Sa} \\), corresponding to \\( K_{Sa} \\), the public key left on the stack after executing \\( \script_a \\) with \\( \input_a \\). -* An script offset public key, \\( K_{Ob} \\). +* A sender offset public key, \\( K_{Ob} \\). * The script offset, \\( \so\\) with: $$ \begin{aligned} @@ -475,7 +477,7 @@ Because Alice is creating the transaction, she has a final say over the script \ [bitcoin transaction], but Bob can also opt to send Alice a script \\(\script\_b\\) of his choosing. If Bob did not send a script, she chooses something akin to a `NOP` script for the script \\(\script\_b\\). -Alice calculates the sender signature \\( s_{Mb} \\) with: +Alice calculates the [sender metadata signature] \\( s_{Mb} \\) with: $$ \begin{aligned} @@ -487,7 +489,7 @@ $$ Alice then adds the following info to Bob's TransactionOutput: * Meta_signature \\(s_{Mb}\\), -* Script offset public key \\( K_{Ob} \\), +* sender offset public key \\( K_{Ob} \\), She completes the transaction as per [standard Mimblewimble protocol] and also adds the script offset \\( \so \\), after which she broadcasts the transaction to the network. @@ -499,7 +501,7 @@ Base nodes validate the transaction as follows: * They check that the usual Mimblewimble balance holds by summing inputs and outputs and validating against the excess signature. This check does not change nor do the other validation rules, such as confirming that all inputs are in the UTXO set etc. -* The sender signature \\(s_{Ma}\\) on Bob's output, +* The sender metadata signature \\(s_{Ma}\\) on Bob's output, * The input script must execute successfully using the provided input data; and the script result must be a valid public key, * The script signature on Alice's input is valid by checking: @@ -517,9 +519,9 @@ Base nodes validate the transaction as follows: \tag{14} $$ -Finally, when Bob spends this output, he will use \\( K\_{Sb} \\) as his script input and sign it with his private key -\\( k\_{Sb} \\). He will choose a new \\( K\_{Oc} \\) to give to the recipient, and he will construct the -script offset, \\( \so_b \\) as follows: +Finally, when Bob spends this output, he will use \\( K\_{Sb} \\) as his script input and sign it with his script +private key \\( k\_{Sb} \\). He will choose a new sender offset public key \\( K\_{Oc} \\) to give to the recipient, and +he will construct the script offset, \\( \so_b \\) as follows: $$ \begin{aligned} @@ -548,9 +550,9 @@ Bob requires the value \\( v_b \\) and blinding factor \\( k_b \\) to claim his claim it without asking Alice for them. This information can be obtained by using Diffie-Hellman and Bulletproof rewinding. If the blinding factor \\( k\_b \\) -was calculated with Diffie-Hellman using the script offset keypair, (\\( k\_{Ob} \\),\\( K\_{Ob} \\)) as the sender keypair -and the script keypair, \\( (k\_{Sb} \\),\\( K\_{Sb}) \\) as the receiver keypair, the blinding factor \\( k\_b \\) -can be securely calculated without communication. +was calculated with Diffie-Hellman using the sender offset keypair, (\\( k\_{Ob} \\),\\( K\_{Ob} \\)) as the sender +keypair and the script keypair, \\( (k\_{Sb} \\),\\( K\_{Sb}) \\) as the receiver keypair, the blinding factor +\\( k\_b \\) can be securely calculated without communication. Alice uses Bob's public key to create a shared secret, \\( k\_b \\) for the output commitment, \\( C\_b \\), using Diffie-Hellman key exchange. @@ -570,7 +572,7 @@ key. Alice knows the script-redeeming private key \\( k_{Sa}\\) for the transaction input. -Alice will create the entire transaction including, generating a new script offset keypair and calculating the +Alice will create the entire transaction including, generating a new sender offset keypair and calculating the script offset, $$ @@ -620,9 +622,8 @@ To summarise, the information required for one-sided transactions is as follows: | features | \\( F_a \\) | Public | | script | \\( \alpha_a \\) | Public | | script input | \\( \input_a \\) | Public | -| height | \\( h_a \\) | Public | | script signature | \\( a_{Sa},b_{Sa}, R_{Sa} \\) | Alice knows \\( k_{Sa},\\, r_{Sa} \\) and \\( k_{a},\\, v_{a} \\) of the commitment \\(C_a\\) | -| script offset public key | \\( K_{Oa} \\) | Not used in this transaction | +| sender offset public key | \\( K_{Oa} \\) | Not used in this transaction | | Transaction output | Symbols | Knowledge | |---------------------------|---------------------------------------|------------------------------------------------------------| @@ -630,7 +631,7 @@ To summarise, the information required for one-sided transactions is as follows: | features | \\( F_b \\) | Public | | script | \\( \script_b \\) | Script is public. Only Bob knows the correct script input. | | range proof | | Alice and Bob know opening parameters | -| script offset public key | \\( K_{Ob} \\) | Alice knows \\( k_{Ob} \\) | +| sender offset public key | \\( K_{Ob} \\) | Alice knows \\( k_{Ob} \\) | | sender metadata signature | \\( s_{Mb}, R_{Mb} \\) | Alice knows \\( k_{Ob} \\) and the metadata) | @@ -652,11 +653,11 @@ C_a \Rightarrow C_s \Rightarrow C_x $$ Alice owns \\( C_a\\), so she knows the blinding factor \\( k_a\\) and the correct input for the script's spending -conditions. Alice also generates the script offset keypair, \\( (k_{Os}, K_{Os} )\\). +conditions. Alice also generates the sender offset keypair, \\( (k_{Os}, K_{Os} )\\). Now Alice and Bob proceed with the standard transaction flow. -Alice ensures that the script offset public key \\( K_{Os}\\) is part of the output metadata that contains commitment +Alice ensures that the sender offset public key \\( K_{Os}\\) is part of the output metadata that contains commitment \\( C_s\\). Alice will fill in the script with her \\( k_{Sa}\\) to unlock the commitment \\( C_a\\). Because Alice owns \\( C_a\\) she needs to construct \\( \so\\) with: @@ -689,7 +690,7 @@ The script input needs to be signed by this aggregate key, and so Alice and Bob following the usual Schnorr aggregate mechanics, but one person needs to add in the signature of the blinding factor and value. -In an analogous fashion, Alice and Bob also generate an aggregate script offset private key \\( k_{Ox}\\), each using +In an analogous fashion, Alice and Bob also generate an aggregate sender offset private key \\( k_{Ox}\\), each using their own \\( k_{OxA} \\) and \\( k_{OxB}\\). To be specific, Alice calculates her portion from @@ -724,7 +725,7 @@ Notice also that because the script resolves to an aggregate key \\( K_s\\) neit commitment \\( C_s\\) without the other party's key. If either party tries to cheat by editing the input, the script validation will fail. -If either party tries to cheat by creating a new output, the script offset will not validate correctly as the script offset locks +If either party tries to cheat by creating a new output, the script offset will not validate correctly as it locks the output of the transaction. A base node validating the transaction will also not be able to tell this is an aggregate transaction as all keys are @@ -740,9 +741,8 @@ To summarise, the information required for creating a multiparty UTXO is as foll | features | \\( F_a \\) | Public | | script | \\( \alpha_a \\) | Public | | script input | \\( \input_a \\) | Public | -| height | \\( h_a \\) | Public | | script signature | \\( (a_{Sa},b_{Sa}, R_{Sa}) \\) | Alice knows \\( k_{Sa},\\, r_{Sa} \\) and \\( k_{a},\\, v_{a} \\) of the commitment \\(C_a\\) | -| script offset public key | \\( K_{Oa} \\) | Not used in this transaction | +| sender offset public key | \\( K_{Oa} \\) | Not used in this transaction |
@@ -752,7 +752,7 @@ To summarise, the information required for creating a multiparty UTXO is as foll | features | \\( F_s \\) | Public | | script | \\( \script_s \\) | Script is public. Alice and Bob only knows their part of the correct script input. | | range proof | | Alice and Bob know opening parameters | -| script offset public key | \\( K_{Os} = K_{OsA} + K_{OsB}\\) | Alice knows \\( k_{OsA} \\), Bob knows \\( k_{OsB} \\). Neither party knows \\( k_{Os} \\) | +| sender offset public key | \\( K_{Os} = K_{OsA} + K_{OsB}\\) | Alice knows \\( k_{OsA} \\), Bob knows \\( k_{OsB} \\). Neither party knows \\( k_{Os} \\) | | sender signature | \\( s_{Ms} = s_{MsA} + s_{MsB}, \\, \\, R_{Ss} = R_{SsA} + R_{SsB} \\) | Alice knows \\( (s_{MsA}, R_{SsA}) \\), Bob knows \\( (s_{MsB}, R_{SsB}) \\) | When spending the multi-party input: @@ -763,9 +763,8 @@ When spending the multi-party input: | features | \\( F_s \\) | Public | | script | \\( \alpha_s \\) | Public | | script input | \\( \input_s \\) | Public | -| height | \\( h_a \\) | Public | | script signature | \\( (a_{Ss} ,b_{Ss} , R_{Ss}) \\) | Alice knows \\( (k_{SsA},\\, r_{SsA}) \\), Bob knows \\( (k_{SsB},\\, r_{SsB}) \\). Both parties know \\( (k_{s},\\, v_{s}) \\). Neither party knows \\( k_{Ss}\\) | -| script offset public key | \\( K_{Os} \\) | As above, Alice and Bob each know part of the script offset key | +| sender offset public key | \\( K_{Os} \\) | As above, Alice and Bob each know part of the sender offset key | ### Cut-through @@ -775,10 +774,10 @@ spent in the same block it was created. This makes it so that the intervening UT and balances carried in that UTXO. It's also impossible to prove without additional information that cut-through even occurred (though one may suspect, since the "one" transaction would contribute two kernels to the block). -In particular, cut-through is devastating for an idea like Tari Script which relies on conditions present in the UTXO +In particular, cut-through is devastating for an idea like TariScript which relies on conditions present in the UTXO being enforced. -This is a reason for the presence of the script offset in the Tari Script proposal. It mathematically links all inputs +This is a reason for the presence of the script offset in the TariScript proposal. It mathematically links all inputs and outputs of all the transactions in a block and that tallied up to create the script offset. Providing the script offset requires knowledge of keys that miners do not possess; thus they are unable to produce the necessary script offset when attempting to perform cut-through on a pair of transactions. @@ -821,10 +820,10 @@ $$ \end{aligned} $$ -A third party cannot generate a new script offset as only the original owner can provide the private script key \\(k\_{Sa}\\) +A third party cannot generate a new script offset as only the original owner can provide the script private key \\(k\_{Sa}\\) to create a new script offset. -### Script Offset security +### Script offset security If all the inputs in a transaction or a block contain scripts such as just `NOP` or `CompareHeight` commands, then the hypothesis is that it is possible to recreate a false script offset. Lets show by example why this is not possible. In @@ -836,10 +835,10 @@ $$ Alice has an output \\(C\_{a}\\) which contains a script that only has a `NOP` command in it. This means that the script \\( \script\_a \\) will immediately exit on execution leaving the entire input data \\( \input\_a \\)on the stack. She sends all the required information to Bob as per the [standard mw transaction](#standard-mw-transaction), who -creates an output \\(C\_{b}\\). Because of the `NOP` script \\( \script\_a \\), Bob can change the public script key +creates an output \\(C\_{b}\\). Because of the `NOP` script \\( \script\_a \\), Bob can change the script public key \\( K\_{Sa}\\) contained in the input data. Bob can now use his own \\(k'\_{Sa}\\) as the script private key. He -replaces the script offset public key with his own \\(K'\_{Ob}\\) allowing him to change the script -\\( \script\_b \\) and generate a new signature as in (2). Bob cab now generate a new script offset with +replaces the sender offset public key with his own \\(K'\_{Ob}\\) allowing him to change the script +\\( \script\_b \\) and generate a new signature as in (2). Bob can now generate a new script offset with \\(\so' = k'\_{Sa} - k'\_{Ob} \\). Up to this point, it all seems valid. No one can detect that Bob changed the script to \\( \script\_b \\). @@ -853,14 +852,14 @@ Mimblewimble terms is the person who knows \\( k\_a, v\_a\\), can generate the ### Script lock key generation At face value, it looks like the burden for wallets has tripled, since each UTXO owner has to remember three private -keys, the spend key, \\( k_i \\), the script offset key \\( k_{O} \\) and the script key \\( k_{S} \\). In practice, the script -key will often be a static key associated with the user's node or wallet. Even if it is not, the script and script offset keys -can be deterministically derived from the spend key. For example, \\( k_{S} \\) could be +keys, the spend key, \\( k_i \\), the sender offset key \\( k_{O} \\) and the script key \\( k_{S} \\). In practice, the +script key will often be a static key associated with the user's node or wallet. Even if it is not, the script and +sender offset keys can be deterministically derived from the spend key. For example, \\( k_{S} \\) could be \\( \hash{ k_i \cat \alpha} \\). ### Blockchain bloat -The most obvious drawback to Tari Script is the effect it will have on blockchain size. UTXOs are substantially larger, +The most obvious drawback to TariScript is the effect it will have on blockchain size. UTXOs are substantially larger, with the addition of the script, script signature, and a public key to every output. These can eventually be pruned, but will increase storage and bandwidth requirements. @@ -874,7 +873,7 @@ Every header will also be bigger as it includes an extra blinding factor that wi ### Fodder for chain analysis -Another potential drawback of Tari Script is the additional information that is handed to entities wishing to perform +Another potential drawback of TariScript is the additional information that is handed to entities wishing to perform chain analysis. Having scripts attached to outputs will often clearly mark the purpose of that UTXO. Users may wish to re-spend outputs into vanilla, default UTXOs in a mixing transaction to disassociate Tari funds from a particular script. @@ -883,7 +882,7 @@ script. Where possible, the "usual" notation is used to denote terms commonly found in cryptocurrency literature. Lower case characters are used as private keys, while uppercase characters are used as public keys. New terms -introduced by Tari Script are assigned greek lowercase letters in most cases. The capital letter subscripts, _R_ and _S_ +introduced by TariScript are assigned greek lowercase letters in most cases. The capital letter subscripts, _R_ and _S_ refer to a UTXO _receiver_ and _script_ respectively. | Symbol | Definition | @@ -892,23 +891,23 @@ refer to a UTXO _receiver_ and _script_ respectively. | \\( F_i \\) | Output features for UTXO _i_. | | \\( f_t \\) | Transaction fee for transaction _t_. | | \\( m_t \\) | Metadata for transaction _t_. Currently this includes the lock height. | -| \\( (k_{Oi}\, K_{Oi}) \\) | The private - public keypair for the UTXO script offset key. | +| \\( (k_{Oi}\, K_{Oi}) \\) | The private - public keypair for the UTXO sender offset key. | | \\( (k_{Si}\, K_{Si}) \\) | The private - public keypair for the script key. The script, \\( \script_i \\) resolves to \\( K_S \\) after completing execution. | | \\( \so_t \\) | The script offset for transaction _t_, as \\( \so_t = \sum_j{ k_{Sjt}} - \sum_i{k_{Oit}}\\) | | \\( C_i \\) | A Pedersen commitment to a value \\( v_i \\), as \\( C_i = k_i \cdot{G} + v_i \cdot H \\) | | \\( \input_i \\) | The serialised input for script \\( \script_i \\) | | \\( s_{Si} \\) | A script signature for output \\( i \\), as \\( s_{Si} = (a_{Si}, b_{Si}, R_{Si} ) = (r_{Si_a} + e(v_{i})), (r_{Si_b} + e(k_{Si}+k_i)) \\; \text{where} \\; e = \hash{ R_{Si} \cat \script_i \cat \input_i \cat K_{Si} \cat C_i} \\) | -| \\( s_{Mi} \\) | A sender signature for output \\( i \\), as \\( s_{Mi} = r_{Mi} + k_{Oi}\hash{ \script_i \cat F_i \cat R_{Mi} } \\) | +| \\( s_{Mi} \\) | A sender metadata signature for output \\( i \\), as \\( s_{Mi} = r_{Mi} + k_{Oi}\hash{ \script_i \cat F_i \cat R_{Mi} } \\) | ## Extensions ### Covenants -Tari Script places restrictions on _who_ can spend UTXOs. It will also be useful for Tari digital asset applications to +TariScript places restrictions on _who_ can spend UTXOs. It will also be useful for Tari digital asset applications to restrict _how_ or _where_ UTXOs may be spent in some cases. The general term for these sorts of restrictions are termed _covenants_. The [Handshake white paper] has a fairly good description of how covenants work. -It is beyond the scope of this RFC, but it's anticipated that Tari Script would play a key role in the introduction of +It is beyond the scope of this RFC, but it's anticipated that TariScript would play a key role in the introduction of generalised covenant support into Tari. ### Lock-time malleability @@ -916,7 +915,7 @@ generalised covenant support into Tari. The current Tari protocol has an issue with Transaction Output Maturity malleability. This output feature is enforced in the consensus rules, but it is actually possible for a miner to change the value without invalidating the transaction. -With Tari Script, output features are properly committed to in the transaction and verified as part of the script offset +With TariScript, output features are properly committed to in the transaction and verified as part of the script offset validation. ### Credits @@ -937,3 +936,10 @@ Thanks to David Burkett for proposing a method to prevent cut-through and willin [cut-through]: https://tlu.tarilabs.com/protocols/grin-protocol-overview/MainReport.html#cut-through [standard Mimblewimble protocol]: https://tlu.tarilabs.com/protocols/mimblewimble-1/MainReport.html [bitcoin transaction]: https://en.bitcoin.it/wiki/Transaction + +[TariScript]: Glossary.md#tariscript +[sender metadata signature]: Glossary.md#sender-metadata-signature +[script private key]: Glossary.md#script-keypair +[script public key]: Glossary.md#script-keypair +[sender offset]: Glossary.md#sender-offset-keypair +[script offset]: Glossary.md#script-offset \ No newline at end of file diff --git a/RFC/src/RFC-0202_TariScriptOpcodes.md b/RFC/src/RFC-0202_TariScriptOpcodes.md index d2bc0ec774..0cff3054b1 100644 --- a/RFC/src/RFC-0202_TariScriptOpcodes.md +++ b/RFC/src/RFC-0202_TariScriptOpcodes.md @@ -1,6 +1,6 @@ # RFC-0202/TariScriptOpcodes -## Tari Script Opcodes +## TariScript Opcodes ![status: draft](theme/images/status-draft.svg) @@ -61,11 +61,11 @@ examples and applicaitons. ## Introduction -## Tari Script semantics +## TariScript semantics The proposal for TariScript is straightforward. It is based on Bitcoin script and inherits most of its ideas. -The main properties of Tari script are +The main properties of [TariScript] are * The scripting language is stack-based. At redeem time, the UTXO spender must supply an input stack. The script runs by operating on the stack contents. @@ -74,7 +74,7 @@ The main properties of Tari script are on the stack. The script fails if the stack is empty, or contains more than one element, or aborts early. * It is not Turing complete, so there are no loops or timing functions. * The opcodes enforce type safety. e.g. A public key cannot be added to an integer scalar. Errors of this kind MUST cause - the script to fail. The Rust implementation of Tari Script automatically applies the type safety rules. + the script to fail. The Rust implementation of [TariScript] automatically applies the type safety rules. ### Failure modes @@ -112,7 +112,7 @@ The full list of [Error codes](#error-codes) is given below. ## Opcodes -Tari Script opcodes range from 0 to 255 and are represented as a single unsigned byte. The opcode set is +[TariScript] opcodes range from 0 to 255 and are represented as a single unsigned byte. The opcode set is limited to allow for the applications specified in this RFC, but can be expanded in the future. ### Block height checks @@ -348,7 +348,7 @@ If `ELSE` is encountered, instructions are executed until `ENDIF` is reached. ## Serialisation -Tari Script and the execution stack are serialised into byte strings using a simple linear parser. Since all opcodes are +TariScript and the execution stack are serialised into byte strings using a simple linear parser. Since all opcodes are a single byte, it's very easy to read and write script byte strings. If an opcode has a parameter associated with it, e.g. `PushHash` then it is equally known how many bytes following the opcode will contain the parameter. @@ -570,3 +570,5 @@ or Bob can spend the output. Thanks to [@philipr-za](https://github.com/philipr-za) and [@SWvheerden](https://github.com/SWvheerden) for their input and contributions to this RFC. + +[TariScript]: Glossary.md#tariscript diff --git a/applications/tari_app_grpc/Cargo.toml b/applications/tari_app_grpc/Cargo.toml index 1dcbaf5b01..8155e216d1 100644 --- a/applications/tari_app_grpc/Cargo.toml +++ b/applications/tari_app_grpc/Cargo.toml @@ -11,7 +11,7 @@ edition = "2018" tari_common_types = { version = "^0.8", path = "../../base_layer/common_types"} tari_core = { path = "../../base_layer/core"} tari_wallet = { path = "../../base_layer/wallet"} -tari_crypto = { git = "ssh://git@github.com/tari-project/tari-crypto.git", branch = "main" } +tari_crypto = { git = "ssh://git@github.com/tari-project/tari-crypto.git", branch = "main" } #switch back to official after merge tari_comms = { path = "../../comms"} chrono = "0.4.6" diff --git a/applications/tari_app_grpc/proto/types.proto b/applications/tari_app_grpc/proto/types.proto index 993791d2e0..4405594ac5 100644 --- a/applications/tari_app_grpc/proto/types.proto +++ b/applications/tari_app_grpc/proto/types.proto @@ -44,23 +44,25 @@ message BlockHeader { // This is the UTXO merkle root of the outputs // This is calculated as Hash (txo MMR root || roaring bitmap hash of UTXO indices) bytes output_mr = 6; - // This is the MMR root of the range proofs - bytes range_proof_mr = 7; + // This is the MMR root of the the output witness data + bytes witness_mr = 7; // This is the MMR root of the kernels bytes kernel_mr = 8; + // This is the Merkle root of the inputs in this block + bytes input_mr = 9; // Total accumulated sum of kernel offsets since genesis block. We can derive the kernel offset sum for *this* // block from the total kernel offset of the previous block header. - bytes total_kernel_offset = 9; + bytes total_kernel_offset = 10; // Nonce increment used to mine this block. - uint64 nonce = 10; + uint64 nonce = 11; // Proof of work metadata - ProofOfWork pow = 11; + ProofOfWork pow = 12; // Kernel MMR size - uint64 kernel_mmr_size = 12; + uint64 kernel_mmr_size = 13; // Output MMR size - uint64 output_mmr_size = 13; + uint64 output_mmr_size = 14; // Sum of script offsets for all kernels in this block. - bytes total_script_offset = 14; + bytes total_script_offset = 15; } // Metadata required for validating the Proof of Work calculation @@ -102,7 +104,7 @@ message HistoricalBlock { } -// The NewBlockHeaderTemplate is used for the construction of a new mineable block. It contains all the metadata for the block that the Base Node is able to complete on behalf of a Miner. +// The NewBlockHeaderTemplate is used for the construction of a new mine-able block. It contains all the metadata for the block that the Base Node is able to complete on behalf of a Miner. message NewBlockHeaderTemplate { // Version of the block uint32 version = 1; @@ -164,11 +166,9 @@ message TransactionInput { bytes script = 4; // The script input data, if any bytes input_data = 5; - // The block height that the UTXO was mined - uint64 height = 6; // A signature with k_s, signing the script, input data, and mined height - Signature script_signature = 7; - // The offset pubkey, K_O + ComSignature script_signature = 7; + // The offset public key, K_O bytes script_offset_public_key = 8; } @@ -186,7 +186,7 @@ message TransactionOutput { bytes hash = 4; // Tari script serialised script bytes script = 5; - // Tari script offset pubkey, K_O + // Tari script offset public key, K_O bytes script_offset_public_key = 6; // UTXO signature with the script offset private key, k_O Signature sender_metadata_signature = 7; @@ -227,6 +227,14 @@ message Signature { bytes signature = 2; } +// Define the explicit ComSignature implementation for the Tari base layer. A different signature scheme can be +// employed by redefining this type. +message ComSignature { + bytes public_nonce_commitment = 1; + bytes signature_u = 2; + bytes signature_v = 3; +} + /// Consensus Constants response message ConsensusConstants { /// The min height maturity a coinbase utxo must have @@ -308,8 +316,6 @@ message UnblindedOutput { bytes script = 4; // Tari script input data for spending bytes input_data = 5; - // The block height that the UTXO was mined - uint64 height = 6; // Tari script private key bytes script_private_key = 7; // Tari script offset pubkey, K_O diff --git a/applications/tari_app_grpc/src/blocks.rs b/applications/tari_app_grpc/src/blocks.rs deleted file mode 100644 index fdf9f9a8ff..0000000000 --- a/applications/tari_app_grpc/src/blocks.rs +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2020. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::tari_grpc::base_node_grpc as grpc; -use prost_types::Timestamp; -use std::convert::{TryFrom, TryInto}; -use tari_core::{ - blocks::{Block, BlockHeader, NewBlockHeaderTemplate, NewBlockTemplate}, - chain_storage::HistoricalBlock, - proof_of_work::{Difficulty, PowAlgorithm, ProofOfWork}, - transactions::types::BlindingFactor, -}; -use tari_crypto::tari_utilities::{epoch_time::EpochTime, ByteArray, Hashable}; - -/// Utility function that converts a `chrono::DateTime` to a `prost::Timestamp` -pub fn datetime_to_timestamp(datetime: EpochTime) -> Timestamp { - Timestamp { - seconds: datetime.as_u64() as i64, - nanos: 0, - } -} - -pub(crate) fn timestamp_to_datetime(timestamp: Timestamp) -> EpochTime { - (timestamp.seconds as u64).into() -} - -impl From for grpc::HistoricalBlock { - fn from(hb: HistoricalBlock) -> Self { - Self { - confirmations: hb.confirmations, - spent_commitments: hb.spent_commitments.iter().map(|c| Vec::from(c.as_bytes())).collect(), - block: Some(hb.block.into()), - } - } -} - -impl From for grpc::Block { - fn from(block: Block) -> Self { - Self { - body: Some(grpc::AggregateBody { - inputs: block - .body - .inputs() - .iter() - .map(|input| grpc::TransactionInput { - features: Some(grpc::OutputFeatures { - flags: input.features.flags.bits() as u32, - maturity: input.features.maturity, - }), - commitment: Vec::from(input.commitment.as_bytes()), - }) - .collect(), - outputs: block - .body - .outputs() - .iter() - .map(|output| grpc::TransactionOutput { - features: Some(grpc::OutputFeatures { - flags: output.features.flags.bits() as u32, - maturity: output.features.maturity, - }), - commitment: Vec::from(output.commitment.as_bytes()), - range_proof: Vec::from(output.proof.as_bytes()), - hash: Vec::from(output.hash.as_bytes()), - script: Vec::from(output.script.as_bytes()), - script_offset_public_key: Vec::from(output.script_offset_public_key.as_bytes()), - sender_metadata_signature: Some(grpc::Signature { - public_nonce: Vec::from(output.sender_metadata_signature.get_public_nonce().as_bytes()), - signature: Vec::from(output.sender_metadata_signature.get_signature().as_bytes()), - }), - }) - .collect(), - kernels: block - .body - .kernels() - .iter() - .map(|kernel| grpc::TransactionKernel { - features: kernel.features.bits() as u32, - fee: kernel.fee.0, - lock_height: kernel.lock_height, - excess: Vec::from(kernel.excess.as_bytes()), - excess_sig: Some(grpc::Signature { - public_nonce: Vec::from(kernel.excess_sig.get_public_nonce().as_bytes()), - signature: Vec::from(kernel.excess_sig.get_signature().as_bytes()), - }), - }) - .collect(), - }), - header: Some(block.header.into()), - } - } -} - -impl From for grpc::BlockHeader { - fn from(h: BlockHeader) -> Self { - Self { - hash: h.hash(), - version: h.version as u32, - height: h.height, - prev_hash: h.prev_hash.clone(), - timestamp: Some(datetime_to_timestamp(h.timestamp)), - output_mr: h.output_mr.clone(), - range_proof_mr: h.range_proof_mr.clone(), - kernel_mr: h.kernel_mr.clone(), - total_kernel_offset: Vec::from(h.total_kernel_offset.as_bytes()), - nonce: h.nonce, - pow: Some(grpc::ProofOfWork { - pow_algo: match h.pow.pow_algo { - PowAlgorithm::Monero => 0, - PowAlgorithm::Blake => 1, - PowAlgorithm::Sha3 => 2, - }, - accumulated_monero_difficulty: h.pow.accumulated_monero_difficulty.into(), - accumulated_blake_difficulty: h.pow.accumulated_blake_difficulty.into(), - pow_data: h.pow.pow_data, - target_difficulty: h.pow.target_difficulty.as_u64(), - }), - } - } -} - -impl From for grpc::NewBlockTemplate { - fn from(block: NewBlockTemplate) -> Self { - let header = grpc::NewBlockHeaderTemplate { - version: block.header.version as u32, - height: block.header.height, - prev_hash: block.header.prev_hash.clone(), - total_kernel_offset: Vec::from(block.header.total_kernel_offset.as_bytes()), - pow: Some(grpc::ProofOfWork { - pow_algo: match block.header.pow.pow_algo { - PowAlgorithm::Monero => 0, - PowAlgorithm::Blake => 1, - PowAlgorithm::Sha3 => 2, - }, - accumulated_monero_difficulty: block.header.pow.accumulated_monero_difficulty.into(), - accumulated_blake_difficulty: block.header.pow.accumulated_blake_difficulty.into(), - pow_data: block.header.pow.pow_data, - target_difficulty: block.header.pow.target_difficulty.as_u64(), - }), - }; - Self { - body: Some(grpc::AggregateBody { - inputs: block - .body - .inputs() - .iter() - .map(|input| grpc::TransactionInput { - features: Some(grpc::OutputFeatures { - flags: input.features.flags.bits() as u32, - maturity: input.features.maturity, - }), - commitment: Vec::from(input.commitment.as_bytes()), - }) - .collect(), - outputs: block - .body - .outputs() - .iter() - .map(|output| grpc::TransactionOutput { - features: Some(grpc::OutputFeatures { - flags: output.features.flags.bits() as u32, - maturity: output.features.maturity, - }), - commitment: Vec::from(output.commitment.as_bytes()), - range_proof: Vec::from(output.proof.as_bytes()), - hash: Vec::from(output.hash.as_bytes()), - script: Vec::from(output.script.as_bytes()), - script_offset_public_key: Vec::from(output.script_offset_public_key.as_bytes()), - sender_metadata_signature: Some(grpc::Signature { - public_nonce: Vec::from(output.sender_metadata_signature.get_public_nonce().as_bytes()), - signature: Vec::from(output.sender_metadata_signature.get_signature().as_bytes()), - }), - }) - .collect(), - kernels: block - .body - .kernels() - .iter() - .map(|kernel| grpc::TransactionKernel { - features: kernel.features.bits() as u32, - fee: kernel.fee.0, - lock_height: kernel.lock_height, - excess: Vec::from(kernel.excess.as_bytes()), - excess_sig: Some(grpc::Signature { - public_nonce: Vec::from(kernel.excess_sig.get_public_nonce().as_bytes()), - signature: Vec::from(kernel.excess_sig.get_signature().as_bytes()), - }), - }) - .collect(), - }), - header: Some(header), - } - } -} - -impl TryFrom for Block { - type Error = String; - - fn try_from(block: grpc::Block) -> Result { - let header = block - .header - .map(TryInto::try_into) - .ok_or_else(|| "Block header not provided".to_string())??; - - let body = block - .body - .map(TryInto::try_into) - .ok_or_else(|| "Block body not provided".to_string())??; - - Ok(Self { header, body }) - } -} - -impl TryFrom for BlockHeader { - type Error = String; - - fn try_from(header: grpc::BlockHeader) -> Result { - let total_kernel_offset = - BlindingFactor::from_bytes(&header.total_kernel_offset).map_err(|err| err.to_string())?; - - let timestamp = header - .timestamp - .map(timestamp_to_datetime) - .ok_or_else(|| "timestamp not provided".to_string())?; - - let pow = match header.pow { - Some(p) => ProofOfWork::try_from(p)?, - None => return Err("No proof of work provided".into()), - }; - Ok(Self { - version: header.version as u16, - height: header.height, - prev_hash: header.prev_hash, - timestamp, - output_mr: header.output_mr, - range_proof_mr: header.range_proof_mr, - kernel_mr: header.kernel_mr, - total_kernel_offset, - nonce: header.nonce, - pow, - }) - } -} - -impl TryFrom for NewBlockTemplate { - type Error = String; - - fn try_from(block: grpc::NewBlockTemplate) -> Result { - let header = block.header.clone().ok_or_else(|| "No header provided".to_string())?; - let total_kernel_offset = - BlindingFactor::from_bytes(&header.total_kernel_offset).map_err(|err| err.to_string())?; - let pow = match header.pow { - Some(p) => ProofOfWork::try_from(p)?, - None => return Err("No proof of work provided".into()), - }; - let header = NewBlockHeaderTemplate { - version: header.version as u16, - height: header.height, - prev_hash: header.prev_hash, - total_kernel_offset, - pow, - }; - let body = block - .body - .map(TryInto::try_into) - .ok_or_else(|| "Block body not provided".to_string())??; - - Ok(Self { header, body }) - } -} - -impl TryFrom for ProofOfWork { - type Error = String; - - fn try_from(pow: grpc::ProofOfWork) -> Result { - Ok(Self { - pow_algo: PowAlgorithm::try_from(pow.pow_algo)?, - accumulated_monero_difficulty: Difficulty::from(pow.accumulated_monero_difficulty), - accumulated_blake_difficulty: Difficulty::from(pow.accumulated_blake_difficulty), - target_difficulty: Difficulty::from(pow.target_difficulty), - pow_data: pow.pow_data, - }) - } -} diff --git a/applications/tari_app_grpc/src/conversions/block_header.rs b/applications/tari_app_grpc/src/conversions/block_header.rs index d8c9d30ae8..3b660f21e1 100644 --- a/applications/tari_app_grpc/src/conversions/block_header.rs +++ b/applications/tari_app_grpc/src/conversions/block_header.rs @@ -30,22 +30,24 @@ use tari_crypto::tari_utilities::{ByteArray, Hashable}; impl From for grpc::BlockHeader { fn from(h: BlockHeader) -> Self { + let pow_algo = h.pow_algo(); Self { hash: h.hash(), version: h.version as u32, height: h.height, - prev_hash: h.prev_hash.clone(), + prev_hash: h.prev_hash, timestamp: Some(datetime_to_timestamp(h.timestamp)), - output_mr: h.output_mr.clone(), - range_proof_mr: h.range_proof_mr.clone(), + input_mr: h.input_mr, + output_mr: h.output_mr, output_mmr_size: h.output_mmr_size, - kernel_mr: h.kernel_mr.clone(), + kernel_mr: h.kernel_mr, kernel_mmr_size: h.kernel_mmr_size, - total_kernel_offset: Vec::from(h.total_kernel_offset.as_bytes()), - total_script_offset: Vec::from(h.total_script_offset.as_bytes()), + witness_mr: h.witness_mr, + total_kernel_offset: h.total_kernel_offset.to_vec(), + total_script_offset: h.total_script_offset.to_vec(), nonce: h.nonce, pow: Some(grpc::ProofOfWork { - pow_algo: h.pow_algo().as_u64(), + pow_algo: pow_algo.as_u64(), pow_data: h.pow.pow_data, }), } @@ -76,8 +78,9 @@ impl TryFrom for BlockHeader { height: header.height, prev_hash: header.prev_hash, timestamp, + input_mr: header.input_mr, output_mr: header.output_mr, - range_proof_mr: header.range_proof_mr, + witness_mr: header.witness_mr, output_mmr_size: header.output_mmr_size, kernel_mr: header.kernel_mr, kernel_mmr_size: header.kernel_mmr_size, diff --git a/applications/tari_app_grpc/src/conversions/com_signature.rs b/applications/tari_app_grpc/src/conversions/com_signature.rs new file mode 100644 index 0000000000..e10e48ffe8 --- /dev/null +++ b/applications/tari_app_grpc/src/conversions/com_signature.rs @@ -0,0 +1,42 @@ +// Copyright 2020. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::convert::TryFrom; +use tari_crypto::tari_utilities::ByteArray; + +use crate::tari_rpc as grpc; +use tari_core::transactions::types::{ComSignature, Commitment, PrivateKey}; + +impl TryFrom for ComSignature { + type Error = String; + + fn try_from(sig: grpc::ComSignature) -> Result { + let public_nonce = Commitment::from_bytes(&sig.public_nonce_commitment) + .map_err(|_| "Could not get public nonce commitment".to_string())?; + let signature_u = + PrivateKey::from_bytes(&sig.signature_u).map_err(|_| "Could not get partial signature u".to_string())?; + let signature_v = + PrivateKey::from_bytes(&sig.signature_v).map_err(|_| "Could not get partial signature v".to_string())?; + + Ok(Self::new(public_nonce, signature_u, signature_v)) + } +} diff --git a/applications/tari_app_grpc/src/conversions/mod.rs b/applications/tari_app_grpc/src/conversions/mod.rs index f3f1a1cec6..e404d52d1a 100644 --- a/applications/tari_app_grpc/src/conversions/mod.rs +++ b/applications/tari_app_grpc/src/conversions/mod.rs @@ -24,6 +24,7 @@ mod aggregate_body; mod block; mod block_header; mod chain_metadata; +mod com_signature; mod consensus_constants; mod historical_block; mod new_block_template; @@ -42,6 +43,7 @@ pub use self::{ block::*, block_header::*, chain_metadata::*, + com_signature::*, consensus_constants::*, historical_block::*, new_block_template::*, diff --git a/applications/tari_app_grpc/src/conversions/transaction_input.rs b/applications/tari_app_grpc/src/conversions/transaction_input.rs index 9002c26f01..69cbf8d5e6 100644 --- a/applications/tari_app_grpc/src/conversions/transaction_input.rs +++ b/applications/tari_app_grpc/src/conversions/transaction_input.rs @@ -59,7 +59,6 @@ impl TryFrom for TransactionInput { commitment, script, input_data, - height: input.height, script_signature, script_offset_public_key, }) @@ -78,10 +77,10 @@ impl From for grpc::TransactionInput { hash, script: input.script.as_bytes(), input_data: input.input_data.as_bytes(), - height: input.height, - script_signature: Some(grpc::Signature { - public_nonce: Vec::from(input.script_signature.get_public_nonce().as_bytes()), - signature: Vec::from(input.script_signature.get_signature().as_bytes()), + script_signature: Some(grpc::ComSignature { + public_nonce_commitment: Vec::from(input.script_signature.public_nonce().as_bytes()), + signature_u: Vec::from(input.script_signature.u().as_bytes()), + signature_v: Vec::from(input.script_signature.v().as_bytes()), }), script_offset_public_key: input.script_offset_public_key.as_bytes().to_vec(), } diff --git a/applications/tari_app_grpc/src/conversions/unblinded_output.rs b/applications/tari_app_grpc/src/conversions/unblinded_output.rs index 3258cfb73b..504ab987ac 100644 --- a/applications/tari_app_grpc/src/conversions/unblinded_output.rs +++ b/applications/tari_app_grpc/src/conversions/unblinded_output.rs @@ -43,7 +43,6 @@ impl From for grpc::UnblindedOutput { }), script: output.script.as_bytes(), input_data: output.input_data.as_bytes(), - height: output.height, script_private_key: output.script_private_key.as_bytes().to_vec(), script_offset_public_key: output.script_offset_public_key.as_bytes().to_vec(), sender_metadata_signature: Some(grpc::Signature { @@ -89,7 +88,6 @@ impl TryFrom for UnblindedOutput { features, script, input_data, - height: output.height, script_private_key, script_offset_public_key, sender_metadata_signature, diff --git a/applications/tari_base_node/src/bootstrap.rs b/applications/tari_base_node/src/bootstrap.rs index 78a3b34c38..85235f6a57 100644 --- a/applications/tari_base_node/src/bootstrap.rs +++ b/applications/tari_base_node/src/bootstrap.rs @@ -226,6 +226,7 @@ where B: BlockchainBackend + 'static fn create_comms_config(&self) -> CommsConfig { CommsConfig { + network: self.config.network, node_identity: self.node_identity.clone(), transport_type: self.create_transport_type(), datastore_path: self.config.peer_db_path.clone(), @@ -236,7 +237,6 @@ where B: BlockchainBackend + 'static database_url: DbConnectionUrl::File(self.config.data_dir.join("dht.db")), auto_join: true, allow_test_addresses: self.config.allow_test_addresses, - network: self.config.network.into(), flood_ban_max_msg_count: self.config.flood_ban_max_msg_count, saf_msg_validity: self.config.saf_expiry_duration, ..Default::default() diff --git a/applications/tari_base_node/src/builder.rs b/applications/tari_base_node/src/builder.rs index 716dec3e07..c321e43b71 100644 --- a/applications/tari_base_node/src/builder.rs +++ b/applications/tari_base_node/src/builder.rs @@ -29,7 +29,7 @@ use tari_comms_dht::Dht; use tari_core::{ base_node::{state_machine_service::states::StatusInfo, LocalNodeCommsInterface, StateMachineHandle}, chain_storage::{create_lmdb_database, BlockchainDatabase, BlockchainDatabaseConfig, LMDBDatabase, Validators}, - consensus::ConsensusManagerBuilder, + consensus::ConsensusManager, mempool::{service::LocalMempoolService, Mempool, MempoolConfig}, proof_of_work::randomx_factory::{RandomXConfig, RandomXFactory}, transactions::types::CryptoFactories, @@ -193,7 +193,7 @@ async fn build_node_context( ) -> Result { //---------------------------------- Blockchain --------------------------------------------// - let rules = ConsensusManagerBuilder::new(config.network.into()).build(); + let rules = ConsensusManager::builder(config.network).build(); let factories = CryptoFactories::default(); let randomx_factory = RandomXFactory::new(RandomXConfig::default(), config.max_randomx_vms); let validators = Validators::new( diff --git a/applications/tari_base_node/src/command_handler.rs b/applications/tari_base_node/src/command_handler.rs index 03f5557356..d662d7c936 100644 --- a/applications/tari_base_node/src/command_handler.rs +++ b/applications/tari_base_node/src/command_handler.rs @@ -49,7 +49,7 @@ use tari_core::{ }, blocks::BlockHeader, chain_storage::{async_db::AsyncBlockchainDb, ChainHeader, LMDBDatabase}, - consensus::{ConsensusManager, Network}, + consensus::ConsensusManager, mempool::service::LocalMempoolService, proof_of_work::PowAlgorithm, tari_utilities::{hex::Hex, message_format::MessageFormat}, @@ -921,7 +921,7 @@ impl CommandHandler { let start_height = cmp::max(start_height, 1); let mut prev_header = try_or_print!(db.fetch_chain_header(start_height - 1).await); - let consensus_rules = ConsensusManager::builder(Network::from(network)).build(); + let consensus_rules = ConsensusManager::builder(network).build(); writeln!( output, diff --git a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs index 58970de17c..0a416e11ae 100644 --- a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs +++ b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs @@ -34,6 +34,7 @@ use tari_app_grpc::{ tari_rpc, tari_rpc::{CalcType, Sorting}, }; +use tari_common::configuration::Network; use tari_comms::PeerManager; use tari_core::{ base_node::{ @@ -43,7 +44,7 @@ use tari_core::{ StateMachineHandle, }, blocks::{Block, BlockHeader, NewBlockTemplate}, - consensus::{emission::Emission, ConsensusManager, ConsensusManagerBuilder, Network}, + consensus::{emission::Emission, ConsensusManager, NetworkConsensus}, crypto::tari_utilities::hex::Hex, mempool::{service::LocalMempoolService, TxStorageResponse}, proof_of_work::PowAlgorithm, @@ -75,7 +76,7 @@ const LIST_HEADERS_DEFAULT_NUM_HEADERS: u64 = 10; pub struct BaseNodeGrpcServer { node_service: LocalNodeCommsInterface, mempool_service: LocalMempoolService, - network: Network, + network: NetworkConsensus, state_machine_handle: StateMachineHandle, peer_manager: Arc, consensus_rules: ConsensusManager, @@ -93,7 +94,7 @@ impl BaseNodeGrpcServer { node_service: local_node, mempool_service: local_mempool, consensus_rules: ConsensusManager::builder(network).build(), - network, + network: network.into(), state_machine_handle, peer_manager, } @@ -914,7 +915,7 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { heights = heights .drain(..cmp::min(heights.len(), GET_TOKENS_IN_CIRCULATION_MAX_HEIGHTS)) .collect(); - let consensus_manager = ConsensusManagerBuilder::new(self.network).build(); + let consensus_manager = ConsensusManager::builder(self.network.as_network()).build(); let (mut tx, rx) = mpsc::channel(GET_TOKENS_IN_CIRCULATION_PAGE_SIZE); task::spawn(async move { diff --git a/applications/tari_base_node/src/main.rs b/applications/tari_base_node/src/main.rs index e02e1b524d..ff48b12ea2 100644 --- a/applications/tari_base_node/src/main.rs +++ b/applications/tari_base_node/src/main.rs @@ -27,10 +27,6 @@ #![deny(unused_must_use)] #![deny(unreachable_patterns)] #![deny(unknown_lints)] -// Enable 'impl Trait' type aliases -#![allow(incomplete_features)] -#![feature(type_alias_impl_trait)] -#![feature(min_type_alias_impl_trait)] /// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣶⣿⣿⣿⣿⣶⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ /// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣾⣿⡿⠋⠀⠀⠀⠀⠉⠛⠿⣿⣿⣶⣤⣀⠀⠀⠀⠀⠀⠀⢰⣿⣾⣾⣾⣾⣾⣾⣾⣾⣾⣿⠀⠀⠀⣾⣾⣾⡀⠀⠀⠀⠀⢰⣾⣾⣾⣾⣿⣶⣶⡀⠀⠀⠀⢸⣾⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -223,7 +219,7 @@ async fn run_node(node_config: Arc, bootstrap: ConfigBootstrap) -> let grpc = crate::grpc::base_node_grpc_server::BaseNodeGrpcServer::new( ctx.local_node(), ctx.local_mempool(), - node_config.network.into(), + node_config.network, ctx.state_machine(), ctx.base_node_comms().peer_manager(), ); diff --git a/applications/tari_base_node/src/recovery.rs b/applications/tari_base_node/src/recovery.rs index cdabc8d95a..d8893a5b80 100644 --- a/applications/tari_base_node/src/recovery.rs +++ b/applications/tari_base_node/src/recovery.rs @@ -30,7 +30,7 @@ use std::{ sync::Arc, }; use tari_app_utilities::utilities::ExitCodes; -use tari_common::{DatabaseType, GlobalConfig}; +use tari_common::{configuration::Network, DatabaseType, GlobalConfig}; use tari_core::{ chain_storage::{ async_db::AsyncBlockchainDb, @@ -41,7 +41,7 @@ use tari_core::{ BlockchainDatabaseConfig, Validators, }, - consensus::{ConsensusManagerBuilder, Network as NetworkType}, + consensus::ConsensusManager, proof_of_work::randomx_factory::{RandomXConfig, RandomXFactory}, transactions::types::CryptoFactories, validation::{ @@ -92,7 +92,7 @@ pub async fn run_recovery(node_config: &GlobalConfig) -> Result<(), anyhow::Erro return Err(anyhow!("Recovery mode is only available for LMDB")); }, }; - let rules = ConsensusManagerBuilder::new(node_config.network.into()).build(); + let rules = ConsensusManager::builder(node_config.network).build(); let factories = CryptoFactories::default(); let randomx_factory = RandomXFactory::new(RandomXConfig::default(), node_config.max_randomx_vms); let validators = Validators::new( @@ -140,7 +140,7 @@ async fn do_recovery( source_backend: D, ) -> Result<(), anyhow::Error> { // We dont care about the values, here, so we just use mock validators, and a mainnet CM. - let rules = ConsensusManagerBuilder::new(NetworkType::LocalNet).build(); + let rules = ConsensusManager::builder(Network::LocalNet).build(); let validators = Validators::new( MockValidator::new(true), MockValidator::new(true), diff --git a/applications/tari_console_wallet/Cargo.toml b/applications/tari_console_wallet/Cargo.toml index 7af7242786..0a2f15433d 100644 --- a/applications/tari_console_wallet/Cargo.toml +++ b/applications/tari_console_wallet/Cargo.toml @@ -16,6 +16,7 @@ tari_app_grpc = { path = "../tari_app_grpc" } tari_shutdown = { path = "../../infrastructure/shutdown" } tari_key_manager = { path = "../../base_layer/key_manager" } +bitflags = "1.2.1" chrono = { version = "0.4.6", features = ["serde"]} chrono-english = "0.1" futures = { version = "^0.3.1", default-features = false, features = ["alloc"]} @@ -43,4 +44,3 @@ features = ["transactions", "mempool_proto", "base_node_proto"] version = "^0.12" default-features = false features = ["crossterm"] - diff --git a/applications/tari_console_wallet/src/automation/commands.rs b/applications/tari_console_wallet/src/automation/commands.rs index d8195a0d01..ac8205b92d 100644 --- a/applications/tari_console_wallet/src/automation/commands.rs +++ b/applications/tari_console_wallet/src/automation/commands.rs @@ -645,13 +645,13 @@ fn write_utxos_to_csv_file(utxos: Vec, file_path: String) -> Re let mut csv_file = LineWriter::new(file); writeln!( csv_file, - r##""index","value","spending_key","commitment","flags","maturity","script","input_data","height","script_private_key","script_offset_public_key","signature","public_nonce""## + r##""index","value","spending_key","commitment","flags","maturity","script","input_data","script_private_key","script_offset_public_key","signature","public_nonce""## ) .map_err(|e| CommandError::CSVFile(e.to_string()))?; for (i, utxo) in utxos.iter().enumerate() { writeln!( csv_file, - r##""{}","{}","{}","{}","{:?}","{}","{}","{}","{}","{}","{}","{}","{}""##, + r##""{}","{}","{}","{}","{:?}","{}","{}","{}","{}","{}","{}","{}""##, i + 1, utxo.value.0, utxo.spending_key.to_hex(), @@ -660,7 +660,6 @@ fn write_utxos_to_csv_file(utxos: Vec, file_path: String) -> Re utxo.features.maturity, utxo.script.to_hex(), utxo.input_data.to_hex(), - utxo.height, utxo.script_private_key.to_hex(), utxo.script_offset_public_key.to_hex(), utxo.sender_metadata_signature.get_signature().to_hex(), diff --git a/applications/tari_console_wallet/src/init/mod.rs b/applications/tari_console_wallet/src/init/mod.rs index 60784e0b19..ef022864ca 100644 --- a/applications/tari_console_wallet/src/init/mod.rs +++ b/applications/tari_console_wallet/src/init/mod.rs @@ -29,17 +29,14 @@ use rpassword::prompt_password_stdout; use rustyline::Editor; use std::{fs, path::PathBuf, str::FromStr, sync::Arc}; use tari_app_utilities::utilities::{setup_wallet_transport_type, ExitCodes}; -use tari_common::{ConfigBootstrap, GlobalConfig, Network}; +use tari_common::{ConfigBootstrap, GlobalConfig}; use tari_comms::{ peer_manager::{Peer, PeerFeatures}, types::CommsSecretKey, NodeIdentity, }; use tari_comms_dht::{DbConnectionUrl, DhtConfig}; -use tari_core::{ - consensus::Network as NetworkType, - transactions::types::{CryptoFactories, PrivateKey}, -}; +use tari_core::transactions::types::{CryptoFactories, PrivateKey}; use tari_p2p::{ initialization::CommsConfig, seed_peer::SeedPeer, @@ -318,6 +315,7 @@ pub async fn init_wallet( }; let comms_config = CommsConfig { + network: config.network, node_identity, user_agent: format!("tari/wallet/{}", env!("CARGO_PKG_VERSION")), transport_type, @@ -330,7 +328,6 @@ pub async fn init_wallet( database_url: DbConnectionUrl::File(config.data_dir.join("dht-console-wallet.db")), auto_join: true, allow_test_addresses: config.allow_test_addresses, - network: config.network.into(), flood_ban_max_msg_count: config.flood_ban_max_msg_count, saf_msg_validity: config.saf_expiry_duration, ..Default::default() @@ -345,15 +342,6 @@ pub async fn init_wallet( dns_seeds_use_dnssec: true, }; - let network = match &config.network { - Network::MainNet => NetworkType::MainNet, - Network::Ridcully => NetworkType::Ridcully, - Network::LocalNet => NetworkType::LocalNet, - Network::Stibbons => NetworkType::Stibbons, - Network::Weatherwax => NetworkType::Weatherwax, - Network::Rincewind => unimplemented!("Rincewind has been retired"), - }; - let base_node_service_config = BaseNodeServiceConfig::new( config.wallet_base_node_service_refresh_interval, config.wallet_base_node_service_request_max_age, @@ -379,7 +367,7 @@ pub async fn init_wallet( prevent_fee_gt_amount: config.prevent_fee_gt_amount, ..Default::default() }), - network, + config.network.into(), Some(base_node_service_config), Some(config.buffer_size_base_node_wallet), Some(config.buffer_rate_limit_base_node_wallet), diff --git a/applications/tari_console_wallet/src/ui/app.rs b/applications/tari_console_wallet/src/ui/app.rs index 1b96bbbd21..b9df53d852 100644 --- a/applications/tari_console_wallet/src/ui/app.rs +++ b/applications/tari_console_wallet/src/ui/app.rs @@ -38,7 +38,7 @@ use crate::{ }, wallet_modes::PeerConfig, }; -use tari_common::{GlobalConfig, Network}; +use tari_common::{configuration::Network, GlobalConfig}; use tari_comms::peer_manager::Peer; use tari_wallet::WalletSqlite; use tokio::runtime::Handle; diff --git a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs index f67efc0f92..11d8db5f05 100644 --- a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs +++ b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs @@ -71,11 +71,17 @@ impl TransactionsTab { .title(Span::styled("(P)ending Transactions", style)); f.render_widget(block, list_areas[0]); + self.draw_pending_transactions(f, list_areas[0], app_state); + self.draw_completed_transactions(f, list_areas[1], app_state); + } + + fn draw_pending_transactions(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) + where B: Backend { // Pending Transactions self.pending_list_state.set_num_items(app_state.get_pending_txs().len()); let mut pending_list_state = self .pending_list_state - .get_list_state((list_areas[0].height as usize).saturating_sub(3)); + .get_list_state((area.height as usize).saturating_sub(3)); let window = self.pending_list_state.get_start_end(); let windowed_view = app_state.get_pending_txs_slice(window.0, window.1); @@ -132,8 +138,11 @@ impl TransactionsTab { .add_column(Some("Amount"), Some(18), column1_items) .add_column(Some("Local Date/Time"), Some(20), column2_items) .add_column(Some("Message"), None, column3_items); - column_list.render(f, list_areas[0], &mut pending_list_state); + column_list.render(f, area, &mut pending_list_state); + } + fn draw_completed_transactions(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) + where B: Backend { // Completed Transactions let style = if self.selected_tx_list == SelectedTransactionList::CompletedTxs { Style::default().fg(Color::Magenta).add_modifier(Modifier::BOLD) @@ -143,15 +152,26 @@ impl TransactionsTab { let block = Block::default() .borders(Borders::ALL) .title(Span::styled("Completed (T)ransactions", style)); - f.render_widget(block, list_areas[1]); + f.render_widget(block, area); - self.completed_list_state - .set_num_items(app_state.get_completed_txs().len()); + let completed_txs = app_state.get_completed_txs(); + self.completed_list_state.set_num_items(completed_txs.len()); let mut completed_list_state = self .completed_list_state - .get_list_state((list_areas[1].height as usize).saturating_sub(3)); - let window = self.completed_list_state.get_start_end(); - let windowed_view = app_state.get_completed_txs_slice(window.0, window.1); + .get_list_state((area.height as usize).saturating_sub(3)); + let (start, end) = self.completed_list_state.get_start_end(); + let windowed_view = &completed_txs[start..end]; + + let text_colors: HashMap = [(true, Color::DarkGray), (false, Color::Reset)] + .iter() + .cloned() + .collect(); + + let base_node_state = app_state.get_base_node_state(); + let chain_height = base_node_state + .chain_metadata + .as_ref() + .map(|cm| cm.height_of_longest_chain()); let mut column0_items = Vec::new(); let mut column1_items = Vec::new(); @@ -159,7 +179,8 @@ impl TransactionsTab { let mut column3_items = Vec::new(); for t in windowed_view.iter() { - let text_color = text_colors.get(&t.cancelled).unwrap_or(&Color::Reset).to_owned(); + let cancelled = t.cancelled || !t.valid; + let text_color = text_colors.get(&cancelled).unwrap_or(&Color::Reset).to_owned(); if t.direction == TransactionDirection::Outbound { column0_items.push(ListItem::new(Span::styled( format!("{}", t.destination_public_key), @@ -176,11 +197,20 @@ impl TransactionsTab { format!("{}", t.source_public_key), Style::default().fg(text_color), ))); - let amount_style = if t.cancelled { - Style::default().fg(Color::Green).add_modifier(Modifier::DIM) + let maturity = if let Some(output) = t.transaction.body.outputs().first() { + output.features.maturity } else { - Style::default().fg(Color::Green) + 0 + }; + let color = match (t.cancelled, chain_height) { + // cancelled + (true, _) => Color::DarkGray, + // not mature yet + (_, Some(height)) if maturity > height => Color::Yellow, + // default + _ => Color::Green, }; + let amount_style = Style::default().fg(color); column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))); } let local_time = DateTime::::from_utc(t.timestamp, Local::now().offset().to_owned()); @@ -188,7 +218,9 @@ impl TransactionsTab { format!("{}", local_time.format("%Y-%m-%d %H:%M:%S")), Style::default().fg(text_color), ))); - let status = if t.cancelled { + let status = if t.cancelled && t.status == TransactionStatus::Coinbase { + "Abandoned".to_string() + } else if t.cancelled { "Cancelled".to_string() } else if !t.valid { "Invalid".to_string() @@ -207,7 +239,7 @@ impl TransactionsTab { .add_column(Some("Local Date/Time"), Some(20), column2_items) .add_column(Some("Status"), None, column3_items); - column_list.render(f, list_areas[1], &mut completed_list_state); + column_list.render(f, area, &mut completed_list_state); } fn draw_detailed_transaction(&self, f: &mut Frame, area: Rect, app_state: &AppState) @@ -224,27 +256,9 @@ impl TransactionsTab { .margin(1) .split(area); - // Labels: - let label_layout = Layout::default() - .constraints( - [ - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - ] - .as_ref(), - ) - .split(columns[0]); + // Labels + let constraints = [Constraint::Length(1); 13]; + let label_layout = Layout::default().constraints(constraints).split(columns[0]); let tx_id = Span::styled("TxID:", Style::default().fg(Color::Magenta)); let source_public_key = Span::styled("Source Public Key:", Style::default().fg(Color::Magenta)); @@ -258,53 +272,41 @@ impl TransactionsTab { let excess = Span::styled("Excess:", Style::default().fg(Color::Magenta)); let confirmations = Span::styled("Confirmations:", Style::default().fg(Color::Magenta)); let mined_height = Span::styled("Mined Height:", Style::default().fg(Color::Magenta)); - let paragraph = Paragraph::new(tx_id).wrap(Wrap { trim: true }); + let maturity = Span::styled("Maturity:", Style::default().fg(Color::Magenta)); + + let trim = Wrap { trim: true }; + let paragraph = Paragraph::new(tx_id).wrap(trim); f.render_widget(paragraph, label_layout[0]); - let paragraph = Paragraph::new(source_public_key).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(source_public_key).wrap(trim); f.render_widget(paragraph, label_layout[1]); - let paragraph = Paragraph::new(destination_public_key).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(destination_public_key).wrap(trim); f.render_widget(paragraph, label_layout[2]); - let paragraph = Paragraph::new(direction).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(direction).wrap(trim); f.render_widget(paragraph, label_layout[3]); - let paragraph = Paragraph::new(amount).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(amount).wrap(trim); f.render_widget(paragraph, label_layout[4]); - let paragraph = Paragraph::new(fee).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(fee).wrap(trim); f.render_widget(paragraph, label_layout[5]); - let paragraph = Paragraph::new(status).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(status).wrap(trim); f.render_widget(paragraph, label_layout[6]); - let paragraph = Paragraph::new(message).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(message).wrap(trim); f.render_widget(paragraph, label_layout[7]); - let paragraph = Paragraph::new(timestamp).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(timestamp).wrap(trim); f.render_widget(paragraph, label_layout[8]); - let paragraph = Paragraph::new(excess).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(excess).wrap(trim); f.render_widget(paragraph, label_layout[9]); - let paragraph = Paragraph::new(confirmations).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(confirmations).wrap(trim); f.render_widget(paragraph, label_layout[10]); - let paragraph = Paragraph::new(mined_height).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(mined_height).wrap(trim); f.render_widget(paragraph, label_layout[11]); - // Content: + let paragraph = Paragraph::new(maturity).wrap(trim); + f.render_widget(paragraph, label_layout[12]); + + // Content let required_confirmations = app_state.get_required_confirmations(); if let Some(tx) = self.detailed_transaction.as_ref() { - let content_layout = Layout::default() - .constraints( - [ - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - Constraint::Length(1), - ] - .as_ref(), - ) - .split(columns[1]); + let constraints = [Constraint::Length(1); 13]; + let content_layout = Layout::default().constraints(constraints).split(columns[1]); let tx_id = Span::styled(format!("{}", tx.tx_id), Style::default().fg(Color::White)); let source_public_key = @@ -364,31 +366,46 @@ impl TransactionsTab { .unwrap_or_else(|| "N/A".to_string()), Style::default().fg(Color::White), ); + let maturity = tx + .transaction + .body + .outputs() + .first() + .map(|o| o.features.maturity) + .unwrap_or_else(|| 0); + let maturity = if maturity > 0 { + format!("Spendable at Block #{}", maturity) + } else { + "N/A".to_string() + }; + let maturity = Span::styled(maturity, Style::default().fg(Color::White)); - let paragraph = Paragraph::new(tx_id).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(tx_id).wrap(trim); f.render_widget(paragraph, content_layout[0]); - let paragraph = Paragraph::new(source_public_key).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(source_public_key).wrap(trim); f.render_widget(paragraph, content_layout[1]); - let paragraph = Paragraph::new(destination_public_key).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(destination_public_key).wrap(trim); f.render_widget(paragraph, content_layout[2]); - let paragraph = Paragraph::new(direction).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(direction).wrap(trim); f.render_widget(paragraph, content_layout[3]); - let paragraph = Paragraph::new(amount).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(amount).wrap(trim); f.render_widget(paragraph, content_layout[4]); - let paragraph = Paragraph::new(fee).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(fee).wrap(trim); f.render_widget(paragraph, content_layout[5]); - let paragraph = Paragraph::new(status).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(status).wrap(trim); f.render_widget(paragraph, content_layout[6]); - let paragraph = Paragraph::new(message).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(message).wrap(trim); f.render_widget(paragraph, content_layout[7]); - let paragraph = Paragraph::new(timestamp).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(timestamp).wrap(trim); f.render_widget(paragraph, content_layout[8]); - let paragraph = Paragraph::new(excess).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(excess).wrap(trim); f.render_widget(paragraph, content_layout[9]); - let paragraph = Paragraph::new(confirmations).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(confirmations).wrap(trim); f.render_widget(paragraph, content_layout[10]); - let paragraph = Paragraph::new(mined_height).wrap(Wrap { trim: true }); + let paragraph = Paragraph::new(mined_height).wrap(trim); f.render_widget(paragraph, content_layout[11]); + let paragraph = Paragraph::new(maturity).wrap(trim); + f.render_widget(paragraph, content_layout[12]); } } } @@ -401,7 +418,7 @@ impl Component for TransactionsTab { Constraint::Length(3), Constraint::Length(1), Constraint::Min(10), - Constraint::Length(14), + Constraint::Length(15), ] .as_ref(), ) @@ -425,6 +442,8 @@ impl Component for TransactionsTab { span_vec.push(Span::raw(" selects a transaction, ")); span_vec.push(Span::styled("C", Style::default().add_modifier(Modifier::BOLD))); span_vec.push(Span::raw(" cancels a selected Pending Tx, ")); + span_vec.push(Span::styled("A", Style::default().add_modifier(Modifier::BOLD))); + span_vec.push(Span::raw(" shows abandoned coinbase Txs, ")); span_vec.push(Span::styled("Esc", Style::default().add_modifier(Modifier::BOLD))); span_vec.push(Span::raw(" exits the list.")); @@ -512,6 +531,7 @@ impl Component for TransactionsTab { self.confirmation_dialog = true; } }, + 'a' => app_state.toggle_abandoned_coinbase_filter(), '\n' => match self.selected_tx_list { SelectedTransactionList::None => {}, SelectedTransactionList::PendingTxs => { diff --git a/applications/tari_console_wallet/src/ui/state/app_state.rs b/applications/tari_console_wallet/src/ui/state/app_state.rs index d2fa501b6e..1374cf7d9d 100644 --- a/applications/tari_console_wallet/src/ui/state/app_state.rs +++ b/applications/tari_console_wallet/src/ui/state/app_state.rs @@ -33,11 +33,12 @@ use crate::{ utils::db::{CUSTOM_BASE_NODE_ADDRESS_KEY, CUSTOM_BASE_NODE_PUBLIC_KEY_KEY}, wallet_modes::PeerConfig, }; +use bitflags::bitflags; use futures::{stream::Fuse, StreamExt}; use log::*; use qrcode::{render::unicode, QrCode}; use std::{collections::HashMap, sync::Arc}; -use tari_common::{GlobalConfig, Network}; +use tari_common::{configuration::Network, GlobalConfig}; use tari_comms::{ connectivity::ConnectivityEventRx, multiaddr::Multiaddr, @@ -71,6 +72,7 @@ const LOG_TARGET: &str = "wallet::console_wallet::app_state"; pub struct AppState { inner: Arc>, cached_data: AppStateData, + completed_tx_filter: TransactionFilter, node_config: GlobalConfig, } @@ -85,9 +87,11 @@ impl AppState { ) -> Self { let inner = AppStateInner::new(node_identity, network, wallet, base_node_selected, base_node_config); let cached_data = inner.data.clone(); + Self { inner: Arc::new(RwLock::new(inner)), cached_data, + completed_tx_filter: TransactionFilter::ABANDONED_COINBASES, node_config, } } @@ -100,33 +104,31 @@ impl AppState { pub async fn refresh_transaction_state(&mut self) -> Result<(), UiError> { let mut inner = self.inner.write().await; inner.refresh_full_transaction_state().await?; - if let Some(data) = inner.get_updated_app_state() { - self.cached_data = data; - } + drop(inner); + self.update_cache().await; Ok(()) } pub async fn refresh_contacts_state(&mut self) -> Result<(), UiError> { let mut inner = self.inner.write().await; inner.refresh_contacts_state().await?; - if let Some(data) = inner.get_updated_app_state() { - self.cached_data = data; - } + drop(inner); + self.update_cache().await; Ok(()) } pub async fn refresh_connected_peers_state(&mut self) -> Result<(), UiError> { let mut inner = self.inner.write().await; inner.refresh_connected_peers_state().await?; - if let Some(data) = inner.get_updated_app_state() { - self.cached_data = data; - } + drop(inner); + self.update_cache().await; Ok(()) } pub async fn update_cache(&mut self) { let mut inner = self.inner.write().await; - if let Some(data) = inner.get_updated_app_state() { + let updated_state = inner.get_updated_app_state(); + if let Some(data) = updated_state { self.cached_data = data; } } @@ -145,9 +147,8 @@ impl AppState { inner.wallet.contacts_service.upsert_contact(contact).await?; inner.refresh_contacts_state().await?; - if let Some(data) = inner.get_updated_app_state() { - self.cached_data = data; - } + drop(inner); + self.update_cache().await; Ok(()) } @@ -161,9 +162,8 @@ impl AppState { inner.wallet.contacts_service.remove_contact(public_key).await?; inner.refresh_contacts_state().await?; - if let Some(data) = inner.get_updated_app_state() { - self.cached_data = data; - } + drop(inner); + self.update_cache().await; Ok(()) } @@ -274,16 +274,19 @@ impl AppState { } } - pub fn get_completed_txs_slice(&self, start: usize, end: usize) -> &[CompletedTransaction] { - if self.cached_data.completed_txs.is_empty() || start > end || end > self.cached_data.completed_txs.len() { - return &[]; + pub fn get_completed_txs(&self) -> Vec<&CompletedTransaction> { + if self + .completed_tx_filter + .contains(TransactionFilter::ABANDONED_COINBASES) + { + self.cached_data + .completed_txs + .iter() + .filter(|tx| !(tx.cancelled && tx.status == TransactionStatus::Coinbase)) + .collect() + } else { + self.cached_data.completed_txs.iter().collect() } - - &self.cached_data.completed_txs[start..end] - } - - pub fn get_completed_txs(&self) -> &Vec { - &self.cached_data.completed_txs } pub fn get_confirmations(&self, tx_id: &TxId) -> Option<&u64> { @@ -291,8 +294,9 @@ impl AppState { } pub fn get_completed_tx(&self, index: usize) -> Option<&CompletedTransaction> { - if index < self.cached_data.completed_txs.len() { - Some(&self.cached_data.completed_txs[index]) + let filtered_completed_txs = self.get_completed_txs(); + if index < filtered_completed_txs.len() { + Some(filtered_completed_txs[index]) } else { None } @@ -363,6 +367,10 @@ impl AppState { pub fn get_required_confirmations(&self) -> u64 { (&self.node_config.transaction_num_confirmations_required).to_owned() } + + pub fn toggle_abandoned_coinbase_filter(&mut self) { + self.completed_tx_filter.toggle(TransactionFilter::ABANDONED_COINBASES); + } } pub struct AppStateInner { @@ -864,3 +872,10 @@ pub enum UiTransactionSendStatus { SentViaSaf, Error(String), } + +bitflags! { + pub struct TransactionFilter: u8 { + const NONE = 0b0000_0000; + const ABANDONED_COINBASES = 0b0000_0001; + } +} diff --git a/applications/tari_merge_mining_proxy/src/main.rs b/applications/tari_merge_mining_proxy/src/main.rs index 56784b8ba8..dd73207c8a 100644 --- a/applications/tari_merge_mining_proxy/src/main.rs +++ b/applications/tari_merge_mining_proxy/src/main.rs @@ -19,9 +19,6 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#![allow(incomplete_features)] -#![feature(type_alias_impl_trait)] -#![feature(min_type_alias_impl_trait)] #![cfg_attr(not(debug_assertions), deny(unused_variables))] #![cfg_attr(not(debug_assertions), deny(unused_imports))] #![cfg_attr(not(debug_assertions), deny(dead_code))] diff --git a/applications/tari_merge_mining_proxy/src/proxy.rs b/applications/tari_merge_mining_proxy/src/proxy.rs index 9a3b9946ea..9acd9f76e3 100644 --- a/applications/tari_merge_mining_proxy/src/proxy.rs +++ b/applications/tari_merge_mining_proxy/src/proxy.rs @@ -37,6 +37,7 @@ use std::{ convert::TryFrom, future::Future, net::SocketAddr, + pin::Pin, sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -45,7 +46,7 @@ use std::{ time::Instant, }; use tari_app_grpc::{tari_rpc as grpc, tari_rpc::GetCoinbaseRequest}; -use tari_common::{GlobalConfig, Network}; +use tari_common::{configuration::Network, GlobalConfig}; use tari_core::{ blocks::{Block, NewBlockTemplate}, proof_of_work::monero_rx, @@ -116,19 +117,19 @@ impl MergeMiningProxyService { } } +#[allow(clippy::type_complexity)] impl Service> for MergeMiningProxyService { type Error = hyper::Error; + type Future = Pin> + Send>>; type Response = Response; - type Future = impl Future>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: Request) -> Self::Future { let inner = self.inner.clone(); - async move { + let future = async move { match inner.handle(req).await { Ok(resp) => Ok(resp), Err(err) => { @@ -145,7 +146,9 @@ impl Service> for MergeMiningProxyService { .expect("unexpected failure")) }, } - } + }; + + Box::pin(future) } } diff --git a/applications/tari_mining_node/src/difficulty.rs b/applications/tari_mining_node/src/difficulty.rs index 9e0f51da99..a7ada26a48 100644 --- a/applications/tari_mining_node/src/difficulty.rs +++ b/applications/tari_mining_node/src/difficulty.rs @@ -23,7 +23,7 @@ use crate::errors::{err_empty, MinerError}; use sha3::{Digest, Sha3_256}; use tari_app_grpc::tari_rpc::BlockHeader; -use tari_core::large_ints::U256; +use tari_core::{large_ints::U256, tari_utilities::ByteArray}; pub type Difficulty = u64; @@ -31,7 +31,6 @@ pub struct BlockHeaderSha3 { header: BlockHeader, pow_bytes: Vec, hash_before_timestamp: Sha3_256, - hash_before_nonce: Sha3_256, pub timestamp: u64, pub nonce: u64, pub hashes: u64, @@ -49,19 +48,11 @@ impl BlockHeaderSha3 { let hash_before_timestamp = Sha3_256::new() .chain((header.version as u16).to_le_bytes()) .chain(header.height.to_le_bytes()) - .chain(&header.prev_hash); - let hash_before_nonce = hash_before_timestamp - .clone() - .chain((timestamp.seconds as u64).to_le_bytes()) - .chain(&header.output_mr) - .chain(&header.range_proof_mr) - .chain(&header.kernel_mr) - .chain(&header.total_kernel_offset); + .chain(header.prev_hash.as_bytes()); Ok(Self { pow_bytes: pow.to_bytes(), hash_before_timestamp, - hash_before_nonce, timestamp: timestamp.seconds as u64, nonce: header.nonce, header, @@ -69,15 +60,22 @@ impl BlockHeaderSha3 { }) } - pub fn set_timestamp(&mut self, timestamp: u64) { - self.hash_before_nonce = self - .hash_before_timestamp + #[inline] + fn get_hash_before_nonce(&self) -> Sha3_256 { + self.hash_before_timestamp .clone() - .chain(timestamp.to_le_bytes()) - .chain(&self.header.output_mr) - .chain(&self.header.range_proof_mr) - .chain(&self.header.kernel_mr) - .chain(&self.header.total_kernel_offset); + .chain(self.timestamp.to_le_bytes()) + .chain(self.header.input_mr.as_bytes()) + .chain(self.header.output_mr.as_bytes()) + .chain(self.header.output_mmr_size.to_le_bytes()) + .chain(self.header.witness_mr.as_bytes()) + .chain(self.header.kernel_mr.as_bytes()) + .chain(self.header.kernel_mmr_size.to_le_bytes()) + .chain(self.header.total_kernel_offset.as_bytes()) + .chain(self.header.total_script_offset.as_bytes()) + } + + pub fn set_timestamp(&mut self, timestamp: u64) { self.timestamp = timestamp; } @@ -95,8 +93,7 @@ impl BlockHeaderSha3 { pub fn difficulty(&mut self) -> Difficulty { self.hashes = self.hashes.saturating_add(1); let hash = self - .hash_before_nonce - .clone() + .get_hash_before_nonce() .chain(self.nonce.to_le_bytes()) .chain(&self.pow_bytes) .finalize(); diff --git a/base_layer/core/src/base_node/chain_metadata_service/initializer.rs b/base_layer/core/src/base_node/chain_metadata_service/initializer.rs index 27dacc54f8..1310f22702 100644 --- a/base_layer/core/src/base_node/chain_metadata_service/initializer.rs +++ b/base_layer/core/src/base_node/chain_metadata_service/initializer.rs @@ -24,18 +24,16 @@ use super::{service::ChainMetadataService, LOG_TARGET}; use crate::base_node::{chain_metadata_service::handle::ChainMetadataHandle, comms_interface::LocalNodeCommsInterface}; use futures::{future, pin_mut}; use log::*; -use std::future::Future; use tari_comms::connectivity::ConnectivityRequester; use tari_p2p::services::liveness::LivenessHandle; -use tari_service_framework::{ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; +use tari_service_framework::{async_trait, ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; use tokio::sync::broadcast; pub struct ChainMetadataServiceInitializer; +#[async_trait] impl ServiceInitializer for ChainMetadataServiceInitializer { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { // Buffer size set to 1 because only the most recent metadata is applicable let (publisher, _) = broadcast::channel(1); @@ -53,6 +51,6 @@ impl ServiceInitializer for ChainMetadataServiceInitializer { info!(target: LOG_TARGET, "ChainMetadataService has shut down"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/core/src/base_node/proto/mmr_tree.proto b/base_layer/core/src/base_node/proto/mmr_tree.proto index 7c92ebbc5e..cbd8251c4f 100644 --- a/base_layer/core/src/base_node/proto/mmr_tree.proto +++ b/base_layer/core/src/base_node/proto/mmr_tree.proto @@ -8,5 +8,5 @@ enum MmrTree { MmrTreeNone = 0; MmrTreeUtxo = 1; MmrTreeKernel = 2; - MmrTreeRangeProof = 3; + MmrTreeWitness = 3; } diff --git a/base_layer/core/src/base_node/proto/mmr_tree.rs b/base_layer/core/src/base_node/proto/mmr_tree.rs index 52675af257..cc5c3c1fb5 100644 --- a/base_layer/core/src/base_node/proto/mmr_tree.rs +++ b/base_layer/core/src/base_node/proto/mmr_tree.rs @@ -32,7 +32,7 @@ impl TryFrom for MmrTree { None => return Err("MmrTree not provided".to_string()), Utxo => MmrTree::Utxo, Kernel => MmrTree::Kernel, - RangeProof => MmrTree::RangeProof, + Witness => MmrTree::Witness, }) } } @@ -43,7 +43,7 @@ impl From for proto::MmrTree { match tree { Utxo => proto::MmrTree::Utxo, Kernel => proto::MmrTree::Kernel, - RangeProof => proto::MmrTree::RangeProof, + Witness => proto::MmrTree::Witness, } } } diff --git a/base_layer/core/src/base_node/service/initializer.rs b/base_layer/core/src/base_node/service/initializer.rs index 820f13165b..ae6be0b519 100644 --- a/base_layer/core/src/base_node/service/initializer.rs +++ b/base_layer/core/src/base_node/service/initializer.rs @@ -33,7 +33,7 @@ use crate::{ proto as shared_protos, proto::base_node as proto, }; -use futures::{channel::mpsc, future, Future, Stream, StreamExt}; +use futures::{channel::mpsc, future, Stream, StreamExt}; use log::*; use std::{convert::TryFrom, sync::Arc}; use tari_comms_dht::Dht; @@ -44,6 +44,7 @@ use tari_p2p::{ tari_message::TariMessageType, }; use tari_service_framework::{ + async_trait, reply_channel, ServiceInitializationError, ServiceInitializer, @@ -139,12 +140,11 @@ async fn extract_block(msg: Arc) -> Option> } } +#[async_trait] impl ServiceInitializer for BaseNodeServiceInitializer where T: BlockchainBackend + 'static { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { // Create streams for receiving Base Node requests and response messages from comms let inbound_request_stream = self.inbound_request_stream(); let inbound_response_stream = self.inbound_response_stream(); @@ -197,6 +197,6 @@ where T: BlockchainBackend + 'static info!(target: LOG_TARGET, "Base Node Service shutdown"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/core/src/base_node/state_machine_service/initializer.rs b/base_layer/core/src/base_node/state_machine_service/initializer.rs index 468ef5a894..0402d8849f 100644 --- a/base_layer/core/src/base_node/state_machine_service/initializer.rs +++ b/base_layer/core/src/base_node/state_machine_service/initializer.rs @@ -37,11 +37,10 @@ use crate::{ proof_of_work::randomx_factory::{RandomXConfig, RandomXFactory}, transactions::types::CryptoFactories, }; -use futures::{future, Future}; use log::*; use std::sync::Arc; use tari_comms::{connectivity::ConnectivityRequester, PeerManager}; -use tari_service_framework::{ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; +use tari_service_framework::{async_trait, ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; use tokio::sync::{broadcast, watch}; const LOG_TARGET: &str = "c::bn::state_machine_service::initializer"; @@ -71,12 +70,11 @@ where B: BlockchainBackend + 'static } } +#[async_trait] impl ServiceInitializer for BaseNodeStateMachineInitializer where B: BlockchainBackend + 'static { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { trace!(target: LOG_TARGET, "init of base_node"); let (state_event_publisher, _) = broadcast::channel(500); let (status_event_sender, status_event_receiver) = watch::channel(StatusInfo::new()); @@ -123,6 +121,6 @@ where B: BlockchainBackend + 'static info!(target: LOG_TARGET, "Base Node State Machine Service has shut down"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/horizon_state_synchronization.rs b/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/horizon_state_synchronization.rs index b797f3fdd9..15a5b3005b 100644 --- a/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/horizon_state_synchronization.rs +++ b/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/horizon_state_synchronization.rs @@ -313,7 +313,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { let db = self.db().clone(); let mut output_hashes = vec![]; - let mut rp_hashes = vec![]; + let mut witness_hashes = vec![]; let mut txn = db.write_transaction(); let mut unpruned_outputs = vec![]; let mut mmr_position = start; @@ -326,7 +326,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { let (_, output_pruned_set, rp_pruned_set, mut deleted) = block_data.dissolve(); let mut output_mmr = MerkleMountainRange::::new(output_pruned_set); - let mut proof_mmr = MerkleMountainRange::::new(rp_pruned_set); + let mut witness_mmr = MerkleMountainRange::::new(rp_pruned_set); while let Some(response) = output_stream.next().await { let res: SyncUtxosResponse = response?; @@ -355,7 +355,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { height_utxo_counter += 1; let output = TransactionOutput::try_from(output).map_err(HorizonSyncError::ConversionError)?; output_hashes.push(output.hash()); - rp_hashes.push(output.proof().hash()); + witness_hashes.push(output.witness_hash()); unpruned_outputs.push(output.clone()); txn.insert_output_via_horizon_sync( output, @@ -376,7 +376,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { ); height_txo_counter += 1; output_hashes.push(utxo.hash.clone()); - rp_hashes.push(utxo.rangeproof_hash.clone()); + witness_hashes.push(utxo.rangeproof_hash.clone()); txn.insert_pruned_output_via_horizon_sync( utxo.hash, utxo.rangeproof_hash, @@ -412,8 +412,8 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { output_mmr.push(hash)?; } - for hash in rp_hashes.drain(..) { - proof_mmr.push(hash)?; + for hash in witness_hashes.drain(..) { + witness_mmr.push(hash)?; } // Add in the changes @@ -434,12 +434,12 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { }); } - let mmr_root = proof_mmr.get_merkle_root()?; - if mmr_root != current_header.header().range_proof_mr { + let mmr_root = witness_mmr.get_merkle_root()?; + if mmr_root != current_header.header().witness_mr { return Err(HorizonSyncError::InvalidMmrRoot { - mmr_tree: MmrTree::RangeProof, + mmr_tree: MmrTree::Witness, at_height: current_header.height(), - expected_hex: current_header.header().range_proof_mr.to_hex(), + expected_hex: current_header.header().witness_mr.to_hex(), actual_hex: mmr_root.to_hex(), }); } @@ -452,9 +452,9 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { txn.update_pruned_hash_set(MmrTree::Utxo, current_header.hash().clone(), pruned_output_set); txn.update_pruned_hash_set( - MmrTree::RangeProof, + MmrTree::Witness, current_header.hash().clone(), - proof_mmr.get_pruned_hash_set()?, + witness_mmr.get_pruned_hash_set()?, ); txn.update_deleted_with_diff(current_header.hash().clone(), output_mmr.deleted().clone()); diff --git a/base_layer/core/src/base_node/sync/header_sync/validator.rs b/base_layer/core/src/base_node/sync/header_sync/validator.rs index 15f33782f6..55c6dd4d9e 100644 --- a/base_layer/core/src/base_node/sync/header_sync/validator.rs +++ b/base_layer/core/src/base_node/sync/header_sync/validator.rs @@ -235,11 +235,12 @@ mod test { use crate::{ blocks::BlockHeader, chain_storage::{async_db::AsyncBlockchainDb, BlockHeaderAccumulatedData}, - consensus::{ConsensusManager, Network}, + consensus::ConsensusManager, crypto::tari_utilities::{hex::Hex, Hashable}, proof_of_work::{randomx_factory::RandomXFactory, PowAlgorithm}, test_helpers::blockchain::{create_new_blockchain, TempDatabase}, }; + use tari_common::configuration::Network; use tari_test_utils::unpack_enum; fn setup() -> (BlockHeaderSyncValidator, AsyncBlockchainDb) { diff --git a/base_layer/core/src/blocks/block_header.rs b/base_layer/core/src/blocks/block_header.rs index d16079ba68..5048bb128a 100644 --- a/base_layer/core/src/blocks/block_header.rs +++ b/base_layer/core/src/blocks/block_header.rs @@ -98,7 +98,7 @@ pub struct BlockHeader { pub output_mr: BlockHash, /// This is the MMR root of the range proofs #[serde(with = "hash_serializer")] - pub range_proof_mr: BlockHash, + pub witness_mr: BlockHash, /// The size (number of leaves) of the output and range proof MMRs at the time of this header pub output_mmr_size: u64, /// This is the MMR root of the kernels @@ -106,6 +106,9 @@ pub struct BlockHeader { pub kernel_mr: BlockHash, /// The number of MMR leaves in the kernel MMR pub kernel_mmr_size: u64, + /// This is the Merkle root of the inputs in this block + #[serde(with = "hash_serializer")] + pub input_mr: BlockHash, /// Sum of kernel offsets for all kernels in this block. pub total_kernel_offset: BlindingFactor, /// Sum of script offsets for all kernels in this block. @@ -125,10 +128,11 @@ impl BlockHeader { prev_hash: vec![0; BLOCK_HASH_LENGTH], timestamp: EpochTime::now(), output_mr: vec![0; BLOCK_HASH_LENGTH], - range_proof_mr: vec![0; BLOCK_HASH_LENGTH], + witness_mr: vec![0; BLOCK_HASH_LENGTH], output_mmr_size: 0, kernel_mr: vec![0; BLOCK_HASH_LENGTH], kernel_mmr_size: 0, + input_mr: vec![0; BLOCK_HASH_LENGTH], total_kernel_offset: BlindingFactor::default(), total_script_offset: BlindingFactor::default(), nonce: 0, @@ -147,10 +151,11 @@ impl BlockHeader { prev_hash, timestamp: EpochTime::now(), output_mr: vec![0; BLOCK_HASH_LENGTH], - range_proof_mr: vec![0; BLOCK_HASH_LENGTH], + witness_mr: vec![0; BLOCK_HASH_LENGTH], output_mmr_size: prev.output_mmr_size, kernel_mr: vec![0; BLOCK_HASH_LENGTH], kernel_mmr_size: prev.kernel_mmr_size, + input_mr: vec![0; BLOCK_HASH_LENGTH], total_kernel_offset: BlindingFactor::default(), total_script_offset: BlindingFactor::default(), nonce: 0, @@ -200,9 +205,10 @@ impl BlockHeader { .chain(self.height.to_le_bytes()) .chain(self.prev_hash.as_bytes()) .chain(self.timestamp.as_u64().to_le_bytes()) + .chain(self.input_mr.as_bytes()) .chain(self.output_mr.as_bytes()) - .chain(self.range_proof_mr.as_bytes()) .chain(self.output_mmr_size.to_le_bytes()) + .chain(self.witness_mr.as_bytes()) .chain(self.kernel_mr.as_bytes()) .chain(self.kernel_mmr_size.to_le_bytes()) .chain(self.total_kernel_offset.as_bytes()) @@ -231,11 +237,12 @@ impl From for BlockHeader { prev_hash: header_template.prev_hash, timestamp: EpochTime::now(), output_mr: vec![], - range_proof_mr: vec![], + witness_mr: vec![], // TODO: put mmr sizes in template output_mmr_size: 0, kernel_mr: vec![], kernel_mmr_size: 0, + input_mr: vec![], total_kernel_offset: header_template.total_kernel_offset, total_script_offset: header_template.total_script_offset, nonce: 0, @@ -266,30 +273,32 @@ impl Eq for BlockHeader {} impl Display for BlockHeader { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { let datetime: DateTime = self.timestamp.into(); - let msg = format!( - "Version: {}\nBlock height: {}\nPrevious block hash: {}\nTimestamp: {}\n", + writeln!( + fmt, + "Version: {}\nBlock height: {}\nPrevious block hash: {}\nTimestamp: {}", self.version, self.height, self.prev_hash.to_hex(), datetime.to_rfc2822() - ); - fmt.write_str(&msg)?; - let msg = format!( - "Merkle roots:\nOutputs: {} ({})\nRange proofs: {}\nKernels: {} ({})\n", + )?; + writeln!( + fmt, + "Merkle roots:\nInputs: {},\n Outputs: {} ({})\nRange proofs: {}\nKernels: {} ({})\n", + self.input_mr.to_hex(), self.output_mr.to_hex(), self.output_mmr_size, - self.range_proof_mr.to_hex(), + self.witness_mr.to_hex(), self.kernel_mr.to_hex(), self.kernel_mmr_size - ); - fmt.write_str(&msg)?; - fmt.write_str(&format!( + )?; + writeln!( + fmt, "Total offset: {}\nTotal script offset: {}\nNonce: {}\nProof of work:\n{}", self.total_kernel_offset.to_hex(), self.total_script_offset.to_hex(), self.nonce, self.pow - )) + ) } } diff --git a/base_layer/core/src/blocks/genesis_block.rs b/base_layer/core/src/blocks/genesis_block.rs index 2968317006..ec93403053 100644 --- a/base_layer/core/src/blocks/genesis_block.rs +++ b/base_layer/core/src/blocks/genesis_block.rs @@ -37,6 +37,7 @@ use crate::{ }, }; use std::sync::Arc; +use tari_common_types::types::BLOCK_HASH_LENGTH; use tari_crypto::{ script::TariScript, tari_utilities::{hash::Hashable, hex::*}, @@ -49,7 +50,7 @@ pub fn get_mainnet_genesis_block() -> ChainBlock { pub fn get_stibbons_genesis_block() -> ChainBlock { // lets get the block let mut block = get_stibbons_genesis_block_raw(); - // Lets load in the stibbons faucet tx's + // Lets load in the stibbons faucet transactions let mut utxos = Vec::new(); let file = include_str!("faucets/stibbons_faucet.json"); // last 2 lines are used for the kernel creation @@ -66,7 +67,7 @@ pub fn get_stibbons_genesis_block() -> ChainBlock { } // fix headers to new mmr roots after adding utxos block.header.output_mr = from_hex("a939fda2579fb0b6fd906111f61e37c5ea23eccd8b737eb7da517fde71a98078").unwrap(); - block.header.range_proof_mr = from_hex("90a557390ce185318375546cb1244ffda3bb62274cce591880e2d012c38b1755").unwrap(); + block.header.witness_mr = from_hex("90a557390ce185318375546cb1244ffda3bb62274cce591880e2d012c38b1755").unwrap(); block.header.output_mmr_size += 4000; block.header.kernel_mr = from_hex("f5e08e66e9c0e5e3818d96a694f4f6eafd689f38cea2e52e771eab2cc7a3941a").unwrap(); block.header.kernel_mmr_size += 1; @@ -88,7 +89,7 @@ pub fn get_stibbons_genesis_block() -> ChainBlock { pub fn get_weatherwax_genesis_block() -> ChainBlock { // lets get the block let mut block = get_weatherwax_genesis_block_raw(); - // Lets load in the weatherwax faucet tx's + // Lets load in the weatherwax faucet transactions let mut utxos = Vec::new(); let file = include_str!("faucets/weatherwax_faucet.json"); // last 2 lines are used for the kernel creation @@ -105,7 +106,7 @@ pub fn get_weatherwax_genesis_block() -> ChainBlock { } // fix headers to new mmr roots after adding utxos block.header.output_mr = from_hex("a939fda2579fb0b6fd906111f61e37c5ea23eccd8b737eb7da517fde71a98078").unwrap(); - block.header.range_proof_mr = from_hex("90a557390ce185318375546cb1244ffda3bb62274cce591880e2d012c38b1755").unwrap(); + block.header.witness_mr = from_hex("90a557390ce185318375546cb1244ffda3bb62274cce591880e2d012c38b1755").unwrap(); block.header.output_mmr_size += 4000; block.header.kernel_mr = from_hex("f5e08e66e9c0e5e3818d96a694f4f6eafd689f38cea2e52e771eab2cc7a3941a").unwrap(); block.header.kernel_mmr_size += 1; @@ -164,14 +165,13 @@ pub fn get_stibbons_genesis_block_raw() -> Block { header: BlockHeader { version: 0, height: 0, - prev_hash: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], + prev_hash: vec![0; BLOCK_HASH_LENGTH], timestamp: 1_611_835_200.into(), // 28/01/2021 @ 12:00pm (UTC) output_mr: from_hex("dcc44f39b65e5e1e526887e7d56f7b85e2ea44bd29bc5bc195e6e015d19e1c06").unwrap(), - range_proof_mr: from_hex("e4d7dab49a66358379a901b9a36c10f070aa9d7bdc8ae752947b6fc4e55d255f").unwrap(), + witness_mr: from_hex("e4d7dab49a66358379a901b9a36c10f070aa9d7bdc8ae752947b6fc4e55d255f").unwrap(), output_mmr_size: 1, kernel_mr: from_hex("589bc62ac5d9139f921c68b8075c32d8d130024acaf3196d1d6a89df601e2bcf").unwrap(), + input_mr: vec![0; BLOCK_HASH_LENGTH], kernel_mmr_size: 1, total_kernel_offset: PrivateKey::from_hex( "0000000000000000000000000000000000000000000000000000000000000000", @@ -232,15 +232,14 @@ pub fn get_weatherwax_genesis_block_raw() -> Block { header: BlockHeader { version: 0, height: 0, - prev_hash: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], - timestamp: 1_624_020_963.into(), // Fri Jun 18 2021 12:56:03 GMT+0000 + prev_hash: vec![0; BLOCK_HASH_LENGTH], + timestamp: 1_624_957_036.into(), // Tue Jun 29 2021 08:57:16 GMT+0000 output_mr: from_hex("dcc44f39b65e5e1e526887e7d56f7b85e2ea44bd29bc5bc195e6e015d19e1c06").unwrap(), - range_proof_mr: from_hex("e4d7dab49a66358379a901b9a36c10f070aa9d7bdc8ae752947b6fc4e55d255f").unwrap(), + witness_mr: from_hex("e4d7dab49a66358379a901b9a36c10f070aa9d7bdc8ae752947b6fc4e55d255f").unwrap(), output_mmr_size: 1, kernel_mr: from_hex("589bc62ac5d9139f921c68b8075c32d8d130024acaf3196d1d6a89df601e2bcf").unwrap(), kernel_mmr_size: 1, + input_mr: vec![0; BLOCK_HASH_LENGTH], total_kernel_offset: PrivateKey::from_hex( "0000000000000000000000000000000000000000000000000000000000000000", ) @@ -280,7 +279,7 @@ pub fn get_ridcully_genesis_block() -> ChainBlock { } // fix headers to new mmr roots after adding utxos block.header.output_mr = from_hex("a939fda2579fb0b6fd906111f61e37c5ea23eccd8b737eb7da517fde71a98078").unwrap(); - block.header.range_proof_mr = from_hex("90a557390ce185318375546cb1244ffda3bb62274cce591880e2d012c38b1755").unwrap(); + block.header.witness_mr = from_hex("90a557390ce185318375546cb1244ffda3bb62274cce591880e2d012c38b1755").unwrap(); block.header.output_mmr_size += 4000; block.header.kernel_mr = from_hex("f5e08e66e9c0e5e3818d96a694f4f6eafd689f38cea2e52e771eab2cc7a3941a").unwrap(); block.header.kernel_mmr_size += 1; @@ -340,15 +339,14 @@ pub fn get_ridcully_genesis_block_raw() -> Block { header: BlockHeader { version: 0, height: 0, - prev_hash: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], + prev_hash: vec![0; BLOCK_HASH_LENGTH], timestamp: 1_603_843_200.into(), // 10/28/2020 @ 12:00am (UTC) output_mr: from_hex("dcc44f39b65e5e1e526887e7d56f7b85e2ea44bd29bc5bc195e6e015d19e1c06").unwrap(), - range_proof_mr: from_hex("e4d7dab49a66358379a901b9a36c10f070aa9d7bdc8ae752947b6fc4e55d255f").unwrap(), + witness_mr: from_hex("e4d7dab49a66358379a901b9a36c10f070aa9d7bdc8ae752947b6fc4e55d255f").unwrap(), output_mmr_size: 1, kernel_mr: from_hex("589bc62ac5d9139f921c68b8075c32d8d130024acaf3196d1d6a89df601e2bcf").unwrap(), kernel_mmr_size: 1, + input_mr: vec![0; BLOCK_HASH_LENGTH], total_kernel_offset: PrivateKey::from_hex( "0000000000000000000000000000000000000000000000000000000000000000", ) diff --git a/base_layer/core/src/chain_storage/blockchain_database.rs b/base_layer/core/src/chain_storage/blockchain_database.rs index bb1d67865e..e74e919794 100644 --- a/base_layer/core/src/chain_storage/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/blockchain_database.rs @@ -656,8 +656,9 @@ where B: BlockchainBackend let roots = self.calculate_mmr_roots(&block)?; block.header.kernel_mr = roots.kernel_mr; block.header.kernel_mmr_size = roots.kernel_mmr_size; + block.header.input_mr = roots.input_mr; block.header.output_mr = roots.output_mr; - block.header.range_proof_mr = roots.range_proof_mr; + block.header.witness_mr = roots.witness_mr; block.header.output_mmr_size = roots.output_mmr_size; Ok(block) } @@ -899,8 +900,9 @@ fn unexpected_result(req: DbKey, res: DbValue) -> Result(db: &T, block: &Block) -> Resul let deleted = deleted.deleted; let mut kernel_mmr = MerkleMountainRange::::new(kernels); let mut output_mmr = MutableMmr::::new(outputs, deleted)?; - let mut proof_mmr = MerkleMountainRange::::new(range_proofs); + let mut witness_mmr = MerkleMountainRange::::new(range_proofs); + let mut input_mmr = MutableMmr::::new(Vec::new(), Bitmap::create())?; for kernel in body.kernels().iter() { kernel_mmr.push(kernel.hash())?; @@ -933,17 +936,18 @@ pub fn calculate_mmr_roots(db: &T, block: &Block) -> Resul for output in body.outputs().iter() { output_mmr.push(output.hash())?; - proof_mmr.push(output.proof().hash())?; + witness_mmr.push(output.witness_hash())?; } for input in body.inputs().iter() { - let index = - db.fetch_mmr_leaf_index(MmrTree::Utxo, &input.hash())? - .ok_or_else(|| ChainStorageError::ValueNotFound { - entity: "UTXO".to_string(), - field: "hash".to_string(), - value: input.hash().to_hex(), - })?; + let index = db + .fetch_mmr_leaf_index(MmrTree::Utxo, &input.output_hash())? + .ok_or_else(|| ChainStorageError::ValueNotFound { + entity: "UTXO".to_string(), + field: "hash".to_string(), + value: input.output_hash().to_hex(), + })?; + input_mmr.push(input.hash())?; if !output_mmr.delete(index) { let len = output_mmr.len(); @@ -959,9 +963,10 @@ pub fn calculate_mmr_roots(db: &T, block: &Block) -> Resul let mmr_roots = MmrRoots { kernel_mr: kernel_mmr.get_merkle_root()?, kernel_mmr_size: kernel_mmr.get_leaf_count()? as u64, + input_mr: input_mmr.get_merkle_root()?, output_mr: output_mmr.get_merkle_root()?, - output_mmr_size: proof_mmr.get_leaf_count()? as u64, - range_proof_mr: proof_mmr.get_merkle_root()?, + output_mmr_size: witness_mmr.get_leaf_count()? as u64, + witness_mr: witness_mmr.get_merkle_root()?, }; Ok(mmr_roots) } @@ -1909,8 +1914,7 @@ mod test { chain_strength_comparer::strongest_chain, consensus_constants::PowAlgorithmConstants, ConsensusConstantsBuilder, - ConsensusManagerBuilder, - Network, + ConsensusManager, }, proof_of_work::AchievedTargetDifficulty, test_helpers::{ @@ -1921,6 +1925,7 @@ mod test { validation::{header_validator::HeaderValidator, mocks::MockValidator}, }; use std::collections::HashMap; + use tari_common::configuration::Network; #[test] fn lmdb_fetch_monero_seeds() { @@ -2451,7 +2456,7 @@ mod test { let mock_validator = Box::new(MockValidator::new(true)); // A real validator is needed here to test target difficulties - let consensus = ConsensusManagerBuilder::new(Network::LocalNet) + let consensus = ConsensusManager::builder(Network::LocalNet) .with_consensus_constants( ConsensusConstantsBuilder::new(Network::LocalNet) .clear_proof_of_work() diff --git a/base_layer/core/src/chain_storage/db_transaction.rs b/base_layer/core/src/chain_storage/db_transaction.rs index 6e3f908cfc..92e189ce11 100644 --- a/base_layer/core/src/chain_storage/db_transaction.rs +++ b/base_layer/core/src/chain_storage/db_transaction.rs @@ -387,7 +387,7 @@ impl fmt::Display for WriteOperation { } => write!( f, "Insert input {} in block: {} position: {}", - input.hash().to_hex(), + input.output_hash().to_hex(), header_hash.to_hex(), mmr_position ), diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs index 8e4ab59e05..933318b0e1 100644 --- a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs +++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs @@ -806,11 +806,11 @@ impl LMDBDatabase { } let mut output_mmr = MutableMmr::::new(pruned_output_set, deleted.deleted)?; - let mut proof_mmr = MerkleMountainRange::::new(pruned_proof_set); + let mut witness_mmr = MerkleMountainRange::::new(pruned_proof_set); for output in outputs { total_utxo_sum = &total_utxo_sum + &output.commitment; output_mmr.push(output.hash())?; - proof_mmr.push(output.proof().hash())?; + witness_mmr.push(output.witness_hash())?; trace!( target: LOG_TARGET, "Inserting output `{}`", @@ -821,14 +821,14 @@ impl LMDBDatabase { block_hash.clone(), header.height, output, - (proof_mmr.get_leaf_count()? - 1) as u32, + (witness_mmr.get_leaf_count()? - 1) as u32, )?; } for input in inputs { total_utxo_sum = &total_utxo_sum - &input.commitment; let index = self - .fetch_mmr_leaf_index(&**txn, MmrTree::Utxo, &input.hash())? + .fetch_mmr_leaf_index(&**txn, MmrTree::Utxo, &input.output_hash())? .ok_or(ChainStorageError::UnspendableInput)?; if !output_mmr.delete(index) { return Err(ChainStorageError::InvalidOperation(format!( @@ -851,7 +851,7 @@ impl LMDBDatabase { &BlockAccumulatedData::new( kernel_mmr.get_pruned_hash_set()?, output_mmr.mmr().get_pruned_hash_set()?, - proof_mmr.get_pruned_hash_set()?, + witness_mmr.get_pruned_hash_set()?, output_mmr.deleted().clone(), total_kernel_sum, ), @@ -957,7 +957,7 @@ impl LMDBDatabase { match mmr_tree { MmrTree::Kernel => block_accum_data.kernels = pruned_hash_set, MmrTree::Utxo => block_accum_data.outputs = pruned_hash_set, - MmrTree::RangeProof => block_accum_data.range_proofs = pruned_hash_set, + MmrTree::Witness => block_accum_data.range_proofs = pruned_hash_set, } lmdb_replace(&write_txn, &self.block_accumulated_data_db, &height, &block_accum_data)?; @@ -1647,7 +1647,7 @@ impl BlockchainBackend for LMDBDatabase { match tree { MmrTree::Kernel => Ok(lmdb_len(&txn, &self.kernels_db)? as u64), MmrTree::Utxo => Ok(lmdb_len(&txn, &self.utxos_db)? as u64), - MmrTree::RangeProof => { + MmrTree::Witness => { // lmdb_len(&txn, &self.utxo) unimplemented!("Need to get rangeproof mmr size") }, diff --git a/base_layer/core/src/chain_storage/mmr_tree.rs b/base_layer/core/src/chain_storage/mmr_tree.rs index d0a1141dab..7dcfa33365 100644 --- a/base_layer/core/src/chain_storage/mmr_tree.rs +++ b/base_layer/core/src/chain_storage/mmr_tree.rs @@ -30,13 +30,13 @@ use std::{ pub enum MmrTree { Utxo, Kernel, - RangeProof, + Witness, } impl Display for MmrTree { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { match self { - MmrTree::RangeProof => f.write_str("Range Proof"), + MmrTree::Witness => f.write_str("Witness"), MmrTree::Utxo => f.write_str("UTXO"), MmrTree::Kernel => f.write_str("Kernel"), } @@ -50,7 +50,7 @@ impl TryFrom for MmrTree { match v { 0 => Ok(MmrTree::Utxo), 1 => Ok(MmrTree::Kernel), - 2 => Ok(MmrTree::RangeProof), + 2 => Ok(MmrTree::Witness), _ => Err("Invalid MmrTree".into()), } } diff --git a/base_layer/core/src/consensus/consensus_constants.rs b/base_layer/core/src/consensus/consensus_constants.rs index 96f8ec8303..52c0620e8c 100644 --- a/base_layer/core/src/consensus/consensus_constants.rs +++ b/base_layer/core/src/consensus/consensus_constants.rs @@ -21,12 +21,13 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - consensus::{network::Network, KERNEL_WEIGHT, WEIGHT_PER_OUTPUT}, + consensus::{network::NetworkConsensus, KERNEL_WEIGHT, WEIGHT_PER_OUTPUT}, proof_of_work::{Difficulty, PowAlgorithm}, transactions::tari_amount::{uT, MicroTari, T}, }; use chrono::{DateTime, Duration, Utc}; use std::{collections::HashMap, ops::Add}; +use tari_common::configuration::Network; use tari_crypto::tari_utilities::epoch_time::EpochTime; /// This is the inner struct used to control all consensus values. @@ -400,7 +401,10 @@ impl ConsensusConstantsBuilder { pub fn new(network: Network) -> Self { Self { // TODO: Resolve this unwrap - consensus: network.create_consensus_constants().pop().unwrap(), + consensus: NetworkConsensus::from(network) + .create_consensus_constants() + .pop() + .expect("Empty consensus constants"), } } diff --git a/base_layer/core/src/consensus/consensus_manager.rs b/base_layer/core/src/consensus/consensus_manager.rs index db7765df35..0663d153ca 100644 --- a/base_layer/core/src/consensus/consensus_manager.rs +++ b/base_layer/core/src/consensus/consensus_manager.rs @@ -34,13 +34,14 @@ use crate::{ consensus::{ chain_strength_comparer::{strongest_chain, ChainStrengthComparer}, emission::{Emission, EmissionSchedule}, - network::Network, ConsensusConstants, + NetworkConsensus, }, proof_of_work::{DifficultyAdjustmentError, PowAlgorithm, TargetDifficultyWindow}, transactions::tari_amount::MicroTari, }; use std::{convert::TryFrom, sync::Arc}; +use tari_common::configuration::Network; use thiserror::Error; #[derive(Debug, Error)] @@ -71,7 +72,7 @@ impl ConsensusManager { /// Returns the genesis block for the selected network. pub fn get_genesis_block(&self) -> ChainBlock { - match self.inner.network { + match self.inner.network.as_network() { Network::MainNet => get_mainnet_genesis_block(), Network::Ridcully => get_ridcully_genesis_block(), Network::Stibbons => get_stibbons_genesis_block(), @@ -137,7 +138,7 @@ impl ConsensusManager { } /// This is the currently configured chain network. - pub fn network(&self) -> Network { + pub fn network(&self) -> NetworkConsensus { self.inner.network } } @@ -148,7 +149,7 @@ struct ConsensusManagerInner { /// This is the inner struct used to control all consensus values. pub consensus_constants: Vec, /// The configured chain network. - pub network: Network, + pub network: NetworkConsensus, /// The configuration for the emission schedule for integer only. pub emission: EmissionSchedule, /// This allows the user to set a custom Genesis block @@ -160,7 +161,7 @@ struct ConsensusManagerInner { /// Constructor for the consensus manager struct pub struct ConsensusManagerBuilder { consensus_constants: Vec, - network: Network, + network: NetworkConsensus, gen_block: Option, chain_strength_comparer: Option>, } @@ -170,7 +171,7 @@ impl ConsensusManagerBuilder { pub fn new(network: Network) -> Self { ConsensusManagerBuilder { consensus_constants: vec![], - network, + network: network.into(), gen_block: None, chain_strength_comparer: None, } diff --git a/base_layer/core/src/consensus/mod.rs b/base_layer/core/src/consensus/mod.rs index 1ddfdedc61..f0432fbf29 100644 --- a/base_layer/core/src/consensus/mod.rs +++ b/base_layer/core/src/consensus/mod.rs @@ -43,4 +43,4 @@ pub use consensus_constants::{ConsensusConstants, ConsensusConstantsBuilder}; #[cfg(feature = "base_node")] pub use consensus_manager::{ConsensusManager, ConsensusManagerBuilder, ConsensusManagerError}; #[cfg(any(feature = "base_node", feature = "transactions"))] -pub use network::Network; +pub use network::NetworkConsensus; diff --git a/base_layer/core/src/consensus/network.rs b/base_layer/core/src/consensus/network.rs index 4866852af5..55e17e3c98 100644 --- a/base_layer/core/src/consensus/network.rs +++ b/base_layer/core/src/consensus/network.rs @@ -21,46 +21,32 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use super::consensus_constants::ConsensusConstants; -use tari_common::configuration::Network as GlobalNetwork; -/// Specifies the configured chain network. +use tari_common::configuration::Network; + +/// Represents the consensus used for a given network #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Network { - /// Mainnet of Tari, currently should panic if network is set to this. - MainNet, - /// Alpha net version - // Rincewind, - /// Second test net version - Ridcully, - /// Third test net - Stibbons, - /// Fourth test net, includes tari script - Weatherwax, - /// Local network constants used inside of unit and integration tests. Contains the genesis block to be used for - /// that chain. - LocalNet, -} +pub struct NetworkConsensus(Network); -impl Network { +impl NetworkConsensus { pub fn create_consensus_constants(&self) -> Vec { - match self { - Network::MainNet => ConsensusConstants::mainnet(), - Network::Ridcully => ConsensusConstants::ridcully(), - Network::Stibbons => ConsensusConstants::stibbons(), - Network::Weatherwax => ConsensusConstants::weatherwax(), - Network::LocalNet => ConsensusConstants::localnet(), + use Network::*; + match self.as_network() { + MainNet => ConsensusConstants::mainnet(), + Ridcully => ConsensusConstants::ridcully(), + Stibbons => ConsensusConstants::stibbons(), + Weatherwax => ConsensusConstants::weatherwax(), + LocalNet => ConsensusConstants::localnet(), } } + + #[inline] + pub fn as_network(self) -> Network { + self.0 + } } -impl From for Network { - fn from(global_network: GlobalNetwork) -> Self { - match global_network { - GlobalNetwork::MainNet => Network::MainNet, - GlobalNetwork::Ridcully => Network::Ridcully, - GlobalNetwork::Stibbons => Network::Stibbons, - GlobalNetwork::Weatherwax => Network::Weatherwax, - GlobalNetwork::LocalNet => Network::LocalNet, - GlobalNetwork::Rincewind => unimplemented!("Rincewind has been retired"), - } +impl From for NetworkConsensus { + fn from(global_network: Network) -> Self { + Self(global_network) } } diff --git a/base_layer/core/src/lib.rs b/base_layer/core/src/lib.rs index 0b60f04c50..2e4bdc2f49 100644 --- a/base_layer/core/src/lib.rs +++ b/base_layer/core/src/lib.rs @@ -22,11 +22,6 @@ // Needed to make futures::select! work #![recursion_limit = "512"] -// Used to eliminate the need for boxing futures in many cases. -// Tracking issue: https://github.com/rust-lang/rust/issues/63063 -#![allow(incomplete_features)] -#![feature(type_alias_impl_trait)] -#![feature(min_type_alias_impl_trait)] #![feature(shrink_to)] // #![cfg_attr(not(debug_assertions), deny(unused_variables))] // #![cfg_attr(not(debug_assertions), deny(unused_imports))] diff --git a/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs b/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs index bd102bbef6..5626242474 100644 --- a/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs +++ b/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs @@ -154,12 +154,13 @@ impl Clone for ReorgPool { mod test { use super::*; use crate::{ - consensus::{ConsensusManagerBuilder, Network}, + consensus::ConsensusManagerBuilder, test_helpers::create_orphan_block, transactions::tari_amount::MicroTari, tx, }; use std::{thread, time::Duration}; + use tari_common::configuration::Network; #[test] fn test_insert_rlu_and_ttl() { diff --git a/base_layer/core/src/mempool/service/initializer.rs b/base_layer/core/src/mempool/service/initializer.rs index e31227c5bd..cb57d58e94 100644 --- a/base_layer/core/src/mempool/service/initializer.rs +++ b/base_layer/core/src/mempool/service/initializer.rs @@ -37,7 +37,7 @@ use crate::{ proto, transactions::transaction::Transaction, }; -use futures::{channel::mpsc, future, Future, Stream, StreamExt}; +use futures::{channel::mpsc, future, Stream, StreamExt}; use log::*; use std::{convert::TryFrom, sync::Arc}; use tari_comms_dht::Dht; @@ -48,6 +48,7 @@ use tari_p2p::{ tari_message::TariMessageType, }; use tari_service_framework::{ + async_trait, reply_channel, ServiceInitializationError, ServiceInitializer, @@ -134,10 +135,9 @@ async fn extract_transaction(msg: Arc) -> Option>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { // Create streams for receiving Mempool service requests and response messages from comms let inbound_request_stream = self.inbound_request_stream(); let inbound_response_stream = self.inbound_response_stream(); @@ -189,6 +189,6 @@ impl ServiceInitializer for MempoolServiceInitializer { info!(target: LOG_TARGET, "Mempool Service shutdown"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/core/src/mempool/sync_protocol/initializer.rs b/base_layer/core/src/mempool/sync_protocol/initializer.rs index 97fe8186f7..df40de648a 100644 --- a/base_layer/core/src/mempool/sync_protocol/initializer.rs +++ b/base_layer/core/src/mempool/sync_protocol/initializer.rs @@ -28,13 +28,13 @@ use crate::{ MempoolServiceConfig, }, }; -use futures::{channel::mpsc, future}; +use futures::channel::mpsc; use tari_comms::{ connectivity::ConnectivityRequester, protocol::{ProtocolExtension, ProtocolExtensionContext, ProtocolExtensionError, ProtocolNotification}, Substream, }; -use tari_service_framework::{ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; +use tari_service_framework::{async_trait, ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; pub struct MempoolSyncInitializer { config: MempoolServiceConfig, @@ -63,10 +63,9 @@ impl MempoolSyncInitializer { } } +#[async_trait] impl ServiceInitializer for MempoolSyncInitializer { - type Future = future::Ready>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { let config = self.config; let mempool = self.mempool.clone(); let notif_rx = self.notif_rx.take().unwrap(); @@ -84,6 +83,6 @@ impl ServiceInitializer for MempoolSyncInitializer { .run() }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs index 4c9a328326..5cfe2b6125 100644 --- a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs +++ b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs @@ -304,7 +304,7 @@ impl UnconfirmedPool { mod test { use super::*; use crate::{ - consensus::{ConsensusManagerBuilder, Network}, + consensus::ConsensusManagerBuilder, test_helpers::create_orphan_block, transactions::{ fee::Fee, @@ -316,6 +316,7 @@ mod test { }, tx, }; + use tari_common::configuration::Network; use tari_crypto::script; #[test] diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index 5ffbacaace..0dcb26b0b5 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -289,10 +289,11 @@ mod test { prev_hash: vec![0], timestamp: Default::default(), output_mr: vec![0], - range_proof_mr: vec![0], + witness_mr: vec![0], output_mmr_size: 0, kernel_mr: vec![0], kernel_mmr_size: 0, + input_mr: vec![0], total_kernel_offset: Default::default(), total_script_offset: Default::default(), nonce: 0, @@ -343,10 +344,11 @@ mod test { prev_hash: vec![0], timestamp: Default::default(), output_mr: vec![0], - range_proof_mr: vec![0], + witness_mr: vec![0], output_mmr_size: 0, kernel_mr: vec![0], kernel_mmr_size: 0, + input_mr: vec![0], total_kernel_offset: Default::default(), total_script_offset: Default::default(), nonce: 0, @@ -393,10 +395,11 @@ mod test { prev_hash: vec![0], timestamp: Default::default(), output_mr: vec![0], - range_proof_mr: vec![0], + witness_mr: vec![0], output_mmr_size: 0, kernel_mr: vec![0], kernel_mmr_size: 0, + input_mr: vec![0], total_kernel_offset: Default::default(), total_script_offset: Default::default(), nonce: 0, @@ -441,10 +444,11 @@ mod test { prev_hash: vec![0], timestamp: Default::default(), output_mr: vec![0], - range_proof_mr: vec![0], + witness_mr: vec![0], output_mmr_size: 0, kernel_mr: vec![0], kernel_mmr_size: 0, + input_mr: vec![0], total_kernel_offset: Default::default(), total_script_offset: Default::default(), nonce: 0, @@ -494,10 +498,11 @@ mod test { prev_hash: vec![0], timestamp: Default::default(), output_mr: vec![0], - range_proof_mr: vec![0], + witness_mr: vec![0], output_mmr_size: 0, kernel_mr: vec![0], kernel_mmr_size: 0, + input_mr: vec![0], total_kernel_offset: Default::default(), total_script_offset: Default::default(), nonce: 0, @@ -543,10 +548,11 @@ mod test { prev_hash: vec![0], timestamp: Default::default(), output_mr: vec![0], - range_proof_mr: vec![0], + witness_mr: vec![0], output_mmr_size: 0, kernel_mr: vec![0], kernel_mmr_size: 0, + input_mr: vec![0], total_kernel_offset: Default::default(), total_script_offset: Default::default(), nonce: 0, @@ -583,10 +589,11 @@ mod test { prev_hash: vec![0], timestamp: Default::default(), output_mr: vec![0], - range_proof_mr: vec![0], + witness_mr: vec![0], output_mmr_size: 0, kernel_mr: vec![0], kernel_mmr_size: 0, + input_mr: vec![0], total_kernel_offset: Default::default(), total_script_offset: Default::default(), nonce: 0, diff --git a/base_layer/core/src/proof_of_work/sha3_pow.rs b/base_layer/core/src/proof_of_work/sha3_pow.rs index 7eb81e6889..cf10321dab 100644 --- a/base_layer/core/src/proof_of_work/sha3_pow.rs +++ b/base_layer/core/src/proof_of_work/sha3_pow.rs @@ -42,10 +42,14 @@ pub fn sha3_hash(header: &BlockHeader) -> Vec { .chain(header.height.to_le_bytes()) .chain(header.prev_hash.as_bytes()) .chain(header.timestamp.as_u64().to_le_bytes()) + .chain(header.input_mr.as_bytes()) .chain(header.output_mr.as_bytes()) - .chain(header.range_proof_mr.as_bytes()) + .chain(header.output_mmr_size.to_le_bytes()) + .chain(header.witness_mr.as_bytes()) .chain(header.kernel_mr.as_bytes()) + .chain(header.kernel_mmr_size.to_le_bytes()) .chain(header.total_kernel_offset.as_bytes()) + .chain(header.total_script_offset.as_bytes()) .chain(header.nonce.to_le_bytes()) .chain(header.pow.to_bytes()) .finalize() @@ -90,6 +94,6 @@ pub mod test { fn validate_max_target() { let mut header = get_header(); header.nonce = 1; - assert_eq!(sha3_difficulty(&header), Difficulty::from(2)); + assert_eq!(sha3_difficulty(&header), Difficulty::from(1)); } } diff --git a/base_layer/core/src/proto/block.proto b/base_layer/core/src/proto/block.proto index f3f4857b2b..a61f46347f 100644 --- a/base_layer/core/src/proto/block.proto +++ b/base_layer/core/src/proto/block.proto @@ -29,23 +29,25 @@ message BlockHeader { // This is the UTXO merkle root of the outputs // This is calculated as Hash (txo MMR root || roaring bitmap hash of UTXO indices) bytes output_mr = 6; - // This is the MMR root of the range proofs - bytes range_proof_mr = 7; + // This is the MMR root of the output witness data + bytes witness_mr = 7; // This is the MMR root of the kernels bytes kernel_mr = 8; + // This is the Merkle root of the inputs in this block + bytes input_mr = 9; // Total accumulated sum of kernel offsets since genesis block. We can derive the kernel offset sum for *this* // block from the total kernel offset of the previous block header. - bytes total_kernel_offset = 9; + bytes total_kernel_offset = 10; // Nonce increment used to mine this block. - uint64 nonce = 10; + uint64 nonce = 11; // Proof of work metadata - ProofOfWork pow = 11; + ProofOfWork pow = 12; // The size of the kernel MMR - uint64 kernel_mmr_size = 12; + uint64 kernel_mmr_size = 13; // The size of the output MMR - uint64 output_mmr_size = 13; + uint64 output_mmr_size = 14; // Sum of script offsets for all kernels in this block. - bytes total_script_offset = 14; + bytes total_script_offset = 15; } // A Tari block. Blocks are linked together into a blockchain. diff --git a/base_layer/core/src/proto/block_header.rs b/base_layer/core/src/proto/block_header.rs index 947c877d1a..4258836106 100644 --- a/base_layer/core/src/proto/block_header.rs +++ b/base_layer/core/src/proto/block_header.rs @@ -57,10 +57,11 @@ impl TryFrom for BlockHeader { prev_hash: header.prev_hash, timestamp, output_mr: header.output_mr, - range_proof_mr: header.range_proof_mr, + witness_mr: header.witness_mr, output_mmr_size: header.output_mmr_size, kernel_mr: header.kernel_mr, kernel_mmr_size: header.kernel_mmr_size, + input_mr: header.input_mr, total_kernel_offset, total_script_offset, nonce: header.nonce, @@ -77,8 +78,9 @@ impl From for proto::BlockHeader { prev_hash: header.prev_hash, timestamp: Some(datetime_to_timestamp(header.timestamp)), output_mr: header.output_mr, - range_proof_mr: header.range_proof_mr, + witness_mr: header.witness_mr, kernel_mr: header.kernel_mr, + input_mr: header.input_mr, total_kernel_offset: header.total_kernel_offset.to_vec(), total_script_offset: header.total_script_offset.to_vec(), nonce: header.nonce, diff --git a/base_layer/core/src/proto/transaction.proto b/base_layer/core/src/proto/transaction.proto index 002b3dcc95..acebf1f100 100644 --- a/base_layer/core/src/proto/transaction.proto +++ b/base_layer/core/src/proto/transaction.proto @@ -38,10 +38,8 @@ message TransactionInput { bytes script = 3; // The script input data, if any bytes input_data = 4; - // The block height that the UTXO was mined - uint64 height = 5; // A signature with k_s, signing the script, input data, and mined height - Signature script_signature = 6; + ComSignature script_signature = 6; // The offset pubkey, K_O bytes script_offset_public_key = 7; } diff --git a/base_layer/core/src/proto/transaction.rs b/base_layer/core/src/proto/transaction.rs index 382aa8cf92..b2d242f8a9 100644 --- a/base_layer/core/src/proto/transaction.rs +++ b/base_layer/core/src/proto/transaction.rs @@ -121,7 +121,6 @@ impl TryFrom for TransactionInput { commitment, script: TariScript::from_bytes(input.script.as_slice()).map_err(|err| format!("{:?}", err))?, input_data: ExecutionStack::from_bytes(input.input_data.as_slice()).map_err(|err| format!("{:?}", err))?, - height: input.height, script_signature, script_offset_public_key, }) @@ -135,7 +134,6 @@ impl From for proto::types::TransactionInput { commitment: Some(input.commitment.into()), script: input.script.as_bytes(), input_data: input.input_data.as_bytes(), - height: input.height, script_signature: Some(input.script_signature.into()), script_offset_public_key: input.script_offset_public_key.as_bytes().to_vec(), } diff --git a/base_layer/core/src/proto/types.proto b/base_layer/core/src/proto/types.proto index 608aa518db..4fb8dd2d1e 100644 --- a/base_layer/core/src/proto/types.proto +++ b/base_layer/core/src/proto/types.proto @@ -19,6 +19,14 @@ message Signature { bytes signature = 2; } +// Define the explicit ComSignature implementation for the Tari base layer. A different signature scheme can be +// employed by redefining this type. +message ComSignature { + bytes public_nonce_commitment = 1; + bytes signature_u = 2; + bytes signature_v = 3; +} + // BlindingFactor wrapper message BlindingFactor { bytes data = 1; diff --git a/base_layer/core/src/proto/types_impls.rs b/base_layer/core/src/proto/types_impls.rs index d2377c6d4f..e978d86724 100644 --- a/base_layer/core/src/proto/types_impls.rs +++ b/base_layer/core/src/proto/types_impls.rs @@ -21,7 +21,15 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use super::types as proto; -use crate::transactions::types::{BlindingFactor, Commitment, HashOutput, PrivateKey, PublicKey, Signature}; +use crate::transactions::types::{ + BlindingFactor, + ComSignature, + Commitment, + HashOutput, + PrivateKey, + PublicKey, + Signature, +}; use std::convert::TryFrom; use tari_crypto::tari_utilities::{ByteArray, ByteArrayError}; @@ -65,6 +73,30 @@ impl From for proto::Signature { } } +//---------------------------------- ComSignature --------------------------------------------// + +impl TryFrom for ComSignature { + type Error = ByteArrayError; + + fn try_from(sig: proto::ComSignature) -> Result { + let public_nonce = Commitment::from_bytes(&sig.public_nonce_commitment)?; + let signature_u = PrivateKey::from_bytes(&sig.signature_u)?; + let signature_v = PrivateKey::from_bytes(&sig.signature_v)?; + + Ok(Self::new(public_nonce, signature_u, signature_v)) + } +} + +impl From for proto::ComSignature { + fn from(sig: ComSignature) -> Self { + Self { + public_nonce_commitment: sig.public_nonce().to_vec(), + signature_u: sig.u().to_vec(), + signature_v: sig.v().to_vec(), + } + } +} + //---------------------------------- HashOutput --------------------------------------------// impl From for HashOutput { diff --git a/base_layer/core/src/test_helpers/blockchain.rs b/base_layer/core/src/test_helpers/blockchain.rs index dfde3a1d13..9d91017976 100644 --- a/base_layer/core/src/test_helpers/blockchain.rs +++ b/base_layer/core/src/test_helpers/blockchain.rs @@ -41,13 +41,7 @@ use crate::{ PrunedOutput, Validators, }, - consensus::{ - chain_strength_comparer::ChainStrengthComparerBuilder, - ConsensusConstantsBuilder, - ConsensusManager, - ConsensusManagerBuilder, - Network, - }, + consensus::{chain_strength_comparer::ChainStrengthComparerBuilder, ConsensusConstantsBuilder, ConsensusManager}, transactions::{ transaction::{TransactionInput, TransactionKernel, TransactionOutput}, types::{CryptoFactories, HashOutput, Signature}, @@ -64,6 +58,7 @@ use std::{ ops::Deref, path::{Path, PathBuf}, }; +use tari_common::configuration::Network; use tari_common_types::chain_metadata::ChainMetadata; use tari_storage::lmdb_store::LMDBConfig; use tari_test_utils::paths::create_temporary_data_path; @@ -73,7 +68,7 @@ pub fn create_new_blockchain() -> BlockchainDatabase { let network = Network::Weatherwax; let consensus_constants = ConsensusConstantsBuilder::new(network).build(); let genesis = get_weatherwax_genesis_block(); - let consensus_manager = ConsensusManagerBuilder::new(network) + let consensus_manager = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants) .with_block(genesis) .on_ties(ChainStrengthComparerBuilder::new().by_height().build()) @@ -121,7 +116,7 @@ pub fn create_store_with_consensus(rules: ConsensusManager) -> BlockchainDatabas } pub fn create_test_blockchain_db() -> BlockchainDatabase { let network = Network::Weatherwax; - let rules = ConsensusManagerBuilder::new(network).build(); + let rules = ConsensusManager::builder(network).build(); create_store_with_consensus(rules) } diff --git a/base_layer/core/src/test_helpers/mod.rs b/base_layer/core/src/test_helpers/mod.rs index c8a4c0cc8c..cf8c19bd32 100644 --- a/base_layer/core/src/test_helpers/mod.rs +++ b/base_layer/core/src/test_helpers/mod.rs @@ -27,19 +27,15 @@ pub mod blockchain; use crate::{ blocks::{Block, BlockHeader}, - consensus::ConsensusManager, - transactions::transaction::Transaction, -}; - -use crate::{ chain_storage::{BlockHeaderAccumulatedData, ChainHeader}, - consensus::{ConsensusManagerBuilder, Network}, + consensus::ConsensusManager, crypto::tari_utilities::Hashable, proof_of_work::{sha3_difficulty, AchievedTargetDifficulty, Difficulty}, - transactions::{types::CryptoFactories, CoinbaseBuilder}, + transactions::{transaction::Transaction, types::CryptoFactories, CoinbaseBuilder}, }; use rand::{distributions::Alphanumeric, Rng}; use std::{iter, path::Path, sync::Arc}; +use tari_common::configuration::Network; use tari_comms::PeerManager; use tari_storage::{lmdb_store::LMDBBuilder, LMDBWrapper}; @@ -57,7 +53,7 @@ pub fn create_block(block_version: u16, block_height: u64, transactions: Vec (Vec, Vec, Vec) { @@ -378,12 +378,16 @@ impl AggregateBody { } /// this will validate the script offset of the aggregate body. - fn validate_script_offset(&self, script_offset: PublicKey) -> Result<(), TransactionError> { + fn validate_script_offset( + &self, + script_offset: PublicKey, + factory: &CommitmentFactory, + ) -> Result<(), TransactionError> { trace!(target: LOG_TARGET, "Checking script offset"); // lets count up the input script public keys let mut input_keys = PublicKey::default(); for input in &self.inputs { - input_keys = input_keys + input.run_and_verify_script()?; + input_keys = input_keys + input.run_and_verify_script(factory)?; } // Now lets gather the output public keys and hashes. @@ -416,7 +420,7 @@ impl AggregateBody { fn validate_sender_signatures(&self) -> Result<(), TransactionError> { trace!(target: LOG_TARGET, "Checking sender signatures"); for o in &self.outputs { - let _ = o.verify_sender_signature()?; + o.verify_sender_signature()?; } Ok(()) } diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index a0f9545823..fcbe01fd5a 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -201,7 +201,6 @@ impl CoinbaseBuilder { Some(output_features), script, inputs!(PublicKey::from_secret_key(&script_private_key)), - height, script_private_key, script_offset_pub_key, sender_sig, @@ -241,7 +240,7 @@ impl CoinbaseBuilder { #[cfg(test)] mod test { use crate::{ - consensus::{emission::Emission, ConsensusManager, ConsensusManagerBuilder, Network}, + consensus::{emission::Emission, ConsensusManager, ConsensusManagerBuilder}, transactions::{ coinbase_builder::CoinbaseBuildError, helpers::TestParams, @@ -253,6 +252,7 @@ mod test { }, }; use rand::rngs::OsRng; + use tari_common::configuration::Network; use tari_crypto::{commitment::HomomorphicCommitmentFactory, keys::SecretKey as SecretKeyTrait}; fn get_builder() -> (CoinbaseBuilder, ConsensusManager, CryptoFactories) { diff --git a/base_layer/core/src/transactions/helpers.rs b/base_layer/core/src/transactions/helpers.rs index dbc96ca3ea..e5976f6463 100644 --- a/base_layer/core/src/transactions/helpers.rs +++ b/base_layer/core/src/transactions/helpers.rs @@ -169,7 +169,6 @@ pub fn create_unblinded_output( Some(output_features), script, inputs!(PublicKey::from_secret_key(&test_params.script_private_key)), - 0, // TODO: This will be removed by a later PR test_params.script_private_key, test_params.script_offset_pub_key, sender_sig, @@ -211,12 +210,11 @@ macro_rules! tx { /// The output of this macro is intended to be used in [spend_utxos]. #[macro_export] macro_rules! txn_schema { - (from: $input:expr, to: $outputs:expr, fee: $fee:expr, lock: $lock:expr, mined_height: $mined_height:expr, features: $features:expr) => {{ + (from: $input:expr, to: $outputs:expr, fee: $fee:expr, lock: $lock:expr, features: $features:expr) => {{ $crate::transactions::helpers::TransactionSchema { from: $input.clone(), to: $outputs.clone(), fee: $fee, - mined_height: $mined_height, lock_height: $lock, features: $features } @@ -228,7 +226,6 @@ macro_rules! txn_schema { to:$outputs, fee:$fee, lock:$lock, - mined_height: 0, features: $features ) }}; @@ -259,7 +256,6 @@ macro_rules! txn_schema { pub struct TransactionSchema { pub from: Vec, pub to: Vec, - pub mined_height: u64, pub fee: MicroTari, pub lock_height: u64, pub features: OutputFeatures, @@ -354,8 +350,7 @@ pub fn spend_utxos(schema: TransactionSchema) -> (Transaction, Vec (Transaction, Vec, script: TariScript, input_data: ExecutionStack, - height: u64, script_private_key: PrivateKey, script_offset_public_key: PublicKey, sender_metadata_signature: Signature, @@ -234,7 +234,6 @@ impl UnblindedOutput { features: features.unwrap_or_default(), script, input_data, - height, script_private_key, script_offset_public_key, sender_metadata_signature, @@ -244,25 +243,33 @@ impl UnblindedOutput { /// Commits an UnblindedOutput into a Transaction input pub fn as_transaction_input(&self, factory: &CommitmentFactory) -> Result { let commitment = factory.commit(&self.spending_key, &self.value.into()); - let script_nonce = PrivateKey::random(&mut OsRng); - let public_script_nonce = PublicKey::from_secret_key(&script_nonce); + let script_nonce_a = PrivateKey::random(&mut OsRng); + let script_nonce_b = PrivateKey::random(&mut OsRng); + let nonce_commitment = factory.commit(&script_nonce_b, &script_nonce_a); + let e = Challenge::new() - .chain(public_script_nonce.as_bytes()) + .chain(nonce_commitment.as_bytes()) .chain(self.script.as_bytes().as_slice()) .chain(self.input_data.as_bytes().as_slice()) - //.chain(&self.height.to_le_bytes()) //TODO decide if the height should remain in this signature + .chain(PublicKey::from_secret_key(&self.script_private_key).as_bytes()) + .chain(commitment.as_bytes()) .result() .to_vec(); - - let script_signature = Signature::sign(self.script_private_key.clone(), script_nonce, &e) - .map_err(|_| TransactionError::InvalidSignatureError("Generating script signature".to_string()))?; + let script_signature = ComSignature::sign( + self.value.into(), + self.script_private_key.clone() + self.spending_key.clone(), + script_nonce_a, + script_nonce_b, + &e, + factory, + ) + .map_err(|_| TransactionError::InvalidSignatureError("Generating script signature".to_string()))?; Ok(TransactionInput { features: self.features.clone(), commitment, script: self.script.clone(), input_data: self.input_data.clone(), - height: self.height, script_signature, script_offset_public_key: self.script_offset_public_key.clone(), }) @@ -370,10 +377,8 @@ pub struct TransactionInput { pub script: TariScript, /// The script input data, if any pub input_data: ExecutionStack, - /// The block height that the UTXO was mined - pub height: u64, /// A signature with k_s, signing the script, input data, and mined height - pub script_signature: Signature, + pub script_signature: ComSignature, /// The offset pubkey, K_O pub script_offset_public_key: PublicKey, } @@ -386,8 +391,7 @@ impl TransactionInput { commitment: Commitment, script: TariScript, input_data: ExecutionStack, - height: u64, - script_signature: Signature, + script_signature: ComSignature, script_offset_public_key: PublicKey, ) -> TransactionInput { TransactionInput { @@ -395,7 +399,6 @@ impl TransactionInput { commitment, script, input_data, - height, script_signature, script_offset_public_key, } @@ -412,7 +415,7 @@ impl TransactionInput { } /// This will check if the input and the output is the same commitment by looking at the commitment and features. - /// This will ignore the output rangeproof + /// This will ignore the output range proof pub fn is_equal_to(&self, output: &TransactionOutput) -> bool { self.commitment == output.commitment && self.features == output.features } @@ -428,16 +431,24 @@ impl TransactionInput { } } - pub fn validate_script_signature(&self, key: &PublicKey) -> Result<(), TransactionError> { - let r = self.script_signature.get_public_nonce(); - let m = HashDigest::new() - .chain(r.as_bytes()) - .chain(self.script.as_bytes()) - .chain(self.input_data.as_bytes()) - //.chain(self.height.to_le_bytes()) //TODO decide if the height should remain in the script signature + pub fn validate_script_signature( + &self, + public_script_key: &PublicKey, + factory: &CommitmentFactory, + ) -> Result<(), TransactionError> { + let nonce_commitment = self.script_signature.public_nonce(); + let m = Challenge::new() + .chain(nonce_commitment.as_bytes()) + .chain(self.script.as_bytes().as_slice()) + .chain(self.input_data.as_bytes().as_slice()) + .chain(public_script_key.as_bytes()) + .chain(self.commitment.as_bytes()) .result() .to_vec(); - if self.script_signature.verify_challenge(key, &m) { + if self + .script_signature + .verify_challenge(&(&self.commitment + public_script_key), &m, factory) + { Ok(()) } else { Err(TransactionError::InvalidSignatureError( @@ -448,16 +459,25 @@ impl TransactionInput { /// This will run the script and verify the script signature. If its valid, it will return the resulting public key /// from the script. - pub fn run_and_verify_script(&self) -> Result { + pub fn run_and_verify_script(&self, factory: &CommitmentFactory) -> Result { let key = self.run_script()?; - self.validate_script_signature(&key)?; + self.validate_script_signature(&key, factory)?; Ok(key) } + + /// Returns the hash of the output data contained in this input. + /// This hash matches the hash of a transaction output that this input spends. + pub fn output_hash(&self) -> Vec { + HashDigest::new() + .chain(self.features.to_bytes()) + .chain(self.commitment.as_bytes()) + .chain(self.script.as_bytes()) + .result() + .to_vec() + } } /// Implement the canonical hashing function for TransactionInput for use in ordering -// Note we use the hash of an UTXO to ID it, so we need the hash of the TransactionInput to equal the hash of the -// TransactionOutput impl Hashable for TransactionInput { fn hash(&self) -> Vec { HashDigest::new() @@ -465,6 +485,10 @@ impl Hashable for TransactionInput { .chain(self.commitment.as_bytes()) .chain(self.script.as_bytes()) .chain(self.script_offset_public_key.as_bytes()) + .chain(self.script_signature.u().as_bytes()) + .chain(self.script_signature.v().as_bytes()) + .chain(self.script_signature.public_nonce().as_bytes()) + .chain(self.input_data.as_bytes()) .result() .to_vec() } @@ -601,7 +625,7 @@ impl TransactionOutput { } /// This will check if the input and the output is the same commitment by looking at the commitment and features. - /// This will ignore the output rangeproof + /// This will ignore the output range proof #[inline] pub fn is_equal_to(&self, output: &TransactionInput) -> bool { self.commitment == output.commitment && self.features == output.features @@ -617,18 +641,18 @@ impl TransactionOutput { script: &TariScript, features: &OutputFeatures, script_offset_public_key: &PublicKey, - puplic_nonce: &PublicKey, + public_nonce: &PublicKey, ) -> MessageHash { Challenge::new() .chain(script.as_bytes()) .chain(features.to_bytes()) .chain(script_offset_public_key.as_bytes()) - .chain(puplic_nonce.as_bytes()) + .chain(public_nonce.as_bytes()) .result() .to_vec() } - /// Create sender signature fore the output meta data + /// Create sender signature for the output meta data pub fn create_sender_signature( script: &TariScript, output_features: &OutputFeatures, @@ -643,6 +667,15 @@ impl TransactionOutput { ); Signature::sign(script_offset_private_key.clone(), sender_sig_pvt_nonce, &e).unwrap() } + + pub fn witness_hash(&self) -> Vec { + HashDigest::new() + .chain(self.proof.as_bytes()) + .chain(self.sender_metadata_signature.get_signature().as_bytes()) + .chain(self.sender_metadata_signature.get_public_nonce().as_bytes()) + .result() + .to_vec() + } } /// Implement the canonical hashing function for TransactionOutput for use in ordering. @@ -658,7 +691,6 @@ impl Hashable for TransactionOutput { .chain(self.commitment.as_bytes()) // .chain(range proof) // See docs as to why we exclude this .chain(self.script.as_bytes()) - .chain(self.script_offset_public_key.as_bytes()) .result() .to_vec() } @@ -1011,7 +1043,7 @@ impl Transaction { .fold(0, |max_maturity, input| max(max_maturity, input.features.maturity)) } - /// Returns the maximum timelock of the kernels inside of the transaction + /// Returns the maximum time lock of the kernels inside of the transaction pub fn max_kernel_timelock(&self) -> u64 { self.body .kernels() @@ -1172,6 +1204,17 @@ mod test { script::ExecutionStack, }; + #[test] + fn input_and_output_hash_match() { + let test_params = TestParams::new(); + let factory = PedersenCommitmentFactory::default(); + + let i = create_unblinded_output(script!(Nop), OutputFeatures::default(), test_params, 10.into()); + let output = i.as_transaction_output(&CryptoFactories::default()).unwrap(); + let input = i.as_transaction_input(&factory).unwrap(); + assert_eq!(output.hash(), input.output_hash()); + } + #[test] fn unblinded_input() { let test_params = TestParams::new(); @@ -1315,15 +1358,13 @@ mod test { let script = TariScript::default(); let input_data = ExecutionStack::default(); - let height = 0; - let script_signature = Signature::default(); + let script_signature = ComSignature::default(); let offset_pub_key = PublicKey::default(); let mut input = TransactionInput::new( OutputFeatures::default(), c, script, input_data, - height, script_signature, offset_pub_key, ); @@ -1331,7 +1372,7 @@ mod test { let mut kernel = create_test_kernel(0.into(), 0); let mut tx = Transaction::new(Vec::new(), Vec::new(), Vec::new(), 0.into(), 0.into()); - // lets add timelocks + // lets add time locks input.features.maturity = 5; kernel.lock_height = 2; tx.body.add_input(input.clone()); diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index ef57f18af7..431164b963 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -58,6 +58,8 @@ use tari_crypto::{ /// This struct contains all the information that a transaction initiator (the sender) will manage throughout the /// Transaction construction process. +// TODO: Investigate necessity to use the 'Serialize' and 'Deserialize' traits here; this could potentially leak +// TODO: information when least expected. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub(super) struct RawTransactionInfo { pub num_recipients: usize, diff --git a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index 3ba24e9c9c..b1b3825b5e 100644 --- a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -117,7 +117,7 @@ impl SingleReceiverTransactionProtocol { sender_info.script_offset_public_key.clone(), sender_info.sender_metadata_signature.clone(), ); - let _ = output.verify_sender_signature()?; + output.verify_sender_signature()?; Ok(output) } } diff --git a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs index 47b4392126..de5ea895fd 100644 --- a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs +++ b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs @@ -85,7 +85,7 @@ pub struct SenderTransactionInitializer { private_nonce: Option, message: Option, prevent_fee_gt_amount: bool, - recipient_outpout_features: FixedSet, + recipient_output_features: FixedSet, recipient_scripts: FixedSet, recipient_script_offset_private_keys: FixedSet, } @@ -123,7 +123,7 @@ impl SenderTransactionInitializer { excess_blinding_factor: BlindingFactor::default(), message: None, prevent_fee_gt_amount: true, - recipient_outpout_features: FixedSet::new(num_recipients), + recipient_output_features: FixedSet::new(num_recipients), recipient_scripts: FixedSet::new(num_recipients), recipient_script_offset_private_keys: FixedSet::new(num_recipients), } @@ -149,10 +149,10 @@ impl SenderTransactionInitializer { receiver_index: usize, script: TariScript, recipient_script_offset_private_key: PrivateKey, - recipient_outpout_features: OutputFeatures, + recipient_output_features: OutputFeatures, ) -> &mut Self { - self.recipient_outpout_features - .set_item(receiver_index, recipient_outpout_features); + self.recipient_output_features + .set_item(receiver_index, recipient_output_features); self.recipient_scripts.set_item(receiver_index, script); self.recipient_script_offset_private_keys .set_item(receiver_index, recipient_script_offset_private_key); @@ -287,7 +287,6 @@ impl SenderTransactionInitializer { .as_ref() .ok_or("Change script was not provided")? .clone(), - 0, self.change_script_private_key .as_ref() .ok_or("Change script private key was not provided")? @@ -486,7 +485,7 @@ impl SenderTransactionInitializer { amount_to_self, ids, amounts: self.amounts.into_vec(), - recipient_output_features: self.recipient_outpout_features.into_vec(), + recipient_output_features: self.recipient_output_features.into_vec(), recipient_scripts: self.recipient_scripts.into_vec(), recipient_script_offset_private_keys: self.recipient_script_offset_private_keys.into_vec(), change, @@ -722,7 +721,6 @@ mod test { let factories = CryptoFactories::default(); let p = TestParams::new(); let (utxo, input) = create_test_input(MicroTari(500), 0, &factories.commitment); - let script = script!(Nop); let output = create_unblinded_output(script.clone(), OutputFeatures::default(), p.clone(), MicroTari(400)); // Start the builder @@ -747,7 +745,6 @@ mod test { let factories = CryptoFactories::default(); let p = TestParams::new(); let (utxo, input) = create_test_input(MicroTari(400), 0, &factories.commitment); - let script = script!(Nop); let output = create_unblinded_output(script.clone(), OutputFeatures::default(), p.clone(), MicroTari(400)); // Start the builder @@ -772,7 +769,6 @@ mod test { let factories = CryptoFactories::default(); let p = TestParams::new(); let (utxo, input) = create_test_input(MicroTari(100_000), 0, &factories.commitment); - let script = script!(Nop); let output = create_unblinded_output(script.clone(), OutputFeatures::default(), p.clone(), MicroTari(15000)); // Start the builder diff --git a/base_layer/core/src/transactions/types.rs b/base_layer/core/src/transactions/types.rs index 3154c81203..40b0fa625d 100644 --- a/base_layer/core/src/transactions/types.rs +++ b/base_layer/core/src/transactions/types.rs @@ -27,6 +27,7 @@ use tari_crypto::{ ristretto::{ dalek_range_proof::DalekRangeProofService, pedersen::{PedersenCommitment, PedersenCommitmentFactory}, + RistrettoComSig, RistrettoPublicKey, RistrettoSchnorr, RistrettoSecretKey, @@ -36,6 +37,8 @@ use tari_crypto::{ /// Define the explicit Signature implementation for the Tari base layer. A different signature scheme can be /// employed by redefining this type. pub type Signature = RistrettoSchnorr; +/// Define the explicit Commitment Signature implementation for the Tari base layer. +pub type ComSignature = RistrettoComSig; /// Define the explicit Commitment implementation for the Tari base layer. pub type Commitment = PedersenCommitment; diff --git a/base_layer/core/src/validation/block_validators.rs b/base_layer/core/src/validation/block_validators.rs index 6bf2df4a78..c835a3a785 100644 --- a/base_layer/core/src/validation/block_validators.rs +++ b/base_layer/core/src/validation/block_validators.rs @@ -163,7 +163,7 @@ fn check_inputs_are_utxos(block: &Block, db: &B) -> Result .ok_or(ValidationError::PreviousHashNotFound)?; for input in block.body.inputs() { - if let Some((_, index, _height)) = db.fetch_output(&input.hash())? { + if let Some((_, index, _height)) = db.fetch_output(&input.output_hash())? { if data.deleted().contains(index) { warn!( target: LOG_TARGET, @@ -208,6 +208,16 @@ fn check_not_duplicate_txos(block: &Block, db: &B) -> Resu fn check_mmr_roots(block: &Block, db: &B) -> Result<(), ValidationError> { let mmr_roots = chain_storage::calculate_mmr_roots(db, &block)?; let header = &block.header; + if header.input_mr != mmr_roots.input_mr { + warn!( + target: LOG_TARGET, + "Block header input merkle root in {} do not match calculated root. Expected: {}, Actual:{}", + block.hash().to_hex(), + header.input_mr.to_hex(), + mmr_roots.input_mr.to_hex() + ); + return Err(ValidationError::BlockError(BlockValidationError::MismatchedMmrRoots)); + } if header.kernel_mr != mmr_roots.kernel_mr { warn!( target: LOG_TARGET, @@ -242,7 +252,7 @@ fn check_mmr_roots(block: &Block, db: &B) -> Result<(), Va ); return Err(ValidationError::BlockError(BlockValidationError::MismatchedMmrRoots)); }; - if header.range_proof_mr != mmr_roots.range_proof_mr { + if header.witness_mr != mmr_roots.witness_mr { warn!( target: LOG_TARGET, "Block header range_proof MMR roots in {} do not match calculated roots", @@ -404,7 +414,7 @@ impl BlockValidator { ); return Err(ValidationError::BlockError(BlockValidationError::MismatchedMmrRoots)); } - if header.range_proof_mr != mmr_roots.range_proof_mr { + if header.witness_mr != mmr_roots.witness_mr { warn!( target: LOG_TARGET, "Block header range_proof MMR roots in {} do not match calculated roots", diff --git a/base_layer/core/src/validation/test.rs b/base_layer/core/src/validation/test.rs index 54fdf87573..9216f44cad 100644 --- a/base_layer/core/src/validation/test.rs +++ b/base_layer/core/src/validation/test.rs @@ -22,10 +22,11 @@ use crate::{ blocks::BlockHeader, - consensus::{ConsensusManagerBuilder, Network}, + consensus::ConsensusManagerBuilder, test_helpers::{blockchain::create_store_with_consensus, create_chain_header}, validation::header_iter::HeaderIter, }; +use tari_common::configuration::Network; #[test] fn header_iter_empty_and_invalid_height() { diff --git a/base_layer/core/src/validation/transaction_validators.rs b/base_layer/core/src/validation/transaction_validators.rs index e1cfa7fae3..99062a2832 100644 --- a/base_layer/core/src/validation/transaction_validators.rs +++ b/base_layer/core/src/validation/transaction_validators.rs @@ -27,7 +27,6 @@ use crate::{ validation::{MempoolTransactionValidation, ValidationError}, }; use log::*; -use tari_crypto::tari_utilities::hash::Hashable; pub const LOG_TARGET: &str = "c::val::transaction_validators"; @@ -132,7 +131,7 @@ fn verify_not_stxos(tx: &Transaction, db: &B) -> Result<() ) }); for input in tx.body.inputs() { - if let Some((_, index, _height)) = db.fetch_output(&input.hash())? { + if let Some((_, index, _height)) = db.fetch_output(&input.output_hash())? { if data.deleted().contains(index) { warn!( target: LOG_TARGET, diff --git a/base_layer/core/tests/async_db.rs b/base_layer/core/tests/async_db.rs index da1ca8ffe4..a69e8c4c14 100644 --- a/base_layer/core/tests/async_db.rs +++ b/base_layer/core/tests/async_db.rs @@ -30,10 +30,10 @@ use helpers::{ sample_blockchains::{create_blockchain_db_no_cut_through, create_new_blockchain}, }; use std::ops::Deref; +use tari_common::configuration::Network; use tari_core::{ blocks::Block, chain_storage::{async_db::AsyncBlockchainDb, BlockAddResult}, - consensus::Network, transactions::{ helpers::schema_to_transaction, tari_amount::T, diff --git a/base_layer/core/tests/base_node_rpc.rs b/base_layer/core/tests/base_node_rpc.rs index 995247a3c0..9b96512d47 100644 --- a/base_layer/core/tests/base_node_rpc.rs +++ b/base_layer/core/tests/base_node_rpc.rs @@ -49,6 +49,7 @@ use crate::helpers::{ nodes::{BaseNodeBuilder, NodeInterfaces}, }; use std::convert::TryFrom; +use tari_common::configuration::Network; use tari_comms::protocol::rpc::mock::RpcRequestMock; use tari_core::{ base_node::{ @@ -64,7 +65,7 @@ use tari_core::{ state_machine_service::states::{ListeningInfo, StateInfo, StatusInfo}, }, chain_storage::ChainBlock, - consensus::{ConsensusManager, ConsensusManagerBuilder, Network}, + consensus::{ConsensusManager, ConsensusManagerBuilder, NetworkConsensus}, crypto::tari_utilities::Hashable, proto::{ base_node::{FetchMatchingUtxos, Signatures as SignaturesProto}, @@ -92,7 +93,7 @@ fn setup() -> ( Runtime, TempDir, ) { - let network = Network::LocalNet; + let network = NetworkConsensus::from(Network::LocalNet); let consensus_constants = network.create_consensus_constants(); let factories = CryptoFactories::default(); let mut runtime = Runtime::new().unwrap(); @@ -100,8 +101,7 @@ fn setup() -> ( let (block0, utxo0) = create_genesis_block_with_coinbase_value(&factories, 100_000_000.into(), &consensus_constants[0]); - let consensus_manager = ConsensusManagerBuilder::new(network) - .with_consensus_constants(consensus_constants[0].clone()) + let consensus_manager = ConsensusManagerBuilder::new(network.as_network()) .with_block(block0.clone()) .build(); @@ -145,7 +145,7 @@ fn test_base_node_wallet_rpc() { from: vec![utxos1[0].clone()], to: vec![400_000 * uT, 590_000 * uT] )]); - let mut tx2 = (*txs2[0]).clone(); + let tx2 = (*txs2[0]).clone(); let tx2_sig = tx2.first_kernel_excess_sig().unwrap().clone(); // Query Tx1 @@ -193,10 +193,6 @@ fn test_base_node_wallet_rpc() { .block_on(base_node.local_nci.submit_block(block1.clone(), Broadcast::from(true))) .is_ok()); - // fix tx mined height - for input in tx2.body.inputs_mut() { - input.height = 1; - } // Check that subitting Tx2 will now be accepted let msg = TransactionProto::from(tx2); let req = request_mock.request_with_context(Default::default(), msg); diff --git a/base_layer/core/tests/block_validation.rs b/base_layer/core/tests/block_validation.rs index 0d19e19436..ec6d7b96e4 100644 --- a/base_layer/core/tests/block_validation.rs +++ b/base_layer/core/tests/block_validation.rs @@ -23,15 +23,11 @@ use crate::helpers::block_builders::chain_block_with_new_coinbase; use monero::blockdata::block::Block as MoneroBlock; use std::sync::Arc; +use tari_common::configuration::Network; use tari_core::{ blocks::{Block, BlockHeaderValidationError}, chain_storage::{BlockchainDatabase, BlockchainDatabaseConfig, ChainStorageError, Validators}, - consensus::{ - consensus_constants::PowAlgorithmConstants, - ConsensusConstantsBuilder, - ConsensusManagerBuilder, - Network, - }, + consensus::{consensus_constants::PowAlgorithmConstants, ConsensusConstantsBuilder, ConsensusManagerBuilder}, crypto::tari_utilities::hex::Hex, proof_of_work::{ monero_rx, diff --git a/base_layer/core/tests/chain_storage_tests/chain_backend.rs b/base_layer/core/tests/chain_storage_tests/chain_backend.rs index 5e59b45155..77ee4747e9 100644 --- a/base_layer/core/tests/chain_storage_tests/chain_backend.rs +++ b/base_layer/core/tests/chain_storage_tests/chain_backend.rs @@ -21,9 +21,10 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::helpers::database::create_orphan_block; +use tari_common::configuration::Network; use tari_core::{ chain_storage::{create_lmdb_database, BlockchainBackend, ChainStorageError, DbKey, DbTransaction, DbValue}, - consensus::{ConsensusManagerBuilder, Network}, + consensus::ConsensusManagerBuilder, test_helpers::blockchain::create_test_db, tx, }; diff --git a/base_layer/core/tests/chain_storage_tests/chain_storage.rs b/base_layer/core/tests/chain_storage_tests/chain_storage.rs index 1e3eede9f1..b3d50b82fa 100644 --- a/base_layer/core/tests/chain_storage_tests/chain_storage.rs +++ b/base_layer/core/tests/chain_storage_tests/chain_storage.rs @@ -38,6 +38,7 @@ use crate::helpers::{ test_blockchain::TestBlockchain, }; use rand::{rngs::OsRng, RngCore}; +use tari_common::configuration::Network; use tari_common_types::types::BlockHash; use tari_core::{ blocks::{genesis_block, Block, BlockHeader}, @@ -51,7 +52,7 @@ use tari_core::{ DbTransaction, Validators, }, - consensus::{emission::Emission, ConsensusConstantsBuilder, ConsensusManagerBuilder, Network}, + consensus::{emission::Emission, ConsensusConstantsBuilder, ConsensusManagerBuilder}, proof_of_work::Difficulty, test_helpers::blockchain::{ create_store_with_consensus, diff --git a/base_layer/core/tests/helpers/block_builders.rs b/base_layer/core/tests/helpers/block_builders.rs index 05656f9426..95098e986d 100644 --- a/base_layer/core/tests/helpers/block_builders.rs +++ b/base_layer/core/tests/helpers/block_builders.rs @@ -23,6 +23,7 @@ use croaring::Bitmap; use rand::{rngs::OsRng, RngCore}; use std::{iter::repeat_with, sync::Arc}; +use tari_common::configuration::Network; use tari_core::{ blocks::{Block, BlockHeader, NewBlockTemplate}, chain_storage::{ @@ -34,7 +35,7 @@ use tari_core::{ ChainHeader, ChainStorageError, }, - consensus::{emission::Emission, ConsensusConstants, ConsensusManager, ConsensusManagerBuilder, Network}, + consensus::{emission::Emission, ConsensusConstants, ConsensusManager, ConsensusManagerBuilder}, proof_of_work::{sha3_difficulty, AchievedTargetDifficulty, Difficulty}, transactions::{ helpers::{ @@ -67,9 +68,6 @@ use tari_crypto::{ }; use tari_mmr::MutableMmr; -const _MAINNET: Network = Network::MainNet; -const _WEATHERWAX: Network = Network::Weatherwax; - pub fn create_coinbase( factories: &CryptoFactories, value: MicroTari, @@ -111,7 +109,7 @@ fn genesis_template( // This is a helper function to generate and print out a block that can be used as the genesis block. // #[test] pub fn _create_act_gen_block() { - let network = _WEATHERWAX; + let network = Network::Weatherwax; let consensus_manager: ConsensusManager = ConsensusManagerBuilder::new(network).build(); let factories = CryptoFactories::default(); let mut header = BlockHeader::new(consensus_manager.consensus_constants(0).blockchain_version()); @@ -128,11 +126,11 @@ pub fn _create_act_gen_block() { .unwrap(); let utxo_hash = utxo.hash(); - let rp = utxo.proof().hash(); + let witness_hash = utxo.witness_hash(); let kern = kernel.hash(); header.kernel_mr = kern; header.output_mr = utxo_hash; - header.range_proof_mr = rp; + header.witness_mr = witness_hash; let block = header.into_builder().with_coinbase_utxo(utxo, kernel).build(); println!("{}", &block); dbg!(&key.to_hex()); @@ -158,7 +156,7 @@ fn update_genesis_block_mmr_roots(template: NewBlockTemplate) -> Result = body.kernels().iter().map(|k| k.hash()).collect(); let out_hashes: Vec = body.outputs().iter().map(|out| out.hash()).collect(); - let rp_hashes: Vec = body.outputs().iter().map(|out| out.proof().hash()).collect(); + let rp_hashes: Vec = body.outputs().iter().map(|out| out.witness_hash()).collect(); let mut header = BlockHeader::from(header); header.kernel_mr = MutableMmr::::new(kernel_hashes, Bitmap::create()) @@ -167,7 +165,7 @@ fn update_genesis_block_mmr_roots(template: NewBlockTemplate) -> Result::new(out_hashes, Bitmap::create()) .unwrap() .get_merkle_root()?; - header.range_proof_mr = MutableMmr::::new(rp_hashes, Bitmap::create()) + header.witness_mr = MutableMmr::::new(rp_hashes, Bitmap::create()) .unwrap() .get_merkle_root()?; Ok(Block { header, body }) @@ -506,11 +504,11 @@ pub fn generate_block_with_coinbase( consensus, ); let new_block = db.prepare_block_merkle_roots(template)?; - let result = db.add_block(new_block.into()); - if let Ok(BlockAddResult::Ok(ref b)) = result { + let result = db.add_block(new_block.into())?; + if let BlockAddResult::Ok(ref b) = result { blocks.push(b.as_ref().clone()); } - result + Ok(result) } #[allow(dead_code)] diff --git a/base_layer/core/tests/helpers/mock_state_machine.rs b/base_layer/core/tests/helpers/mock_state_machine.rs index 692f08d28b..7d49f93e85 100644 --- a/base_layer/core/tests/helpers/mock_state_machine.rs +++ b/base_layer/core/tests/helpers/mock_state_machine.rs @@ -20,9 +20,8 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use futures::future; use tari_core::base_node::{state_machine_service::states::StatusInfo, StateMachineHandle}; -use tari_service_framework::{ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; +use tari_service_framework::{async_trait, ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; use tokio::sync::{broadcast, watch}; pub struct MockBaseNodeStateMachine { @@ -55,10 +54,9 @@ pub struct MockBaseNodeStateMachineInitializer { status_receiver: watch::Receiver, } +#[async_trait] impl ServiceInitializer for MockBaseNodeStateMachineInitializer { - type Future = future::Ready>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { let (state_event_publisher, _) = broadcast::channel(10); let handle = StateMachineHandle::new( @@ -67,6 +65,6 @@ impl ServiceInitializer for MockBaseNodeStateMachineInitializer { context.get_shutdown_signal(), ); context.register_handle(handle); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/core/tests/helpers/nodes.rs b/base_layer/core/tests/helpers/nodes.rs index fde899d4d1..ebb4b6a3e1 100644 --- a/base_layer/core/tests/helpers/nodes.rs +++ b/base_layer/core/tests/helpers/nodes.rs @@ -24,6 +24,7 @@ use crate::helpers::mock_state_machine::MockBaseNodeStateMachine; use futures::Sink; use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; use std::{error::Error, iter, path::Path, sync::Arc, time::Duration}; +use tari_common::configuration::Network; use tari_comms::{ peer_manager::{NodeIdentity, PeerFeatures}, protocol::messaging::MessagingEventSender, @@ -40,7 +41,7 @@ use tari_core::{ StateMachineHandle, }, chain_storage::{BlockchainDatabase, Validators}, - consensus::{ConsensusManager, ConsensusManagerBuilder, Network}, + consensus::{ConsensusManager, ConsensusManagerBuilder, NetworkConsensus}, mempool::{ service::{LocalMempoolService, MempoolHandle}, Mempool, @@ -106,13 +107,13 @@ pub struct BaseNodeBuilder { liveness_service_config: Option, validators: Option>, consensus_manager: Option, - network: Network, + network: NetworkConsensus, } #[allow(dead_code)] impl BaseNodeBuilder { /// Create a new BaseNodeBuilder - pub fn new(network: Network) -> Self { + pub fn new(network: NetworkConsensus) -> Self { Self { node_identity: None, peers: None, @@ -189,7 +190,7 @@ impl BaseNodeBuilder { MockValidator::new(true), ) }); - let network = self.network; + let network = self.network.as_network(); let consensus_manager = self .consensus_manager .unwrap_or_else(|| ConsensusManagerBuilder::new(network).build()); @@ -234,11 +235,11 @@ pub fn create_network_with_2_base_nodes( let bob_node_identity = random_node_identity(); let network = Network::LocalNet; - let (alice_node, consensus_manager) = BaseNodeBuilder::new(network) + let (alice_node, consensus_manager) = BaseNodeBuilder::new(network.into()) .with_node_identity(alice_node_identity.clone()) .with_peers(vec![bob_node_identity.clone()]) .start(runtime, data_path); - let (bob_node, consensus_manager) = BaseNodeBuilder::new(network) + let (bob_node, consensus_manager) = BaseNodeBuilder::new(network.into()) .with_node_identity(bob_node_identity) .with_peers(vec![alice_node_identity]) .with_consensus_manager(consensus_manager) @@ -262,14 +263,14 @@ pub fn create_network_with_2_base_nodes_with_config>( let alice_node_identity = random_node_identity(); let bob_node_identity = random_node_identity(); let network = Network::LocalNet; - let (alice_node, consensus_manager) = BaseNodeBuilder::new(network) + let (alice_node, consensus_manager) = BaseNodeBuilder::new(network.into()) .with_node_identity(alice_node_identity.clone()) .with_base_node_service_config(base_node_service_config) .with_mempool_service_config(mempool_service_config) .with_liveness_service_config(liveness_service_config.clone()) .with_consensus_manager(consensus_manager) .start(runtime, data_path.as_ref().join("alice").as_os_str().to_str().unwrap()); - let (bob_node, consensus_manager) = BaseNodeBuilder::new(network) + let (bob_node, consensus_manager) = BaseNodeBuilder::new(network.into()) .with_node_identity(bob_node_identity) .with_peers(vec![alice_node_identity]) .with_base_node_service_config(base_node_service_config) @@ -322,14 +323,14 @@ pub fn create_network_with_3_base_nodes_with_config>( bob_node_identity.node_id().short_str(), carol_node_identity.node_id().short_str() ); - let (carol_node, consensus_manager) = BaseNodeBuilder::new(network) + let (carol_node, consensus_manager) = BaseNodeBuilder::new(network.into()) .with_node_identity(carol_node_identity.clone()) .with_base_node_service_config(base_node_service_config) .with_mempool_service_config(mempool_service_config) .with_liveness_service_config(liveness_service_config.clone()) .with_consensus_manager(consensus_manager) .start(runtime, data_path.as_ref().join("carol").as_os_str().to_str().unwrap()); - let (bob_node, consensus_manager) = BaseNodeBuilder::new(network) + let (bob_node, consensus_manager) = BaseNodeBuilder::new(network.into()) .with_node_identity(bob_node_identity.clone()) .with_peers(vec![carol_node_identity.clone()]) .with_base_node_service_config(base_node_service_config) @@ -337,7 +338,7 @@ pub fn create_network_with_3_base_nodes_with_config>( .with_liveness_service_config(liveness_service_config.clone()) .with_consensus_manager(consensus_manager) .start(runtime, data_path.as_ref().join("bob").as_os_str().to_str().unwrap()); - let (alice_node, consensus_manager) = BaseNodeBuilder::new(network) + let (alice_node, consensus_manager) = BaseNodeBuilder::new(network.into()) .with_node_identity(alice_node_identity) .with_peers(vec![bob_node_identity, carol_node_identity]) .with_base_node_service_config(base_node_service_config) diff --git a/base_layer/core/tests/helpers/sample_blockchains.rs b/base_layer/core/tests/helpers/sample_blockchains.rs index 169357181e..ddc6398fed 100644 --- a/base_layer/core/tests/helpers/sample_blockchains.rs +++ b/base_layer/core/tests/helpers/sample_blockchains.rs @@ -23,6 +23,7 @@ use crate::helpers::block_builders::{create_genesis_block, generate_new_block}; +use tari_common::configuration::Network; use tari_core::{ chain_storage::{ create_lmdb_database, @@ -32,7 +33,7 @@ use tari_core::{ LMDBDatabase, Validators, }, - consensus::{ConsensusConstants, ConsensusConstantsBuilder, ConsensusManager, ConsensusManagerBuilder, Network}, + consensus::{ConsensusConstants, ConsensusConstantsBuilder, ConsensusManager, ConsensusManagerBuilder}, test_helpers::blockchain::{create_store_with_consensus, TempDatabase}, transactions::{ tari_amount::{uT, T}, diff --git a/base_layer/core/tests/helpers/test_blockchain.rs b/base_layer/core/tests/helpers/test_blockchain.rs index 769dc19f30..b602f4d056 100644 --- a/base_layer/core/tests/helpers/test_blockchain.rs +++ b/base_layer/core/tests/helpers/test_blockchain.rs @@ -30,9 +30,10 @@ use crate::helpers::{ use log::*; use rand::{rngs::OsRng, RngCore}; use std::{collections::HashMap, sync::Arc}; +use tari_common::configuration::Network; use tari_core::{ chain_storage::{BlockAddResult, BlockchainDatabase}, - consensus::{ConsensusManager, Network}, + consensus::ConsensusManager, test_helpers::blockchain::TempDatabase, transactions::types::CryptoFactories, }; diff --git a/base_layer/core/tests/mempool.rs b/base_layer/core/tests/mempool.rs index 9c47c60c9c..d5ec884636 100644 --- a/base_layer/core/tests/mempool.rs +++ b/base_layer/core/tests/mempool.rs @@ -38,6 +38,7 @@ use helpers::{ use tari_crypto::keys::PublicKey as PublicKeyTrait; // use crate::helpers::database::create_store; use std::{ops::Deref, sync::Arc, time::Duration}; +use tari_common::configuration::Network; use tari_comms_dht::domain_message::OutboundDomainMessage; use tari_core::{ base_node::{ @@ -45,7 +46,7 @@ use tari_core::{ service::BaseNodeServiceConfig, state_machine_service::states::{ListeningInfo, StateInfo, StatusInfo}, }, - consensus::{ConsensusConstantsBuilder, ConsensusManagerBuilder, Network}, + consensus::{ConsensusConstantsBuilder, ConsensusManager, NetworkConsensus}, mempool::{Mempool, MempoolConfig, MempoolServiceConfig, MempoolServiceError, TxStorageResponse}, proof_of_work::Difficulty, proto, @@ -77,14 +78,14 @@ fn test_insert_and_process_published_block() { // Create a block with 4 outputs let txs = vec![txn_schema!( from: vec![outputs[0][0].clone()], - to: vec![2 * T, 2 * T, 2 * T, 2 * T],fee: 25.into(), lock: 0,mined_height: 0, features: OutputFeatures::default() + to: vec![2 * T, 2 * T, 2 * T, 2 * T],fee: 25.into(), lock: 0, features: OutputFeatures::default() )]; generate_new_block(&mut store, &mut blocks, &mut outputs, txs, &consensus_manager).unwrap(); // Create 6 new transactions to add to the mempool let (orphan, _, _) = tx!(1*T, fee: 100*uT); let orphan = Arc::new(orphan); - let tx2 = txn_schema!(from: vec![outputs[1][0].clone()], to: vec![1*T], fee: 20*uT, lock: 0,mined_height: 1, features: OutputFeatures::default()); + let tx2 = txn_schema!(from: vec![outputs[1][0].clone()], to: vec![1*T], fee: 20*uT, lock: 0, features: OutputFeatures::default()); let tx2 = Arc::new(spend_utxos(tx2).0); let tx3 = txn_schema!( @@ -92,7 +93,6 @@ fn test_insert_and_process_published_block() { to: vec![1*T], fee: 20*uT, lock: 4, - mined_height: 1, features: OutputFeatures::with_maturity(1) ); let tx3 = Arc::new(spend_utxos(tx3).0); @@ -102,11 +102,10 @@ fn test_insert_and_process_published_block() { to: vec![1*T], fee: 20*uT, lock: 3, - mined_height: 1, features: OutputFeatures::with_maturity(2) ); let tx5 = Arc::new(spend_utxos(tx5).0); - let tx6 = txn_schema!(from: vec![outputs[1][3].clone()], to: vec![1 * T], fee: 25*uT, lock: 0,mined_height: 1, features: OutputFeatures::default()); + let tx6 = txn_schema!(from: vec![outputs[1][3].clone()], to: vec![1 * T], fee: 25*uT, lock: 0, features: OutputFeatures::default()); let tx6 = spend_utxos(tx6).0; mempool.insert(orphan.clone()).unwrap(); @@ -212,12 +211,12 @@ fn test_time_locked() { // Create a block with 4 outputs let txs = vec![txn_schema!( from: vec![outputs[0][0].clone()], - to: vec![2 * T, 2 * T, 2 * T, 2 * T], fee: 25*uT, lock: 0,mined_height: 0, features: OutputFeatures::default() + to: vec![2 * T, 2 * T, 2 * T, 2 * T], fee: 25*uT, lock: 0, features: OutputFeatures::default() )]; generate_new_block(&mut store, &mut blocks, &mut outputs, txs, &consensus_manager).unwrap(); mempool.process_published_block(blocks[1].to_arc_block()).unwrap(); // Block height should be 1 - let mut tx2 = txn_schema!(from: vec![outputs[1][0].clone()], to: vec![1*T], fee: 20*uT, lock: 0,mined_height: 1, features: OutputFeatures::default()); + let mut tx2 = txn_schema!(from: vec![outputs[1][0].clone()], to: vec![1*T], fee: 20*uT, lock: 0, features: OutputFeatures::default()); tx2.lock_height = 3; let tx2 = Arc::new(spend_utxos(tx2).0); @@ -226,7 +225,6 @@ fn test_time_locked() { to: vec![1*T], fee: 20*uT, lock: 4, - mined_height: 1, features: OutputFeatures::with_maturity(1) ); tx3.lock_height = 2; @@ -263,17 +261,17 @@ fn test_retrieve() { mempool.process_published_block(blocks[1].to_arc_block()).unwrap(); // 1-Block, 8 UTXOs, empty mempool let txs = vec![ - txn_schema!(from: vec![outputs[1][0].clone()], to: vec![], fee: 30*uT, lock: 0, mined_height: 1, features: OutputFeatures::default()), - txn_schema!(from: vec![outputs[1][1].clone()], to: vec![], fee: 20*uT, lock: 0, mined_height: 1, features: OutputFeatures::default()), - txn_schema!(from: vec![outputs[1][2].clone()], to: vec![], fee: 40*uT, lock: 0, mined_height: 1, features: OutputFeatures::default()), - txn_schema!(from: vec![outputs[1][3].clone()], to: vec![], fee: 50*uT, lock: 0, mined_height: 1, features: OutputFeatures::default()), - txn_schema!(from: vec![outputs[1][4].clone()], to: vec![], fee: 20*uT, lock: 2, mined_height: 1, features: OutputFeatures::default()), - txn_schema!(from: vec![outputs[1][5].clone()], to: vec![], fee: 20*uT, lock: 3, mined_height: 1, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][0].clone()], to: vec![], fee: 30*uT, lock: 0, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][1].clone()], to: vec![], fee: 20*uT, lock: 0, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][2].clone()], to: vec![], fee: 40*uT, lock: 0, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][3].clone()], to: vec![], fee: 50*uT, lock: 0, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][4].clone()], to: vec![], fee: 20*uT, lock: 2, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][5].clone()], to: vec![], fee: 20*uT, lock: 3, features: OutputFeatures::default()), // Will be time locked when a tx is added to mempool with this as an input: - txn_schema!(from: vec![outputs[1][6].clone()], to: vec![800_000*uT], fee: 60*uT, lock: 0, mined_height: 1, + txn_schema!(from: vec![outputs[1][6].clone()], to: vec![800_000*uT], fee: 60*uT, lock: 0, features: OutputFeatures::with_maturity(4)), // Will be time locked when a tx is added to mempool with this as an input: - txn_schema!(from: vec![outputs[1][7].clone()], to: vec![800_000*uT], fee: 25*uT, lock: 0, mined_height: 1, + txn_schema!(from: vec![outputs[1][7].clone()], to: vec![800_000*uT], fee: 25*uT, lock: 0, features: OutputFeatures::with_maturity(3)), ]; let (tx, utxos) = schema_to_transaction(&txs); @@ -310,9 +308,9 @@ fn test_retrieve() { assert_eq!(stats.reorg_txs, 5); // Create transactions wih time-locked inputs let txs = vec![ - txn_schema!(from: vec![outputs[2][6].clone()], to: vec![], fee: 80*uT, lock: 0,mined_height: 2, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[2][6].clone()], to: vec![], fee: 80*uT, lock: 0, features: OutputFeatures::default()), // account for change output - txn_schema!(from: vec![outputs[2][8].clone()], to: vec![], fee: 40*uT, lock: 0,mined_height: 2, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[2][8].clone()], to: vec![], fee: 40*uT, lock: 0, features: OutputFeatures::default()), ]; let (tx2, _) = schema_to_transaction(&txs); tx2.iter().for_each(|t| { @@ -343,16 +341,16 @@ fn test_reorg() { // "Mine" Block 1 let txs = vec![ - txn_schema!(from: vec![outputs[0][0].clone()], to: vec![1 * T, 1 * T], fee: 25*uT, lock: 0,mined_height: 0, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[0][0].clone()], to: vec![1 * T, 1 * T], fee: 25*uT, lock: 0, features: OutputFeatures::default()), ]; generate_new_block(&mut db, &mut blocks, &mut outputs, txs, &consensus_manager).unwrap(); mempool.process_published_block(blocks[1].to_arc_block()).unwrap(); // "Mine" block 2 let schemas = vec![ - txn_schema!(from: vec![outputs[1][0].clone()], to: vec![], fee: 25*uT, lock: 0,mined_height: 1, features: OutputFeatures::default()), - txn_schema!(from: vec![outputs[1][1].clone()], to: vec![], fee: 25*uT, lock: 0,mined_height: 1, features: OutputFeatures::default()), - txn_schema!(from: vec![outputs[1][2].clone()], to: vec![], fee: 25*uT, lock: 0,mined_height: 1, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][0].clone()], to: vec![], fee: 25*uT, lock: 0, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][1].clone()], to: vec![], fee: 25*uT, lock: 0, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][2].clone()], to: vec![], fee: 25*uT, lock: 0, features: OutputFeatures::default()), ]; let (txns2, utxos) = schema_to_transaction(&schemas); outputs.push(utxos); @@ -367,9 +365,9 @@ fn test_reorg() { // "Mine" block 3 let schemas = vec![ - txn_schema!(from: vec![outputs[2][0].clone()], to: vec![], fee: 25*uT, lock: 0,mined_height: 2, features: OutputFeatures::default()), - txn_schema!(from: vec![outputs[2][1].clone()], to: vec![], fee: 25*uT, lock: 5, mined_height: 2, features: OutputFeatures::default()), - txn_schema!(from: vec![outputs[2][2].clone()], to: vec![], fee: 25*uT, lock: 0,mined_height: 2, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[2][0].clone()], to: vec![], fee: 25*uT, lock: 0, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[2][1].clone()], to: vec![], fee: 25*uT, lock: 5, features: OutputFeatures::default()), + txn_schema!(from: vec![outputs[2][2].clone()], to: vec![], fee: 25*uT, lock: 0, features: OutputFeatures::default()), ]; let (txns3, utxos) = schema_to_transaction(&schemas); outputs.push(utxos); @@ -428,7 +426,7 @@ fn request_response_get_stats() { .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); let (block0, utxo) = create_genesis_block(&factories, &consensus_constants); - let consensus_manager = ConsensusManagerBuilder::new(network) + let consensus_manager = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants) .with_block(block0) .build(); @@ -483,7 +481,7 @@ fn request_response_get_tx_state_by_excess_sig() { .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); let (block0, utxo) = create_genesis_block(&factories, &consensus_constants); - let consensus_manager = ConsensusManagerBuilder::new(network) + let consensus_manager = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants) .with_block(block0) .build(); @@ -553,7 +551,7 @@ fn receive_and_propagate_transaction() { .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); let (block0, utxo) = create_genesis_block(&factories, &consensus_constants); - let consensus_manager = ConsensusManagerBuilder::new(network) + let consensus_manager = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants) .with_block(block0) .build(); @@ -748,7 +746,7 @@ fn consensus_validation_large_tx() { fn service_request_timeout() { let mut runtime = Runtime::new().unwrap(); let network = Network::LocalNet; - let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let consensus_manager = ConsensusManager::builder(network).build(); let mempool_service_config = MempoolServiceConfig { request_timeout: Duration::from_millis(1), ..Default::default() @@ -789,13 +787,13 @@ fn block_event_and_reorg_event_handling() { // When block B2B is submitted with TX2B, TX3B, then TX2A, TX3A are discarded (Not Stored) let factories = CryptoFactories::default(); let network = Network::LocalNet; - let consensus_constants = network.create_consensus_constants(); + let consensus_constants = NetworkConsensus::from(network).create_consensus_constants(); let mut runtime = Runtime::new().unwrap(); let temp_dir = tempdir().unwrap(); let (block0, utxos0) = create_genesis_block_with_coinbase_value(&factories, 100_000_000.into(), &consensus_constants[0]); - let consensus_manager = ConsensusManagerBuilder::new(network) + let consensus_manager = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants[0].clone()) .with_block(block0.clone()) .build(); diff --git a/base_layer/core/tests/node_comms_interface.rs b/base_layer/core/tests/node_comms_interface.rs index 23b9794006..7677b76860 100644 --- a/base_layer/core/tests/node_comms_interface.rs +++ b/base_layer/core/tests/node_comms_interface.rs @@ -26,6 +26,7 @@ mod helpers; use futures::{channel::mpsc, StreamExt}; use helpers::block_builders::append_block; use std::sync::Arc; +use tari_common::configuration::Network; use tari_common_types::chain_metadata::ChainMetadata; use tari_comms::peer_manager::NodeId; use tari_core::{ @@ -33,9 +34,9 @@ use tari_core::{ comms_interface::{CommsInterfaceError, InboundNodeCommsHandlers, NodeCommsRequest, NodeCommsResponse}, OutboundNodeCommsInterface, }, - blocks::{genesis_block, BlockBuilder, BlockHeader}, + blocks::{BlockBuilder, BlockHeader}, chain_storage::{BlockchainDatabaseConfig, DbTransaction, HistoricalBlock, Validators}, - consensus::{ConsensusManagerBuilder, Network}, + consensus::{ConsensusManager, NetworkConsensus}, mempool::{Mempool, MempoolConfig}, test_helpers::blockchain::{create_store_with_consensus_and_validators_and_config, create_test_blockchain_db}, transactions::{helpers::create_utxo, tari_amount::MicroTari, types::CryptoFactories}, @@ -80,7 +81,7 @@ async fn inbound_get_metadata() { let mempool = new_mempool(); let network = Network::LocalNet; - let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let consensus_manager = ConsensusManager::builder(network).build(); let (block_event_sender, _) = broadcast::channel(50); let (request_sender, _) = reply_channel::unbounded(); let (block_sender, _) = mpsc::unbounded(); @@ -111,7 +112,7 @@ async fn inbound_fetch_kernel_by_excess_sig() { let mempool = new_mempool(); let network = Network::LocalNet; - let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let consensus_manager = ConsensusManager::builder(network).build(); let (block_event_sender, _) = broadcast::channel(50); let (request_sender, _) = reply_channel::unbounded(); let (block_sender, _) = mpsc::unbounded(); @@ -160,10 +161,7 @@ async fn inbound_fetch_headers() { let store = create_test_blockchain_db(); let mempool = new_mempool(); let network = Network::LocalNet; - let consensus_constants = network.create_consensus_constants(); - let consensus_manager = ConsensusManagerBuilder::new(network) - .with_consensus_constants(consensus_constants[0].clone()) - .build(); + let consensus_manager = ConsensusManager::builder(network).build(); let (block_event_sender, _) = broadcast::channel(50); let (request_sender, _) = reply_channel::unbounded(); let (block_sender, _) = mpsc::unbounded(); @@ -214,10 +212,7 @@ async fn inbound_fetch_utxos() { let store = create_test_blockchain_db(); let mempool = new_mempool(); let network = Network::LocalNet; - let consensus_constants = network.create_consensus_constants(); - let consensus_manager = ConsensusManagerBuilder::new(network) - .with_consensus_constants(consensus_constants[0].clone()) - .build(); + let consensus_manager = ConsensusManager::builder(network).build(); let (block_event_sender, _) = broadcast::channel(50); let (request_sender, _) = reply_channel::unbounded(); let (block_sender, _) = mpsc::unbounded(); @@ -277,10 +272,7 @@ async fn inbound_fetch_txos() { let mempool = new_mempool(); let (block_event_sender, _) = broadcast::channel(50); let network = Network::LocalNet; - let consensus_constants = network.create_consensus_constants(); - let consensus_manager = ConsensusManagerBuilder::new(network) - .with_consensus_constants(consensus_constants[0].clone()) - .build(); + let consensus_manager = ConsensusManager::builder(network).build(); let (request_sender, _) = reply_channel::unbounded(); let (block_sender, _) = mpsc::unbounded(); let outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); @@ -324,7 +316,7 @@ async fn outbound_fetch_blocks() { let (block_sender, _) = mpsc::unbounded(); let mut outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); let network = Network::LocalNet; - let consensus_constants = network.create_consensus_constants(); + let consensus_constants = NetworkConsensus::from(network).create_consensus_constants(); let gb = BlockBuilder::new(consensus_constants[0].blockchain_version()).build(); let block = HistoricalBlock::new(gb, 0, Default::default(), vec![], 0); let block_response = NodeCommsResponse::HistoricalBlocks(vec![block.clone()]); @@ -343,10 +335,7 @@ async fn inbound_fetch_blocks() { let mempool = new_mempool(); let (block_event_sender, _) = broadcast::channel(50); let network = Network::LocalNet; - let consensus_constants = network.create_consensus_constants(); - let consensus_manager = ConsensusManagerBuilder::new(network) - .with_consensus_constants(consensus_constants[0].clone()) - .build(); + let consensus_manager = ConsensusManager::builder(network).build(); let (request_sender, _) = reply_channel::unbounded(); let (block_sender, _) = mpsc::unbounded(); let outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); @@ -375,12 +364,8 @@ async fn inbound_fetch_blocks() { // Test needs to be updated to new pruned structure. async fn inbound_fetch_blocks_before_horizon_height() { let network = Network::LocalNet; - let consensus_constants = network.create_consensus_constants(); - let block0 = genesis_block::get_weatherwax_genesis_block(); - let consensus_manager = ConsensusManagerBuilder::new(network) - .with_consensus_constants(consensus_constants[0].clone()) - .with_block(block0.clone()) - .build(); + let consensus_manager = ConsensusManager::builder(network).build(); + let block0 = consensus_manager.get_genesis_block(); let validators = Validators::new( MockValidator::new(true), MockValidator::new(true), diff --git a/base_layer/core/tests/node_service.rs b/base_layer/core/tests/node_service.rs index 021871907d..37bb4c153d 100644 --- a/base_layer/core/tests/node_service.rs +++ b/base_layer/core/tests/node_service.rs @@ -42,6 +42,7 @@ use helpers::{ }, }; use std::time::Duration; +use tari_common::configuration::Network; use tari_comms::protocol::messaging::MessagingEvent; use tari_core::{ base_node::{ @@ -51,7 +52,7 @@ use tari_core::{ }, blocks::NewBlock, chain_storage::ChainBlock, - consensus::{ConsensusConstantsBuilder, ConsensusManagerBuilder, Network}, + consensus::{ConsensusConstantsBuilder, ConsensusManager, NetworkConsensus}, mempool::MempoolServiceConfig, proof_of_work::PowAlgorithm, transactions::{ @@ -78,7 +79,7 @@ fn request_response_get_metadata() { .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); let (block0, _) = create_genesis_block(&factories, &consensus_constants); - let consensus_manager = ConsensusManagerBuilder::new(network) + let consensus_manager = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants) .with_block(block0) .build(); @@ -111,7 +112,7 @@ fn request_and_response_fetch_blocks() { .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); let (block0, _) = create_genesis_block(&factories, &consensus_constants); - let consensus_manager = ConsensusManagerBuilder::new(network) + let consensus_manager = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants) .with_block(block0.clone()) .build(); @@ -168,7 +169,7 @@ fn request_and_response_fetch_blocks_with_hashes() { .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); let (block0, _) = create_genesis_block(&factories, &consensus_constants); - let consensus_manager = ConsensusManagerBuilder::new(network) + let consensus_manager = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants) .with_block(block0.clone()) .build(); @@ -248,25 +249,25 @@ fn propagate_and_forward_many_valid_blocks() { .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); let (block0, _) = create_genesis_block(&factories, &consensus_constants); - let rules = ConsensusManagerBuilder::new(network) + let rules = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants) .with_block(block0.clone()) .build(); - let (mut alice_node, rules) = BaseNodeBuilder::new(network) + let (mut alice_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(alice_node_identity.clone()) .with_consensus_manager(rules) .start(&mut runtime, temp_dir.path().join("alice").to_str().unwrap()); - let (mut bob_node, rules) = BaseNodeBuilder::new(network) + let (mut bob_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(bob_node_identity.clone()) .with_peers(vec![alice_node_identity]) .with_consensus_manager(rules) .start(&mut runtime, temp_dir.path().join("bob").to_str().unwrap()); - let (mut carol_node, rules) = BaseNodeBuilder::new(network) + let (mut carol_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(carol_node_identity.clone()) .with_peers(vec![bob_node_identity.clone()]) .with_consensus_manager(rules) .start(&mut runtime, temp_dir.path().join("carol").to_str().unwrap()); - let (mut dan_node, rules) = BaseNodeBuilder::new(network) + let (mut dan_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(dan_node_identity) .with_peers(vec![carol_node_identity, bob_node_identity]) .with_consensus_manager(rules) @@ -357,20 +358,20 @@ fn propagate_and_forward_invalid_block_hash() { .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); let (block0, _) = create_genesis_block(&factories, &consensus_constants); - let rules = ConsensusManagerBuilder::new(network) + let rules = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants) .with_block(block0.clone()) .build(); - let (mut alice_node, rules) = BaseNodeBuilder::new(network) + let (mut alice_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(alice_node_identity.clone()) .with_consensus_manager(rules) .start(&mut runtime, temp_dir.path().join("alice").to_str().unwrap()); - let (mut bob_node, rules) = BaseNodeBuilder::new(network) + let (mut bob_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(bob_node_identity.clone()) .with_peers(vec![alice_node_identity]) .with_consensus_manager(rules) .start(&mut runtime, temp_dir.path().join("bob").to_str().unwrap()); - let (mut carol_node, rules) = BaseNodeBuilder::new(network) + let (mut carol_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(carol_node_identity) .with_peers(vec![bob_node_identity]) .with_consensus_manager(rules) @@ -457,18 +458,18 @@ fn propagate_and_forward_invalid_block() { .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); let (block0, _) = create_genesis_block(&factories, &consensus_constants); - let rules = ConsensusManagerBuilder::new(network) + let rules = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants) .with_block(block0.clone()) .build(); let stateless_block_validator = OrphanBlockValidator::new(rules.clone(), factories); let mock_validator = MockValidator::new(false); - let (mut dan_node, rules) = BaseNodeBuilder::new(network) + let (mut dan_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(dan_node_identity.clone()) .with_consensus_manager(rules) .start(&mut runtime, temp_dir.path().join("dan").to_str().unwrap()); - let (mut carol_node, rules) = BaseNodeBuilder::new(network) + let (mut carol_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(carol_node_identity.clone()) .with_peers(vec![dan_node_identity.clone()]) .with_consensus_manager(rules) @@ -478,13 +479,13 @@ fn propagate_and_forward_invalid_block() { stateless_block_validator.clone(), ) .start(&mut runtime, temp_dir.path().join("carol").to_str().unwrap()); - let (mut bob_node, rules) = BaseNodeBuilder::new(network) + let (mut bob_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(bob_node_identity.clone()) .with_peers(vec![dan_node_identity]) .with_consensus_manager(rules) .with_validators(mock_validator.clone(), mock_validator, stateless_block_validator) .start(&mut runtime, temp_dir.path().join("bob").to_str().unwrap()); - let (mut alice_node, rules) = BaseNodeBuilder::new(network) + let (mut alice_node, rules) = BaseNodeBuilder::new(network.into()) .with_node_identity(alice_node_identity) .with_peers(vec![bob_node_identity, carol_node_identity]) .with_consensus_manager(rules) @@ -554,7 +555,7 @@ fn propagate_and_forward_invalid_block() { fn service_request_timeout() { let mut runtime = Runtime::new().unwrap(); let network = Network::LocalNet; - let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let consensus_manager = ConsensusManager::builder(network).build(); let base_node_service_config = BaseNodeServiceConfig { service_request_timeout: Duration::from_millis(1), fetch_blocks_timeout: Default::default(), @@ -585,7 +586,7 @@ fn local_get_metadata() { let temp_dir = tempdir().unwrap(); let network = Network::LocalNet; let (mut node, consensus_manager) = - BaseNodeBuilder::new(network).start(&mut runtime, temp_dir.path().to_str().unwrap()); + BaseNodeBuilder::new(network.into()).start(&mut runtime, temp_dir.path().to_str().unwrap()); let db = &node.blockchain_db; let block0 = db.fetch_block(0).unwrap().try_into_chain_block().unwrap(); let block1 = append_block(db, &block0, vec![], &consensus_manager, 1.into()).unwrap(); @@ -606,13 +607,13 @@ fn local_get_new_block_template_and_get_new_block() { let mut runtime = Runtime::new().unwrap(); let temp_dir = tempdir().unwrap(); let network = Network::LocalNet; - let consensus_constants = network.create_consensus_constants(); + let consensus_constants = NetworkConsensus::from(network).create_consensus_constants(); let (block0, outputs) = create_genesis_block_with_utxos(&factories, &[T, T], &consensus_constants[0]); - let rules = ConsensusManagerBuilder::new(network) + let rules = ConsensusManager::builder(network) .with_consensus_constants(consensus_constants[0].clone()) .with_block(block0) .build(); - let (mut node, _rules) = BaseNodeBuilder::new(network) + let (mut node, _rules) = BaseNodeBuilder::new(network.into()) .with_consensus_manager(rules) .start(&mut runtime, temp_dir.path().to_str().unwrap()); @@ -649,7 +650,7 @@ fn local_submit_block() { let temp_dir = tempdir().unwrap(); let network = Network::LocalNet; let (mut node, consensus_manager) = - BaseNodeBuilder::new(network).start(&mut runtime, temp_dir.path().to_str().unwrap()); + BaseNodeBuilder::new(network.into()).start(&mut runtime, temp_dir.path().to_str().unwrap()); let db = &node.blockchain_db; let mut event_stream = node.local_nci.get_block_event_stream(); diff --git a/base_layer/core/tests/node_state_machine.rs b/base_layer/core/tests/node_state_machine.rs index c9224d004f..bcbeeea436 100644 --- a/base_layer/core/tests/node_state_machine.rs +++ b/base_layer/core/tests/node_state_machine.rs @@ -30,6 +30,7 @@ use helpers::{ nodes::{create_network_with_2_base_nodes_with_config, wait_until_online, BaseNodeBuilder}, }; use std::{thread, time::Duration}; +use tari_common::configuration::Network; use tari_core::{ base_node::{ chain_metadata_service::PeerChainMetadata, @@ -42,7 +43,7 @@ use tari_core::{ }, SyncValidators, }, - consensus::{ConsensusConstantsBuilder, ConsensusManagerBuilder, Network}, + consensus::{ConsensusConstantsBuilder, ConsensusManagerBuilder}, mempool::MempoolServiceConfig, proof_of_work::randomx_factory::RandomXFactory, test_helpers::blockchain::create_test_blockchain_db, @@ -141,7 +142,7 @@ fn test_event_channel() { let temp_dir = tempdir().unwrap(); let mut runtime = Runtime::new().unwrap(); let (node, consensus_manager) = - BaseNodeBuilder::new(Network::Weatherwax).start(&mut runtime, temp_dir.path().to_str().unwrap()); + BaseNodeBuilder::new(Network::Weatherwax.into()).start(&mut runtime, temp_dir.path().to_str().unwrap()); // let shutdown = Shutdown::new(); let db = create_test_blockchain_db(); let shutdown = Shutdown::new(); diff --git a/base_layer/p2p/src/comms_connector/inbound_connector.rs b/base_layer/p2p/src/comms_connector/inbound_connector.rs index 615c365a6f..ed16cd578d 100644 --- a/base_layer/p2p/src/comms_connector/inbound_connector.rs +++ b/base_layer/p2p/src/comms_connector/inbound_connector.rs @@ -45,28 +45,28 @@ impl InboundDomainConnector { impl Service for InboundDomainConnector where - TSink: Sink> + Unpin + Clone, + TSink: Sink> + Unpin + Clone + 'static, TSink::Error: std::error::Error + Send + Sync + 'static, { type Error = PipelineError; + type Future = Pin>>>; type Response = (); - type Future = impl Future>; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.sink).poll_ready(cx).map_err(Into::into) } fn call(&mut self, msg: DecryptedDhtMessage) -> Self::Future { let mut sink = self.sink.clone(); - async move { + let future = async move { let peer_message = Self::construct_peer_message(msg)?; // If this fails there is something wrong with the sink and the pubsub middleware should not // continue sink.send(Arc::new(peer_message)).await?; Ok(()) - } + }; + Box::pin(future) } } diff --git a/base_layer/p2p/src/initialization.rs b/base_layer/p2p/src/initialization.rs index 2b27897f1c..a8b890cf95 100644 --- a/base_layer/p2p/src/initialization.rs +++ b/base_layer/p2p/src/initialization.rs @@ -25,6 +25,8 @@ use crate::{ dns_seed::DnsSeedResolver, seed_peer::SeedPeer, transport::{TorConfig, TransportType}, + MAJOR_NETWORK_VERSION, + MINOR_NETWORK_VERSION, }; use fs2::FileExt; use futures::{channel::mpsc, future, Sink}; @@ -33,7 +35,6 @@ use rand::{distributions::Alphanumeric, thread_rng, Rng}; use std::{ error::Error, fs::File, - future::Future, iter, net::SocketAddr, path::{Path, PathBuf}, @@ -41,6 +42,7 @@ use std::{ sync::Arc, time::{Duration, Instant}, }; +use tari_common::configuration::Network; use tari_comms::{ backoff::ConstantBackoff, peer_manager::{NodeIdentity, Peer, PeerFeatures, PeerManagerError}, @@ -49,6 +51,7 @@ use tari_comms::{ protocol::{ messaging::{MessagingEventSender, MessagingProtocolExtension}, rpc::RpcServer, + NodeNetworkInfo, }, tor, tor::HiddenServiceControllerError, @@ -61,7 +64,7 @@ use tari_comms::{ UnspawnedCommsNode, }; use tari_comms_dht::{Dht, DhtBuilder, DhtConfig, DhtInitializationError}; -use tari_service_framework::{ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; +use tari_service_framework::{async_trait, ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; use tari_shutdown::ShutdownSignal; use tari_storage::{ lmdb_store::{LMDBBuilder, LMDBConfig}, @@ -121,6 +124,8 @@ pub struct CommsConfig { pub outbound_buffer_size: usize, /// Configuration for DHT pub dht: DhtConfig, + /// The p2p network currently being connected to. + pub network: Network, /// The identity of this node on the network pub node_identity: Arc, /// The type of transport to use @@ -340,15 +345,19 @@ where .with_peer_storage(peer_database, Some(file_lock)) .build()?; + let peer_manager = comms.peer_manager(); + let connectivity = comms.connectivity(); + let node_identity = comms.node_identity(); + let shutdown_signal = comms.shutdown_signal(); // Create outbound channel let (outbound_tx, outbound_rx) = mpsc::channel(config.outbound_buffer_size); let dht = DhtBuilder::new( - comms.node_identity(), - comms.peer_manager(), + node_identity.clone(), + peer_manager, outbound_tx, - comms.connectivity(), - comms.shutdown_signal(), + connectivity, + shutdown_signal, ) .with_config(config.dht.clone()) .build() @@ -357,10 +366,7 @@ where let dht_outbound_layer = dht.outbound_middleware_layer(); // DHT RPC service is only available for communication nodes - if comms - .node_identity() - .has_peer_features(PeerFeatures::COMMUNICATION_NODE) - { + if node_identity.has_peer_features(PeerFeatures::COMMUNICATION_NODE) { comms = comms.add_rpc_server(RpcServer::new().add_service(dht.rpc_service())); } @@ -525,42 +531,46 @@ impl P2pInitializer { } } +#[async_trait] impl ServiceInitializer for P2pInitializer { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { let config = self.config.clone(); let connector = self.connector.take().expect("P2pInitializer called more than once"); - async move { - let mut builder = CommsBuilder::new() - .with_shutdown_signal(context.get_shutdown_signal()) - .with_node_identity(config.node_identity.clone()) - .with_user_agent(&config.user_agent); - - if config.allow_test_addresses { - builder = builder.allow_test_addresses(); - } + let mut builder = CommsBuilder::new() + .with_shutdown_signal(context.get_shutdown_signal()) + .with_node_identity(config.node_identity.clone()) + .with_node_info(NodeNetworkInfo { + major_version: MAJOR_NETWORK_VERSION, + minor_version: MINOR_NETWORK_VERSION, + network_byte: config.network.as_byte(), + user_agent: config.user_agent.clone(), + }); + + if config.allow_test_addresses { + builder = builder.allow_test_addresses(); + } - let (comms, dht) = configure_comms_and_dht(builder, &config, connector).await?; + let (comms, dht) = configure_comms_and_dht(builder, &config, connector).await?; - let peers = Self::try_parse_seed_peers(&config.peer_seeds)?; - add_all_peers(&comms.peer_manager(), &comms.node_identity(), peers).await?; + let peers = Self::try_parse_seed_peers(&config.peer_seeds)?; + let peer_manager = comms.peer_manager(); + let node_identity = comms.node_identity(); + add_all_peers(&peer_manager, &node_identity, peers).await?; - let peers = Self::try_resolve_dns_seeds( - config.dns_seeds_name_server, - &config.dns_seeds, - config.dns_seeds_use_dnssec, - ) - .await?; - add_all_peers(&comms.peer_manager(), &comms.node_identity(), peers).await?; + let peers = Self::try_resolve_dns_seeds( + config.dns_seeds_name_server, + &config.dns_seeds, + config.dns_seeds_use_dnssec, + ) + .await?; + add_all_peers(&peer_manager, &node_identity, peers).await?; - context.register_handle(comms.connectivity()); - context.register_handle(comms.peer_manager()); - context.register_handle(comms); - context.register_handle(dht); + context.register_handle(comms.connectivity()); + context.register_handle(peer_manager); + context.register_handle(comms); + context.register_handle(dht); - Ok(()) - } + Ok(()) } } diff --git a/base_layer/p2p/src/lib.rs b/base_layer/p2p/src/lib.rs index 59f436830a..fdf12e983d 100644 --- a/base_layer/p2p/src/lib.rs +++ b/base_layer/p2p/src/lib.rs @@ -22,11 +22,6 @@ // Needed to make futures::select! work #![recursion_limit = "256"] -// Used to eliminate the need for boxing futures in many cases. -// Tracking issue: https://github.com/rust-lang/rust/issues/63063 -#![allow(incomplete_features)] -#![feature(type_alias_impl_trait)] -#![feature(min_type_alias_impl_trait)] #![cfg_attr(not(debug_assertions), deny(unused_variables))] #![cfg_attr(not(debug_assertions), deny(unused_imports))] #![cfg_attr(not(debug_assertions), deny(dead_code))] @@ -50,4 +45,13 @@ pub mod services; pub mod tari_message; pub mod transport; +// Re-export +pub use tari_common::configuration::Network; + pub const DEFAULT_DNS_SEED_RESOLVER: &str = "1.1.1.1:53"; + +/// Major network version. Peers will refuse connections if this value differs +pub const MAJOR_NETWORK_VERSION: u32 = 0; +/// Minor network version. This should change with each time the network protocol has changed in a backward-compatible +/// way. +pub const MINOR_NETWORK_VERSION: u32 = 0; diff --git a/base_layer/p2p/src/services/liveness/mod.rs b/base_layer/p2p/src/services/liveness/mod.rs index 258ad0b43e..0051eedf61 100644 --- a/base_layer/p2p/src/services/liveness/mod.rs +++ b/base_layer/p2p/src/services/liveness/mod.rs @@ -70,12 +70,13 @@ use crate::{ }, tari_message::TariMessageType, }; -use futures::{future, Future, Stream, StreamExt}; +use futures::{Stream, StreamExt}; use log::*; use std::sync::Arc; use tari_comms::connectivity::ConnectivityRequester; use tari_comms_dht::Dht; use tari_service_framework::{ + async_trait, reply_channel, ServiceInitializationError, ServiceInitializer, @@ -112,10 +113,9 @@ impl LivenessInitializer { } } +#[async_trait] impl ServiceInitializer for LivenessInitializer { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { let (sender, receiver) = reply_channel::unbounded(); let (publisher, _) = broadcast::channel(200); @@ -152,6 +152,6 @@ impl ServiceInitializer for LivenessInitializer { debug!(target: LOG_TARGET, "Liveness service has shut down"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/p2p/src/services/liveness/service.rs b/base_layer/p2p/src/services/liveness/service.rs index 71b1c4258b..3374ec9777 100644 --- a/base_layer/p2p/src/services/liveness/service.rs +++ b/base_layer/p2p/src/services/liveness/service.rs @@ -315,7 +315,7 @@ mod test { test_utils::mocks::create_connectivity_mock, }; use tari_comms_dht::{ - envelope::{DhtMessageHeader, DhtMessageType, Network}, + envelope::{DhtMessageHeader, DhtMessageType}, outbound::{DhtOutboundRequest, MessageSendState, SendMessageResponse}, }; use tari_crypto::keys::PublicKey; @@ -425,12 +425,12 @@ mod test { ); DomainMessage { dht_header: DhtMessageHeader { - version: 0, + major: 0, + minor: 0, destination: Default::default(), origin_mac: Vec::new(), ephemeral_public_key: None, message_type: DhtMessageType::None, - network: Network::LocalTest, flags: Default::default(), message_tag: MessageTag::new(), expires: None, diff --git a/base_layer/p2p/src/test_utils.rs b/base_layer/p2p/src/test_utils.rs index 4dac942a8c..816f080fec 100644 --- a/base_layer/p2p/src/test_utils.rs +++ b/base_layer/p2p/src/test_utils.rs @@ -28,7 +28,7 @@ use tari_comms::{ peer_manager::{NodeIdentity, Peer, PeerFeatures, PeerFlags}, }; use tari_comms_dht::{ - envelope::{DhtMessageFlags, DhtMessageHeader, DhtMessageType, Network, NodeDestination}, + envelope::{DhtMessageFlags, DhtMessageHeader, DhtMessageType, NodeDestination}, inbound::DhtInboundMessage, }; @@ -62,12 +62,12 @@ pub fn make_node_identity() -> Arc { pub fn make_dht_header(trace: MessageTag) -> DhtMessageHeader { DhtMessageHeader { - version: 0, + major: 0, + minor: 0, destination: NodeDestination::Unknown, origin_mac: Vec::new(), ephemeral_public_key: None, message_type: DhtMessageType::None, - network: Network::LocalTest, flags: DhtMessageFlags::NONE, message_tag: trace, expires: None, diff --git a/base_layer/service_framework/Cargo.toml b/base_layer/service_framework/Cargo.toml index 1b303dbbe3..4210ee5e28 100644 --- a/base_layer/service_framework/Cargo.toml +++ b/base_layer/service_framework/Cargo.toml @@ -11,12 +11,14 @@ license = "BSD-3-Clause" [dependencies] tari_shutdown = { version = "^0.8", path="../../infrastructure/shutdown" } -thiserror = "1.0.20" + +anyhow = "1.0.32" +async-trait = "0.1.50" futures = { version = "^0.3.1", features=["async-await"]} -tower-service = { version="0.3.0" } -tokio = { version = "0.2.10" } log = "0.4.8" -anyhow = "1.0.32" +thiserror = "1.0.20" +tokio = { version = "0.2.10" } +tower-service = { version="0.3.0" } [dev-dependencies] tari_test_utils = { version = "^0.8", path="../../infrastructure/test_utils" } diff --git a/base_layer/service_framework/examples/services/service_a.rs b/base_layer/service_framework/examples/services/service_a.rs index 5f7fe4fd3d..c898696415 100644 --- a/base_layer/service_framework/examples/services/service_a.rs +++ b/base_layer/service_framework/examples/services/service_a.rs @@ -21,7 +21,8 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::services::ServiceBHandle; -use futures::{future, pin_mut, Future, StreamExt}; +use async_trait::async_trait; +use futures::{pin_mut, StreamExt}; use tari_service_framework::{ reply_channel, reply_channel::SenderService, @@ -117,10 +118,9 @@ impl ServiceAInitializer { } } +#[async_trait] impl ServiceInitializer for ServiceAInitializer { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { let (sender, receiver) = reply_channel::unbounded(); let service_a_handle = ServiceAHandle::new(sender); @@ -140,6 +140,6 @@ impl ServiceInitializer for ServiceAInitializer { service.run().await; println!("Service A has shutdown and initializer spawned task is now ending"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/service_framework/examples/services/service_b.rs b/base_layer/service_framework/examples/services/service_b.rs index 73471e3b4d..decf53ab14 100644 --- a/base_layer/service_framework/examples/services/service_b.rs +++ b/base_layer/service_framework/examples/services/service_b.rs @@ -20,7 +20,8 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use futures::{pin_mut, Future, StreamExt}; +use async_trait::async_trait; +use futures::{pin_mut, StreamExt}; use std::time::Duration; use tari_service_framework::{ reply_channel, @@ -111,10 +112,9 @@ impl ServiceBInitializer { } } +#[async_trait] impl ServiceInitializer for ServiceBInitializer { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { let (sender, receiver) = reply_channel::unbounded(); let service_b_handle = ServiceBHandle::new(sender); @@ -134,9 +134,7 @@ impl ServiceInitializer for ServiceBInitializer { println!("Service B has shutdown and initializer spawned task is now ending"); }); - async { - delay_for(Duration::from_secs(10)).await; - Ok(()) - } + delay_for(Duration::from_secs(10)).await; + Ok(()) } } diff --git a/base_layer/service_framework/examples/stack_builder_example.rs b/base_layer/service_framework/examples/stack_builder_example.rs index 898f82159e..35fd785ed7 100644 --- a/base_layer/service_framework/examples/stack_builder_example.rs +++ b/base_layer/service_framework/examples/stack_builder_example.rs @@ -19,10 +19,6 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#![allow(incomplete_features)] -#![feature(type_alias_impl_trait)] -#![feature(min_type_alias_impl_trait)] - pub mod services; use crate::services::{ServiceAHandle, ServiceAInitializer, ServiceBHandle, ServiceBInitializer}; diff --git a/base_layer/service_framework/src/context/handles.rs b/base_layer/service_framework/src/context/handles.rs index bd3cd6b30e..9661d8bd13 100644 --- a/base_layer/service_framework/src/context/handles.rs +++ b/base_layer/service_framework/src/context/handles.rs @@ -67,7 +67,7 @@ impl ServiceInitializerContext { /// Insert a service handle with the given name pub fn register_handle(&self, handle: H) - where H: Any + Send + Sync { + where H: Any + Send { self.inner.register(handle); } @@ -160,7 +160,7 @@ impl ServiceHandles { /// Register a handle pub fn register(&self, handle: H) - where H: Any + Send + Sync { + where H: Any + Send { acquire_lock!(self.handles).insert(TypeId::of::(), Box::new(handle)); } diff --git a/base_layer/service_framework/src/initializer.rs b/base_layer/service_framework/src/initializer.rs index 99a0127542..71c46faa71 100644 --- a/base_layer/service_framework/src/initializer.rs +++ b/base_layer/service_framework/src/initializer.rs @@ -21,28 +21,18 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::context::ServiceInitializerContext; -use futures::{Future, FutureExt}; -use std::pin::Pin; +use async_trait::async_trait; pub type ServiceInitializationError = anyhow::Error; +type Output = Result<(), ServiceInitializationError>; + /// Implementors of this trait will initialize a service /// The `StackBuilder` builds impls of this trait. +#[async_trait] pub trait ServiceInitializer { - /// The future returned from the initialize function - type Future: Future>; - /// Async initialization code for a service - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future; - - /// Create a boxed version of this ServiceInitializer. - fn boxed(self) -> BoxedServiceInitializer - where - Self: Sized + Send + 'static, - Self::Future: Send + 'static, - { - BoxedServiceInitializer::new(self) - } + async fn initialize(&mut self, context: ServiceInitializerContext) -> Output; } /// Implementation of ServiceInitializer for any function matching the signature of `ServiceInitializer::initialize` @@ -56,14 +46,11 @@ pub trait ServiceInitializer { /// futures::future::ready(Result::<_, ()>::Ok(())) /// }; /// ``` -impl ServiceInitializer for TFunc -where - TFunc: FnMut(ServiceInitializerContext) -> TFut, - TFut: Future>, +#[async_trait] +impl ServiceInitializer for TFunc +where TFunc: FnMut(ServiceInitializerContext) -> Output + Send { - type Future = TFut; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Output { (self)(context) } } @@ -77,70 +64,12 @@ impl InitializerFn { } } -impl ServiceInitializer for InitializerFn -where - TFunc: FnOnce(ServiceInitializerContext) -> TFut, - TFut: Future>, +#[async_trait] +impl ServiceInitializer for InitializerFn +where TFunc: FnOnce(ServiceInitializerContext) -> Output + Send { - type Future = TFut; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Output { let f = self.0.take().expect("initializer called more than once"); (f)(context) } } - -//---------------------------------- Boxed Service Initializer --------------------------------------------// -// The following code is essentially a substitute for async trait functions. Any initializer can -// converted to the boxed form by using ServiceInitializer::boxed(). This is done for you when -// using `StackBuilder::add_initializer`. - -/// A pinned, boxed form of the future resulting from a boxed ServiceInitializer -type ServiceInitializationFuture = Pin> + Send>>; - -/// This trait mirrors the ServiceInitializer trait, with the exception -/// of always returning a boxed future (aliased ServiceInitializationFuture type), -/// therefore it does not need the `Future` associated type. This makes it -/// possible to store a boxed dyn `AbstractServiceInitializer`. -pub trait AbstractServiceInitializer { - fn initialize(&mut self, context: ServiceInitializerContext) -> ServiceInitializationFuture; -} - -/// AbstractServiceInitializer impl for every T: ServiceInitializer. -impl AbstractServiceInitializer for T -where - T: ServiceInitializer, - T::Future: Send + 'static, -{ - fn initialize(&mut self, context: ServiceInitializerContext) -> ServiceInitializationFuture { - let initialization = self.initialize(context); - initialization.boxed() as ServiceInitializationFuture - } -} - -/// A concrete boxed version of a ServiceInitializer. This makes it possible -/// to have a collection of ServiceInitializers which return various boxed future types. -/// This type is used in StackBuilder's internal vec. -pub struct BoxedServiceInitializer { - inner: Box, -} - -impl BoxedServiceInitializer { - pub(super) fn new(initializer: T) -> Self - where - T: ServiceInitializer + Send + 'static, - T::Future: Send + 'static, - { - Self { - inner: Box::new(initializer), - } - } -} - -impl ServiceInitializer for BoxedServiceInitializer { - type Future = ServiceInitializationFuture; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { - self.inner.initialize(context) - } -} diff --git a/base_layer/service_framework/src/lib.rs b/base_layer/service_framework/src/lib.rs index 298882244a..89a36e10e4 100644 --- a/base_layer/service_framework/src/lib.rs +++ b/base_layer/service_framework/src/lib.rs @@ -70,12 +70,6 @@ //! [ServiceHandlesFuture]: ./handles/future/struct.ServiceHandlesFuture.html //! [SenderService]: ./reply_channel/struct.SenderService.html -// Used to eliminate the need for boxing futures in many cases. -// Tracking issue: https://github.com/rust-lang/rust/issues/63063 -#![allow(incomplete_features)] -#![feature(type_alias_impl_trait)] -#![feature(min_type_alias_impl_trait)] - mod context; pub use context::{LazyService, ServiceHandles, ServiceInitializerContext}; @@ -92,4 +86,5 @@ mod utilities; pub use utilities::RegisterHandle; // Re-export +pub use async_trait::async_trait; pub use tower_service::Service; diff --git a/base_layer/service_framework/src/stack.rs b/base_layer/service_framework/src/stack.rs index 152b77502e..2489006d56 100644 --- a/base_layer/service_framework/src/stack.rs +++ b/base_layer/service_framework/src/stack.rs @@ -22,18 +22,17 @@ use crate::{ context::{create_context_notifier_pair, ServiceHandles}, - initializer::{BoxedServiceInitializer, InitializerFn, ServiceInitializationError, ServiceInitializer}, + initializer::{InitializerFn, ServiceInitializationError, ServiceInitializer}, ServiceInitializerContext, }; use futures::future; -use std::future::Future; use tari_shutdown::ShutdownSignal; /// Responsible for building and collecting handles and (usually long-running) service futures. /// `finish` is an async function which resolves once all the services are initialized, or returns /// an error if any one of the services fails to initialize. pub struct StackBuilder { - initializers: Vec, + initializers: Vec>, shutdown_signal: ShutdownSignal, } @@ -49,25 +48,19 @@ impl StackBuilder { impl StackBuilder { /// Add an impl of ServiceInitializer to the stack pub fn add_initializer(self, initializer: I) -> Self - where - I: ServiceInitializer + Send + 'static, - I::Future: Send + 'static, - { - self.add_initializer_boxed(initializer.boxed()) + where I: ServiceInitializer + Send + 'static { + self.add_initializer_boxed(initializer) } /// Add an impl of ServiceInitializer to the stack - pub fn add_initializer_fn(self, initializer: TFunc) -> Self - where - TFunc: FnOnce(ServiceInitializerContext) -> TFut + Send + 'static, - TFut: Future> + Send + 'static, - { - self.add_initializer_boxed(InitializerFn::new(initializer).boxed()) + pub fn add_initializer_fn(self, initializer: TFunc) -> Self + where TFunc: FnOnce(ServiceInitializerContext) -> Result<(), ServiceInitializationError> + Send + 'static { + self.add_initializer_boxed(InitializerFn::new(initializer)) } /// Add a ServiceInitializer which has been boxed using `ServiceInitializer::boxed` - pub fn add_initializer_boxed(mut self, initializer: BoxedServiceInitializer) -> Self { - self.initializers.push(initializer); + pub fn add_initializer_boxed(mut self, initializer: impl ServiceInitializer + Send + 'static) -> Self { + self.initializers.push(Box::new(initializer)); self } @@ -83,9 +76,7 @@ impl StackBuilder { let (mut notifier, context) = create_context_notifier_pair(shutdown_signal); // Collect all the initialization futures - let init_futures = initializers - .iter_mut() - .map(|init| ServiceInitializer::initialize(init, context.clone())); + let init_futures = initializers.iter_mut().map(|init| init.initialize(context.clone())); // Run all the initializers concurrently and check each Result returning an error // on the first one that failed. @@ -103,7 +94,8 @@ impl StackBuilder { mod test { use super::*; use crate::{initializer::ServiceInitializer, ServiceInitializerContext}; - use futures::{executor::block_on, future, Future}; + use async_trait::async_trait; + use futures::{executor::block_on, future}; use std::sync::{ atomic::{AtomicUsize, Ordering}, Arc, @@ -114,7 +106,7 @@ mod test { #[tokio_macros::test] async fn service_defn_simple() { // This is less of a test and more of a demo of using the short-hand implementation of ServiceInitializer - let simple_initializer = |_: ServiceInitializerContext| future::ok(()); + let simple_initializer = |_: ServiceInitializerContext| Ok(()); let shutdown = Shutdown::new(); @@ -138,16 +130,15 @@ mod test { } } + #[async_trait] impl ServiceInitializer for DummyInitializer { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { // Add a handle context.register_handle(DummyServiceHandle(123)); // This demonstrates the chicken and egg problem with services and handles. Specifically, // that we have a service which requires the handles of other services to be able to - // create it's own handle. Here we wait for the handles_fut to resolve before continuing + // create its own handle. Here we wait for the handles_fut to resolve before continuing // to initialize the service. // // Critically, you should never wait for handles in the initialize method because @@ -160,7 +151,7 @@ mod test { }); self.state.fetch_add(1, Ordering::AcqRel); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/service_framework/src/tower/service_ext.rs b/base_layer/service_framework/src/tower/service_ext.rs index 9ab33f59a1..d83dc78794 100644 --- a/base_layer/service_framework/src/tower/service_ext.rs +++ b/base_layer/service_framework/src/tower/service_ext.rs @@ -117,10 +117,9 @@ mod test { impl Service for ReadyLater { type Error = (); + type Future = future::Ready>; type Response = u32; - type Future = impl Future>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { if self.flag.load(Ordering::Acquire) { Ok(()).into() diff --git a/base_layer/service_framework/src/utilities.rs b/base_layer/service_framework/src/utilities.rs index 394ec87e3e..ccb39555e2 100644 --- a/base_layer/service_framework/src/utilities.rs +++ b/base_layer/service_framework/src/utilities.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ServiceInitializationError, ServiceInitializer, ServiceInitializerContext}; -use futures::future; +use async_trait::async_trait; /// This initializer adds a handle to the service context. pub struct RegisterHandle { @@ -34,15 +34,14 @@ impl RegisterHandle { } } +#[async_trait] impl ServiceInitializer for RegisterHandle { - type Future = future::Ready>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { context.register_handle( self.handle .take() .expect("RegisterHandle: ServiceInitializer called more than once"), ); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/wallet/migrations/2021-06-15-162255_sender_meta_signature/down.sql b/base_layer/wallet/migrations/2021-06-22-143855_sender_meta_signature/down.sql similarity index 100% rename from base_layer/wallet/migrations/2021-06-15-162255_sender_meta_signature/down.sql rename to base_layer/wallet/migrations/2021-06-22-143855_sender_meta_signature/down.sql diff --git a/base_layer/wallet/migrations/2021-06-15-162255_sender_meta_signature/up.sql b/base_layer/wallet/migrations/2021-06-22-143855_sender_meta_signature/up.sql similarity index 88% rename from base_layer/wallet/migrations/2021-06-15-162255_sender_meta_signature/up.sql rename to base_layer/wallet/migrations/2021-06-22-143855_sender_meta_signature/up.sql index 1efa598904..846a2e4fa4 100644 --- a/base_layer/wallet/migrations/2021-06-15-162255_sender_meta_signature/up.sql +++ b/base_layer/wallet/migrations/2021-06-22-143855_sender_meta_signature/up.sql @@ -18,7 +18,8 @@ CREATE TABLE outputs ( height INTEGER NOT NULL, script_private_key BLOB NOT NULL, script_offset_public_key BLOB NOT NULL, - sender_metadata_signature TEXT NOT NULL, + sender_metadata_signature_key BLOB NOT NULL, + sender_metadata_signature_nonce BLOB NOT NULL, CONSTRAINT unique_commitment UNIQUE (commitment) ); PRAGMA foreign_keys=on; diff --git a/base_layer/wallet/migrations/2021-07-02-090239_remove_height_from_output/down.sql b/base_layer/wallet/migrations/2021-07-02-090239_remove_height_from_output/down.sql new file mode 100644 index 0000000000..9cca4de384 --- /dev/null +++ b/base_layer/wallet/migrations/2021-07-02-090239_remove_height_from_output/down.sql @@ -0,0 +1,27 @@ +PRAGMA foreign_keys=off; +ALTER TABLE outputs RENAME TO outputs_old; +CREATE TABLE outputs ( + id INTEGER NOT NULL PRIMARY KEY, + commitment BLOB NOT NULL, + spending_key BLOB NOT NULL, + value INTEGER NOT NULL, + flags INTEGER NOT NULL, + maturity INTEGER NOT NULL, + status INTEGER NOT NULL, + tx_id INTEGER NULL, + hash BLOB NOT NULL, + script BLOB NOT NULL, + input_data BLOB NOT NULL, + height INTEGER NOT NULL, + script_private_key BLOB NOT NULL, + script_offset_public_key BLOB NOT NULL, + sender_metadata_signature_key BLOB NOT NULL, + sender_metadata_signature_nonce BLOB NOT NULL, + CONSTRAINT unique_commitment UNIQUE (commitment) +); + +INSERT INTO outputs (id, commitment, spending_key, value, flags, maturity, status, tx_id, hash, script, input_data, height, script_private_key, script_offset_public_key, sender_metadata_signature_key, sender_metadata_signature_nonce) +SELECT id, commitment, spending_key, value, flags, maturity, status, tx_id, hash, script, input_data, 0, script_private_key, script_offset_public_key, sender_metadata_signature_key, sender_metadata_signature_nonce +FROM outputs_old; +DROP TABLE outputs_old; +PRAGMA foreign_keys=on; \ No newline at end of file diff --git a/base_layer/wallet/migrations/2021-07-02-090239_remove_height_from_output/up.sql b/base_layer/wallet/migrations/2021-07-02-090239_remove_height_from_output/up.sql new file mode 100644 index 0000000000..4a06483da7 --- /dev/null +++ b/base_layer/wallet/migrations/2021-07-02-090239_remove_height_from_output/up.sql @@ -0,0 +1,26 @@ +PRAGMA foreign_keys=off; +ALTER TABLE outputs RENAME TO outputs_old; +CREATE TABLE outputs ( + id INTEGER NOT NULL PRIMARY KEY, + commitment BLOB NOT NULL, + spending_key BLOB NOT NULL, + value INTEGER NOT NULL, + flags INTEGER NOT NULL, + maturity INTEGER NOT NULL, + status INTEGER NOT NULL, + tx_id INTEGER NULL, + hash BLOB NOT NULL, + script BLOB NOT NULL, + input_data BLOB NOT NULL, + script_private_key BLOB NOT NULL, + script_offset_public_key BLOB NOT NULL, + sender_metadata_signature_key BLOB NOT NULL, + sender_metadata_signature_nonce BLOB NOT NULL, + CONSTRAINT unique_commitment UNIQUE (commitment) +); + +INSERT INTO outputs (id, commitment, spending_key, value, flags, maturity, status, tx_id, hash, script, input_data, script_private_key, script_offset_public_key, sender_metadata_signature_key, sender_metadata_signature_nonce) +SELECT id, commitment, spending_key, value, flags, maturity, status, tx_id, hash, script, input_data, script_private_key, script_offset_public_key, sender_metadata_signature_key, sender_metadata_signature_nonce +FROM outputs_old; +DROP TABLE outputs_old; +PRAGMA foreign_keys=on; \ No newline at end of file diff --git a/base_layer/wallet/src/base_node_service/mod.rs b/base_layer/wallet/src/base_node_service/mod.rs index 41c99705a0..3c067dd712 100644 --- a/base_layer/wallet/src/base_node_service/mod.rs +++ b/base_layer/wallet/src/base_node_service/mod.rs @@ -32,10 +32,10 @@ use crate::{ base_node_service::{config::BaseNodeServiceConfig, handle::BaseNodeServiceHandle, service::BaseNodeService}, storage::database::{WalletBackend, WalletDatabase}, }; -use futures::{future, Future}; use log::*; use tari_comms::connectivity::ConnectivityRequester; use tari_service_framework::{ + async_trait, reply_channel, ServiceInitializationError, ServiceInitializer, @@ -60,12 +60,11 @@ where T: WalletBackend + 'static } } +#[async_trait] impl ServiceInitializer for BaseNodeServiceInitializer where T: WalletBackend + 'static { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { info!(target: LOG_TARGET, "Wallet base node service initializing."); let (sender, request_stream) = reply_channel::unbounded(); @@ -97,6 +96,6 @@ where T: WalletBackend + 'static info!(target: LOG_TARGET, "Wallet Base Node Service shutdown"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/wallet/src/config.rs b/base_layer/wallet/src/config.rs index e182fb8a07..cd17024068 100644 --- a/base_layer/wallet/src/config.rs +++ b/base_layer/wallet/src/config.rs @@ -26,7 +26,7 @@ use crate::{ transaction_service::config::TransactionServiceConfig, }; use std::time::Duration; -use tari_core::{consensus::Network, transactions::types::CryptoFactories}; +use tari_core::{consensus::NetworkConsensus, transactions::types::CryptoFactories}; use tari_p2p::initialization::CommsConfig; pub const KEY_MANAGER_COMMS_SECRET_KEY_BRANCH_KEY: &str = "comms"; @@ -39,7 +39,7 @@ pub struct WalletConfig { pub output_manager_service_config: Option, pub buffer_size: usize, pub rate_limit: usize, - pub network: Network, + pub network: NetworkConsensus, pub base_node_service_config: BaseNodeServiceConfig, pub scan_for_utxo_interval: Duration, } @@ -51,7 +51,7 @@ impl WalletConfig { factories: CryptoFactories, transaction_service_config: Option, output_manager_service_config: Option, - network: Network, + network: NetworkConsensus, base_node_service_config: Option, buffer_size: Option, rate_limit: Option, diff --git a/base_layer/wallet/src/contacts_service/mod.rs b/base_layer/wallet/src/contacts_service/mod.rs index d1bc431237..b9f0e1eb97 100644 --- a/base_layer/wallet/src/contacts_service/mod.rs +++ b/base_layer/wallet/src/contacts_service/mod.rs @@ -30,9 +30,10 @@ use crate::contacts_service::{ service::ContactsService, storage::database::{ContactsBackend, ContactsDatabase}, }; -use futures::{future, Future}; +use futures::future; use log::*; use tari_service_framework::{ + async_trait, reply_channel, ServiceInitializationError, ServiceInitializer, @@ -55,12 +56,11 @@ where T: ContactsBackend } } +#[async_trait] impl ServiceInitializer for ContactsServiceInitializer where T: ContactsBackend + 'static { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { let (sender, receiver) = reply_channel::unbounded(); let contacts_handle = ContactsServiceHandle::new(sender); @@ -82,6 +82,6 @@ where T: ContactsBackend + 'static future::select(service, shutdown_signal).await; info!(target: LOG_TARGET, "Contacts service shutdown"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/wallet/src/lib.rs b/base_layer/wallet/src/lib.rs index ed518a2689..53e06b041a 100644 --- a/base_layer/wallet/src/lib.rs +++ b/base_layer/wallet/src/lib.rs @@ -7,9 +7,6 @@ #![deny(unknown_lints)] #![recursion_limit = "2048"] #![feature(drain_filter)] -#![allow(incomplete_features)] -#![feature(type_alias_impl_trait)] -#![feature(min_type_alias_impl_trait)] #[macro_use] mod macros; diff --git a/base_layer/wallet/src/output_manager_service/handle.rs b/base_layer/wallet/src/output_manager_service/handle.rs index dbb3d6406c..b165a64f3d 100644 --- a/base_layer/wallet/src/output_manager_service/handle.rs +++ b/base_layer/wallet/src/output_manager_service/handle.rs @@ -72,9 +72,8 @@ pub enum OutputManagerRequest { RemoveEncryption, GetPublicRewindKeys, FeeEstimate((MicroTari, MicroTari, u64, u64)), - ScanForRecoverableOutputs(Vec, u64), - ScanOutputs(Vec, u64), - UpdateMinedHeight(u64, u64), + ScanForRecoverableOutputs(Vec), + ScanOutputs(Vec), AddKnownOneSidedPaymentScript(KnownOneSidedPaymentScript), } @@ -105,9 +104,8 @@ impl fmt::Display for OutputManagerRequest { GetCoinbaseTransaction(_) => write!(f, "GetCoinbaseTransaction"), GetPublicRewindKeys => write!(f, "GetPublicRewindKeys"), FeeEstimate(_) => write!(f, "FeeEstimate"), - ScanForRecoverableOutputs(_, _) => write!(f, "ScanForRecoverableOutputs"), - ScanOutputs(_, _) => write!(f, "ScanRewindAndImportOutputs"), - UpdateMinedHeight(_, _) => write!(f, "UpdateMinedHeight"), + ScanForRecoverableOutputs(_) => write!(f, "ScanForRecoverableOutputs"), + ScanOutputs(_) => write!(f, "ScanRewindAndImportOutputs"), AddKnownOneSidedPaymentScript(_) => write!(f, "AddKnownOneSidedPaymentScript"), } } @@ -141,7 +139,6 @@ pub enum OutputManagerResponse { FeeEstimate(MicroTari), RewoundOutputs(Vec), ScanOutputs(Vec), - MinedHeightUpdated, AddKnownOneSidedPaymentScript, } @@ -472,11 +469,10 @@ impl OutputManagerHandle { pub async fn scan_for_recoverable_outputs( &mut self, outputs: Vec, - height: u64, ) -> Result, OutputManagerError> { match self .handle - .call(OutputManagerRequest::ScanForRecoverableOutputs(outputs, height)) + .call(OutputManagerRequest::ScanForRecoverableOutputs(outputs)) .await?? { OutputManagerResponse::RewoundOutputs(outputs) => Ok(outputs), @@ -487,13 +483,8 @@ impl OutputManagerHandle { pub async fn scan_outputs_for_one_sided_payments( &mut self, outputs: Vec, - height: u64, ) -> Result, OutputManagerError> { - match self - .handle - .call(OutputManagerRequest::ScanOutputs(outputs, height)) - .await?? - { + match self.handle.call(OutputManagerRequest::ScanOutputs(outputs)).await?? { OutputManagerResponse::ScanOutputs(outputs) => Ok(outputs), _ => Err(OutputManagerError::UnexpectedApiResponse), } @@ -531,15 +522,4 @@ impl OutputManagerHandle { _ => Err(OutputManagerError::UnexpectedApiResponse), } } - - pub async fn update_mined_height(&mut self, tx_id: u64, height: u64) -> Result<(), OutputManagerError> { - match self - .handle - .call(OutputManagerRequest::UpdateMinedHeight(tx_id, height)) - .await?? - { - OutputManagerResponse::MinedHeightUpdated => Ok(()), - _ => Err(OutputManagerError::UnexpectedApiResponse), - } - } } diff --git a/base_layer/wallet/src/output_manager_service/mod.rs b/base_layer/wallet/src/output_manager_service/mod.rs index 39d0158091..aac9c7b882 100644 --- a/base_layer/wallet/src/output_manager_service/mod.rs +++ b/base_layer/wallet/src/output_manager_service/mod.rs @@ -30,14 +30,15 @@ use crate::{ }, transaction_service::handle::TransactionServiceHandle, }; -use futures::{future, Future}; +use futures::future; use log::*; use tari_comms::{connectivity::ConnectivityRequester, types::CommsSecretKey}; use tari_core::{ - consensus::{ConsensusConstantsBuilder, Network}, + consensus::{ConsensusConstantsBuilder, NetworkConsensus}, transactions::types::CryptoFactories, }; use tari_service_framework::{ + async_trait, reply_channel, ServiceInitializationError, ServiceInitializer, @@ -69,7 +70,7 @@ where T: OutputManagerBackend config: OutputManagerServiceConfig, backend: Option, factories: CryptoFactories, - network: Network, + network: NetworkConsensus, master_secret_key: CommsSecretKey, } @@ -80,7 +81,7 @@ where T: OutputManagerBackend + 'static config: OutputManagerServiceConfig, backend: T, factories: CryptoFactories, - network: Network, + network: NetworkConsensus, master_secret_key: CommsSecretKey, ) -> Self { Self { @@ -93,12 +94,11 @@ where T: OutputManagerBackend + 'static } } +#[async_trait] impl ServiceInitializer for OutputManagerServiceInitializer where T: OutputManagerBackend + 'static { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { trace!( target: LOG_TARGET, "Output manager initialization: Base node query timeout: {}s", @@ -118,7 +118,7 @@ where T: OutputManagerBackend + 'static .expect("Cannot start Output Manager Service without setting a storage backend"); let factories = self.factories.clone(); let config = self.config.clone(); - let constants = ConsensusConstantsBuilder::new(self.network).build(); + let constants = ConsensusConstantsBuilder::new(self.network.as_network()).build(); let master_secret_key = self.master_secret_key.clone(); context.spawn_when_ready(move |handles| async move { let transaction_service = handles.expect_handle::(); @@ -146,6 +146,6 @@ where T: OutputManagerBackend + 'static future::select(service, handles.get_shutdown_signal()).await; info!(target: LOG_TARGET, "Output manager service shutdown"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs index 001915145f..19510d727f 100644 --- a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs +++ b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs @@ -64,7 +64,6 @@ where TBackend: OutputManagerBackend + 'static pub async fn scan_and_recover_outputs( &mut self, outputs: Vec, - height: u64, ) -> Result, OutputManagerError> { let mut rewound_outputs: Vec = outputs .into_iter() @@ -94,7 +93,6 @@ where TBackend: OutputManagerBackend + 'static Some(features), script, inputs!(PublicKey::from_secret_key(&output.blinding_factor)), - height, output.blinding_factor, script_offset_public_key, sender_metadata_signature, diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 1811be653a..85888f4f4d 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -323,29 +323,22 @@ where TBackend: OutputManagerBackend + 'static OutputManagerRequest::GetPublicRewindKeys => Ok(OutputManagerResponse::PublicRewindKeys(Box::new( self.resources.master_key_manager.get_rewind_public_keys(), ))), - OutputManagerRequest::ScanForRecoverableOutputs(outputs, height) => StandardUtxoRecoverer::new( + OutputManagerRequest::ScanForRecoverableOutputs(outputs) => StandardUtxoRecoverer::new( self.resources.master_key_manager.clone(), self.resources.factories.clone(), self.resources.db.clone(), ) - .scan_and_recover_outputs(outputs, height) + .scan_and_recover_outputs(outputs) .await .map(OutputManagerResponse::RewoundOutputs), - OutputManagerRequest::ScanOutputs(outputs, height) => self - .scan_outputs_for_one_sided_payments(outputs, height) + OutputManagerRequest::ScanOutputs(outputs) => self + .scan_outputs_for_one_sided_payments(outputs) .await .map(OutputManagerResponse::ScanOutputs), OutputManagerRequest::AddKnownOneSidedPaymentScript(known_script) => self .add_known_script(known_script) .await .map(|_| OutputManagerResponse::AddKnownOneSidedPaymentScript), - OutputManagerRequest::UpdateMinedHeight(tx_id, height) => self - .resources - .db - .update_output_mined_height(tx_id, height) - .await - .map(|_| OutputManagerResponse::MinedHeightUpdated) - .map_err(OutputManagerError::OutputManagerStorageError), } } @@ -439,7 +432,6 @@ where TBackend: OutputManagerBackend + 'static single_round_sender_data.script.clone(), // TODO: The input data should be variable; this will only work for a Nop script inputs!(PublicKey::from_secret_key(&script_private_key)), - 0, script_private_key, single_round_sender_data.script_offset_public_key.clone(), single_round_sender_data.sender_metadata_signature.clone(), @@ -667,12 +659,12 @@ where TBackend: OutputManagerBackend + 'static .cancel_pending_transaction_at_block_height(block_height) .await?; - // Clear any matching coinbase outputs for this block_height AND commitment. Even if the older output is valid + // Clear any matching outputs for this commitment. Even if the older output is valid // we are losing no information as this output has the same commitment. match self .resources .db - .remove_coinbase_output_at_block_height(output.commitment.clone(), output.unblinded_output.height) + .remove_output_by_commitment(output.commitment.clone()) .await { Ok(_) => {}, @@ -686,6 +678,7 @@ where TBackend: OutputManagerBackend + 'static .await?; self.confirm_encumberance(tx_id).await?; + Ok(tx) } @@ -736,7 +729,6 @@ where TBackend: OutputManagerBackend + 'static Some(output_features), script, inputs!(PublicKey::from_secret_key(&script_private_key)), - 0, script_private_key, PublicKey::from_secret_key(&script_offset_private_key), sender_signature, @@ -1118,7 +1110,6 @@ where TBackend: OutputManagerBackend + 'static Some(output_features), script, inputs!(PublicKey::from_secret_key(&script_private_key)), - 0, script_private_key, PublicKey::from_secret_key(&script_offset_private_key), sender_signature, @@ -1170,7 +1161,6 @@ where TBackend: OutputManagerBackend + 'static async fn scan_outputs_for_one_sided_payments( &mut self, outputs: Vec, - height: u64, ) -> Result, OutputManagerError> { let known_one_sided_payment_scripts: Vec = self.resources.db.get_all_known_one_sided_payment_scripts().await?; @@ -1200,7 +1190,6 @@ where TBackend: OutputManagerBackend + 'static Some(output.features), known_one_sided_payment_scripts[i].script.clone(), known_one_sided_payment_scripts[i].input.clone(), - height, known_one_sided_payment_scripts[i].private_key.clone(), output.script_offset_public_key, output.sender_metadata_signature, @@ -1274,8 +1263,11 @@ impl Balance { impl fmt::Display for Balance { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "Available balance: {}", self.available_balance)?; + if let Some(locked) = self.time_locked_balance { + writeln!(f, "Time locked: {}", locked)?; + } writeln!(f, "Pending incoming balance: {}", self.pending_incoming_balance)?; - write!(f, "Pending outgoing balance: {}", self.pending_outgoing_balance)?; + writeln!(f, "Pending outgoing balance: {}", self.pending_outgoing_balance)?; Ok(()) } } diff --git a/base_layer/wallet/src/output_manager_service/storage/database.rs b/base_layer/wallet/src/output_manager_service/storage/database.rs index af137974ba..81eea92ff8 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database.rs @@ -99,8 +99,6 @@ pub trait OutputManagerBackend: Send + Sync + Clone { &self, commitment: &Commitment, ) -> Result; - /// Update the mined height for all outputs for this tx_id - fn update_mined_height(&self, tx_id: u64, height: u64) -> Result<(), OutputManagerStorageError>; } /// Holds the outputs that have been selected for a given pending transaction waiting for confirmation @@ -125,6 +123,7 @@ pub struct KeyManagerState { pub enum DbKey { SpentOutput(BlindingFactor), UnspentOutput(BlindingFactor), + AnyOutputByCommitment(Commitment), PendingTransactionOutputs(TxId), TimeLockedUnspentOutputs(u64), UnspentOutputs, @@ -133,7 +132,6 @@ pub enum DbKey { KeyManagerState, InvalidOutputs, KnownOneSidedPaymentScripts, - CoinbaseOutput { commitment: Commitment, block_height: u64 }, } #[derive(Debug)] @@ -147,7 +145,7 @@ pub enum DbValue { AllPendingTransactionOutputs(HashMap), KeyManagerState(KeyManagerState), KnownOneSidedPaymentScripts(Vec), - CoinbaseOutput(Box), + AnyOutput(Box), } pub enum DbKeyValuePair { @@ -633,54 +631,6 @@ where T: OutputManagerBackend + 'static .and_then(|inner_result| inner_result) } - pub async fn remove_coinbase_output_at_block_height( - &self, - commitment: Commitment, - block_height: u64, - ) -> Result<(), OutputManagerStorageError> { - let db_clone = self.db.clone(); - tokio::task::spawn_blocking(move || { - match db_clone.write(WriteOperation::Remove(DbKey::CoinbaseOutput { - commitment: commitment.clone(), - block_height, - })) { - Ok(None) => log_error( - DbKey::CoinbaseOutput { - commitment, - block_height, - }, - OutputManagerStorageError::ValueNotFound, - ), - Ok(Some(DbValue::CoinbaseOutput(_))) => Ok(()), - Ok(Some(other)) => unexpected_result( - DbKey::CoinbaseOutput { - commitment, - block_height, - }, - other, - ), - Err(e) => log_error( - DbKey::CoinbaseOutput { - commitment, - block_height, - }, - e, - ), - } - }) - .await - .map_err(|err| OutputManagerStorageError::BlockingTaskSpawnError(err.to_string()))??; - Ok(()) - } - - pub async fn update_output_mined_height(&self, tx_id: u64, height: u64) -> Result<(), OutputManagerStorageError> { - let db_clone = self.db.clone(); - tokio::task::spawn_blocking(move || db_clone.update_mined_height(tx_id, height)) - .await - .map_err(|err| OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())) - .and_then(|inner_result| inner_result) - } - pub async fn apply_encryption(&self, cipher: Aes256Gcm) -> Result<(), OutputManagerStorageError> { let db_clone = self.db.clone(); tokio::task::spawn_blocking(move || db_clone.apply_encryption(cipher)) @@ -731,6 +681,24 @@ where T: OutputManagerBackend + 'static Ok(()) } + + pub async fn remove_output_by_commitment(&self, commitment: Commitment) -> Result<(), OutputManagerStorageError> { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || { + match db_clone.write(WriteOperation::Remove(DbKey::AnyOutputByCommitment(commitment.clone()))) { + Ok(None) => log_error( + DbKey::AnyOutputByCommitment(commitment.clone()), + OutputManagerStorageError::ValueNotFound, + ), + Ok(Some(DbValue::AnyOutput(_))) => Ok(()), + Ok(Some(other)) => unexpected_result(DbKey::AnyOutputByCommitment(commitment.clone()), other), + Err(e) => log_error(DbKey::AnyOutputByCommitment(commitment), e), + } + }) + .await + .map_err(|err| OutputManagerStorageError::BlockingTaskSpawnError(err.to_string()))??; + Ok(()) + } } fn unexpected_result(req: DbKey, res: DbValue) -> Result { @@ -754,7 +722,7 @@ impl Display for DbKey { DbKey::InvalidOutputs => f.write_str(&"Invalid Outputs Key"), DbKey::TimeLockedUnspentOutputs(_t) => f.write_str(&"Timelocked Outputs"), DbKey::KnownOneSidedPaymentScripts => f.write_str(&"Known claiming scripts"), - DbKey::CoinbaseOutput { .. } => f.write_str("Coinbase Output"), + DbKey::AnyOutputByCommitment(_) => f.write_str(&"AnyOutputByCommitment"), } } } @@ -771,7 +739,7 @@ impl Display for DbValue { DbValue::KeyManagerState(_) => f.write_str("Key Manager State"), DbValue::InvalidOutputs(_) => f.write_str("Invalid Outputs"), DbValue::KnownOneSidedPaymentScripts(_) => f.write_str(&"Known claiming scripts"), - DbValue::CoinbaseOutput(_) => f.write_str("Coinbase Output"), + DbValue::AnyOutput(_) => f.write_str(&"Any Output"), } } } diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs index c2ac61d110..58f8408a20 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs @@ -57,7 +57,7 @@ use tari_core::{ transactions::{ tari_amount::MicroTari, transaction::{OutputFeatures, OutputFlags, UnblindedOutput}, - types::{Commitment, CryptoFactories, PrivateKey, PublicKey}, + types::{Commitment, CryptoFactories, PrivateKey, PublicKey, Signature}, }, }; use tari_crypto::{ @@ -135,6 +135,21 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { None }, }, + DbKey::AnyOutputByCommitment(commitment) => { + match OutputSql::find_by_commitment(&commitment.to_vec(), &(*conn)) { + Ok(mut o) => { + self.decrypt_if_necessary(&mut o)?; + Some(DbValue::SpentOutput(Box::new(DbUnblindedOutput::try_from(o)?))) + }, + Err(e) => { + match e { + OutputManagerStorageError::DieselError(DieselError::NotFound) => (), + e => return Err(e), + }; + None + }, + } + }, DbKey::PendingTransactionOutputs(tx_id) => match PendingTransactionOutputSql::find(*tx_id, &(*conn)) { Ok(p) => { let mut outputs = OutputSql::find_by_tx_id_and_encumbered(*tx_id, &(*conn))?; @@ -253,22 +268,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { .collect::, _>>()?, )) }, - DbKey::CoinbaseOutput { - commitment, - block_height, - } => match OutputSql::find_by_commitment_and_block_height(&commitment.to_vec(), *block_height, &(*conn)) { - Ok(mut o) => { - self.decrypt_if_necessary(&mut o)?; - Some(DbValue::CoinbaseOutput(Box::new(DbUnblindedOutput::try_from(o)?))) - }, - Err(e) => { - match e { - OutputManagerStorageError::DieselError(DieselError::NotFound) => (), - e => return Err(e), - }; - None - }, - }, }; Ok(result) @@ -280,7 +279,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { match op { WriteOperation::Insert(kvp) => match kvp { DbKeyValuePair::SpentOutput(c, o) => { - if OutputSql::find_by_commitment(&c.to_vec(), &(*conn)).is_ok() { + if OutputSql::find_by_commitment_and_cancelled(&c.to_vec(), false, &(*conn)).is_ok() { return Err(OutputManagerStorageError::DuplicateOutput); } let mut new_output = NewOutputSql::new(*o, OutputStatus::Spent, None)?; @@ -290,7 +289,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { new_output.commit(&(*conn))? }, DbKeyValuePair::UnspentOutput(c, o) => { - if OutputSql::find_by_commitment(&c.to_vec(), &(*conn)).is_ok() { + if OutputSql::find_by_commitment_and_cancelled(&c.to_vec(), false, &(*conn)).is_ok() { return Err(OutputManagerStorageError::DuplicateOutput); } let mut new_output = NewOutputSql::new(*o, OutputStatus::Unspent, None)?; @@ -298,7 +297,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { new_output.commit(&(*conn))? }, DbKeyValuePair::UnspentOutputWithTxId(c, (tx_id, o)) => { - if OutputSql::find_by_commitment(&c.to_vec(), &(*conn)).is_ok() { + if OutputSql::find_by_commitment_and_cancelled(&c.to_vec(), false, &(*conn)).is_ok() { return Err(OutputManagerStorageError::DuplicateOutput); } let mut new_output = NewOutputSql::new(*o, OutputStatus::Unspent, Some(tx_id))?; @@ -364,14 +363,11 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { }; }, }, - DbKey::CoinbaseOutput { - commitment, - block_height, - } => { - match OutputSql::find_by_commitment_and_block_height(&commitment.to_vec(), block_height, &(*conn)) { + DbKey::AnyOutputByCommitment(commitment) => { + match OutputSql::find_by_commitment(&commitment.to_vec(), &(*conn)) { Ok(o) => { o.delete(&(*conn))?; - return Ok(Some(DbValue::CoinbaseOutput(Box::new(DbUnblindedOutput::try_from(o)?)))); + return Ok(Some(DbValue::AnyOutput(Box::new(DbUnblindedOutput::try_from(o)?)))); }, Err(e) => { match e { @@ -433,7 +429,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { status: Some(OutputStatus::Unspent), tx_id: None, spending_key: None, - height: None, script_private_key: None, }, &(*conn), @@ -444,7 +439,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { status: Some(OutputStatus::Spent), tx_id: None, spending_key: None, - height: None, script_private_key: None, }, &(*conn), @@ -475,7 +469,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { let mut outputs_to_be_spent = Vec::with_capacity(outputs_to_send.len()); for i in outputs_to_send { - let output = OutputSql::find_by_commitment(i.commitment.as_bytes(), &(*conn))?; + let output = OutputSql::find_by_commitment_and_cancelled(i.commitment.as_bytes(), false, &(*conn))?; if output.status == (OutputStatus::Spent as i32) { return Err(OutputManagerStorageError::OutputAlreadySpent); } @@ -490,7 +484,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { status: Some(OutputStatus::EncumberedToBeSpent), tx_id: Some(tx_id), spending_key: None, - height: None, script_private_key: None, }, &(*conn), @@ -553,7 +546,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { status: Some(OutputStatus::CancelledInbound), tx_id: None, spending_key: None, - height: None, script_private_key: None, }, &(*conn), @@ -564,7 +556,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { status: Some(OutputStatus::Unspent), tx_id: None, spending_key: None, - height: None, script_private_key: None, }, &(*conn), @@ -620,14 +611,13 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { fn invalidate_unspent_output(&self, output: &DbUnblindedOutput) -> Result, OutputManagerStorageError> { let conn = self.database_connection.acquire_lock(); - let output = OutputSql::find_by_commitment(&output.commitment.to_vec(), &conn)?; + let output = OutputSql::find_by_commitment_and_cancelled(&output.commitment.to_vec(), false, &conn)?; let tx_id = output.tx_id.map(|id| id as u64); let _ = output.update( UpdateOutput { status: Some(OutputStatus::Invalid), tx_id: None, spending_key: None, - height: None, script_private_key: None, }, &(*conn), @@ -638,7 +628,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { fn revalidate_unspent_output(&self, commitment: &Commitment) -> Result<(), OutputManagerStorageError> { let conn = self.database_connection.acquire_lock(); - let output = OutputSql::find_by_commitment(&commitment.to_vec(), &conn)?; + let output = OutputSql::find_by_commitment_and_cancelled(&commitment.to_vec(), false, &conn)?; if OutputStatus::try_from(output.status)? != OutputStatus::Invalid { return Err(OutputManagerStorageError::ValuesNotFound); @@ -648,7 +638,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { status: Some(OutputStatus::Unspent), tx_id: None, spending_key: None, - height: None, script_private_key: None, }, &(*conn), @@ -661,7 +650,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { commitment: &Commitment, ) -> Result { let conn = self.database_connection.acquire_lock(); - let output = OutputSql::find_by_commitment(&commitment.to_vec(), &conn)?; + let output = OutputSql::find_by_commitment_and_cancelled(&commitment.to_vec(), false, &conn)?; if OutputStatus::try_from(output.status)? != OutputStatus::Spent { return Err(OutputManagerStorageError::ValuesNotFound); @@ -672,7 +661,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { status: Some(OutputStatus::Unspent), tx_id: None, spending_key: None, - height: None, script_private_key: None, }, &(*conn), @@ -694,26 +682,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { Ok(()) } - fn update_mined_height(&self, tx_id: u64, height: u64) -> Result<(), OutputManagerStorageError> { - let conn = self.database_connection.acquire_lock(); - let output = OutputSql::find_by_tx_id(tx_id, &conn)?; - - for o in output.iter() { - let _ = o.update( - UpdateOutput { - status: None, - tx_id: None, - spending_key: None, - height: Some(height), - script_private_key: None, - }, - &(*conn), - )?; - } - - Ok(()) - } - fn apply_encryption(&self, cipher: Aes256Gcm) -> Result<(), OutputManagerStorageError> { let mut current_cipher = acquire_write_lock!(self.cipher); @@ -880,10 +848,10 @@ struct NewOutputSql { hash: Option>, script: Vec, input_data: Vec, - height: i64, script_private_key: Vec, script_offset_public_key: Vec, - sender_metadata_signature: String, + sender_metadata_signature_key: Vec, + sender_metadata_signature_nonce: Vec, } impl NewOutputSql { @@ -892,8 +860,6 @@ impl NewOutputSql { status: OutputStatus, tx_id: Option, ) -> Result { - let sender_metadata_signature = serde_json::to_string(&output.unblinded_output.sender_metadata_signature) - .map_err(|e| OutputManagerStorageError::UnexpectedResult(e.to_string()))?; Ok(Self { commitment: Some(output.commitment.to_vec()), spending_key: output.unblinded_output.spending_key.to_vec(), @@ -905,10 +871,18 @@ impl NewOutputSql { hash: Some(output.hash), script: output.unblinded_output.script.as_bytes(), input_data: output.unblinded_output.input_data.as_bytes(), - height: output.unblinded_output.height as i64, script_private_key: output.unblinded_output.script_private_key.to_vec(), script_offset_public_key: output.unblinded_output.script_offset_public_key.to_vec(), - sender_metadata_signature, + sender_metadata_signature_key: output + .unblinded_output + .sender_metadata_signature + .get_signature() + .to_vec(), + sender_metadata_signature_nonce: output + .unblinded_output + .sender_metadata_signature + .get_public_nonce() + .to_vec(), }) } @@ -947,10 +921,10 @@ struct OutputSql { hash: Option>, script: Vec, input_data: Vec, - height: i64, script_private_key: Vec, script_offset_public_key: Vec, - sender_metadata_signature: String, + sender_metadata_signature_key: Vec, + sender_metadata_signature_nonce: Vec, } impl OutputSql { @@ -986,29 +960,26 @@ impl OutputSql { commitment: &[u8], conn: &SqliteConnection, ) -> Result { - let cancelled = OutputStatus::CancelledInbound as i32; Ok(outputs::table - .filter(outputs::status.ne(cancelled)) .filter(outputs::commitment.eq(commitment)) .first::(conn)?) } - pub fn find_by_commitment_and_block_height( + pub fn find_by_commitment_and_cancelled( commitment: &[u8], - height: u64, + cancelled: bool, conn: &SqliteConnection, ) -> Result { - Ok(outputs::table - .filter(outputs::commitment.eq(commitment)) - .filter(outputs::height.eq(height as i64)) - .first::(conn)?) - } + let cancelled_flag = OutputStatus::CancelledInbound as i32; - /// Find outputs via tx_id - pub fn find_by_tx_id(tx_id: TxId, conn: &SqliteConnection) -> Result, OutputManagerStorageError> { - Ok(outputs::table - .filter(outputs::tx_id.eq(Some(tx_id as i64))) - .load(conn)?) + let mut request = outputs::table.filter(outputs::commitment.eq(commitment)).into_boxed(); + if cancelled { + request = request.filter(outputs::status.eq(cancelled_flag)) + } else { + request = request.filter(outputs::status.ne(cancelled_flag)) + }; + + Ok(request.first::(conn)?) } /// Find outputs via tx_id that are encumbered. Any outputs that are encumbered cannot be marked as spent. @@ -1093,7 +1064,6 @@ impl OutputSql { status: None, tx_id: None, spending_key: Some(self.spending_key.clone()), - height: None, script_private_key: Some(self.script_private_key.clone()), }, conn, @@ -1122,7 +1092,6 @@ impl TryFrom for DbUnblindedOutput { }), TariScript::from_bytes(o.script.as_slice())?, ExecutionStack::from_bytes(o.input_data.as_slice())?, - o.height as u64, PrivateKey::from_vec(&o.script_private_key).map_err(|_| { error!( target: LOG_TARGET, @@ -1133,12 +1102,26 @@ impl TryFrom for DbUnblindedOutput { PublicKey::from_vec(&o.script_offset_public_key).map_err(|_| { error!( target: LOG_TARGET, - "Could not create PrivateKey from stored bytes, They might be encrypted" + "Could not create PublicKey from stored bytes, They might be encrypted" ); OutputManagerStorageError::ConversionError })?, - serde_json::from_str(&o.sender_metadata_signature) - .map_err(|e| OutputManagerStorageError::UnexpectedResult(e.to_string()))?, + Signature::new( + PublicKey::from_vec(&o.sender_metadata_signature_nonce).map_err(|_| { + error!( + target: LOG_TARGET, + "Could not create PublicKey from stored bytes, They might be encrypted" + ); + OutputManagerStorageError::ConversionError + })?, + PrivateKey::from_vec(&o.sender_metadata_signature_key).map_err(|_| { + error!( + target: LOG_TARGET, + "Could not create PrivateKey from stored bytes, They might be encrypted" + ); + OutputManagerStorageError::ConversionError + })?, + ), ); let hash = match o.hash { @@ -1193,10 +1176,10 @@ impl From for NewOutputSql { hash: o.hash, script: o.script, input_data: o.input_data, - height: o.height, script_private_key: o.script_private_key, script_offset_public_key: o.script_offset_public_key, - sender_metadata_signature: o.sender_metadata_signature, + sender_metadata_signature_key: o.sender_metadata_signature_key, + sender_metadata_signature_nonce: o.sender_metadata_signature_nonce, } } } @@ -1212,7 +1195,6 @@ pub struct UpdateOutput { status: Option, tx_id: Option, spending_key: Option>, - height: Option, script_private_key: Option>, } @@ -1222,7 +1204,6 @@ pub struct UpdateOutputSql { status: Option, tx_id: Option, spending_key: Option>, - height: Option, script_private_key: Option>, } @@ -1241,7 +1222,6 @@ impl From for UpdateOutputSql { status: u.status.map(|t| t as i32), tx_id: u.tx_id.map(|t| t as i64), spending_key: u.spending_key, - height: u.height.map(|t| t as i64), script_private_key: u.script_private_key, } } @@ -1798,7 +1778,6 @@ mod test { status: Some(OutputStatus::Unspent), tx_id: Some(44u64), spending_key: None, - height: None, script_private_key: None, }, &conn, @@ -1812,7 +1791,6 @@ mod test { status: Some(OutputStatus::EncumberedToBeReceived), tx_id: Some(44u64), spending_key: None, - height: None, script_private_key: None, }, &conn, diff --git a/base_layer/wallet/src/schema.rs b/base_layer/wallet/src/schema.rs index 2f4b01644f..08a109c7c6 100644 --- a/base_layer/wallet/src/schema.rs +++ b/base_layer/wallet/src/schema.rs @@ -97,10 +97,10 @@ table! { hash -> Nullable, script -> Binary, input_data -> Binary, - height -> BigInt, script_private_key -> Binary, script_offset_public_key -> Binary, - sender_metadata_signature -> Text, + sender_metadata_signature_key -> Binary, + sender_metadata_signature_nonce -> Binary, } } diff --git a/base_layer/wallet/src/storage/mod.rs b/base_layer/wallet/src/storage/mod.rs index 6c4a16887e..b4dbde2225 100644 --- a/base_layer/wallet/src/storage/mod.rs +++ b/base_layer/wallet/src/storage/mod.rs @@ -22,10 +22,11 @@ // Note: For help in getting started with diesel as well as how to update the tables look here: // http://diesel.rs/guides/getting-started/ -// - You also need to ensure that you installed diesel with the sqlite feature flag cargo install diesel_cli -// --no-default-features --features sqlite -// - If you updated the tables the following needs to be run from the base_layer/wallet/ folder: diesel setup -// --database-url test.sqlite3 diesel migration run --database-url test.sqlite3 +// - You also need to ensure that you installed diesel with the sqlite feature flag: +// - 'cargo install diesel_cli --no-default-features --features sqlite' +// - If you updated the tables the following needs to be run from the base_layer/wallet/ folder: +// - 'diesel setup --database-url test.sqlite3' +// - 'diesel migration run --database-url test.sqlite3' // - After running this, make sure that the diesel update did not change BigInt to Integer in 'schema.rs' (check for // any unwanted changes) diff --git a/base_layer/wallet/src/testnet_utils.rs b/base_layer/wallet/src/testnet_utils.rs index baa407d075..69dcdf97cd 100644 --- a/base_layer/wallet/src/testnet_utils.rs +++ b/base_layer/wallet/src/testnet_utils.rs @@ -62,22 +62,19 @@ use tari_comms::{ transports::MemoryTransport, types::{CommsPublicKey, CommsSecretKey}, }; -use tari_comms_dht::{envelope::Network as DhtNetwork, DhtConfig}; -use tari_core::{ - consensus::Network, - transactions::{ - helpers::{create_unblinded_output, TestParams as TestParamsHelpers}, - tari_amount::MicroTari, - transaction::{OutputFeatures, Transaction, TransactionInput, UnblindedOutput}, - types::{BlindingFactor, CryptoFactories, PrivateKey, PublicKey}, - }, +use tari_comms_dht::DhtConfig; +use tari_core::transactions::{ + helpers::{create_unblinded_output, TestParams as TestParamsHelpers}, + tari_amount::MicroTari, + transaction::{OutputFeatures, Transaction, TransactionInput, UnblindedOutput}, + types::{BlindingFactor, CryptoFactories, PrivateKey, PublicKey}, }; use tari_crypto::{ keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait}, script, tari_utilities::hex::Hex, }; -use tari_p2p::{initialization::CommsConfig, transport::TransportType}; +use tari_p2p::{initialization::CommsConfig, transport::TransportType, Network}; use tari_shutdown::{Shutdown, ShutdownSignal}; use tokio::{runtime::Handle, time::delay_for}; @@ -140,6 +137,7 @@ pub async fn create_wallet( .expect("Could not construct Node Identity"), ); let comms_config = CommsConfig { + network: Network::Weatherwax, transport_type: TransportType::Memory { listener_address: public_address, }, @@ -151,7 +149,6 @@ pub async fn create_wallet( user_agent: "/tari/wallet/test".to_string(), dht: DhtConfig { discovery_request_timeout: Duration::from_secs(30), - network: DhtNetwork::Weatherwax, allow_test_addresses: true, ..Default::default() }, @@ -169,7 +166,7 @@ pub async fn create_wallet( factories, None, None, - Network::Weatherwax, + Network::Weatherwax.into(), None, None, None, diff --git a/base_layer/wallet/src/transaction_service/mod.rs b/base_layer/wallet/src/transaction_service/mod.rs index f0e8c43008..2ee17b43e1 100644 --- a/base_layer/wallet/src/transaction_service/mod.rs +++ b/base_layer/wallet/src/transaction_service/mod.rs @@ -37,7 +37,7 @@ use crate::{ storage::database::{TransactionBackend, TransactionDatabase}, }, }; -use futures::{future, Future, Stream, StreamExt}; +use futures::{Stream, StreamExt}; use log::*; use std::sync::Arc; use tari_comms::{connectivity::ConnectivityRequester, peer_manager::NodeIdentity}; @@ -53,6 +53,7 @@ use tari_p2p::{ tari_message::TariMessageType, }; use tari_service_framework::{ + async_trait, reply_channel, ServiceInitializationError, ServiceInitializer, @@ -159,12 +160,11 @@ where T: TransactionBackend } } +#[async_trait] impl ServiceInitializer for TransactionServiceInitializer where T: TransactionBackend + 'static { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { let (sender, receiver) = reply_channel::unbounded(); let transaction_stream = self.transaction_stream(); let transaction_reply_stream = self.transaction_reply_stream(); @@ -219,6 +219,6 @@ where T: TransactionBackend + 'static info!(target: LOG_TARGET, "Transaction Service shutdown"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index b59803e4ef..953499edc3 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -87,9 +87,6 @@ use tari_crypto::{keys::DiffieHellmanSharedSecret, script, tari_utilities::ByteA use tari_p2p::domain_message::DomainMessage; use tari_service_framework::{reply_channel, reply_channel::Receiver}; use tari_shutdown::ShutdownSignal; - -#[cfg(feature = "test_harness")] -use tokio::runtime::Handle; use tokio::{sync::broadcast, task::JoinHandle}; const LOG_TARGET: &str = "wallet::transaction_service::service"; @@ -1804,7 +1801,7 @@ where MicroTari::from(0), tx.clone(), TransactionStatus::Coinbase, - format!("Coinbase Transaction for Block {}", block_height), + format!("Coinbase Transaction for Block #{}", block_height), Utc::now().naive_utc(), TransactionDirection::Inbound, Some(block_height), @@ -2053,7 +2050,7 @@ where _tx_id: TxId, amount: MicroTari, source_public_key: CommsPublicKey, - handle: Handle, + handle: tokio::runtime::Handle, ) -> Result<(), TransactionServiceError> { use crate::{ base_node_service::{handle::BaseNodeServiceHandle, mock_base_node_service::MockBaseNodeService}, @@ -2068,7 +2065,8 @@ where transaction_service::{handle::TransactionServiceHandle, storage::models::InboundTransaction}, }; use tari_comms::types::CommsSecretKey; - use tari_core::consensus::{ConsensusConstantsBuilder, Network}; + use tari_core::consensus::ConsensusConstantsBuilder; + use tari_p2p::Network; use tempfile::tempdir; let (_sender, receiver) = reply_channel::unbounded(); diff --git a/base_layer/wallet/src/utxo_scanner_service/mod.rs b/base_layer/wallet/src/utxo_scanner_service/mod.rs index 98f64ec89e..41d5eb379a 100644 --- a/base_layer/wallet/src/utxo_scanner_service/mod.rs +++ b/base_layer/wallet/src/utxo_scanner_service/mod.rs @@ -29,12 +29,13 @@ use crate::{ utxo_scanning::{UtxoScannerMode, UtxoScannerService}, }, }; -use futures::{future, Future}; +use futures::future; use log::*; use std::{sync::Arc, time::Duration}; use tari_comms::{connectivity::ConnectivityRequester, NodeIdentity}; use tari_core::transactions::types::CryptoFactories; use tari_service_framework::{ + async_trait, reply_channel, ServiceInitializationError, ServiceInitializer, @@ -75,12 +76,11 @@ where T: WalletBackend + 'static } } +#[async_trait] impl ServiceInitializer for UtxoScannerServiceInitializer where T: WalletBackend + 'static { - type Future = impl Future>; - - fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future { + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { trace!(target: LOG_TARGET, "Utxo scanner initialization"); let (sender, receiver) = reply_channel::unbounded(); @@ -125,6 +125,6 @@ where T: WalletBackend + 'static future::select(scanning_service, handles.get_shutdown_signal()).await; info!(target: LOG_TARGET, "Utxo scanner service shutdown"); }); - future::ready(Ok(())) + Ok(()) } } diff --git a/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs b/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs index 264746f7c3..17e129d662 100644 --- a/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs +++ b/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs @@ -447,13 +447,12 @@ where TBackend: WalletBackend + 'static outputs: Vec, ) -> Result, UtxoScannerError> { let mut found_outputs: Vec<(UnblindedOutput, String)> = Vec::new(); - let height = 0; if self.mode == UtxoScannerMode::Recovery { found_outputs.append( &mut self .resources .output_manager_service - .scan_for_recoverable_outputs(outputs.clone(), height) + .scan_for_recoverable_outputs(outputs.clone()) .await? .into_iter() .map(|v| (v, format!("Recovered on {}.", Utc::now().naive_utc()))) @@ -464,7 +463,7 @@ where TBackend: WalletBackend + 'static &mut self .resources .output_manager_service - .scan_outputs_for_one_sided_payments(outputs.clone(), height) + .scan_outputs_for_one_sided_payments(outputs.clone()) .await? .into_iter() .map(|v| { diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index 7b487169ab..5ed26f536d 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -316,7 +316,7 @@ where source_public_key: &CommsPublicKey, features: OutputFeatures, message: String, - sender_signature: Signature, + sender_metadata_signature: Signature, script_private_key: &PrivateKey, script_offset_public_key: &PublicKey, ) -> Result { @@ -326,10 +326,9 @@ where Some(features.clone()), script, input_data, - 0, script_private_key.clone(), script_offset_public_key.clone(), - sender_signature, + sender_metadata_signature, ); let tx_id = self diff --git a/base_layer/wallet/tests/mod.rs b/base_layer/wallet/tests/mod.rs index 7621ba53f4..a7a8824b0b 100644 --- a/base_layer/wallet/tests/mod.rs +++ b/base_layer/wallet/tests/mod.rs @@ -20,10 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#![allow(incomplete_features)] -#![feature(type_alias_impl_trait)] -#![feature(min_type_alias_impl_trait)] - pub mod contacts_service; pub mod output_manager_service; pub mod support; diff --git a/base_layer/wallet/tests/output_manager_service/service.rs b/base_layer/wallet/tests/output_manager_service/service.rs index bd673399e8..ea5f5bf52e 100644 --- a/base_layer/wallet/tests/output_manager_service/service.rs +++ b/base_layer/wallet/tests/output_manager_service/service.rs @@ -40,7 +40,7 @@ use tari_comms::{ }; use tari_core::{ base_node::rpc::BaseNodeWalletRpcServer, - consensus::{ConsensusConstantsBuilder, Network}, + consensus::ConsensusConstantsBuilder, transactions::{ fee::Fee, helpers::{create_unblinded_output, TestParams as TestParamsHelpers}, @@ -62,6 +62,7 @@ use tari_crypto::{ script, script::TariScript, }; +use tari_p2p::Network; use tari_service_framework::reply_channel; use tari_shutdown::Shutdown; use tari_wallet::{ @@ -297,12 +298,7 @@ fn generate_sender_transaction_message(amount: MicroTari) -> (TxId, TransactionS .with_change_secret(alice.change_spend_key) .with_input(utxo, input) .with_amount(0, amount) - .with_recipient_script( - 0, - script!(Nop), - PrivateKey::random(&mut OsRng), - OutputFeatures::default(), - ) + .with_recipient_script(0, script!(Nop), PrivateKey::random(&mut OsRng), Default::default()) .with_change_script( script!(Nop), inputs!(PublicKey::from_secret_key(&script_private_key)), diff --git a/base_layer/wallet/tests/support/comms_and_services.rs b/base_layer/wallet/tests/support/comms_and_services.rs index 9f89017328..5ca3c15d51 100644 --- a/base_layer/wallet/tests/support/comms_and_services.rs +++ b/base_layer/wallet/tests/support/comms_and_services.rs @@ -30,10 +30,7 @@ use tari_comms::{ types::CommsPublicKey, CommsNode, }; -use tari_comms_dht::{ - envelope::{DhtMessageHeader, Network}, - Dht, -}; +use tari_comms_dht::{envelope::DhtMessageHeader, Dht}; use tari_p2p::{ comms_connector::{InboundDomainConnector, PeerMessage}, domain_message::DomainMessage, @@ -85,12 +82,12 @@ pub fn create_dummy_message(inner: T, public_key: &CommsPublicKey) -> DomainM ); DomainMessage { dht_header: DhtMessageHeader { + major: Default::default(), + minor: Default::default(), ephemeral_public_key: None, origin_mac: Vec::new(), - version: Default::default(), message_type: Default::default(), flags: Default::default(), - network: Network::LocalTest, destination: Default::default(), message_tag: MessageTag::new(), expires: None, diff --git a/base_layer/wallet/tests/transaction_service/service.rs b/base_layer/wallet/tests/transaction_service/service.rs index 334dc3291b..fdff173785 100644 --- a/base_layer/wallet/tests/transaction_service/service.rs +++ b/base_layer/wallet/tests/transaction_service/service.rs @@ -67,7 +67,7 @@ use tari_core::{ proto::wallet_rpc::{TxLocation, TxQueryResponse, TxSubmissionRejectionReason, TxSubmissionResponse}, rpc::BaseNodeWalletRpcServer, }, - consensus::{ConsensusConstantsBuilder, Network}, + consensus::ConsensusConstantsBuilder, proto::base_node as base_node_proto, transactions::{ fee::Fee, @@ -88,7 +88,7 @@ use tari_crypto::{ script, script::{ExecutionStack, TariScript}, }; -use tari_p2p::{comms_connector::pubsub_connector, domain_message::DomainMessage}; +use tari_p2p::{comms_connector::pubsub_connector, domain_message::DomainMessage, Network}; use tari_service_framework::{reply_channel, RegisterHandle, StackBuilder}; use tari_shutdown::{Shutdown, ShutdownSignal}; use tari_wallet::{ @@ -193,7 +193,7 @@ pub fn setup_transaction_service< OutputManagerServiceConfig::default(), oms_backend, factories.clone(), - Network::Weatherwax, + Network::Weatherwax.into(), CommsSecretKey::default(), )) .add_initializer(TransactionServiceInitializer::new( @@ -895,7 +895,7 @@ fn recover_one_sided_transaction() { .expect("Could not find completed one-sided tx"); let outputs = completed_tx.transaction.body.outputs().clone(); - let unblinded = bob_oms.scan_outputs_for_one_sided_payments(outputs, 0).await.unwrap(); + let unblinded = bob_oms.scan_outputs_for_one_sided_payments(outputs).await.unwrap(); // Bob should be able to claim 1 output. assert_eq!(1, unblinded.len()); assert_eq!(value, unblinded[0].value); diff --git a/base_layer/wallet/tests/transaction_service/transaction_protocols.rs b/base_layer/wallet/tests/transaction_service/transaction_protocols.rs index 91693287fa..e2d797aae9 100644 --- a/base_layer/wallet/tests/transaction_service/transaction_protocols.rs +++ b/base_layer/wallet/tests/transaction_service/transaction_protocols.rs @@ -219,7 +219,6 @@ pub async fn oms_reply_channel_task( let response = match request { OutputManagerRequest::ConfirmTransaction(_) => Ok(OutputManagerResponse::TransactionConfirmed), OutputManagerRequest::CancelTransaction(_) => Ok(OutputManagerResponse::TransactionCancelled), - OutputManagerRequest::UpdateMinedHeight(_, _) => Ok(OutputManagerResponse::MinedHeightUpdated), _ => Err(OutputManagerError::InvalidResponseError( "Unhandled request type".to_string(), )), diff --git a/base_layer/wallet/tests/wallet/mod.rs b/base_layer/wallet/tests/wallet/mod.rs index 890a09ed77..336082dd6a 100644 --- a/base_layer/wallet/tests/wallet/mod.rs +++ b/base_layer/wallet/tests/wallet/mod.rs @@ -41,13 +41,10 @@ use tari_comms::{ types::{CommsPublicKey, CommsSecretKey}, }; use tari_comms_dht::DhtConfig; -use tari_core::{ - consensus::Network, - transactions::{ - helpers::{create_unblinded_output, TestParams}, - tari_amount::{uT, MicroTari}, - types::{CryptoFactories, PrivateKey, PublicKey}, - }, +use tari_core::transactions::{ + helpers::{create_unblinded_output, TestParams}, + tari_amount::{uT, MicroTari}, + types::{CryptoFactories, PrivateKey, PublicKey}, }; use tari_crypto::{ common::Blake256, @@ -55,7 +52,7 @@ use tari_crypto::{ keys::{PublicKey as PublicKeyTrait, SecretKey}, script, }; -use tari_p2p::{initialization::CommsConfig, transport::TransportType, DEFAULT_DNS_SEED_RESOLVER}; +use tari_p2p::{initialization::CommsConfig, transport::TransportType, Network, DEFAULT_DNS_SEED_RESOLVER}; use tari_shutdown::{Shutdown, ShutdownSignal}; use tari_wallet::{ contacts_service::storage::database::Contact, @@ -98,9 +95,11 @@ async fn create_wallet( passphrase: Option, recovery_master_key: Option, ) -> Result { + const NETWORK: Network = Network::Weatherwax; let node_identity = NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); let comms_config = CommsConfig { + network: NETWORK, node_identity: Arc::new(node_identity.clone()), transport_type: TransportType::Memory { listener_address: node_identity.public_address(), @@ -144,7 +143,7 @@ async fn create_wallet( factories, Some(transaction_service_config), None, - Network::Weatherwax, + NETWORK.into(), None, None, None, @@ -677,6 +676,7 @@ async fn test_import_utxo() { let temp_dir = tempdir().unwrap(); let (wallet_backend, tx_backend, oms_backend, contacts_backend, _temp_dir) = make_wallet_databases(None); let comms_config = CommsConfig { + network: Network::Weatherwax, node_identity: Arc::new(alice_identity.clone()), transport_type: TransportType::Tcp { listener_address: "/ip4/127.0.0.1/tcp/0".parse().unwrap(), @@ -701,7 +701,7 @@ async fn test_import_utxo() { factories.clone(), None, None, - Network::Weatherwax, + Network::Weatherwax.into(), None, None, None, @@ -763,8 +763,6 @@ async fn test_import_utxo() { #[cfg(feature = "test_harness")] #[tokio_macros::test] async fn test_data_generation() { - use tari_comms_dht::envelope::Network as DhtNetwork; - let mut shutdown = Shutdown::new(); use tari_wallet::testnet_utils::generate_wallet_test_data; let factories = CryptoFactories::default(); @@ -772,6 +770,7 @@ async fn test_data_generation() { NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); let temp_dir = tempdir().unwrap(); let comms_config = CommsConfig { + network: Network::Weatherwax, node_identity: Arc::new(node_id.clone()), transport_type: TransportType::Memory { listener_address: node_id.public_address(), @@ -782,7 +781,6 @@ async fn test_data_generation() { outbound_buffer_size: 100, dht: DhtConfig { discovery_request_timeout: Duration::from_millis(500), - network: DhtNetwork::Weatherwax, allow_test_addresses: true, ..Default::default() }, @@ -801,7 +799,7 @@ async fn test_data_generation() { factories, None, None, - Network::Weatherwax, + Network::Weatherwax.into(), None, None, None, diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 6ad34e5453..8d4464f720 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -162,11 +162,8 @@ use tari_comms::{ tor, types::CommsSecretKey, }; -use tari_comms_dht::{envelope::Network as DhtNetwork, DbConnectionUrl, DhtConfig}; -use tari_core::{ - consensus::Network, - transactions::{tari_amount::MicroTari, transaction::OutputFeatures, types::CryptoFactories}, -}; +use tari_comms_dht::{DbConnectionUrl, DhtConfig}; +use tari_core::transactions::{tari_amount::MicroTari, transaction::OutputFeatures, types::CryptoFactories}; use tari_crypto::{ keys::{PublicKey, SecretKey}, script::ExecutionStack, @@ -214,6 +211,7 @@ use tari_wallet::{ use tari_core::transactions::types::Signature; use tari_crypto::script::TariScript; +use tari_p2p::Network; use tari_wallet::{ types::ValidationRetryStrategy, util::emoji::EmojiIdError, @@ -2628,6 +2626,7 @@ pub unsafe extern "C" fn comms_config_create( match ni { Ok(ni) => { let config = TariCommsConfig { + network: Network::Weatherwax, node_identity: Arc::new(ni), transport_type: (*transport_type).clone(), datastore_path, @@ -2638,7 +2637,6 @@ pub unsafe extern "C" fn comms_config_create( discovery_request_timeout: Duration::from_secs(discovery_timeout_in_secs), database_url: DbConnectionUrl::File(dht_database_path), auto_join: true, - network: DhtNetwork::Weatherwax, saf_msg_validity: Duration::from_secs(saf_message_duration_in_secs), ..Default::default() }, @@ -2910,7 +2908,7 @@ pub unsafe extern "C" fn wallet_create( ..Default::default() }), None, - Network::Weatherwax, + Network::Weatherwax.into(), None, None, None, diff --git a/common/src/configuration/global.rs b/common/src/configuration/global.rs index 0792ef5976..8e0f8aadb5 100644 --- a/common/src/configuration/global.rs +++ b/common/src/configuration/global.rs @@ -22,12 +22,11 @@ // //! # Global configuration of tari base layer system -use super::ConfigurationError; +use crate::{configuration::Network, ConfigurationError}; use config::{Config, ConfigError, Environment}; use multiaddr::Multiaddr; use std::{ convert::TryInto, - fmt::{Display, Formatter, Result as FormatResult}, net::SocketAddr, num::{NonZeroU16, TryFromIntError}, path::PathBuf, @@ -786,50 +785,6 @@ fn config_string(prefix: &str, network: &str, key: &str) -> String { format!("{}.{}.{}", prefix, network, key) } -//--------------------------------------------- Network type ------------------------------------------// -#[derive(Clone, Debug, PartialEq, Copy)] -pub enum Network { - MainNet, - Rincewind, - LocalNet, - Ridcully, - Stibbons, - Weatherwax, -} - -impl FromStr for Network { - type Err = ConfigurationError; - - fn from_str(value: &str) -> Result { - match value.to_lowercase().as_str() { - "rincewind" => Ok(Self::Rincewind), - "ridcully" => Ok(Self::Ridcully), - "stibbons" => Ok(Self::Stibbons), - "weatherwax" => Ok(Self::Weatherwax), - "mainnet" => Ok(Self::MainNet), - "localnet" => Ok(Self::LocalNet), - invalid => Err(ConfigurationError::new( - "network", - &format!("Invalid network option: {}", invalid), - )), - } - } -} - -impl Display for Network { - fn fmt(&self, f: &mut Formatter) -> FormatResult { - let msg = match self { - Self::MainNet => "mainnet", - Self::Rincewind => "rincewind", - Self::Ridcully => "ridcully", - Self::Stibbons => "stibbons", - Self::Weatherwax => "weatherwax", - Self::LocalNet => "localnet", - }; - f.write_str(msg) - } -} - //--------------------------------------------- Database type ------------------------------------------// #[derive(Debug, Clone)] pub enum DatabaseType { diff --git a/common/src/configuration/loader.rs b/common/src/configuration/loader.rs index 67f45c8062..7844ede1e9 100644 --- a/common/src/configuration/loader.rs +++ b/common/src/configuration/loader.rs @@ -49,13 +49,13 @@ //! } //! //! # let mut config = Config::new(); -//! config.set("my_node.network", "rincewind"); -//! config.set("my_node.rincewind.welcome_message", "nice to see you at unseen"); +//! config.set("my_node.network", "weatherwax"); +//! config.set("my_node.weatherwax.welcome_message", "nice to see you at unseen"); //! let my_config = ::load_from(&config).unwrap(); //! assert_eq!(my_config.welcome_message, "nice to see you at unseen"); //! ``` -use super::Network; +use crate::configuration::Network; use config::Config; use std::{ error::Error, diff --git a/common/src/configuration/mod.rs b/common/src/configuration/mod.rs index 23953f3617..d1e9b50594 100644 --- a/common/src/configuration/mod.rs +++ b/common/src/configuration/mod.rs @@ -25,12 +25,12 @@ //! ... //! [base_node] //! # common vars for all base_node instances -//! [base_node.rincewind] +//! [base_node.weatherwax] //! # overrides for rincewnd testnet //! [base_node.mainnet] //! # overrides for mainnet //! [wallet] -//! [wallet.rincewind] +//! [wallet.weatherwax] //! # etc.. //! ``` @@ -38,11 +38,8 @@ pub mod bootstrap; pub mod error; pub mod global; pub mod loader; +mod network; +pub use network::Network; pub mod seconds; pub mod utils; pub mod writer; - -pub use bootstrap::ConfigBootstrap; -pub use global::{CommsTransport, DatabaseType, GlobalConfig, Network, SocksAuthentication, TorControlAuthentication}; -pub use loader::ConfigurationError; -pub use utils::{default_config, install_default_config_file, load_configuration}; diff --git a/common/src/configuration/network.rs b/common/src/configuration/network.rs new file mode 100644 index 0000000000..a4eecceb92 --- /dev/null +++ b/common/src/configuration/network.rs @@ -0,0 +1,85 @@ +// Copyright 2021, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ConfigurationError; +use std::{ + fmt, + fmt::{Display, Formatter}, + str::FromStr, +}; + +/// Represents the available Tari p2p networks. Only nodes with matching byte values will be able to connect, so these +/// should never be changed once released. +#[repr(u8)] +#[derive(Clone, Debug, PartialEq, Eq, Copy)] +pub enum Network { + MainNet = 0x00, + LocalNet = 0x10, + Ridcully = 0x21, + Stibbons = 0x22, + Weatherwax = 0x23, +} + +impl Network { + pub fn as_byte(self) -> u8 { + self as u8 + } +} + +impl Default for Network { + fn default() -> Self { + Network::MainNet + } +} + +impl FromStr for Network { + type Err = ConfigurationError; + + fn from_str(value: &str) -> Result { + use Network::*; + match value.to_lowercase().as_str() { + "ridcully" => Ok(Ridcully), + "stibbons" => Ok(Stibbons), + "weatherwax" => Ok(Weatherwax), + "mainnet" => Ok(MainNet), + "localnet" => Ok(LocalNet), + invalid => Err(ConfigurationError::new( + "network", + &format!("Invalid network option: {}", invalid), + )), + } + } +} + +impl Display for Network { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + use Network::*; + let msg = match self { + MainNet => "mainnet", + Ridcully => "ridcully", + Stibbons => "stibbons", + Weatherwax => "weatherwax", + LocalNet => "localnet", + }; + f.write_str(msg) + } +} diff --git a/common/src/configuration/writer.rs b/common/src/configuration/writer.rs index d14fe50148..4ffaf0026d 100644 --- a/common/src/configuration/writer.rs +++ b/common/src/configuration/writer.rs @@ -45,7 +45,7 @@ //! }; //! // Merging configs into resulting structure, accounting preset network params //! let mut config = Config::new(); -//! config.set(&MyNodeConfig::network_config_key(), "rincewind"); +//! config.set(&MyNodeConfig::network_config_key(), "weatherwax"); //! main_config.merge_into(&mut config).unwrap(); //! node_config.merge_into(&mut config).unwrap(); //! @@ -57,15 +57,15 @@ //! name = "test_server" //! //! [my_node] -//! network = "rincewind" +//! network = "weatherwax" //! -//! [my_node.rincewind] +//! [my_node.weatherwax] //! address = "localhost" //! port = 3001 //! "# //! ); //! ``` -use super::{loader::ConfigPath, ConfigurationError}; +use super::loader::{ConfigPath, ConfigurationError}; use config::Config; /// Configuration writer based on ConfigPath selectors diff --git a/common/src/lib.rs b/common/src/lib.rs index c95bb2f460..cba66cd643 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -63,7 +63,7 @@ //! # use tari_test_utils::random::string; //! # use tempfile::tempdir; //! # use structopt::StructOpt; -//! # use tari_common::configuration::bootstrap::ApplicationType; +//! # use tari_common::configuration::{Network, bootstrap::ApplicationType}; //! let mut args = ConfigBootstrap::from_args(); //! # let temp_dir = tempdir().unwrap(); //! # args.base_path = temp_dir.path().to_path_buf(); @@ -78,19 +78,20 @@ #[cfg(feature = "build")] pub mod build; -pub mod configuration; #[macro_use] mod logging; -pub use configuration::error::ConfigError; - -pub mod dir_utils; +pub mod configuration; pub use configuration::{ bootstrap::{install_configuration, ConfigBootstrap}, - global::{CommsTransport, DatabaseType, GlobalConfig, Network, SocksAuthentication, TorControlAuthentication}, + error::ConfigError, + global::{CommsTransport, DatabaseType, GlobalConfig, SocksAuthentication, TorControlAuthentication}, loader::{ConfigLoader, ConfigPath, ConfigurationError, DefaultConfigLoader, NetworkConfigPath}, utils::{default_config, install_default_config_file, load_configuration}, }; + +pub mod dir_utils; + pub use logging::initialize_logging; pub const DEFAULT_CONFIG: &str = "config/config.toml"; diff --git a/comms/Cargo.toml b/comms/Cargo.toml index 7bcb0fd5b2..d239dd433b 100644 --- a/comms/Cargo.toml +++ b/comms/Cargo.toml @@ -33,7 +33,7 @@ prost = "=0.6.1" rand = "0.7.2" serde = "1.0.119" serde_derive = "1.0.119" -snow = {version="=0.6.2", features=["default-resolver"]} +snow = {version="=0.8.0", features=["default-resolver"]} thiserror = "1.0.20" tokio = {version="~0.2.19", features=["blocking", "time", "tcp", "dns", "sync", "stream", "signal"]} tokio-util = {version="0.2.0", features=["codec"]} @@ -54,7 +54,7 @@ tokio-macros = "0.2.3" tempfile = "3.1.0" [build-dependencies] -tari_common = { version = "^0.8", path="../common"} +tari_common = { version = "^0.8", path="../common", features = ["build"]} [features] avx2 = ["tari_crypto/avx2"] diff --git a/comms/dht/Cargo.toml b/comms/dht/Cargo.toml index c85d065f7d..5a95519de0 100644 --- a/comms/dht/Cargo.toml +++ b/comms/dht/Cargo.toml @@ -10,7 +10,6 @@ license = "BSD-3-Clause" edition = "2018" [dependencies] -tari_common = { version = "^0.8", path = "../../common"} tari_comms = { version = "^0.8", path = "../", features = ["rpc"]} tari_comms_rpc_macros = { version = "^0.8", path = "../rpc_macros"} tari_crypto = { git = "ssh://git@github.com/tari-project/tari-crypto.git", branch = "main" } diff --git a/comms/dht/examples/memory_net/utilities.rs b/comms/dht/examples/memory_net/utilities.rs index e098c5e05c..67ba28af6d 100644 --- a/comms/dht/examples/memory_net/utilities.rs +++ b/comms/dht/examples/memory_net/utilities.rs @@ -917,26 +917,23 @@ async fn setup_comms_dht( .unwrap(); let dht_outbound_layer = dht.outbound_middleware_layer(); + let pipeline = pipeline::Builder::new() + .outbound_buffer_size(10) + .with_outbound_pipeline(outbound_rx, |sink| { + ServiceBuilder::new().layer(dht_outbound_layer).service(sink) + }) + .max_concurrent_inbound_tasks(10) + .with_inbound_pipeline( + ServiceBuilder::new() + .layer(dht.inbound_middleware_layer()) + .service(SinkService::new(inbound_tx)), + ) + .build(); let (messaging_events_tx, _) = broadcast::channel(100); - let comms = comms .add_rpc_server(RpcServer::new().add_service(dht.rpc_service())) - .add_protocol_extension(MessagingProtocolExtension::new( - messaging_events_tx.clone(), - pipeline::Builder::new() - .outbound_buffer_size(10) - .with_outbound_pipeline(outbound_rx, |sink| { - ServiceBuilder::new().layer(dht_outbound_layer).service(sink) - }) - .max_concurrent_inbound_tasks(10) - .with_inbound_pipeline( - ServiceBuilder::new() - .layer(dht.inbound_middleware_layer()) - .service(SinkService::new(inbound_tx)), - ) - .build(), - )) + .add_protocol_extension(MessagingProtocolExtension::new(messaging_events_tx.clone(), pipeline)) .spawn_with_transport(MemoryTransport) .await .unwrap(); diff --git a/comms/dht/src/config.rs b/comms/dht/src/config.rs index 9b4a989406..3e55c647e8 100644 --- a/comms/dht/src/config.rs +++ b/comms/dht/src/config.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{envelope::Network, network_discovery::NetworkDiscoveryConfig, storage::DbConnectionUrl}; +use crate::{network_discovery::NetworkDiscoveryConfig, storage::DbConnectionUrl}; use std::time::Duration; #[derive(Debug, Clone)] @@ -93,8 +93,6 @@ pub struct DhtConfig { /// The interval to change the random pool peers. /// Default: 2 hours pub connectivity_random_pool_refresh: Duration, - /// The active Network. Default: TestNet - pub network: Network, /// Network discovery config pub network_discovery: NetworkDiscoveryConfig, /// Length of time to ban a peer if the peer misbehaves at the DHT-level. @@ -122,15 +120,11 @@ impl DhtConfig { } pub fn default_mainnet() -> Self { - Self { - network: Network::MainNet, - ..Default::default() - } + Default::default() } pub fn default_local_test() -> Self { Self { - network: Network::LocalTest, database_url: DbConnectionUrl::Memory, saf_auto_request: false, auto_join: false, @@ -169,13 +163,12 @@ impl Default for DhtConfig { connectivity_random_pool_refresh: Duration::from_secs(2 * 60 * 60), auto_join: false, join_cooldown_interval: Duration::from_secs(10 * 60), - network: Network::TestNet, network_discovery: Default::default(), ban_duration: Duration::from_secs(6 * 60 * 60), allow_test_addresses: false, flood_ban_max_msg_count: 10000, flood_ban_timespan: Duration::from_secs(100), - offline_peer_cooldown: Duration::from_secs(24 * 60 * 60), + offline_peer_cooldown: Duration::from_secs(2 * 60 * 60), saf_msg_validity: Duration::from_secs(10800), } } diff --git a/comms/dht/src/consts.rs b/comms/dht/src/consts.rs index 69695780ac..9f3f5eac35 100644 --- a/comms/dht/src/consts.rs +++ b/comms/dht/src/consts.rs @@ -21,4 +21,5 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// Version for DHT envelope -pub const DHT_ENVELOPE_HEADER_VERSION: u32 = 0; +pub const DHT_MAJOR_VERSION: u32 = 0; +pub const DHT_MINOR_VERSION: u32 = 0; diff --git a/comms/dht/src/dedup.rs b/comms/dht/src/dedup.rs index 19fd678585..278ae4a485 100644 --- a/comms/dht/src/dedup.rs +++ b/comms/dht/src/dedup.rs @@ -22,7 +22,7 @@ use crate::{actor::DhtRequester, inbound::DhtInboundMessage}; use digest::Input; -use futures::{task::Context, Future}; +use futures::{future::BoxFuture, task::Context}; use log::*; use std::task::Poll; use tari_comms::{pipeline::PipelineError, types::Challenge}; @@ -55,13 +55,14 @@ impl DedupMiddleware { } impl Service for DedupMiddleware -where S: Service + Clone +where + S: Service + Clone + Send + 'static, + S::Future: Send, { type Error = PipelineError; + type Future = BoxFuture<'static, Result>; type Response = (); - type Future = impl Future>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } @@ -69,7 +70,7 @@ where S: Service + Clon fn call(&mut self, message: DhtInboundMessage) -> Self::Future { let next_service = self.next_service.clone(); let mut dht_requester = self.dht_requester.clone(); - async move { + Box::pin(async move { let hash = hash_inbound_message(&message); trace!( target: LOG_TARGET, @@ -96,7 +97,7 @@ where S: Service + Clon message.dht_header.message_tag ); next_service.oneshot(message).await - } + }) } } diff --git a/comms/dht/src/dht.rs b/comms/dht/src/dht.rs index 1bc7d5d53c..dcdeea5730 100644 --- a/comms/dht/src/dht.rs +++ b/comms/dht/src/dht.rs @@ -278,9 +278,8 @@ impl Dht { InboundMessage, Response = (), Error = PipelineError, - Future = impl Future> + Send, - > + Clone - + Send, + Future = impl Future>, + > + Clone, > where S: Service + Clone + Send + Sync + 'static, @@ -291,7 +290,6 @@ impl Dht { ServiceBuilder::new() .layer(MetricsLayer::new(self.metrics_collector.clone())) .layer(inbound::DeserializeLayer::new(self.peer_manager.clone())) - .layer(inbound::ValidateLayer::new(self.config.network)) .layer(DedupLayer::new(self.dht_requester())) .layer(tower_filter::FilterLayer::new(self.unsupported_saf_messages_filter())) .layer(MessageLoggingLayer::new(format!( @@ -341,9 +339,8 @@ impl Dht { DhtOutboundRequest, Response = (), Error = PipelineError, - Future = impl Future> + Send, - > + Clone - + Send, + Future = impl Future>, + > + Clone, > where S: Service + Clone + Send + 'static, @@ -354,8 +351,7 @@ impl Dht { Arc::clone(&self.node_identity), self.dht_requester(), self.discovery_service_requester(), - self.config.network, - chrono::Duration::from_std(self.config.saf_msg_validity).unwrap(), + self.config.saf_msg_validity, )) .layer(MessageLoggingLayer::new(format!( "Outbound [{}]", diff --git a/comms/dht/src/discovery/service.rs b/comms/dht/src/discovery/service.rs index 5bd9c29b8d..5023e50418 100644 --- a/comms/dht/src/discovery/service.rs +++ b/comms/dht/src/discovery/service.rs @@ -247,7 +247,7 @@ impl DhtDiscoveryService { .filter_map(|addr| addr.parse().ok()) .collect::>(); - validate_peer_addresses(&addresses, self.config.network.is_localtest()) + validate_peer_addresses(&addresses, self.config.allow_test_addresses) .map_err(|err| DhtDiscoveryError::InvalidPeerMultiaddr(err.to_string()))?; let peer = self diff --git a/comms/dht/src/envelope.rs b/comms/dht/src/envelope.rs index 7f5bb318d9..0b93546dbb 100644 --- a/comms/dht/src/envelope.rs +++ b/comms/dht/src/envelope.rs @@ -34,7 +34,7 @@ use tari_utilities::{ByteArray, ByteArrayError}; use thiserror::Error; // Re-export applicable protos -pub use crate::proto::envelope::{dht_header::Destination, DhtEnvelope, DhtHeader, DhtMessageType, Network}; +pub use crate::proto::envelope::{dht_header::Destination, DhtEnvelope, DhtHeader, DhtMessageType}; use chrono::{DateTime, NaiveDateTime, Utc}; use prost_types::Timestamp; use tari_utilities::epoch_time::EpochTime; @@ -128,14 +128,14 @@ impl DhtMessageType { /// It is preferable to not to expose the generated prost structs publicly. #[derive(Clone, Debug, PartialEq, Eq)] pub struct DhtMessageHeader { - pub version: u32, + pub major: u32, + pub minor: u32, pub destination: NodeDestination, /// Encoded DhtOrigin. This can refer to the same peer that sent the message /// or another peer if the message is being propagated. pub origin_mac: Vec, pub ephemeral_public_key: Option, pub message_type: DhtMessageType, - pub network: Network, pub flags: DhtMessageFlags, pub message_tag: MessageTag, pub expires: Option, @@ -155,8 +155,8 @@ impl Display for DhtMessageHeader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!( f, - "DhtMessageHeader (Dest:{}, Type:{:?}, Network:{:?}, Flags:{:?}, Trace:{})", - self.destination, self.message_type, self.network, self.flags, self.message_tag + "DhtMessageHeader (Dest:{}, Type:{:?}, Flags:{:?}, Trace:{})", + self.destination, self.message_type, self.flags, self.message_tag ) } } @@ -183,12 +183,12 @@ impl TryFrom for DhtMessageHeader { let expires: Option> = header.expires.map(timestamp_to_datetime); Ok(Self { - version: header.version, + major: header.major, + minor: header.minor, destination, origin_mac: header.origin_mac, ephemeral_public_key, message_type: DhtMessageType::from_i32(header.message_type).ok_or(DhtMessageError::InvalidMessageType)?, - network: Network::from_i32(header.network).ok_or(DhtMessageError::InvalidNetwork)?, flags: DhtMessageFlags::from_bits(header.flags).ok_or(DhtMessageError::InvalidMessageFlags)?, message_tag: MessageTag::from(header.message_tag), expires: expires.map(datetime_to_epochtime), @@ -211,7 +211,8 @@ impl From for DhtHeader { fn from(header: DhtMessageHeader) -> Self { let expires = header.expires.map(epochtime_to_datetime); Self { - version: header.version, + major: header.major, + minor: header.minor, ephemeral_public_key: header .ephemeral_public_key .as_ref() @@ -220,7 +221,6 @@ impl From for DhtHeader { origin_mac: header.origin_mac, destination: Some(header.destination.into()), message_type: header.message_type as i32, - network: header.network as i32, flags: header.flags.bits(), message_tag: header.message_tag.as_value(), expires: expires.map(datetime_to_timestamp), diff --git a/comms/dht/src/inbound/decryption.rs b/comms/dht/src/inbound/decryption.rs index e605227480..9e3a6bbd3e 100644 --- a/comms/dht/src/inbound/decryption.rs +++ b/comms/dht/src/inbound/decryption.rs @@ -27,7 +27,7 @@ use crate::{ proto::envelope::OriginMac, DhtConfig, }; -use futures::{task::Context, Future}; +use futures::{future::BoxFuture, task::Context}; use log::*; use prost::Message; use std::{sync::Arc, task::Poll, time::Duration}; @@ -123,25 +123,26 @@ impl DecryptionService { } impl Service for DecryptionService -where S: Service + Clone +where + S: Service + Clone + Send + 'static, + S::Future: Send, { type Error = PipelineError; + type Future = BoxFuture<'static, Result>; type Response = (); - type Future = impl Future>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, msg: DhtInboundMessage) -> Self::Future { - Self::handle_message( + Box::pin(Self::handle_message( self.inner.clone(), Arc::clone(&self.node_identity), self.connectivity.clone(), self.config.ban_duration, msg, - ) + )) } } @@ -416,10 +417,13 @@ mod test { #[test] fn decrypt_inbound_success() { - let result = Mutex::new(None); - let service = service_fn(|msg: DecryptedDhtMessage| { - *result.lock().unwrap() = Some(msg); - future::ready(Result::<(), PipelineError>::Ok(())) + let result = Arc::new(Mutex::new(None)); + let service = service_fn({ + let result = result.clone(); + move |msg: DecryptedDhtMessage| { + *result.lock().unwrap() = Some(msg); + future::ready(Result::<(), PipelineError>::Ok(())) + } }); let node_identity = make_node_identity(); let (connectivity, _) = create_connectivity_mock(); @@ -441,10 +445,13 @@ mod test { #[test] fn decrypt_inbound_fail() { - let result = Mutex::new(None); - let service = service_fn(|msg: DecryptedDhtMessage| { - *result.lock().unwrap() = Some(msg); - future::ready(Result::<(), PipelineError>::Ok(())) + let result = Arc::new(Mutex::new(None)); + let service = service_fn({ + let result = result.clone(); + move |msg: DecryptedDhtMessage| { + *result.lock().unwrap() = Some(msg); + future::ready(Result::<(), PipelineError>::Ok(())) + } }); let node_identity = make_node_identity(); let (connectivity, _) = create_connectivity_mock(); @@ -466,10 +473,13 @@ mod test { async fn decrypt_inbound_fail_destination() { let (connectivity, mock) = create_connectivity_mock(); mock.spawn(); - let result = Mutex::new(None); - let service = service_fn(|msg: DecryptedDhtMessage| { - *result.lock().unwrap() = Some(msg); - future::ready(Result::<(), PipelineError>::Ok(())) + let result = Arc::new(Mutex::new(None)); + let service = service_fn({ + let result = result.clone(); + move |msg: DecryptedDhtMessage| { + *result.lock().unwrap() = Some(msg); + future::ready(Result::<(), PipelineError>::Ok(())) + } }); let node_identity = make_node_identity(); let mut service = DecryptionService::new(Default::default(), node_identity.clone(), connectivity, service); diff --git a/comms/dht/src/inbound/deserialize.rs b/comms/dht/src/inbound/deserialize.rs index 72cf21ad22..b28a057cb5 100644 --- a/comms/dht/src/inbound/deserialize.rs +++ b/comms/dht/src/inbound/deserialize.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{inbound::DhtInboundMessage, proto::envelope::DhtEnvelope}; -use futures::{task::Context, Future}; +use futures::{future::BoxFuture, task::Context}; use log::*; use prost::Message; use std::{convert::TryInto, sync::Arc, task::Poll}; @@ -51,13 +51,14 @@ impl DhtDeserializeMiddleware { } impl Service for DhtDeserializeMiddleware -where S: Service + Clone + 'static +where + S: Service + Clone + Send + 'static, + S::Future: Send, { type Error = PipelineError; + type Future = BoxFuture<'static, Result>; type Response = (); - type Future = impl Future>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } @@ -65,7 +66,7 @@ where S: Service + Clon fn call(&mut self, message: InboundMessage) -> Self::Future { let next_service = self.next_service.clone(); let peer_manager = self.peer_manager.clone(); - async move { + Box::pin(async move { trace!(target: LOG_TARGET, "Deserializing InboundMessage {}", message.tag); let InboundMessage { @@ -92,6 +93,7 @@ where S: Service + Clon inbound_msg.dht_header.message_tag ); + let next_service = next_service.ready_oneshot().await?; next_service.oneshot(inbound_msg).await }, Err(err) => { @@ -99,7 +101,7 @@ where S: Service + Clon Err(err.into()) }, } - } + }) } } @@ -127,6 +129,7 @@ mod test { use crate::{ envelope::DhtMessageFlags, test_utils::{ + assert_send_static_service, build_peer_manager, make_comms_inbound_message, make_dht_envelope, @@ -144,6 +147,7 @@ mod test { peer_manager.add_peer(node_identity.to_peer()).await.unwrap(); let mut deserialize = DeserializeLayer::new(peer_manager).layer(spy.to_service::()); + assert_send_static_service(&deserialize); let dht_envelope = make_dht_envelope( &node_identity, diff --git a/comms/dht/src/inbound/dht_handler/middleware.rs b/comms/dht/src/inbound/dht_handler/middleware.rs index 8288e36050..6accf65513 100644 --- a/comms/dht/src/inbound/dht_handler/middleware.rs +++ b/comms/dht/src/inbound/dht_handler/middleware.rs @@ -22,7 +22,7 @@ use super::task::ProcessDhtMessage; use crate::{discovery::DhtDiscoveryRequester, inbound::DecryptedDhtMessage, outbound::OutboundMessageRequester}; -use futures::{task::Context, Future}; +use futures::{future::BoxFuture, task::Context}; use std::{sync::Arc, task::Poll}; use tari_comms::{ peer_manager::{NodeIdentity, PeerManager}, @@ -60,26 +60,29 @@ impl DhtHandlerMiddleware { } impl Service for DhtHandlerMiddleware -where S: Service + Clone +where + S: Service + Clone + Send + 'static, + S::Future: Send, { type Error = PipelineError; + type Future = BoxFuture<'static, Result>; type Response = (); - type Future = impl Future>; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.next_service.poll_ready(cx) } fn call(&mut self, message: DecryptedDhtMessage) -> Self::Future { - ProcessDhtMessage::new( - self.next_service.clone(), - Arc::clone(&self.peer_manager), - self.outbound_service.clone(), - Arc::clone(&self.node_identity), - self.discovery_requester.clone(), - message, + Box::pin( + ProcessDhtMessage::new( + self.next_service.clone(), + Arc::clone(&self.peer_manager), + self.outbound_service.clone(), + Arc::clone(&self.node_identity), + self.discovery_requester.clone(), + message, + ) + .run(), ) - .run() } } diff --git a/comms/dht/src/inbound/message.rs b/comms/dht/src/inbound/message.rs index c32f3a0df1..a49ae4b073 100644 --- a/comms/dht/src/inbound/message.rs +++ b/comms/dht/src/inbound/message.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - consts::DHT_ENVELOPE_HEADER_VERSION, + consts::DHT_MAJOR_VERSION, envelope::{DhtMessageFlags, DhtMessageHeader}, }; use std::{ @@ -49,7 +49,7 @@ impl DhtInboundMessage { pub fn new(tag: MessageTag, dht_header: DhtMessageHeader, source_peer: Arc, body: Vec) -> Self { Self { tag, - version: DHT_ENVELOPE_HEADER_VERSION, + version: DHT_MAJOR_VERSION, dht_header, source_peer, is_saf_message: false, diff --git a/comms/dht/src/inbound/metrics.rs b/comms/dht/src/inbound/metrics.rs index 88f85ca336..d028708f73 100644 --- a/comms/dht/src/inbound/metrics.rs +++ b/comms/dht/src/inbound/metrics.rs @@ -21,11 +21,11 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::connectivity::MetricsCollectorHandle; -use futures::{task::Context, Future}; +use futures::task::Context; use log::*; use std::task::Poll; -use tari_comms::{message::InboundMessage, pipeline::PipelineError}; -use tower::{layer::Layer, Service, ServiceExt}; +use tari_comms::message::InboundMessage; +use tower::{layer::Layer, Service}; const LOG_TARGET: &str = "comms::dht::metrics"; @@ -45,19 +45,17 @@ impl Metrics { } impl Service for Metrics -where S: Service + Clone + 'static +where S: Service + Clone + 'static { - type Error = PipelineError; - type Response = (); + type Error = S::Error; + type Future = S::Future; + type Response = S::Response; - type Future = impl Future>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.next_service.poll_ready(cx) } fn call(&mut self, message: InboundMessage) -> Self::Future { - let next_service = self.next_service.clone(); if !self .metric_collector .write_metric_message_received(message.source_peer.clone()) @@ -65,7 +63,7 @@ where S: Service + Clone + debug!(target: LOG_TARGET, "Unable to write metric"); } - next_service.oneshot(message) + self.next_service.call(message) } } diff --git a/comms/dht/src/inbound/mod.rs b/comms/dht/src/inbound/mod.rs index 69d9689f67..ec6f22acbf 100644 --- a/comms/dht/src/inbound/mod.rs +++ b/comms/dht/src/inbound/mod.rs @@ -36,6 +36,3 @@ mod error; mod message; pub use message::{DecryptedDhtMessage, DhtInboundMessage}; - -mod validate; -pub use validate::ValidateLayer; diff --git a/comms/dht/src/inbound/validate.rs b/comms/dht/src/inbound/validate.rs deleted file mode 100644 index 112d8a3179..0000000000 --- a/comms/dht/src/inbound/validate.rs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2019, The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{inbound::DhtInboundMessage, proto::envelope::Network}; -use futures::{task::Context, Future}; -use log::*; -use std::task::Poll; -use tari_comms::pipeline::PipelineError; -use tower::{layer::Layer, Service, ServiceExt}; - -const LOG_TARGET: &str = "comms::dht::validate"; - -/// # DHT validation middleware -/// -/// Takes in a `DhtInboundMessage` and checks the message header for any invalid fields -/// If an invalid message is detected a rejection message is sent to the sending peer. -#[derive(Clone)] -pub struct ValidateMiddleware { - next_service: S, - target_network: Network, -} - -impl ValidateMiddleware { - pub fn new(service: S, target_network: Network) -> Self { - Self { - next_service: service, - target_network, - } - } -} - -impl Service for ValidateMiddleware -where S: Service + Clone + 'static -{ - type Error = PipelineError; - type Response = (); - - type Future = impl Future>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, message: DhtInboundMessage) -> Self::Future { - let next_service = self.next_service.clone(); - let target_network = self.target_network; - async move { - if message.dht_header.network == target_network && message.dht_header.is_valid() { - trace!( - target: LOG_TARGET, - "Passing message {} to next service (Trace: {})", - message.tag, - message.dht_header.message_tag - ); - next_service.oneshot(message).await?; - } else { - debug!( - target: LOG_TARGET, - "Message is for another network (want = {:?} got = {:?}) or message header is invalid. Discarding \ - the message (Trace: {}).", - target_network, - message.dht_header.network, - message.dht_header.message_tag - ); - } - - Ok(()) - } - } -} - -pub struct ValidateLayer { - target_network: Network, -} - -impl ValidateLayer { - pub fn new(target_network: Network) -> Self { - Self { target_network } - } -} - -impl Layer for ValidateLayer { - type Service = ValidateMiddleware; - - fn layer(&self, service: S) -> Self::Service { - ValidateMiddleware::new(service, self.target_network) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - envelope::DhtMessageFlags, - test_utils::{make_dht_inbound_message, make_node_identity, service_spy}, - }; - use tari_test_utils::panic_context; - use tokio::runtime::Runtime; - - #[test] - fn process_message() { - let mut rt = Runtime::new().unwrap(); - let spy = service_spy(); - - let mut validate = ValidateLayer::new(Network::LocalTest).layer(spy.to_service::()); - - panic_context!(cx); - - assert!(validate.poll_ready(&mut cx).is_ready()); - let node_identity = make_node_identity(); - let mut msg = make_dht_inbound_message(&node_identity, Vec::new(), DhtMessageFlags::empty(), false); - msg.dht_header.network = Network::MainNet; - - rt.block_on(validate.call(msg.clone())).unwrap(); - assert_eq!(spy.call_count(), 0); - - msg.dht_header.network = Network::LocalTest; - - rt.block_on(validate.call(msg)).unwrap(); - assert_eq!(spy.call_count(), 1); - } -} diff --git a/comms/dht/src/lib.rs b/comms/dht/src/lib.rs index 7044785833..cab2f8ab6f 100644 --- a/comms/dht/src/lib.rs +++ b/comms/dht/src/lib.rs @@ -111,10 +111,6 @@ //! ``` #![recursion_limit = "256"] -// Details: https://doc.rust-lang.org/beta/unstable-book/language-features/type-alias-impl-trait.html -#![allow(incomplete_features)] -#![feature(type_alias_impl_trait)] -#![feature(min_type_alias_impl_trait)] #[macro_use] extern crate diesel; #[macro_use] diff --git a/comms/dht/src/logging_middleware.rs b/comms/dht/src/logging_middleware.rs index 7fa2b31d0e..edcfc73980 100644 --- a/comms/dht/src/logging_middleware.rs +++ b/comms/dht/src/logging_middleware.rs @@ -20,11 +20,10 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use futures::{task::Context, Future, TryFutureExt}; +use futures::task::Context; use log::*; use std::{borrow::Cow, fmt::Display, marker::PhantomData, task::Poll}; -use tari_comms::pipeline::PipelineError; -use tower::{layer::Layer, Service, ServiceExt}; +use tower::{layer::Layer, Service}; const LOG_TARGET: &str = "comms::middleware::message_logging"; @@ -46,7 +45,6 @@ impl<'a, R> MessageLoggingLayer<'a, R> { impl<'a, S, R> Layer for MessageLoggingLayer<'a, R> where S: Service, - S::Error: Into + Send + Sync + 'static, R: Display, { type Service = MessageLoggingService<'a, S>; @@ -73,22 +71,19 @@ impl<'a, S> MessageLoggingService<'a, S> { impl Service for MessageLoggingService<'_, S> where - S: Service + Clone, - S::Error: Into + Send + Sync + 'static, + S: Service, R: Display, { - type Error = PipelineError; + type Error = S::Error; + type Future = S::Future; type Response = S::Response; - type Future = impl Future>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) } fn call(&mut self, msg: R) -> Self::Future { trace!(target: LOG_TARGET, "{}{}", self.prefix_msg, msg); - let mut inner = self.inner.clone(); - async move { inner.ready_and().and_then(|s| s.call(msg)).await.map_err(Into::into) } + self.inner.call(msg) } } diff --git a/comms/dht/src/network_discovery/on_connect.rs b/comms/dht/src/network_discovery/on_connect.rs index 5a41a6ecbc..b93657f061 100644 --- a/comms/dht/src/network_discovery/on_connect.rs +++ b/comms/dht/src/network_discovery/on_connect.rs @@ -170,7 +170,7 @@ impl OnConnect { } let addresses = peer.addresses.iter(); - match validate_peer_addresses(addresses, self.config().network.is_localtest()) { + match validate_peer_addresses(addresses, self.config().allow_test_addresses) { Ok(_) => { debug!( target: LOG_TARGET, diff --git a/comms/dht/src/outbound/broadcast.rs b/comms/dht/src/outbound/broadcast.rs index 7125e12227..cf27343495 100644 --- a/comms/dht/src/outbound/broadcast.rs +++ b/comms/dht/src/outbound/broadcast.rs @@ -33,7 +33,7 @@ use crate::{ message_send_state::MessageSendState, SendMessageResponse, }, - proto::envelope::{DhtMessageType, Network, OriginMac}, + proto::envelope::{DhtMessageType, OriginMac}, }; use bytes::Bytes; use chrono::{DateTime, Utc}; @@ -41,13 +41,13 @@ use digest::Digest; use futures::{ channel::oneshot, future, + future::BoxFuture, stream::{self, StreamExt}, task::Context, - Future, }; use log::*; use rand::rngs::OsRng; -use std::{sync::Arc, task::Poll}; +use std::{sync::Arc, task::Poll, time::Duration}; use tari_comms::{ message::{MessageExt, MessageTag}, peer_manager::{NodeId, NodeIdentity, Peer}, @@ -68,7 +68,6 @@ pub struct BroadcastLayer { dht_requester: DhtRequester, dht_discovery_requester: DhtDiscoveryRequester, node_identity: Arc, - target_network: Network, message_validity_window: chrono::Duration, } @@ -77,15 +76,14 @@ impl BroadcastLayer { node_identity: Arc, dht_requester: DhtRequester, dht_discovery_requester: DhtDiscoveryRequester, - target_network: Network, - message_validity_window: chrono::Duration, + message_validity_window: Duration, ) -> Self { BroadcastLayer { dht_requester, dht_discovery_requester, node_identity, - target_network, - message_validity_window, + message_validity_window: chrono::Duration::from_std(message_validity_window) + .expect("message_validity_window is too large"), } } } @@ -99,7 +97,6 @@ impl Layer for BroadcastLayer { Arc::clone(&self.node_identity), self.dht_requester.clone(), self.dht_discovery_requester.clone(), - self.target_network, self.message_validity_window, ) } @@ -109,11 +106,10 @@ impl Layer for BroadcastLayer { /// the worker task. #[derive(Clone)] pub struct BroadcastMiddleware { - next: S, + next_service: S, dht_requester: DhtRequester, dht_discovery_requester: DhtDiscoveryRequester, node_identity: Arc, - target_network: Network, message_validity_window: chrono::Duration, } @@ -123,43 +119,43 @@ impl BroadcastMiddleware { node_identity: Arc, dht_requester: DhtRequester, dht_discovery_requester: DhtDiscoveryRequester, - target_network: Network, message_validity_window: chrono::Duration, ) -> Self { Self { - next: service, + next_service: service, dht_requester, dht_discovery_requester, node_identity, - target_network, message_validity_window, } } } impl Service for BroadcastMiddleware -where S: Service + Clone +where + S: Service + Clone + Send + 'static, + S::Future: Send, { type Error = PipelineError; + type Future = BoxFuture<'static, Result>; type Response = (); - type Future = impl Future>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, msg: DhtOutboundRequest) -> Self::Future { - BroadcastTask::new( - self.next.clone(), - Arc::clone(&self.node_identity), - self.dht_requester.clone(), - self.dht_discovery_requester.clone(), - self.target_network, - msg, - self.message_validity_window, + Box::pin( + BroadcastTask::new( + self.next_service.clone(), + Arc::clone(&self.node_identity), + self.dht_requester.clone(), + self.dht_discovery_requester.clone(), + msg, + self.message_validity_window, + ) + .handle(), ) - .handle() } } @@ -169,7 +165,6 @@ struct BroadcastTask { dht_requester: DhtRequester, dht_discovery_requester: DhtDiscoveryRequester, request: Option, - target_network: Network, message_validity_window: chrono::Duration, } type FinalMessageParts = (Option>, Option, Bytes); @@ -182,7 +177,6 @@ where S: Service node_identity: Arc, dht_requester: DhtRequester, dht_discovery_requester: DhtDiscoveryRequester, - target_network: Network, request: DhtOutboundRequest, message_validity_window: chrono::Duration, ) -> Self { @@ -191,7 +185,6 @@ where S: Service node_identity, dht_requester, dht_discovery_requester, - target_network, request: Some(request), message_validity_window, } @@ -438,7 +431,6 @@ where S: Service destination_node_id: node_id, destination: destination.clone(), dht_message_type, - network: self.target_network, dht_flags, custom_header: custom_header.clone(), body: body.clone(), @@ -523,7 +515,14 @@ mod test { use super::*; use crate::{ outbound::SendMessageParams, - test_utils::{create_dht_actor_mock, create_dht_discovery_mock, make_peer, service_spy, DhtDiscoveryMockState}, + test_utils::{ + assert_send_static_service, + create_dht_actor_mock, + create_dht_discovery_mock, + make_peer, + service_spy, + DhtDiscoveryMockState, + }, }; use futures::channel::oneshot; use rand::rngs::OsRng; @@ -582,9 +581,9 @@ mod test { node_identity, dht_requester, dht_discover_requester, - Network::LocalTest, chrono::Duration::seconds(10800), ); + assert_send_static_service(&service); let (reply_tx, _reply_rx) = oneshot::channel(); service @@ -626,7 +625,6 @@ mod test { Arc::new(node_identity), dht_requester, dht_discover_requester, - Network::LocalTest, chrono::Duration::seconds(10800), ); let (reply_tx, reply_rx) = oneshot::channel(); @@ -677,7 +675,6 @@ mod test { Arc::new(node_identity), dht_requester, dht_discover_requester, - Network::LocalTest, chrono::Duration::seconds(10800), ); let (reply_tx, reply_rx) = oneshot::channel(); diff --git a/comms/dht/src/outbound/message.rs b/comms/dht/src/outbound/message.rs index 4de9d95cbc..52356ec364 100644 --- a/comms/dht/src/outbound/message.rs +++ b/comms/dht/src/outbound/message.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - envelope::{DhtMessageFlags, DhtMessageHeader, DhtMessageType, Network, NodeDestination}, + envelope::{DhtMessageFlags, DhtMessageHeader, DhtMessageType, NodeDestination}, outbound::{message_params::FinalSendMessageParams, message_send_state::MessageSendStates}, }; use bytes::Bytes; @@ -164,7 +164,6 @@ pub struct DhtOutboundMessage { pub destination: NodeDestination, pub dht_message_type: DhtMessageType, pub reply: MessagingReplyTx, - pub network: Network, pub dht_flags: DhtMessageFlags, pub is_broadcast: bool, pub expires: Option, @@ -178,8 +177,8 @@ impl fmt::Display for DhtOutboundMessage { .map(|h| format!("{} (Propagated)", h)) .unwrap_or_else(|| { format!( - "Network: {:?}, Flags: {:?}, Destination: {}, Trace: {}", - self.network, self.dht_flags, self.destination, self.tag, + "Flags: {:?}, Destination: {}, Trace: {}", + self.dht_flags, self.destination, self.tag, ) }); write!( diff --git a/comms/dht/src/outbound/serialize.rs b/comms/dht/src/outbound/serialize.rs index 7de8dd5487..97c7c0df58 100644 --- a/comms/dht/src/outbound/serialize.rs +++ b/comms/dht/src/outbound/serialize.rs @@ -21,11 +21,11 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - consts::DHT_ENVELOPE_HEADER_VERSION, + consts::{DHT_MAJOR_VERSION, DHT_MINOR_VERSION}, outbound::message::DhtOutboundMessage, proto::envelope::{DhtEnvelope, DhtHeader}, }; -use futures::{task::Context, Future}; +use futures::task::Context; use log::*; use std::task::Poll; use tari_comms::{ @@ -34,7 +34,7 @@ use tari_comms::{ Bytes, }; use tari_utilities::ByteArray; -use tower::{layer::Layer, Service, ServiceExt}; +use tower::{layer::Layer, util::Oneshot, Service, ServiceExt}; const LOG_TARGET: &str = "comms::dht::serialize"; @@ -50,71 +50,68 @@ impl SerializeMiddleware { } impl Service for SerializeMiddleware -where S: Service + Clone + 'static +where + S: Service + Clone + Send, + S::Future: Send, { type Error = PipelineError; + type Future = Oneshot; type Response = (); - type Future = impl Future>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, message: DhtOutboundMessage) -> Self::Future { let next_service = self.inner.clone(); - async move { - let DhtOutboundMessage { - tag, - destination_node_id, - custom_header, - body, - ephemeral_public_key, - destination, - dht_message_type, - network, - dht_flags, - origin_mac, - reply, - expires, - .. - } = message; - trace!( - target: LOG_TARGET, - "Serializing outbound message {:?} for peer `{}`", - message.tag, - destination_node_id.short_str() - ); - let dht_header = custom_header.map(DhtHeader::from).unwrap_or_else(|| DhtHeader { - version: DHT_ENVELOPE_HEADER_VERSION, - origin_mac: origin_mac.map(|b| b.to_vec()).unwrap_or_else(Vec::new), - ephemeral_public_key: ephemeral_public_key.map(|e| e.to_vec()).unwrap_or_else(Vec::new), - message_type: dht_message_type as i32, - network: network as i32, - flags: dht_flags.bits(), - destination: Some(destination.into()), - message_tag: tag.as_value(), - expires, - }); - let envelope = DhtEnvelope::new(dht_header, body); - - let body = Bytes::from(envelope.to_encoded_bytes()); - - trace!( - target: LOG_TARGET, - "Serialized outbound message {} for peer `{}`. Passing onto next service", - tag, - destination_node_id.short_str() - ); - next_service - .oneshot(OutboundMessage { - tag, - peer_node_id: destination_node_id, - reply, - body, - }) - .await - } + + let DhtOutboundMessage { + tag, + destination_node_id, + custom_header, + body, + ephemeral_public_key, + destination, + dht_message_type, + dht_flags, + origin_mac, + reply, + expires, + .. + } = message; + trace!( + target: LOG_TARGET, + "Serializing outbound message {:?} for peer `{}`", + message.tag, + destination_node_id.short_str() + ); + let dht_header = custom_header.map(DhtHeader::from).unwrap_or_else(|| DhtHeader { + major: DHT_MAJOR_VERSION, + minor: DHT_MINOR_VERSION, + origin_mac: origin_mac.map(|b| b.to_vec()).unwrap_or_else(Vec::new), + ephemeral_public_key: ephemeral_public_key.map(|e| e.to_vec()).unwrap_or_else(Vec::new), + message_type: dht_message_type as i32, + flags: dht_flags.bits(), + destination: Some(destination.into()), + message_tag: tag.as_value(), + expires, + }); + let envelope = DhtEnvelope::new(dht_header, body); + + let body = Bytes::from(envelope.to_encoded_bytes()); + + trace!( + target: LOG_TARGET, + "Serialized outbound message {} for peer `{}`. Passing onto next service", + tag, + destination_node_id.short_str() + ); + next_service.oneshot(OutboundMessage { + tag, + peer_node_id: destination_node_id, + reply, + body, + }) } } @@ -138,24 +135,21 @@ impl Layer for SerializeLayer { #[cfg(test)] mod test { use super::*; - use crate::test_utils::{create_outbound_message, service_spy}; - use futures::executor::block_on; + use crate::test_utils::{assert_send_static_service, create_outbound_message, service_spy}; use prost::Message; use tari_comms::peer_manager::NodeId; - use tari_test_utils::panic_context; - #[test] - fn serialize() { + #[tokio_macros::test_basic] + async fn serialize() { let spy = service_spy(); let mut serialize = SerializeLayer.layer(spy.to_service::()); - panic_context!(cx); - - assert!(serialize.poll_ready(&mut cx).is_ready()); let body = b"A"; let msg = create_outbound_message(body); - block_on(serialize.call(msg)).unwrap(); + assert_send_static_service(&serialize); + let service = serialize.ready_and().await.unwrap(); + service.call(msg).await.unwrap(); let mut msg = spy.pop_request().unwrap(); let dht_envelope = DhtEnvelope::decode(&mut msg.body).unwrap(); assert_eq!(dht_envelope.body, b"A".to_vec()); diff --git a/comms/dht/src/proto/envelope.proto b/comms/dht/src/proto/envelope.proto index 590e8479f9..c1e6407d7d 100644 --- a/comms/dht/src/proto/envelope.proto +++ b/comms/dht/src/proto/envelope.proto @@ -20,48 +20,32 @@ enum DhtMessageType { } message DhtHeader { - uint32 version = 1; + uint32 major = 1; + uint32 minor = 2; oneof destination { // The sender has chosen not to disclose the message destination - bool unknown = 2; + bool unknown = 3; // Destined for a particular public key - bytes public_key = 3; + bytes public_key = 4; // Destined for a particular node id, or network region - bytes node_id = 4; + bytes node_id = 5; } // Origin public key of the message. This can be the same peer that sent the message // or another peer if the message should be forwarded. This is optional but MUST be specified // if the ENCRYPTED flag is set. // If an ephemeral_public_key is specified, this MUST be encrypted using a derived ECDH shared key - bytes origin_mac = 5; + bytes origin_mac = 6; // Ephemeral public key component of the ECDH shared key. MUST be specified if the ENCRYPTED flag is set. - bytes ephemeral_public_key = 6; + bytes ephemeral_public_key = 7; // The type of message - DhtMessageType message_type = 7; - // The network for which this message is intended (e.g. TestNet, MainNet etc.) - Network network = 8; - uint32 flags = 9; + DhtMessageType message_type = 8; + uint32 flags = 10; // Message trace ID // TODO: Remove for mainnet or when testing message traces is not required - uint64 message_tag = 10; + uint64 message_tag = 11; // Expiry timestamp for the message - google.protobuf.Timestamp expires = 11; -} - -enum Network { - // Main net (default) - NetworkMainNet = 0; - // Test net - NetworkTestNet = 1; - // Network used for local tests - NetworkLocalTest = 2; - // Ridcully - NetworkRidcully = 3; - // Stibbons - NetworkStibbons = 4; - // Weatherwax - NetworkWeatherwax = 5; + google.protobuf.Timestamp expires = 12; } message DhtEnvelope { diff --git a/comms/dht/src/proto/mod.rs b/comms/dht/src/proto/mod.rs index c591fcb20a..4d3899c8c4 100644 --- a/comms/dht/src/proto/mod.rs +++ b/comms/dht/src/proto/mod.rs @@ -20,10 +20,9 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::proto::{dht::JoinMessage, envelope::Network}; +use crate::proto::dht::JoinMessage; use rand::{rngs::OsRng, RngCore}; use std::{convert::TryInto, fmt}; -use tari_common::Network as GlobalNetwork; use tari_comms::{ multiaddr::Multiaddr, peer_manager::{NodeId, Peer, PeerFeatures, PeerFlags}, @@ -52,35 +51,6 @@ pub mod message_header { tari_comms::outdir_include!("tari.dht.message_header.rs"); } -//---------------------------------- Network impl --------------------------------------------// - -impl envelope::Network { - pub fn is_mainnet(self) -> bool { - matches!(self, Network::MainNet) - } - - pub fn is_testnet(self) -> bool { - matches!(self, Network::TestNet) - } - - pub fn is_localtest(self) -> bool { - matches!(self, Network::LocalTest) - } -} - -impl From for Network { - fn from(gn: GlobalNetwork) -> Self { - match gn { - GlobalNetwork::MainNet => Network::MainNet, - GlobalNetwork::Rincewind => Network::TestNet, - GlobalNetwork::LocalNet => Network::LocalTest, - GlobalNetwork::Ridcully => Network::Ridcully, - GlobalNetwork::Stibbons => Network::Stibbons, - GlobalNetwork::Weatherwax => Network::Weatherwax, - } - } -} - //---------------------------------- JoinMessage --------------------------------------------// impl> From for JoinMessage { diff --git a/comms/dht/src/store_forward/forward.rs b/comms/dht/src/store_forward/forward.rs index f3f52b7801..607dfe0fd1 100644 --- a/comms/dht/src/store_forward/forward.rs +++ b/comms/dht/src/store_forward/forward.rs @@ -26,7 +26,7 @@ use crate::{ outbound::{OutboundMessageRequester, SendMessageParams}, store_forward::error::StoreAndForwardError, }; -use futures::{task::Context, Future}; +use futures::{future::BoxFuture, task::Context}; use log::*; use std::task::Poll; use tari_comms::{peer_manager::Peer, pipeline::PipelineError}; @@ -84,13 +84,14 @@ impl ForwardMiddleware { } impl Service for ForwardMiddleware -where S: Service + Clone + 'static +where + S: Service + Clone + Send + 'static, + S::Future: Send, { type Error = PipelineError; + type Future = BoxFuture<'static, Result>; type Response = (); - type Future = impl Future>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } @@ -99,7 +100,7 @@ where S: Service + Cl let next_service = self.next_service.clone(); let outbound_service = self.outbound_service.clone(); let is_enabled = self.is_enabled; - async move { + Box::pin(async move { if !is_enabled { trace!( target: LOG_TARGET, @@ -118,7 +119,7 @@ where S: Service + Cl ); let forwarder = Forwarder::new(next_service, outbound_service); forwarder.handle(message).await - } + }) } } diff --git a/comms/dht/src/store_forward/saf_handler/middleware.rs b/comms/dht/src/store_forward/saf_handler/middleware.rs index 0d46855ff8..578fc1dcbc 100644 --- a/comms/dht/src/store_forward/saf_handler/middleware.rs +++ b/comms/dht/src/store_forward/saf_handler/middleware.rs @@ -28,7 +28,7 @@ use crate::{ outbound::OutboundMessageRequester, store_forward::StoreAndForwardRequester, }; -use futures::{channel::mpsc, task::Context, Future}; +use futures::{channel::mpsc, future::BoxFuture, task::Context}; use std::{sync::Arc, task::Poll}; use tari_comms::{ peer_manager::{NodeIdentity, PeerManager}, @@ -75,29 +75,32 @@ impl MessageHandlerMiddleware { } impl Service for MessageHandlerMiddleware -where S: Service + Clone + Sync + Send +where + S: Service + Clone + Send + 'static, + S::Future: Send, { type Error = PipelineError; + type Future = BoxFuture<'static, Result>; type Response = (); - type Future = impl Future>; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.next_service.poll_ready(cx) } fn call(&mut self, message: DecryptedDhtMessage) -> Self::Future { - MessageHandlerTask::new( - self.config.clone(), - self.next_service.clone(), - self.saf_requester.clone(), - self.dht_requester.clone(), - Arc::clone(&self.peer_manager), - self.outbound_service.clone(), - Arc::clone(&self.node_identity), - message, - self.saf_response_signal_sender.clone(), + Box::pin( + MessageHandlerTask::new( + self.config.clone(), + self.next_service.clone(), + self.saf_requester.clone(), + self.dht_requester.clone(), + Arc::clone(&self.peer_manager), + self.outbound_service.clone(), + Arc::clone(&self.node_identity), + message, + self.saf_response_signal_sender.clone(), + ) + .run(), ) - .run() } } diff --git a/comms/dht/src/store_forward/saf_handler/task.rs b/comms/dht/src/store_forward/saf_handler/task.rs index 910eada97f..ecab6b9862 100644 --- a/comms/dht/src/store_forward/saf_handler/task.rs +++ b/comms/dht/src/store_forward/saf_handler/task.rs @@ -39,7 +39,7 @@ use crate::{ store_forward::{error::StoreAndForwardError, service::FetchStoredMessageQuery, StoreAndForwardRequester}, }; use digest::Digest; -use futures::{channel::mpsc, future, stream, Future, SinkExt, StreamExt}; +use futures::{channel::mpsc, future, stream, SinkExt, StreamExt}; use log::*; use prost::Message; use std::{convert::TryInto, sync::Arc}; @@ -267,14 +267,15 @@ where S: Service message_tag ); - let tasks = response - .messages - .into_iter() - // Map to futures which process the stored message - .map(|msg| self.process_incoming_stored_message(Arc::clone(&source_peer), msg)); + let mut results = Vec::with_capacity(response.messages.len()); + for msg in response.messages { + let result = self + .process_incoming_stored_message(Arc::clone(&source_peer), msg) + .await; + results.push(result); + } - let successful_msgs_iter = future::join_all(tasks) - .await + let successful_msgs_iter = results .into_iter() .map(|result| { match &result { @@ -352,71 +353,68 @@ where S: Service Ok(()) } - fn process_incoming_stored_message( - &self, + async fn process_incoming_stored_message( + &mut self, source_peer: Arc, message: ProtoStoredMessage, - ) -> impl Future> { - let node_identity = Arc::clone(&self.node_identity); - let peer_manager = Arc::clone(&self.peer_manager); - let config = self.config.clone(); - let mut dht_requester = self.dht_requester.clone(); - - async move { - if message.dht_header.is_none() { - return Err(StoreAndForwardError::DhtHeaderNotProvided); - } + ) -> Result { + let node_identity = &self.node_identity; + let peer_manager = &self.peer_manager; + let config = &self.config; - let dht_header: DhtMessageHeader = message - .dht_header - .expect("previously checked") - .try_into() - .map_err(StoreAndForwardError::DhtMessageError)?; + if message.dht_header.is_none() { + return Err(StoreAndForwardError::DhtHeaderNotProvided); + } - if !dht_header.is_valid() { - return Err(StoreAndForwardError::InvalidDhtHeader); - } - let message_type = dht_header.message_type; + let dht_header: DhtMessageHeader = message + .dht_header + .expect("previously checked") + .try_into() + .map_err(StoreAndForwardError::DhtMessageError)?; - if message_type.is_dht_message() { - if !message_type.is_dht_discovery() { - debug!( - target: LOG_TARGET, - "Discarding {} message from peer '{}'", - message_type, - source_peer.node_id.short_str() - ); - return Err(StoreAndForwardError::InvalidDhtMessageType); - } - if dht_header.destination.is_unknown() { - debug!( - target: LOG_TARGET, - "Discarding anonymous discovery message from peer '{}'", - source_peer.node_id.short_str() - ); - return Err(StoreAndForwardError::InvalidDhtMessageType); - } + if !dht_header.is_valid() { + return Err(StoreAndForwardError::InvalidDhtHeader); + } + let message_type = dht_header.message_type; + + if message_type.is_dht_message() { + if !message_type.is_dht_discovery() { + debug!( + target: LOG_TARGET, + "Discarding {} message from peer '{}'", + message_type, + source_peer.node_id.short_str() + ); + return Err(StoreAndForwardError::InvalidDhtMessageType); + } + if dht_header.destination.is_unknown() { + debug!( + target: LOG_TARGET, + "Discarding anonymous discovery message from peer '{}'", + source_peer.node_id.short_str() + ); + return Err(StoreAndForwardError::InvalidDhtMessageType); } + } - // Check that the destination is either undisclosed, for us or for our network region - Self::check_destination(&config, &peer_manager, &node_identity, &dht_header).await?; - // Check that the message has not already been received. - Self::check_duplicate(&mut dht_requester, &message.body).await?; + // Check that the destination is either undisclosed, for us or for our network region + Self::check_destination(&config, &peer_manager, &node_identity, &dht_header).await?; + // Check that the message has not already been received. + Self::check_duplicate(&mut self.dht_requester, &message.body).await?; - // Attempt to decrypt the message (if applicable), and deserialize it - let (authenticated_pk, decrypted_body) = - Self::authenticate_and_decrypt_if_required(&node_identity, &dht_header, &message.body)?; + // Attempt to decrypt the message (if applicable), and deserialize it + let (authenticated_pk, decrypted_body) = + Self::authenticate_and_decrypt_if_required(&node_identity, &dht_header, &message.body)?; - let mut inbound_msg = - DhtInboundMessage::new(MessageTag::new(), dht_header, Arc::clone(&source_peer), message.body); - inbound_msg.is_saf_message = true; + let mut inbound_msg = + DhtInboundMessage::new(MessageTag::new(), dht_header, Arc::clone(&source_peer), message.body); + inbound_msg.is_saf_message = true; - Ok(DecryptedDhtMessage::succeeded( - decrypted_body, - authenticated_pk, - inbound_msg, - )) - } + Ok(DecryptedDhtMessage::succeeded( + decrypted_body, + authenticated_pk, + inbound_msg, + )) } async fn check_duplicate(dht_requester: &mut DhtRequester, body: &[u8]) -> Result<(), StoreAndForwardError> { diff --git a/comms/dht/src/store_forward/store.rs b/comms/dht/src/store_forward/store.rs index 115329bf83..32144df2a7 100644 --- a/comms/dht/src/store_forward/store.rs +++ b/comms/dht/src/store_forward/store.rs @@ -31,7 +31,7 @@ use crate::{ }, DhtConfig, }; -use futures::{task::Context, Future}; +use futures::{future::BoxFuture, task::Context}; use log::*; use std::{sync::Arc, task::Poll}; use tari_comms::{ @@ -109,26 +109,29 @@ impl StoreMiddleware { } impl Service for StoreMiddleware -where S: Service + Clone + 'static +where + S: Service + Clone + Send + Sync + 'static, + S::Future: Send, { type Error = PipelineError; + type Future = BoxFuture<'static, Result>; type Response = (); - type Future = impl Future>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, msg: DecryptedDhtMessage) -> Self::Future { - StoreTask::new( - self.next_service.clone(), - self.config.clone(), - Arc::clone(&self.peer_manager), - Arc::clone(&self.node_identity), - self.saf_requester.clone(), + Box::pin( + StoreTask::new( + self.next_service.clone(), + self.config.clone(), + Arc::clone(&self.peer_manager), + Arc::clone(&self.node_identity), + self.saf_requester.clone(), + ) + .handle(msg), ) - .handle(msg) } } @@ -142,7 +145,9 @@ struct StoreTask { saf_requester: StoreAndForwardRequester, } -impl StoreTask { +impl StoreTask +where S: Service + Send + Sync +{ pub fn new( next_service: S, config: DhtConfig, @@ -159,11 +164,7 @@ impl StoreTask { saf_requester, } } -} -impl StoreTask -where S: Service -{ /// Determine if this is a message we should store for our peers and, if so, store it. /// /// The criteria for storing a message is: @@ -181,8 +182,8 @@ where S: Service message.tag, message.dht_header.message_tag ); - self.next_service.oneshot(message).await?; - return Ok(()); + let service = self.next_service.ready_oneshot().await?; + return service.oneshot(message).await; } message.set_saf_stored(false); @@ -198,9 +199,9 @@ where S: Service message.tag, message.dht_header.message_tag ); - self.next_service.oneshot(message).await?; - Ok(()) + let service = self.next_service.ready_oneshot().await?; + return service.oneshot(message).await; } async fn get_storage_priority(&self, message: &DecryptedDhtMessage) -> SafResult> { @@ -436,6 +437,7 @@ mod test { use crate::{ envelope::{DhtMessageFlags, NodeDestination}, test_utils::{ + assert_send_static_service, build_peer_manager, create_store_and_forward_mock, make_dht_inbound_message, @@ -458,6 +460,7 @@ mod test { let node_identity = make_node_identity(); let mut service = StoreLayer::new(Default::default(), peer_manager, node_identity, requester) .layer(spy.to_service::()); + assert_send_static_service(&service); let inbound_msg = make_dht_inbound_message(&make_node_identity(), b"".to_vec(), DhtMessageFlags::empty(), false); diff --git a/comms/dht/src/test_utils/makers.rs b/comms/dht/src/test_utils/makers.rs index 92d3d8f1c2..f91592be2e 100644 --- a/comms/dht/src/test_utils/makers.rs +++ b/comms/dht/src/test_utils/makers.rs @@ -24,7 +24,7 @@ use crate::{ envelope::{DhtMessageFlags, DhtMessageHeader, NodeDestination}, inbound::DhtInboundMessage, outbound::message::DhtOutboundMessage, - proto::envelope::{DhtEnvelope, DhtMessageType, Network, OriginMac}, + proto::envelope::{DhtEnvelope, DhtMessageType, OriginMac}, }; use rand::rngs::OsRng; use std::{convert::TryInto, sync::Arc}; @@ -77,7 +77,8 @@ pub fn make_dht_header( trace: MessageTag, ) -> DhtMessageHeader { DhtMessageHeader { - version: 0, + major: 0, + minor: 0, destination: NodeDestination::Unknown, ephemeral_public_key: if flags.is_encrypted() { Some(e_pk.clone()) } else { None }, origin_mac: if include_origin { @@ -86,7 +87,6 @@ pub fn make_dht_header( Vec::new() }, message_type: DhtMessageType::None, - network: Network::LocalTest, flags, message_tag: trace, expires: None, @@ -184,7 +184,6 @@ pub fn create_outbound_message(body: &[u8]) -> DhtOutboundMessage { destination_node_id: NodeId::default(), destination: Default::default(), dht_message_type: Default::default(), - network: Network::LocalTest, dht_flags: Default::default(), custom_header: None, body: body.to_vec().into(), diff --git a/comms/dht/src/test_utils/mod.rs b/comms/dht/src/test_utils/mod.rs index c103b2aa9a..d3d1dc402d 100644 --- a/comms/dht/src/test_utils/mod.rs +++ b/comms/dht/src/test_utils/mod.rs @@ -51,3 +51,10 @@ pub use service::service_spy; mod store_and_forward_mock; pub use store_and_forward_mock::{create_store_and_forward_mock, StoreAndForwardMockState}; + +pub fn assert_send_static_service(_: &S) +where + S: tower::Service + Send + 'static, + S::Future: Send, +{ +} diff --git a/comms/dht/tests/dht.rs b/comms/dht/tests/dht.rs index 04807a7d49..2681f2a1ab 100644 --- a/comms/dht/tests/dht.rs +++ b/comms/dht/tests/dht.rs @@ -178,24 +178,22 @@ async fn setup_comms_dht( } let dht_outbound_layer = dht.outbound_middleware_layer(); + let pipeline = pipeline::Builder::new() + .outbound_buffer_size(10) + .with_outbound_pipeline(outbound_rx, |sink| { + ServiceBuilder::new().layer(dht_outbound_layer).service(sink) + }) + .max_concurrent_inbound_tasks(10) + .with_inbound_pipeline( + ServiceBuilder::new() + .layer(dht.inbound_middleware_layer()) + .service(SinkService::new(inbound_tx)), + ) + .build(); let (event_tx, _) = broadcast::channel(100); let comms = comms - .add_protocol_extension(MessagingProtocolExtension::new( - event_tx.clone(), - pipeline::Builder::new() - .outbound_buffer_size(10) - .with_outbound_pipeline(outbound_rx, |sink| { - ServiceBuilder::new().layer(dht_outbound_layer).service(sink) - }) - .max_concurrent_inbound_tasks(10) - .with_inbound_pipeline( - ServiceBuilder::new() - .layer(dht.inbound_middleware_layer()) - .service(SinkService::new(inbound_tx)), - ) - .build(), - )) + .add_protocol_extension(MessagingProtocolExtension::new(event_tx.clone(), pipeline)) .spawn_with_transport(MemoryTransport) .await .unwrap(); diff --git a/comms/src/builder/mod.rs b/comms/src/builder/mod.rs index 6d2748ce7b..e3667a1e4a 100644 --- a/comms/src/builder/mod.rs +++ b/comms/src/builder/mod.rs @@ -47,7 +47,7 @@ use crate::{ connectivity::{ConnectivityConfig, ConnectivityRequester}, multiaddr::Multiaddr, peer_manager::{NodeIdentity, PeerManager}, - protocol::ProtocolExtensions, + protocol::{NodeNetworkInfo, ProtocolExtensions}, tor, types::CommsDatabase, }; @@ -106,7 +106,26 @@ impl CommsBuilder { /// Set the user agent string for this comms node. This string is sent once when establishing a connection. pub fn with_user_agent(mut self, user_agent: T) -> Self { - self.connection_manager_config.user_agent = user_agent.to_string(); + self.connection_manager_config.network_info.user_agent = user_agent.to_string(); + self + } + + /// Set a network byte as per [RFC-173 Versioning](https://rfc.tari.com/RFC-0173_Versioning.html) + pub fn with_network_byte(mut self, network_byte: u8) -> Self { + self.connection_manager_config.network_info.network_byte = network_byte; + self + } + + /// Set a network info (versions etc) as per [RFC-173 Versioning](https://rfc.tari.com/RFC-0173_Versioning.html) + pub fn with_node_info(mut self, node_info: NodeNetworkInfo) -> Self { + self.connection_manager_config.network_info = node_info; + self + } + + /// Set a network major and minor version as per [RFC-173 Versioning](https://rfc.tari.com/RFC-0173_Versioning.html) + pub fn with_node_version(mut self, major_version: u32, minor_version: u32) -> Self { + self.connection_manager_config.network_info.major_version = major_version; + self.connection_manager_config.network_info.minor_version = minor_version; self } diff --git a/comms/src/connection_manager/common.rs b/comms/src/connection_manager/common.rs index e3993fb419..73bd20b6ad 100644 --- a/comms/src/connection_manager/common.rs +++ b/comms/src/connection_manager/common.rs @@ -28,13 +28,13 @@ use crate::{ peer_manager::{NodeId, NodeIdentity, Peer, PeerFeatures, PeerFlags}, proto::identity::PeerIdentityMsg, protocol, - protocol::ProtocolId, + protocol::{NodeNetworkInfo, ProtocolId}, types::CommsPublicKey, PeerManager, }; use futures::StreamExt; use log::*; -use tari_crypto::tari_utilities::ByteArray; +use std::convert::TryFrom; const LOG_TARGET: &str = "comms::connection_manager::common"; @@ -46,7 +46,7 @@ pub async fn perform_identity_exchange<'p, P: IntoIterator Result { let mut control = muxer.get_yamux_control(); let stream = match direction { @@ -64,20 +64,11 @@ pub async fn perform_identity_exchange<'p, P: IntoIterator bool { - match NodeId::from_key(public_key) { - Ok(expected_node_id) => &expected_node_id == node_id, - Err(_) => false, - } -} - /// Validate the peer identity info. /// /// The following process is used to validate the peer: @@ -96,19 +87,11 @@ pub async fn validate_and_add_peer_from_peer_identity( dialed_addr: Option<&Multiaddr>, allow_test_addrs: bool, ) -> Result<(NodeId, Vec), ConnectionManagerError> { - // let peer_manager = peer_manager.inner(); - // Validate the given node id for base nodes - // TODO: This is technically a domain-level rule - let peer_node_id = - NodeId::from_bytes(&peer_identity.node_id).map_err(|_| ConnectionManagerError::PeerIdentityInvalidNodeId)?; - if !is_valid_base_node_node_id(&peer_node_id, &authenticated_public_key) { - return Err(ConnectionManagerError::PeerIdentityInvalidNodeId); - } - + let peer_node_id = NodeId::from_public_key(&authenticated_public_key); let addresses = peer_identity .addresses .into_iter() - .filter_map(|addr_str| addr_str.parse().ok()) + .filter_map(|addr_bytes| Multiaddr::try_from(addr_bytes).ok()) .collect::>(); // TODO: #banheuristic diff --git a/comms/src/connection_manager/dialer.rs b/comms/src/connection_manager/dialer.rs index fc6f00e884..973de6953c 100644 --- a/comms/src/connection_manager/dialer.rs +++ b/comms/src/connection_manager/dialer.rs @@ -28,7 +28,6 @@ use crate::{ dial_state::DialState, manager::{ConnectionManagerConfig, ConnectionManagerEvent}, peer_connection, - wire_mode::WireMode, }, multiaddr::Multiaddr, multiplexing::Yamux, @@ -53,7 +52,6 @@ use futures::{ }; use log::*; use std::{collections::HashMap, sync::Arc, time::Duration}; -use tari_crypto::tari_utilities::hex::Hex; use tari_shutdown::{Shutdown, ShutdownSignal}; use tokio::time; @@ -269,20 +267,18 @@ where self.cancel_signals.insert(peer.node_id.clone(), dial_cancel); let backoff = Arc::clone(&self.backoff); - let max_attempts = self.config.max_dial_attempts; let dial_state = DialState::new(peer, reply_tx, cancel_signal); let node_identity = Arc::clone(&self.node_identity); let peer_manager = self.peer_manager.clone(); let conn_man_notifier = self.conn_man_notifier.clone(); let supported_protocols = self.our_supported_protocols.clone(); - let user_agent = self.config.user_agent.clone(); let noise_config = self.noise_config.clone(); - let allow_test_addresses = self.config.allow_test_addresses; + let config = self.config.clone(); let dial_fut = async move { let (dial_state, dial_result) = - Self::dial_peer_with_retry(dial_state, noise_config, transport, backoff, max_attempts).await; + Self::dial_peer_with_retry(dial_state, noise_config, transport, backoff, &config).await; let cancel_signal = dial_state.get_cancel_signal(); @@ -304,8 +300,7 @@ where authenticated_public_key, conn_man_notifier, supported_protocols, - user_agent, - allow_test_addresses, + &config, cancel_signal, ) .await; @@ -343,8 +338,7 @@ where authenticated_public_key: CommsPublicKey, conn_man_notifier: mpsc::Sender, our_supported_protocols: Vec, - user_agent: String, - allow_test_addresses: bool, + config: &ConnectionManagerConfig, cancel_signal: ShutdownSignal, ) -> Result { static CONNECTION_DIRECTION: ConnectionDirection = ConnectionDirection::Outbound; @@ -366,7 +360,7 @@ where &node_identity, CONNECTION_DIRECTION, &our_supported_protocols, - user_agent, + config.network_info.clone(), ) .await?; if cancel_signal.is_terminated() { @@ -378,7 +372,7 @@ where trace!( target: LOG_TARGET, "Peer identity exchange succeeded on Outbound connection for peer '{}' (Features = {:?})", - peer_identity.node_id.to_hex(), + authenticated_public_key, features ); trace!(target: LOG_TARGET, "{:?}", peer_identity); @@ -392,7 +386,7 @@ where authenticated_public_key, peer_identity, Some(&dialed_addr), - allow_test_addresses, + config.allow_test_addresses, ) .await?; @@ -425,7 +419,7 @@ where noise_config: NoiseConfig, transport: TTransport, backoff: Arc, - max_attempts: usize, + config: &ConnectionManagerConfig, ) -> (DialState, DialResult) { // Container for dial state let mut dial_state = Some(dial_state); @@ -448,7 +442,7 @@ where futures::select! { _ = delay => { debug!(target: LOG_TARGET, "[Attempt {}] Connecting to peer '{}'", current_state.num_attempts(), current_state.peer.node_id.short_str()); - match Self::dial_peer(current_state, &noise_config, ¤t_transport).await { + match Self::dial_peer(current_state, &noise_config, ¤t_transport, config.network_info.network_byte).await { (state, Ok((socket, addr))) => { debug!(target: LOG_TARGET, "Dial succeeded for peer '{}' after {} attempt(s)", state.peer.node_id.short_str(), state.num_attempts()); break (state, Ok((socket, addr))); @@ -456,7 +450,7 @@ where // Inflight dial was cancelled (state, Err(ConnectionManagerError::DialCancelled)) => break (state, Err(ConnectionManagerError::DialCancelled)), (state, Err(_err)) => { - if state.num_attempts() >= max_attempts { + if state.num_attempts() >= config.max_dial_attempts { break (state, Err(ConnectionManagerError::ConnectFailedMaximumAttemptsReached)); } @@ -482,6 +476,7 @@ where dial_state: DialState, noise_config: &NoiseConfig, transport: &TTransport, + network_byte: u8, ) -> ( DialState, Result<(NoiseSocket, Multiaddr), ConnectionManagerError>, @@ -510,7 +505,7 @@ where ); socket - .write(&[WireMode::Comms as u8]) + .write(&[network_byte]) .await .map_err(|_| ConnectionManagerError::WireFormatSendFailed)?; diff --git a/comms/src/connection_manager/error.rs b/comms/src/connection_manager/error.rs index 3d35c52ed6..3e1b1dc877 100644 --- a/comms/src/connection_manager/error.rs +++ b/comms/src/connection_manager/error.rs @@ -64,8 +64,6 @@ pub enum ConnectionManagerError { NoiseError(String), #[error("Incoming listener stream unexpectedly closed")] IncomingListenerStreamClosed, - #[error("The peer offered a NodeId that failed to validate against it's public key")] - PeerIdentityInvalidNodeId, #[error("Peer is banned, denying connection")] PeerBanned, #[error("Unable to parse any of the network addresses offered by the connecting peer")] diff --git a/comms/src/connection_manager/listener.rs b/comms/src/connection_manager/listener.rs index 5df3c0384d..545062100c 100644 --- a/comms/src/connection_manager/listener.rs +++ b/comms/src/connection_manager/listener.rs @@ -52,7 +52,6 @@ use std::{ }, time::Duration, }; -use tari_crypto::tari_utilities::hex::Hex; use tari_shutdown::ShutdownSignal; use tokio::time; @@ -195,14 +194,12 @@ where let noise_config = self.noise_config.clone(); let config = self.config.clone(); let our_supported_protocols = self.our_supported_protocols.clone(); - let allow_test_addresses = self.config.allow_test_addresses; let liveness_session_count = self.liveness_session_count.clone(); - let user_agent = self.config.user_agent.clone(); let shutdown_signal = self.shutdown_signal.clone(); let inbound_fut = async move { match Self::read_wire_format(&mut socket, config.time_to_first_byte).await { - Some(WireMode::Comms) => { + Some(WireMode::Comms(byte)) if byte == config.network_info.network_byte => { let this_node_id_str = node_identity.node_id().short_str(); let result = Self::perform_socket_upgrade_procedure( node_identity, @@ -212,8 +209,7 @@ where socket, peer_addr, our_supported_protocols, - user_agent, - allow_test_addresses, + &config, ) .await; @@ -244,6 +240,15 @@ where }, } }, + Some(WireMode::Comms(byte)) => { + warn!( + target: LOG_TARGET, + "Peer at address '{}' sent invalid wire format byte. Expected {:x?} got: {:x?} ", + peer_addr, + config.network_info.network_byte, + byte, + ); + }, Some(WireMode::Liveness) => { if liveness_session_count.load(Ordering::SeqCst) > 0 && Self::is_address_in_liveness_cidr_range(&peer_addr, &config.liveness_cidr_allowlist) @@ -293,8 +298,7 @@ where socket: TTransport::Output, peer_addr: Multiaddr, our_supported_protocols: Vec, - user_agent: String, - allow_test_addresses: bool, + config: &ConnectionManagerConfig, ) -> Result { static CONNECTION_DIRECTION: ConnectionDirection = ConnectionDirection::Inbound; debug!( @@ -331,7 +335,7 @@ where &node_identity, CONNECTION_DIRECTION, &our_supported_protocols, - user_agent, + config.network_info.clone(), ) .await?; @@ -339,7 +343,7 @@ where debug!( target: LOG_TARGET, "Peer identity exchange succeeded on Inbound connection for peer '{}' (Features = {:?})", - peer_identity.node_id.to_hex(), + authenticated_public_key, features ); trace!(target: LOG_TARGET, "{:?}", peer_identity); @@ -350,7 +354,7 @@ where authenticated_public_key, peer_identity, None, - allow_test_addresses, + config.allow_test_addresses, ) .await?; diff --git a/comms/src/connection_manager/manager.rs b/comms/src/connection_manager/manager.rs index e7c5460de8..58b17de26b 100644 --- a/comms/src/connection_manager/manager.rs +++ b/comms/src/connection_manager/manager.rs @@ -32,10 +32,9 @@ use crate::{ multiplexing::Substream, noise::NoiseConfig, peer_manager::{NodeId, NodeIdentity}, - protocol::{ProtocolEvent, ProtocolId, Protocols}, + protocol::{NodeNetworkInfo, ProtocolEvent, ProtocolId, Protocols}, runtime, transports::Transport, - types::DEFAULT_LISTENER_ADDRESS, PeerManager, }; use futures::{ @@ -107,24 +106,25 @@ pub struct ConnectionManagerConfig { /// Set to true to allow peers to send loopback, local-link and other addresses normally not considered valid for /// peer-to-peer comms. Default: false pub allow_test_addresses: bool, + /// Version information for this node + pub network_info: NodeNetworkInfo, /// The maximum time to wait for the first byte before closing the connection. Default: 7s pub time_to_first_byte: Duration, /// The number of liveness check sessions to allow. Default: 0 pub liveness_max_sessions: usize, /// CIDR blocks that allowlist liveness checks. Default: Localhost only (127.0.0.1/32) pub liveness_cidr_allowlist: Vec, - /// The user agent string for this node - pub user_agent: String, } impl Default for ConnectionManagerConfig { fn default() -> Self { Self { - listener_address: DEFAULT_LISTENER_ADDRESS + listener_address: "/ip4/0.0.0.0/tcp/7898" .parse() .expect("DEFAULT_LISTENER_ADDRESS is malformed"), max_dial_attempts: 3, max_simultaneous_inbound_connects: 20, + network_info: Default::default(), #[cfg(not(test))] allow_test_addresses: false, // This must always be true for internal crate tests @@ -133,7 +133,6 @@ impl Default for ConnectionManagerConfig { liveness_max_sessions: 0, time_to_first_byte: Duration::from_secs(7), liveness_cidr_allowlist: vec![cidr::AnyIpCidr::V4("127.0.0.1/32".parse().unwrap())], - user_agent: Default::default(), } } } diff --git a/comms/src/connection_manager/tests/manager.rs b/comms/src/connection_manager/tests/manager.rs index e0731adaa1..ecad64586d 100644 --- a/comms/src/connection_manager/tests/manager.rs +++ b/comms/src/connection_manager/tests/manager.rs @@ -108,7 +108,7 @@ async fn dial_success() { node_identity: node_identity1.clone(), ..Default::default() }; - config.connection_manager_config.user_agent = "node1".to_string(); + config.connection_manager_config.network_info.user_agent = "node1".to_string(); config }, peer_manager1.clone(), @@ -127,7 +127,7 @@ async fn dial_success() { node_identity: node_identity2.clone(), ..Default::default() }; - config.connection_manager_config.user_agent = "node2".to_string(); + config.connection_manager_config.network_info.user_agent = "node2".to_string(); config }, peer_manager2.clone(), @@ -308,7 +308,7 @@ async fn dial_cancelled() { dial_backoff_duration: Duration::from_secs(100), ..Default::default() }; - config.connection_manager_config.user_agent = "node1".to_string(); + config.connection_manager_config.network_info.user_agent = "node1".to_string(); config }, peer_manager1.clone(), diff --git a/comms/src/connection_manager/wire_mode.rs b/comms/src/connection_manager/wire_mode.rs index 57344537ad..e2421c078b 100644 --- a/comms/src/connection_manager/wire_mode.rs +++ b/comms/src/connection_manager/wire_mode.rs @@ -22,13 +22,11 @@ use std::convert::TryFrom; -const COMMS_WIRE_MODE: u8 = 0x07; const LIVENESS_WIRE_MODE: u8 = 0x46; // E -#[repr(u8)] pub enum WireMode { - Comms = COMMS_WIRE_MODE, - Liveness = LIVENESS_WIRE_MODE, + Comms(u8), + Liveness, } impl TryFrom for WireMode { @@ -36,9 +34,8 @@ impl TryFrom for WireMode { fn try_from(value: u8) -> Result { match value { - COMMS_WIRE_MODE => Ok(WireMode::Comms), LIVENESS_WIRE_MODE => Ok(WireMode::Liveness), - _ => Err(()), + v => Ok(WireMode::Comms(v)), } } } diff --git a/comms/src/connectivity/config.rs b/comms/src/connectivity/config.rs index 1ee45994fd..743c8dd741 100644 --- a/comms/src/connectivity/config.rs +++ b/comms/src/connectivity/config.rs @@ -51,7 +51,7 @@ impl Default for ConnectivityConfig { connection_pool_refresh_interval: Duration::from_secs(30), reaper_min_inactive_age: Duration::from_secs(60), is_connection_reaping_enabled: true, - max_failures_mark_offline: 1, + max_failures_mark_offline: 2, connection_tie_break_linger: Duration::from_secs(2), } } diff --git a/comms/src/lib.rs b/comms/src/lib.rs index b22c5a6f60..5e9864fc32 100644 --- a/comms/src/lib.rs +++ b/comms/src/lib.rs @@ -15,9 +15,6 @@ //! [CommsBuilder]: ./builder/index.html // Recursion limit for futures::select! #![recursion_limit = "512"] -// Allow `type Future = impl Future` -#![allow(incomplete_features)] -#![feature(type_alias_impl_trait)] #![feature(min_type_alias_impl_trait)] // Required to use `Ip4Addr::is_global`. Stabilisation imminent https://github.com/rust-lang/rust/issues/27709 #![feature(ip)] @@ -43,7 +40,6 @@ pub mod framing; mod common; pub use common::rate_limit; -mod consts; mod multiplexing; pub use multiplexing::Substream; diff --git a/comms/src/peer_manager/peer.rs b/comms/src/peer_manager/peer.rs index 69637ff735..cb944f6fe1 100644 --- a/comms/src/peer_manager/peer.rs +++ b/comms/src/peer_manager/peer.rs @@ -27,7 +27,6 @@ use super::{ PeerFeatures, }; use crate::{ - consts::PEER_OFFLINE_COOLDOWN_PERIOD, net_address::MultiaddressesWithStats, protocol::ProtocolId, types::CommsPublicKey, @@ -143,16 +142,6 @@ impl Peer { &self.supported_protocols } - /// Returns true if the last connection attempt has failed within the constant - /// [PEER_OFFLINE_COOLDOWN_PERIOD](crate::consts::PEER_OFFLINE_COOLDOWN_PERIOD). - pub fn is_recently_offline(&self) -> bool { - self.connection_stats.failed_attempts() > 1 && - self.connection_stats - .time_since_last_failure() - .map(|last_failure| last_failure <= PEER_OFFLINE_COOLDOWN_PERIOD) - .unwrap_or(false) - } - /// Returns true if the peer is marked as offline pub fn is_offline(&self) -> bool { self.offline_at.is_some() diff --git a/comms/src/peer_manager/peer_storage.rs b/comms/src/peer_manager/peer_storage.rs index c85bb1758a..46e9d00c6d 100644 --- a/comms/src/peer_manager/peer_storage.rs +++ b/comms/src/peer_manager/peer_storage.rs @@ -21,7 +21,6 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - consts::PEER_MANAGER_MAX_FLOOD_PEERS, peer_manager::{ node_id::{NodeDistance, NodeId}, peer::{Peer, PeerFlags}, @@ -40,6 +39,8 @@ use std::{collections::HashMap, time::Duration}; use tari_storage::{IterationResult, KeyValueStore}; const LOG_TARGET: &str = "comms::peer_manager::peer_storage"; +/// The maximum number of peers to return from the flood_identities method in peer manager +const PEER_MANAGER_MAX_FLOOD_PEERS: usize = 1000; /// PeerStorage provides a mechanism to keep a datastore and a local copy of all peers in sync and allow fast searches /// using the node_id, public key or net_address of a peer. diff --git a/comms/src/pipeline/inbound.rs b/comms/src/pipeline/inbound.rs index 7cc2b5239b..c2035cf9f0 100644 --- a/comms/src/pipeline/inbound.rs +++ b/comms/src/pipeline/inbound.rs @@ -42,7 +42,7 @@ pub struct Inbound { impl Inbound where - TStream: Stream + FusedStream + Unpin + Send + 'static, + TStream: Stream + FusedStream + Unpin, TStream::Item: Send + 'static, TSvc: Service + Clone + Send + 'static, TSvc::Error: Display + Send, diff --git a/comms/src/pipeline/outbound.rs b/comms/src/pipeline/outbound.rs index 979f102489..c860166ad0 100644 --- a/comms/src/pipeline/outbound.rs +++ b/comms/src/pipeline/outbound.rs @@ -44,7 +44,7 @@ pub struct Outbound { impl Outbound where - TStream: Stream + FusedStream + Unpin + Send + 'static, + TStream: Stream + FusedStream + Unpin, TStream::Item: Send + 'static, TPipeline: Service + Clone + Send + 'static, TPipeline::Error: Display + Send, diff --git a/comms/src/proto/identity.proto b/comms/src/proto/identity.proto index ad6a2543a9..bf4d9f5a78 100644 --- a/comms/src/proto/identity.proto +++ b/comms/src/proto/identity.proto @@ -3,9 +3,12 @@ syntax = "proto3"; package tari.comms.identity; message PeerIdentityMsg { - bytes node_id = 1; - repeated string addresses = 2; - uint64 features = 3; - repeated bytes supported_protocols = 4; - string user_agent = 5; + repeated bytes addresses = 1; + uint64 features = 2; + repeated bytes supported_protocols = 3; + string user_agent = 4; + // Major node version. This must match the current node's version in order for the connection to be established. + uint32 major = 5; + // Minor node version. This indicates minor non-breaking changes. + uint32 minor = 6; } diff --git a/comms/src/protocol/extensions.rs b/comms/src/protocol/extensions.rs index e29df49af7..fdb6369e97 100644 --- a/comms/src/protocol/extensions.rs +++ b/comms/src/protocol/extensions.rs @@ -31,7 +31,7 @@ use tari_shutdown::ShutdownSignal; pub type ProtocolExtensionError = anyhow::Error; -pub trait ProtocolExtension: Send + Sync { +pub trait ProtocolExtension: Send { // TODO: The Box is easier to do for now at the cost of ProtocolExtension being less generic. fn install(self: Box, context: &mut ProtocolExtensionContext) -> Result<(), ProtocolExtensionError>; } diff --git a/comms/src/protocol/identity.rs b/comms/src/protocol/identity.rs index eee8b05fc1..46a0b87b08 100644 --- a/comms/src/protocol/identity.rs +++ b/comms/src/protocol/identity.rs @@ -25,25 +25,24 @@ use crate::{ message::MessageExt, peer_manager::NodeIdentity, proto::identity::PeerIdentityMsg, - protocol::{ProtocolError, ProtocolId, ProtocolNegotiation}, + protocol::{NodeNetworkInfo, ProtocolError, ProtocolId, ProtocolNegotiation}, }; use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; use log::*; use prost::Message; use std::{io, time::Duration}; -use tari_crypto::tari_utilities::ByteArray; use thiserror::Error; use tokio::time; use tokio_util::codec::{Framed, LengthDelimitedCodec}; -pub static IDENTITY_PROTOCOL: ProtocolId = ProtocolId::from_static(b"/tari/identity/1.0.0"); +pub static IDENTITY_PROTOCOL: ProtocolId = ProtocolId::from_static(b"t/identity/1.0"); const LOG_TARGET: &str = "comms::protocol::identity"; pub async fn identity_exchange<'p, TSocket, P>( node_identity: &NodeIdentity, direction: ConnectionDirection, our_supported_protocols: P, - user_agent: String, + network_info: NodeNetworkInfo, mut socket: TSocket, ) -> Result where @@ -85,11 +84,12 @@ where // Send this node's identity let msg_bytes = PeerIdentityMsg { - node_id: node_identity.node_id().to_vec(), - addresses: vec![node_identity.public_address().to_string()], + addresses: vec![node_identity.public_address().to_vec()], features: node_identity.features().bits(), supported_protocols, - user_agent, + major: network_info.major_version, + minor: network_info.minor_version, + user_agent: network_info.user_agent, } .to_encoded_bytes(); @@ -102,6 +102,17 @@ where .ok_or(IdentityProtocolError::PeerUnexpectedCloseConnection)??; let identity_msg = PeerIdentityMsg::decode(msg_bytes)?; + if identity_msg.major != network_info.major_version { + warn!( + target: LOG_TARGET, + "Peer sent mismatching major protocol version '{}'. This node has version '{}.{}'", + identity_msg.major, + network_info.major_version, + network_info.minor_version + ); + return Err(IdentityProtocolError::ProtocolVersionMismatch); + } + Ok(identity_msg) } @@ -119,6 +130,8 @@ pub enum IdentityProtocolError { PeerUnexpectedCloseConnection, #[error("Timeout waiting for peer to send identity information")] Timeout, + #[error("Protocol version mismatch")] + ProtocolVersionMismatch, } impl From for IdentityProtocolError { @@ -150,12 +163,12 @@ mod test { use crate::{ connection_manager::ConnectionDirection, peer_manager::PeerFeatures, + protocol::{IdentityProtocolError, NodeNetworkInfo}, runtime, test_utils::node_identity::build_node_identity, transports::{MemoryTransport, Transport}, }; use futures::{future, StreamExt}; - use tari_crypto::tari_utilities::ByteArray; #[runtime::test_basic] async fn identity_exchange() { @@ -176,14 +189,20 @@ mod test { &node_identity1, ConnectionDirection::Inbound, &[], - Default::default(), + NodeNetworkInfo { + minor_version: 1, + ..Default::default() + }, in_sock, ), super::identity_exchange( &node_identity2, ConnectionDirection::Outbound, &[], - Default::default(), + NodeNetworkInfo { + minor_version: 2, + ..Default::default() + }, out_sock, ), ) @@ -193,12 +212,55 @@ mod test { let identity2 = result1.unwrap(); let identity1 = result2.unwrap(); - assert_eq!(identity1.node_id, node_identity1.node_id().to_vec()); assert_eq!(identity1.features, node_identity1.features().bits()); - assert_eq!(identity1.addresses, vec![node_identity1.public_address().to_string()]); + assert_eq!(identity1.addresses, vec![node_identity1.public_address().to_vec()]); - assert_eq!(identity2.node_id, node_identity2.node_id().to_vec()); assert_eq!(identity2.features, node_identity2.features().bits()); - assert_eq!(identity2.addresses, vec![node_identity2.public_address().to_string()]); + assert_eq!(identity2.addresses, vec![node_identity2.public_address().to_vec()]); + } + + #[runtime::test_basic] + async fn fail_cases() { + let transport = MemoryTransport; + let addr = "/memory/0".parse().unwrap(); + let (mut listener, addr) = transport.listen(addr).unwrap().await.unwrap(); + + let (out_sock, in_sock) = future::join(transport.dial(addr).unwrap(), listener.next()).await; + + let out_sock = out_sock.unwrap(); + let in_sock = in_sock.unwrap().map(|(f, _)| f).unwrap().await.unwrap(); + + let node_identity1 = build_node_identity(PeerFeatures::COMMUNICATION_NODE); + let node_identity2 = build_node_identity(PeerFeatures::COMMUNICATION_CLIENT); + + let (result1, result2) = future::join( + super::identity_exchange( + &node_identity1, + ConnectionDirection::Inbound, + &[], + NodeNetworkInfo { + major_version: 0, + ..Default::default() + }, + in_sock, + ), + super::identity_exchange( + &node_identity2, + ConnectionDirection::Outbound, + &[], + NodeNetworkInfo { + major_version: 1, + ..Default::default() + }, + out_sock, + ), + ) + .await; + + let err = result1.unwrap_err(); + assert!(matches!(err, IdentityProtocolError::ProtocolVersionMismatch)); + + let err = result2.unwrap_err(); + assert!(matches!(err, IdentityProtocolError::ProtocolVersionMismatch)); } } diff --git a/comms/src/protocol/messaging/extension.rs b/comms/src/protocol/messaging/extension.rs index 692b1034e2..58b8a67248 100644 --- a/comms/src/protocol/messaging/extension.rs +++ b/comms/src/protocol/messaging/extension.rs @@ -51,13 +51,13 @@ impl MessagingProtocolExtension ProtocolExtension for MessagingProtocolExtension where - TOutPipe: Service + Clone + Send + Sync + 'static, - TOutPipe::Error: fmt::Display + Send + Sync, - TOutPipe::Future: Send + Sync + 'static, - TInPipe: Service + Clone + Send + Sync + 'static, - TInPipe::Error: fmt::Display + Send + Sync, - TInPipe::Future: Send + Sync + 'static, - TOutReq: Send + Sync + 'static, + TOutPipe: Service + Clone + Send + 'static, + TOutPipe::Error: fmt::Display + Send, + TOutPipe::Future: Send + 'static, + TInPipe: Service + Clone + Send + 'static, + TInPipe::Error: fmt::Display + Send, + TInPipe::Future: Send + 'static, + TOutReq: Send + 'static, { fn install(self: Box, context: &mut ProtocolExtensionContext) -> Result<(), ProtocolExtensionError> { let (proto_tx, proto_rx) = mpsc::channel(consts::MESSAGING_PROTOCOL_EVENTS_BUFFER_SIZE); diff --git a/comms/src/protocol/messaging/protocol.rs b/comms/src/protocol/messaging/protocol.rs index 5ef5e8de95..6e53e80e6c 100644 --- a/comms/src/protocol/messaging/protocol.rs +++ b/comms/src/protocol/messaging/protocol.rs @@ -50,7 +50,7 @@ use tokio::sync::broadcast; use tokio_util::codec::{Framed, LengthDelimitedCodec}; const LOG_TARGET: &str = "comms::protocol::messaging"; -pub(super) static MESSAGING_PROTOCOL: Bytes = Bytes::from_static(b"/tari/messaging/0.1.0"); +pub(super) static MESSAGING_PROTOCOL: Bytes = Bytes::from_static(b"t/msg/0.1"); const INTERNAL_MESSAGING_EVENT_CHANNEL_SIZE: usize = 150; /// The maximum amount of inbound messages to accept within the `RATE_LIMIT_RESTOCK_INTERVAL` window diff --git a/comms/src/protocol/mod.rs b/comms/src/protocol/mod.rs index 7f22371851..27628a183f 100644 --- a/comms/src/protocol/mod.rs +++ b/comms/src/protocol/mod.rs @@ -32,6 +32,9 @@ pub use identity::{identity_exchange, IdentityProtocolError, IDENTITY_PROTOCOL}; mod negotiation; pub use negotiation::ProtocolNegotiation; +mod network_info; +pub use network_info::NodeNetworkInfo; + mod protocols; pub use protocols::{ProtocolEvent, ProtocolNotification, ProtocolNotificationRx, ProtocolNotificationTx, Protocols}; diff --git a/comms/src/consts.rs b/comms/src/protocol/network_info.rs similarity index 61% rename from comms/src/consts.rs rename to comms/src/protocol/network_info.rs index 3773cc15c0..7a122233a1 100644 --- a/comms/src/consts.rs +++ b/comms/src/protocol/network_info.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Tari Project +// Copyright 2021, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,11 +20,19 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::time::Duration; - -/// The maximum number of peers to return from the flood_identities method in peer manager -pub const PEER_MANAGER_MAX_FLOOD_PEERS: usize = 1000; - -/// The amount of time to consider a peer to be offline (i.e. dial to peer will fail without trying) after a failed -/// connection attempt -pub const PEER_OFFLINE_COOLDOWN_PERIOD: Duration = Duration::from_secs(60); +/// Represents the current nodes network info +#[derive(Debug, Clone, Default)] +pub struct NodeNetworkInfo { + /// Major protocol version. This indicates the protocol version that is supported by this node. A peer MAY reject + /// the connection if a remote peer advertises a different major version number. + pub major_version: u32, + /// Minor protocol version. A version number that represents backward-compatible protocol changes. A peer SHOULD + /// NOT reject the connection if a remote peer advertises a different minor version number. + pub minor_version: u32, + /// The byte that MUST be sent (outbound connections) or MUST be received (inbound connections) for a connection to + /// be established. This byte cannot be 0x46 (E) because that is reserved for liveness. + /// Default: 0x00 + pub network_byte: u8, + /// The user agent string for this node + pub user_agent: String, +} diff --git a/comms/src/types.rs b/comms/src/types.rs index aa3e6e3105..847ecc8476 100644 --- a/comms/src/types.rs +++ b/comms/src/types.rs @@ -28,10 +28,6 @@ use tari_storage::HashmapDatabase; #[cfg(not(test))] use tari_storage::LMDBWrapper; -/// The default port that control services listen on -pub const DEFAULT_CONTROL_PORT_ADDRESS: &str = "/ip4/0.0.0.0/tcp/7899"; -pub const DEFAULT_LISTENER_ADDRESS: &str = "/ip4/0.0.0.0/tcp/7898"; - /// Specify the digest type for the signature challenges pub type Challenge = Blake256; diff --git a/docs/src/tari_script_no_op_vulnerability.md b/docs/src/tari_script_no_op_vulnerability.md index c665be8bc2..cf124c5b89 100644 --- a/docs/src/tari_script_no_op_vulnerability.md +++ b/docs/src/tari_script_no_op_vulnerability.md @@ -24,7 +24,7 @@ The full derivation of these components can be found [here](wallet_to_wallet_wit | Input Data | \\( \theta_a \\) | | height | \\( h \\) | | Script Signature | \\( (s_{sa}, R_{sa}) \\) | -| Script offset public key | \\( K_{Oa} \\) | +| Sender offset public key | \\( K_{Oa} \\) | | Transaction Output | | |--------------------|-------| @@ -32,7 +32,7 @@ The full derivation of these components can be found [here](wallet_to_wallet_wit | Features | Fb | | Rangeproof | \\( RP_b \\) for \\( \hat{C}_b \\) | | Script Hash | \\( \sigma_b = H( \alpha_b) \\) | -| Script offset public key | \\( K_{Ob} \\) | +| Sender offset public key | \\( K_{Ob} \\) | | Transaction Kernel | | |--------------------|-------| @@ -42,7 +42,7 @@ The full derivation of these components can be found [here](wallet_to_wallet_wit | Metadata | m | The mechanism that stops Bob's script, whose hash is \\( \sigma_b \\), from being malleable after the transaction is completed is the following. During Bob's round of -transaction negotiation they commit to the script hash \\( \sigma_b \\), and the Public UTXO script offset, \\( K_{Ob} \\), which is provided by Alice. These are committed +transaction negotiation they commit to the script hash \\( \sigma_b \\), and the Public UTXO sender offset, \\( K_{Ob} \\), which is provided by Alice. These are committed to in the construction of \\( \beta_b = H(\sigma_b || F_b || K_{Ob}) \\). @@ -70,13 +70,13 @@ key of his choosing, \\( \hat{K_{sa}} \\) to which he knows the private key \\( the stack. Bob can choose a new nonce \\( \hat{R_{sa}} \\) and then produce a valid script signature (\\( \hat{s_{sa}}, \hat{R_{sa}} \\)) for the challenge \\( H( \hat{R_{sa}} || \alpha_a || \hat{\theta_a} || h) \\). -Bob can also now produce a new transaction script offset while choosing a new UTXO script offset for his UTXO, \\( \hat{k_{Ob}} \\). This means he can modify the script attached +Bob can also now produce a new transaction script offset while choosing a new UTXO sender offset for his UTXO, \\( \hat{k_{Ob}} \\). This means he can modify the script attached to his UTXO, \\( \hat{\sigma_{b}} \\), calculate a new modified commitment and range proof \\( \hat{RP_{b}} \\) for his new script and commit to it in a new transaction script offset \\( \hat{\gamma} = \hat{k_{sa}} - \hat{k_{Ob}} \hat{U_b} \\). This attack is only possible if Alice did not have a change output in the transaction and all Alice's inputs have the NO_OP script. This is because the case that Alice has a change output, Bob would not know the -private script offset for the change output, \\( k_{oc} \\), to produce +private sender offset for the change output, \\( k_{oc} \\), to produce \\( \gamma = k_{sa} - k_{Ob}U_b - k_{Oc}U_c \\). diff --git a/docs/src/wallet_to_wallet_with_tariscript.md b/docs/src/wallet_to_wallet_with_tariscript.md index 4ce77c7041..cc8a328b91 100644 --- a/docs/src/wallet_to_wallet_with_tariscript.md +++ b/docs/src/wallet_to_wallet_with_tariscript.md @@ -55,7 +55,7 @@ Alice then sends the following values to Bob: | \\( f \\) | fee | | \\( m \\) | Transaction metadata (currently just lockheight) | | \\( \alpha_b \\) | Spending script for Bob's UTXO, \\( C_b \\) | -| \\( K_{Ob} \\) | Public UTXO script offset | +| \\( K_{Ob} \\) | Public UTXO sender offset | | message | A unicode string | ### Bob Replies @@ -127,7 +127,7 @@ Alice can now construct the final transaction: | Input Data | \\( \theta_a \\) | | height | \\( h \\) | | Script Signature | \\( (s_{sa}, R_{sa}) \\) | -| Script offset public key | \\( K_{Oa} \\) | +| Sender offset public key | \\( K_{Oa} \\) | | Transaction Output | | |--------------------|-------| @@ -135,7 +135,7 @@ Alice can now construct the final transaction: | Features | Fb | | Rangeproof | \\( RP_b \\) for \\( \hat{C}_b \\) | | Script Hash | \\( \sigma_b = H( \alpha_b) \\) | -| Script offset public key | \\( K_{Ob} \\) | +| Sender offset public key | \\( K_{Ob} \\) | | Transaction Output | | |--------------------|-------| @@ -143,7 +143,7 @@ Alice can now construct the final transaction: | Features | Fc | | Rangeproof | \\( RP_c \\) for \\( \hat{C}_c \\) | | Script Hash | \\( \sigma_c = H( \alpha_c) \\) | -| Script offset public key | \\( K_{Oc} \\) | +| Sender offset public key | \\( K_{Oc} \\) | | Transaction Kernel | | |--------------------|-------| @@ -185,7 +185,7 @@ pub struct SingleRoundSenderData { /// Hash of the receivers UTXO script, \sigma pub script_hash: Vec { - await waitFor( - async () => client.transactionStateResult(sig), - pool, - 1200 * 1000 - ); - this.lastResult = await client.transactionState(sig); - console.log(`Node ${name} response is: ${this.lastResult.result}`); - expect(this.lastResult.result).to.equal(pool); - }); + await this.forEachClientAsync( + async (client, name) => { + await waitFor( + async () => client.transactionStateResult(sig), + pool, + 1200 * 1000 + ); + this.lastResult = await client.transactionState(sig); + console.log(`Node ${name} response is: ${this.lastResult.result}`); + }, + canFail ? parseInt(canFail) : 0 + ); } ); diff --git a/integration_tests/features/support/world.js b/integration_tests/features/support/world.js index fc759c4bb6..00c4b10b7a 100644 --- a/integration_tests/features/support/world.js +++ b/integration_tests/features/support/world.js @@ -188,16 +188,39 @@ class CustomWorld { return this.proxies[name]; } - async forEachClientAsync(f) { + async forEachClientAsync(f, canFailPercent = 0) { const promises = []; + let total = 0; + let succeeded = 0; + let failed = 0; for (const property in this.seeds) { promises.push(f(this.getClient(property), property)); + ++total; } for (const property in this.nodes) { promises.push(f(this.getClient(property), property)); + ++total; } - await Promise.all(promises); + + // Round up the number of nodes that can fail. + let canFail = Math.ceil((total * canFailPercent) / 100); + + return new Promise((resolve, reject) => { + for (let promise of promises) { + Promise.resolve(promise).then( + () => { + succeeded += 1; + console.log(`${succeeded} of ${total} (need ${total - canFail})`); + if (succeeded >= total - canFail) resolve(); + }, + () => { + failed += 1; + if (failed > canFail) reject("Too many failed."); + } + ); + } + }); } async stopNode(name) { diff --git a/integration_tests/helpers/baseNodeClient.js b/integration_tests/helpers/baseNodeClient.js index c39b7d239c..04d1ffd44c 100644 --- a/integration_tests/helpers/baseNodeClient.js +++ b/integration_tests/helpers/baseNodeClient.js @@ -361,8 +361,9 @@ class BaseNodeClient { hash.update(header.prev_hash); const timestamp = parseInt(header.timestamp.seconds); hash.update(toLittleEndian(timestamp, 64)); + hash.update(header.input_mr); hash.update(header.output_mr); - hash.update(header.range_proof_mr); + hash.update(header.witness_mr); hash.update(header.kernel_mr); hash.update(header.total_kernel_offset); hash.update(toLittleEndian(parseInt(header.nonce), 64)); diff --git a/integration_tests/helpers/transactionBuilder.js b/integration_tests/helpers/transactionBuilder.js index 7b548217ab..9c6742b454 100644 --- a/integration_tests/helpers/transactionBuilder.js +++ b/integration_tests/helpers/transactionBuilder.js @@ -53,15 +53,23 @@ class TransactionBuilder { return Buffer.from(final).toString("hex"); } - buildScriptChallenge(publicNonce, script, input_data) { + buildScriptChallenge( + publicNonce, + script, + input_data, + public_key, + commitment + ) { var KEY = null; // optional key var OUTPUT_LENGTH = 32; // bytes var context = blake2bInit(OUTPUT_LENGTH, KEY); - let buff = Buffer.from(publicNonce, "hex"); - blake2bUpdate(context, buff); + let buff_publicNonce = Buffer.from(publicNonce, "hex"); + let buff_public_key = Buffer.from(public_key, "hex"); + blake2bUpdate(context, buff_publicNonce); blake2bUpdate(context, script); blake2bUpdate(context, input_data); - // blake2bUpdate(context, height); + blake2bUpdate(context, buff_public_key); + blake2bUpdate(context, commitment); let final = blake2bFinal(context); return Buffer.from(final).toString("hex"); } @@ -98,21 +106,34 @@ class TransactionBuilder { Buffer.from([0x04]), Buffer.from(scriptPublicKey, "hex"), ]); - this.kv.new_key("common_nonce"); - let public_nonce = this.kv.public_key("common_nonce"); + this.kv.new_key("common_nonce_1"); + this.kv.new_key("common_nonce_2"); + let private_nonce_1 = this.kv.private_key("common_nonce_1"); + let private_nonce_2 = this.kv.private_key("common_nonce_2"); + let public_nonce = tari_crypto.commit_private_keys( + private_nonce_1, + private_nonce_2 + ).commitment; let challenge = this.buildScriptChallenge( public_nonce, nopScriptBytes, input_data, - 0 + scriptPublicKey, + input.output.commitment ); - let private_nonce = this.kv.private_key("common_nonce"); - let script_sig = tari_crypto.sign_challenge_with_nonce( - input.scriptPrivateKey, - private_nonce, + let amount_key = Buffer.from(toLittleEndian(input.amount, 256)).toString( + "hex" + ); + let total_key = + BigInt("0x" + input.scriptPrivateKey) + BigInt("0x" + input.privateKey); + total_key = "0" + total_key.toString(16); + let script_sig = tari_crypto.sign_comsig_challenge_with_nonce( + amount_key, + total_key, + private_nonce_1, + private_nonce_2, challenge ); - this.inputs.push({ input: { features: input.output.features, @@ -121,8 +142,9 @@ class TransactionBuilder { input_data: input_data, height: 0, script_signature: { - public_nonce: Buffer.from(script_sig.public_nonce, "hex"), - signature: Buffer.from(script_sig.signature, "hex"), + public_nonce_commitment: Buffer.from(script_sig.public_nonce, "hex"), + signature_u: Buffer.from(script_sig.u, "hex"), + signature_v: Buffer.from(script_sig.v, "hex"), }, script_offset_public_key: input.output.script_offset_public_key, }, diff --git a/integration_tests/helpers/util.js b/integration_tests/helpers/util.js index 1ce3043f77..445badd22f 100644 --- a/integration_tests/helpers/util.js +++ b/integration_tests/helpers/util.js @@ -68,10 +68,13 @@ function dec2hex(n) { } function toLittleEndianInner(n) { - const hexar = dec2hex(n); - return hexar - .map((h) => (h < 16 ? "0" : "") + h.toString(16)) - .concat(Array(4 - hexar.length).fill("00")); + let hexar = dec2hex(n); + hexar = hexar.map((h) => (h < 16 ? "0" : "") + h.toString(16)); + if (hexar.length < 4) { + return hexar.concat(Array(4 - hexar.length).fill("00")); + } else { + return hexar; + } } function toLittleEndian(n, numBits) { diff --git a/integration_tests/helpers/walletProcess.js b/integration_tests/helpers/walletProcess.js index e2874236ef..b8cf735a00 100644 --- a/integration_tests/helpers/walletProcess.js +++ b/integration_tests/helpers/walletProcess.js @@ -219,7 +219,6 @@ class WalletProcess { }, script: Buffer.from(row.script, "hex"), input_data: Buffer.from(row.input_data, "hex"), - height: parseInt(row.height), script_private_key: Buffer.from(row.script_private_key, "hex"), script_offset_public_key: Buffer.from( row.script_offset_public_key, diff --git a/integration_tests/package-lock.json b/integration_tests/package-lock.json index e2d7b44f3e..2d747f64ce 100644 --- a/integration_tests/package-lock.json +++ b/integration_tests/package-lock.json @@ -1,4275 +1,8 @@ { "name": "integration_tests", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "integration_tests", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "archiver": "^5.3.0", - "axios": "^0.21.1", - "clone-deep": "^4.0.1", - "csv-parser": "^3.0.0", - "dateformat": "^3.0.3", - "glob": "^7.1.7", - "hex64": "^0.4.0", - "jszip": "^3.6.0", - "sha3": "^2.1.3", - "synchronized-promise": "^0.3.1", - "tari_crypto": "^0.9.0", - "wallet-grpc-client": "git@github.com:tari-project/wallet-grpc-client.git#d1f4ed2dadcc538b656e4aee8b0f42ce634b4f7d" - }, - "devDependencies": { - "@grpc/proto-loader": "^0.5.5", - "blakejs": "^1.1.0", - "chai": "^4.2.0", - "cucumber": "^6.0.5", - "cucumber-html-reporter": "^5.3.0", - "cucumber-pretty": "^6.0.0", - "eslint": "^7.25.0", - "eslint-config-prettier": "^8.3.0", - "eslint-config-standard": "^16.0.2", - "eslint-plugin-import": "^2.2.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "^3.4.0", - "eslint-plugin-promise": "^4.3.1", - "grpc": "^1.24.3", - "grpc-promise": "^1.4.0", - "husky": "^6.0.0", - "prettier": "^2.2.1" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", - "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.5.tgz", - "integrity": "sha512-cBbwXj3F2xjnQJ0ERaFRLjxhUSBYsQPXJ7CERz/ecx6q6hzQ99eTflAPFC3ks4q/IG4CWupNVdflc4jlFBJVsg==", - "dev": true, - "dependencies": { - "core-js-pure": "^3.14.0", - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", - "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.2.tgz", - "integrity": "sha512-UXepkOKCATJrhHGsxt+CGfpZy9zUn1q9mop5kfcXq1fBkTePxVNPOdnISlCbJFlCtld+pSLGyZCzr9/zVprFKA==", - "dependencies": { - "@types/node": ">=12.12.47" - }, - "engines": { - "node": "^8.13.0 || >=10.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.6.tgz", - "integrity": "sha512-DT14xgw3PSzPxwS13auTEwxhMMOoz33DPUKNtmYK/QYbBSpLXJy78FGGs5yVoxVobEqPm4iW9MOIoz0A3bLTRQ==", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "protobufjs": "^6.8.6" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz", - "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==", - "dev": true, - "dependencies": { - "detect-libc": "^1.0.3", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.1", - "nopt": "^5.0.0", - "npmlog": "^4.1.2", - "rimraf": "^3.0.2", - "semver": "^7.3.4", - "tar": "^6.1.0" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" - }, - "node_modules/@types/bytebuffer": { - "version": "5.0.42", - "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.42.tgz", - "integrity": "sha512-lEgKojWUAc/MG2t649oZS5AfYFP2xRNPoDuwDBlBMjHXd8MaGPgFgtCXUK7inZdBOygmVf10qxc1Us8GXC96aw==", - "dev": true, - "dependencies": { - "@types/long": "*", - "@types/node": "*" - } - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "node_modules/@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, - "node_modules/@types/node": { - "version": "15.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", - "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true - }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "node_modules/archiver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz", - "integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==", - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.0", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.0.0", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ascli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", - "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", - "dev": true, - "dependencies": { - "colour": "~0.7.1", - "optjs": "~3.2.2" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/assertion-error-formatter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz", - "integrity": "sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ==", - "dev": true, - "dependencies": { - "diff": "^4.0.1", - "pad-right": "^0.2.2", - "repeat-string": "^1.6.1" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "node_modules/axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", - "dependencies": { - "follow-redirects": "^1.10.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "node_modules/becke-ch--regex--s0-0-v1--base--pl--lib": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/becke-ch--regex--s0-0-v1--base--pl--lib/-/becke-ch--regex--s0-0-v1--base--pl--lib-1.4.0.tgz", - "integrity": "sha1-Qpzuu/pffpNueNc/vcfacWKyDiA=", - "dev": true - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/blakejs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", - "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U=", - "dev": true - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "engines": { - "node": "*" - } - }, - "node_modules/bytebuffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", - "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", - "dev": true, - "dependencies": { - "long": "~3" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/bytebuffer/node_modules/long": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/cli-table3": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", - "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", - "dev": true, - "dependencies": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^2.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/colour": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", - "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/commander": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", - "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", - "dev": true - }, - "node_modules/compress-commons": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", - "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "node_modules/core-js-pure": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.14.0.tgz", - "integrity": "sha512-YVh+LN2FgNU0odThzm61BsdkwrbrchumFq3oztnE9vTKC4KS2fvnPmcx8t6jnqAyOTCTF4ZSiuK8Qhh7SNcL4g==", - "dev": true, - "hasInstallScript": true - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "node_modules/crc-32": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", - "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", - "dependencies": { - "exit-on-epipe": "~1.0.1", - "printj": "~1.1.0" - }, - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz", - "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csv-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.0.0.tgz", - "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "csv-parser": "bin/csv-parser" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cucumber": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cucumber/-/cucumber-6.0.5.tgz", - "integrity": "sha512-x+W9Fwk6TvcapQsYMxwFU5AsQJDOIJVGrPKmH15OC7jzb9/Dk7Hb0ZAyw4WcpaDcUDRc8bi2k2yJejDp5eTRlg==", - "dev": true, - "dependencies": { - "assertion-error-formatter": "^3.0.0", - "bluebird": "^3.4.1", - "cli-table3": "^0.5.1", - "colors": "^1.1.2", - "commander": "^3.0.1", - "cucumber-expressions": "^8.1.0", - "cucumber-tag-expressions": "^2.0.2", - "duration": "^0.2.1", - "escape-string-regexp": "^2.0.0", - "figures": "^3.0.0", - "gherkin": "5.0.0", - "glob": "^7.1.3", - "indent-string": "^4.0.0", - "is-generator": "^1.0.2", - "is-stream": "^2.0.0", - "knuth-shuffle-seeded": "^1.0.6", - "lodash": "^4.17.14", - "mz": "^2.4.0", - "progress": "^2.0.0", - "resolve": "^1.3.3", - "serialize-error": "^4.1.0", - "stack-chain": "^2.0.0", - "stacktrace-js": "^2.0.0", - "string-argv": "^0.3.0", - "title-case": "^2.1.1", - "util-arity": "^1.0.2", - "verror": "^1.9.0" - }, - "bin": { - "cucumber-js": "bin/cucumber-js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cucumber-expressions": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/cucumber-expressions/-/cucumber-expressions-8.3.0.tgz", - "integrity": "sha512-cP2ya0EiorwXBC7Ll7Cj7NELYbasNv9Ty42L4u7sso9KruWemWG1ZiTq4PMqir3SNDSrbykoqI5wZgMbLEDjLQ==", - "dev": true, - "dependencies": { - "becke-ch--regex--s0-0-v1--base--pl--lib": "^1.4.0", - "xregexp": "^4.2.4" - } - }, - "node_modules/cucumber-html-reporter": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/cucumber-html-reporter/-/cucumber-html-reporter-5.4.0.tgz", - "integrity": "sha512-PHaJvH9POQrbydzVdd12jgV9NWS8zO58lLwz6wmyZSTDp4PQOjEdS69SFvPLXOQYTIU8o0MM3QBRMpeUpvulvA==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "find": "^0.3.0", - "fs-extra": "^8.1.0", - "js-base64": "^2.3.2", - "jsonfile": "^5.0.0", - "lodash": "^4.17.11", - "node-emoji": "^1.10.0", - "open": "^6.4.0", - "uuid": "^3.3.3" - } - }, - "node_modules/cucumber-pretty": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cucumber-pretty/-/cucumber-pretty-6.0.0.tgz", - "integrity": "sha512-ddx/VInPVKFB7N86QujgLivihJhuzexKwExMuFaUjSlEs5zVVqBgaf55f88h97VafXTWX+ZAcxTUwMBS4mYj/g==", - "dev": true, - "dependencies": { - "cli-table3": "^0.5.1", - "colors": "^1.4.0", - "figures": "^3.0.0" - } - }, - "node_modules/cucumber-tag-expressions": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/cucumber-tag-expressions/-/cucumber-tag-expressions-2.0.3.tgz", - "integrity": "sha512-+x5j1IfZrBtbvYHuoUX0rl4nUGxaey6Do9sM0CABmZfDCcWXuuRm1fQeCaklIYQgOFHQ6xOHvDSdkMHHpni6tQ==", - "dev": true - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "engines": { - "node": "*" - } - }, - "node_modules/deasync": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.21.tgz", - "integrity": "sha512-kUmM8Y+PZpMpQ+B4AuOW9k2Pfx/mSupJtxOsLzmnHY2WqZUYRFccFn2RhzPAqt3Xb+sorK/badW2D4zNzqZz5w==", - "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^1.7.1" - }, - "engines": { - "node": ">=0.11.0" - } - }, - "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/duration": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", - "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "~0.10.46" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/error-stack-parser": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", - "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", - "dev": true, - "dependencies": { - "stackframe": "^1.1.1" - } - }, - "node_modules/es-abstract": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", - "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.3", - "is-string": "^1.0.6", - "object-inspect": "^1.10.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dev": true, - "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dev": true, - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - } - }, - "node_modules/eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", - "dev": true - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "dependencies": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/eslint-module-utils": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", - "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "pkg-dir": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "dependencies": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.23.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", - "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.1", - "find-up": "^2.0.0", - "has": "^1.0.3", - "is-core-module": "^2.4.0", - "minimatch": "^3.0.4", - "object.values": "^1.1.3", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/eslint-plugin-node/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz", - "integrity": "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/eslint-plugin-promise": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.3.1.tgz", - "integrity": "sha512-bY2sGqyptzFBDLh/GMbAxfdJC+b0f23ME63FOE4+Jao0oZ3E1LEwFtWJX/1pGMJLiTtrSSern2CRM/g+dfc0eQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/exit-on-epipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", - "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dev": true, - "dependencies": { - "type": "^2.0.0" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", - "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==", - "dev": true - }, - "node_modules/extsprintf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", - "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "node_modules/find": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/find/-/find-0.3.0.tgz", - "integrity": "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==", - "dev": true, - "dependencies": { - "traverse-chain": "~0.1.0" - } - }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs-extra/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "node_modules/gherkin": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gherkin/-/gherkin-5.0.0.tgz", - "integrity": "sha1-lt70EZjsOQgli1Ea909lWidk0qE=", - "dev": true, - "bin": { - "gherkin-javascript": "bin/gherkin" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" - }, - "node_modules/grpc": { - "version": "1.24.10", - "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.24.10.tgz", - "integrity": "sha512-mTR+P5IL3WO3oCgNwxKFE5ksXEJfCYP+dk0aIbjB494f7OnHTmssU5r9vznsSq3+cdLcxAzGFskOj5CaPwi8KA==", - "dev": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.4", - "@types/bytebuffer": "^5.0.40", - "lodash.camelcase": "^4.3.0", - "lodash.clone": "^4.5.0", - "nan": "^2.13.2", - "protobufjs": "^5.0.3" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/grpc-promise": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/grpc-promise/-/grpc-promise-1.4.0.tgz", - "integrity": "sha512-4BBXHXb5OjjBh7luylu8vFqL6H6aPn/LeqpQaSBeRzO/Xv95wHW/WkU9TJRqaCTMZ5wq9jTSvlJWp0vRJy1pVA==" - }, - "node_modules/grpc/node_modules/protobufjs": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", - "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", - "dev": true, - "dependencies": { - "ascli": "~1", - "bytebuffer": "~5", - "glob": "^7.0.5", - "yargs": "^3.10.0" - }, - "bin": { - "pbjs": "bin/pbjs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "node_modules/hex64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/hex64/-/hex64-0.4.0.tgz", - "integrity": "sha1-rRB4rIHVfXLeYjKxADvE9vsCh8A=", - "bin": { - "hex64": "bin/hex64" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/husky": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/husky/-/husky-6.0.0.tgz", - "integrity": "sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ==", - "dev": true, - "bin": { - "husky": "lib/bin.js" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", - "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", - "dev": true - }, - "node_modules/is-boolean-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", - "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/is-date-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", - "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-generator": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz", - "integrity": "sha1-wUwhBX7TbjKNuANHlmxpP4hjifM=", - "dev": true - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-number-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", - "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", - "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-string": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", - "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/js-base64": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", - "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsonfile": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-5.0.0.tgz", - "integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6", - "universalify": "^0.1.2" - } - }, - "node_modules/jszip": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.6.0.tgz", - "integrity": "sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - } - }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/knuth-shuffle-seeded": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", - "integrity": "sha1-AfG2VzOqdUDuCNiwF0Fk0iCB5OE=", - "dev": true, - "dependencies": { - "seed-random": "~2.2.0" - } - }, - "node_modules/lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "dependencies": { - "invert-kv": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" - }, - "node_modules/lodash.clone": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", - "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", - "dev": true - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.toarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", - "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" - }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "node_modules/minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, - "node_modules/no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "dependencies": { - "lower-case": "^1.1.1" - } - }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==" - }, - "node_modules/node-emoji": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", - "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", - "dev": true, - "dependencies": { - "lodash.toarray": "^4.4.0" - } - }, - "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true, - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", - "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", - "dev": true - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.values": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", - "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/open": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", - "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", - "dev": true, - "dependencies": { - "is-wsl": "^1.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/optjs": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", - "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=", - "dev": true - }, - "node_modules/os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "dependencies": { - "lcid": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pad-right": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", - "integrity": "sha1-b7ySQEXSRPKiokRQMGDTv8YAl3Q=", - "dev": true, - "dependencies": { - "repeat-string": "^1.5.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/printj": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", - "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==", - "bin": { - "printj": "bin/printj.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdir-glob": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.1.tgz", - "integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==", - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, - "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/seed-random": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", - "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=", - "dev": true - }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-error": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-4.1.0.tgz", - "integrity": "sha512-5j9GgyGsP9vV9Uj1S0lDCvlsd+gc2LEPVK7HHHte7IyPwOD4lVQFeaX143gx3U5AnoCi+wbcb3mvaxVysjpxEw==", - "dev": true, - "dependencies": { - "type-fest": "^0.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "node_modules/set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sha3": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", - "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", - "dependencies": { - "buffer": "6.0.3" - } - }, - "node_modules/sha3/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", - "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", - "dev": true - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/stack-chain": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-2.0.0.tgz", - "integrity": "sha512-GGrHXePi305aW7XQweYZZwiRwR7Js3MWoK/EHzzB9ROdc75nCnjSJVi21rdAGxFl+yCx2L2qdfl5y7NO4lTyqg==", - "dev": true - }, - "node_modules/stack-generator": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.5.tgz", - "integrity": "sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q==", - "dev": true, - "dependencies": { - "stackframe": "^1.1.1" - } - }, - "node_modules/stackframe": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", - "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", - "dev": true - }, - "node_modules/stacktrace-gps": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz", - "integrity": "sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg==", - "dev": true, - "dependencies": { - "source-map": "0.5.6", - "stackframe": "^1.1.1" - } - }, - "node_modules/stacktrace-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", - "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", - "dev": true, - "dependencies": { - "error-stack-parser": "^2.0.6", - "stack-generator": "^2.0.5", - "stacktrace-gps": "^3.0.4" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true, - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/synchronized-promise": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/synchronized-promise/-/synchronized-promise-0.3.1.tgz", - "integrity": "sha512-Iy+JzrERSUrwpOHUDku8HHIddk8V6iLG9bPIzboP2i5RYkn2eSmRB8waSaX7Rc/+DUUsnFsoOHrmniwOp9BOgw==", - "dependencies": { - "deasync": "^0.1.15" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", - "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "node_modules/table/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/table/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/table/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/table/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", - "dev": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tari_crypto": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/tari_crypto/-/tari_crypto-0.9.0.tgz", - "integrity": "sha512-dqdlPcZTMZXkrchchu3Pvqyw5UMtg5LP0mnqueyzzFzAeYp/eSgYb1wW+vb2JnM168aamAtbpKTUuEY0ik6QLw==" - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", - "dev": true, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/title-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", - "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", - "dev": true, - "dependencies": { - "no-case": "^2.2.0", - "upper-case": "^1.0.3" - } - }, - "node_modules/traverse-chain": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz", - "integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE=", - "dev": true - }, - "node_modules/tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - } - }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-arity": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", - "integrity": "sha1-WdAa8f2z/t4KxOYysKtfbOl8kzA=", - "dev": true - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/wallet-grpc-client": { - "name": "@tari/wallet-grpc-client", - "version": "0.0.1", - "resolved": "git+ssh://git@github.com/tari-project/wallet-grpc-client.git#d1f4ed2dadcc538b656e4aee8b0f42ce634b4f7d", - "integrity": "sha512-obqpW6BWsS60vqeO8EzIEbuj05ckMqwWC29eOCPOTj9yjvYbN8M/ZHkgT/FxbpU5Fr3O5w7nbb8/t/upfPCdHw==", - "dependencies": { - "@grpc/grpc-js": "^1.2.3", - "@grpc/proto-loader": "^0.5.5", - "grpc-promise": "^1.4.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", - "dev": true, - "bin": { - "window-size": "cli.js" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/xregexp": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.4.1.tgz", - "integrity": "sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag==", - "dev": true, - "dependencies": { - "@babel/runtime-corejs3": "^7.12.1" - } - }, - "node_modules/y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", - "dev": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", - "dev": true, - "dependencies": { - "camelcase": "^2.0.1", - "cliui": "^3.0.3", - "decamelize": "^1.1.1", - "os-locale": "^1.4.0", - "string-width": "^1.0.1", - "window-size": "^0.1.4", - "y18n": "^3.2.0" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/zip-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", - "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", - "dependencies": { - "archiver-utils": "^2.1.0", - "compress-commons": "^4.1.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - } - }, "dependencies": { "@babel/code-frame": { "version": "7.12.11", @@ -7270,14 +3003,6 @@ "stacktrace-gps": "^3.0.4" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, "string-argv": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", @@ -7314,6 +3039,14 @@ "define-properties": "^1.1.3" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -7445,9 +3178,9 @@ } }, "tari_crypto": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/tari_crypto/-/tari_crypto-0.9.0.tgz", - "integrity": "sha512-dqdlPcZTMZXkrchchu3Pvqyw5UMtg5LP0mnqueyzzFzAeYp/eSgYb1wW+vb2JnM168aamAtbpKTUuEY0ik6QLw==" + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/tari_crypto/-/tari_crypto-0.9.1.tgz", + "integrity": "sha512-K7LAtwQQKCeTH5CyyO8d/TiPDEePRaJ4e6+hrxpWv6jlkkAiS4m6csBuVqpSjyAlKeP8cQJpUQX2n22akOuZVg==" }, "text-table": { "version": "0.2.0", @@ -7607,7 +3340,6 @@ }, "wallet-grpc-client": { "version": "git+ssh://git@github.com/tari-project/wallet-grpc-client.git#d1f4ed2dadcc538b656e4aee8b0f42ce634b4f7d", - "integrity": "sha512-obqpW6BWsS60vqeO8EzIEbuj05ckMqwWC29eOCPOTj9yjvYbN8M/ZHkgT/FxbpU5Fr3O5w7nbb8/t/upfPCdHw==", "from": "wallet-grpc-client@git@github.com:tari-project/wallet-grpc-client.git#d1f4ed2dadcc538b656e4aee8b0f42ce634b4f7d", "requires": { "@grpc/grpc-js": "^1.2.3", diff --git a/integration_tests/package.json b/integration_tests/package.json index 59a0fe27f3..ff26e95b97 100644 --- a/integration_tests/package.json +++ b/integration_tests/package.json @@ -43,7 +43,7 @@ "jszip": "^3.6.0", "sha3": "^2.1.3", "synchronized-promise": "^0.3.1", - "tari_crypto": "^0.9.0", + "tari_crypto": "^0.9.1", "wallet-grpc-client": "git@github.com:tari-project/wallet-grpc-client.git#d1f4ed2dadcc538b656e4aee8b0f42ce634b4f7d" } }