diff --git a/Cargo.lock b/Cargo.lock index 58d3fee..6d74cc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1987,7 +1987,7 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "cross-domain-message-gossip" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "futures", "parity-scale-codec", @@ -2474,7 +2474,7 @@ dependencies = [ [[package]] name = "domain-block-preprocessor" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-trait", "domain-runtime-primitives", @@ -2503,7 +2503,7 @@ dependencies = [ [[package]] name = "domain-runtime-primitives" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "fp-account", "frame-support", @@ -2780,6 +2780,17 @@ dependencies = [ "pin-project-lite 0.2.13", ] +[[package]] +name = "event-listener" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.13", +] + [[package]] name = "event-listener-primitives" version = "2.0.1" @@ -6696,7 +6707,7 @@ dependencies = [ [[package]] name = "orml-vesting" version = "0.4.1-dev" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "frame-support", "frame-system", @@ -6748,7 +6759,7 @@ dependencies = [ [[package]] name = "pallet-domains" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "domain-runtime-primitives", "frame-benchmarking", @@ -6774,7 +6785,7 @@ dependencies = [ [[package]] name = "pallet-messenger" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "frame-benchmarking", "frame-support", @@ -6811,7 +6822,7 @@ dependencies = [ [[package]] name = "pallet-offences-subspace" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "frame-support", "frame-system", @@ -6825,19 +6836,24 @@ dependencies = [ [[package]] name = "pallet-rewards" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", + "log", "parity-scale-codec", "scale-info", + "serde", + "sp-core", + "sp-runtime", "subspace-runtime-primitives", ] [[package]] name = "pallet-runtime-configs" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "frame-benchmarking", "frame-support", @@ -6851,7 +6867,7 @@ dependencies = [ [[package]] name = "pallet-subspace" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "frame-benchmarking", "frame-support", @@ -6874,7 +6890,7 @@ dependencies = [ [[package]] name = "pallet-subspace-mmr" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "frame-support", "frame-system", @@ -6926,7 +6942,7 @@ dependencies = [ [[package]] name = "pallet-transaction-fees" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "frame-support", "frame-system", @@ -6982,7 +6998,7 @@ dependencies = [ [[package]] name = "pallet-transporter" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "domain-runtime-primitives", "frame-benchmarking", @@ -8561,7 +8577,7 @@ dependencies = [ [[package]] name = "sc-consensus-subspace" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-trait", "futures", @@ -8601,7 +8617,7 @@ dependencies = [ [[package]] name = "sc-consensus-subspace-rpc" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-oneshot", "futures", @@ -8632,7 +8648,7 @@ dependencies = [ [[package]] name = "sc-domains" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "sc-client-api", "sc-executor", @@ -8971,7 +8987,7 @@ dependencies = [ [[package]] name = "sc-proof-of-time" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "atomic", "core_affinity", @@ -9193,7 +9209,7 @@ dependencies = [ [[package]] name = "sc-subspace-block-relay" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-channel", "async-trait", @@ -9218,7 +9234,7 @@ dependencies = [ [[package]] name = "sc-subspace-chain-specs" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" [[package]] name = "sc-sysinfo" @@ -9944,7 +9960,7 @@ dependencies = [ [[package]] name = "sp-block-fees" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-trait", "domain-runtime-primitives", @@ -10038,7 +10054,7 @@ dependencies = [ [[package]] name = "sp-consensus-subspace" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-trait", "log", @@ -10174,7 +10190,7 @@ dependencies = [ [[package]] name = "sp-domain-digests" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "parity-scale-codec", "sp-runtime", @@ -10183,7 +10199,7 @@ dependencies = [ [[package]] name = "sp-domains" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "blake2 0.10.6", "domain-runtime-primitives", @@ -10215,7 +10231,7 @@ dependencies = [ [[package]] name = "sp-domains-fraud-proof" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "domain-block-preprocessor", "domain-runtime-primitives", @@ -10248,7 +10264,7 @@ dependencies = [ [[package]] name = "sp-executive" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-trait", "parity-scale-codec", @@ -10339,7 +10355,7 @@ dependencies = [ [[package]] name = "sp-messenger" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-trait", "frame-support", @@ -10360,7 +10376,7 @@ dependencies = [ [[package]] name = "sp-messenger-host-functions" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "domain-block-preprocessor", "parity-scale-codec", @@ -10420,7 +10436,7 @@ dependencies = [ [[package]] name = "sp-objects" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "sp-api", "subspace-core-primitives", @@ -10607,7 +10623,7 @@ dependencies = [ [[package]] name = "sp-subspace-mmr" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "parity-scale-codec", "scale-info", @@ -10751,7 +10767,7 @@ dependencies = [ [[package]] name = "space-acres" -version = "0.1.10" +version = "0.1.11" dependencies = [ "anyhow", "arc-swap", @@ -10947,7 +10963,7 @@ dependencies = [ [[package]] name = "subspace-archiving" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "parity-scale-codec", "rayon", @@ -10960,7 +10976,7 @@ dependencies = [ [[package]] name = "subspace-core-primitives" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "blake3", "derive_more", @@ -10983,7 +10999,7 @@ dependencies = [ [[package]] name = "subspace-erasure-coding" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "kzg", "rust-kzg-blst", @@ -10993,7 +11009,7 @@ dependencies = [ [[package]] name = "subspace-farmer" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "anyhow", "async-lock 3.3.0", @@ -11007,6 +11023,7 @@ dependencies = [ "clap 4.5.2", "criterion", "derive_more", + "event-listener 5.2.0", "event-listener-primitives", "fdlimit", "fs4 0.8.1", @@ -11051,7 +11068,7 @@ dependencies = [ [[package]] name = "subspace-farmer-components" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-lock 3.3.0", "async-trait", @@ -11082,7 +11099,7 @@ dependencies = [ [[package]] name = "subspace-metrics" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "actix-web", "prometheus", @@ -11093,7 +11110,7 @@ dependencies = [ [[package]] name = "subspace-networking" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-mutex", "async-trait", @@ -11131,7 +11148,7 @@ dependencies = [ [[package]] name = "subspace-proof-of-space" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "chacha20", "derive_more", @@ -11144,7 +11161,7 @@ dependencies = [ [[package]] name = "subspace-proof-of-time" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "aes", "subspace-core-primitives", @@ -11154,7 +11171,7 @@ dependencies = [ [[package]] name = "subspace-rpc-primitives" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "hex", "serde", @@ -11166,7 +11183,7 @@ dependencies = [ [[package]] name = "subspace-runtime" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "domain-runtime-primitives", "frame-benchmarking", @@ -11221,7 +11238,7 @@ dependencies = [ [[package]] name = "subspace-runtime-primitives" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "pallet-transaction-payment", "sp-core", @@ -11232,7 +11249,7 @@ dependencies = [ [[package]] name = "subspace-service" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "async-trait", "cross-domain-message-gossip", @@ -11309,7 +11326,7 @@ dependencies = [ [[package]] name = "subspace-verification" version = "0.1.0" -source = "git+https://github.com/subspace/subspace?rev=21194a8d07e1068b08f89fb5fca31a6c827b0416#21194a8d07e1068b08f89fb5fca31a6c827b0416" +source = "git+https://github.com/subspace/subspace?rev=b970b6a7d65bc99963a45b05a93a2b41bf2bbd11#b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" dependencies = [ "parity-scale-codec", "schnorrkel", diff --git a/Cargo.toml b/Cargo.toml index 84e345d..73bdb6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "space-acres" description = "Space Acres is an opinionated GUI application for farming on Subspace Network" license = "0BSD" -version = "0.1.10" +version = "0.1.11" authors = ["Nazar Mokrynskyi "] repository = "https://github.com/subspace/space-acres" edition = "2021" @@ -62,7 +62,7 @@ pallet-balances = { git = "https://github.com/subspace/polkadot-sdk", rev = "d6b parity-scale-codec = "3.6.9" parking_lot = "0.12.1" relm4 = "0.7.0-rc.1" -relm4-icons = { version = "0.7.0-alpha.2", features = ["checkmark", "cross", "grid-filled", "menu-large", "processor", "puzzle-piece", "size-horizontally", "ssd", "wallet2", "warning"] } +relm4-icons = { version = "0.7.0-alpha.2", features = ["checkmark", "cross", "grid-filled", "menu-large", "pause", "processor", "puzzle-piece", "size-horizontally", "ssd", "wallet2", "warning"] } relm4-components = { version = "0.7.0-rc.1", default-features = false } reqwest = { version = "0.11.25", default-features = false, features = ["json", "rustls-tls"] } sc-client-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8", default-features = false } @@ -72,25 +72,25 @@ sc-informant = { git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500 sc-network = { git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8", default-features = false } sc-service = { git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8", default-features = false } sc-storage-monitor = { git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8", default-features = false } -sc-subspace-chain-specs = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } +sc-subspace-chain-specs = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } semver = "1.0.22" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.114" simple_moving_average = "1.0.2" sp-core = { git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8", default-features = false } -sp-consensus-subspace = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } -sp-domains-fraud-proof = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } +sp-consensus-subspace = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } +sp-domains-fraud-proof = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } sp-runtime = { git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8", default-features = false } -subspace-core-primitives = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } -subspace-erasure-coding = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } -subspace-farmer = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416", default-features = false } -subspace-farmer-components = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } -subspace-networking = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } -subspace-proof-of-space = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } -subspace-rpc-primitives = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } -subspace-runtime = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } -subspace-runtime-primitives = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } -subspace-service = { git = "https://github.com/subspace/subspace", rev = "21194a8d07e1068b08f89fb5fca31a6c827b0416" } +subspace-core-primitives = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } +subspace-erasure-coding = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } +subspace-farmer = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11", default-features = false } +subspace-farmer-components = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } +subspace-networking = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } +subspace-proof-of-space = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } +subspace-rpc-primitives = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } +subspace-runtime = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } +subspace-runtime-primitives = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } +subspace-service = { git = "https://github.com/subspace/subspace", rev = "b970b6a7d65bc99963a45b05a93a2b41bf2bbd11" } supports-color = "3.0.0" thiserror = "1.0.57" thread-priority = "0.16.0" diff --git a/src/backend.rs b/src/backend.rs index 7db4be5..9baaaf1 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -8,7 +8,7 @@ mod utils; use crate::backend::config::{Config, ConfigError, RawConfig}; use crate::backend::farmer::maybe_node_client::MaybeNodeRpcClient; use crate::backend::farmer::{ - DiskFarm, Farmer, FarmerNotification, FarmerOptions, InitialFarmState, + DiskFarm, Farmer, FarmerAction, FarmerNotification, FarmerOptions, InitialFarmState, }; use crate::backend::networking::{create_network, NetworkOptions}; use crate::backend::node::{ @@ -231,6 +231,8 @@ pub enum BackendNotification { pub enum BackendAction { /// Config was created or updated NewConfig { raw_config: RawConfig }, + /// Farmer action + Farmer(FarmerAction), } struct LoadedBackend { @@ -299,6 +301,12 @@ pub async fn create( // Try to load config and start again continue 'load; } + BackendAction::Farmer(farmer_action) => { + warn!( + ?farmer_action, + "Farmer action is not expected before initialization, ignored" + ); + } } } @@ -550,6 +558,8 @@ async fn run( }) }); + let mut farmer_action_sender = farmer.action_sender(); + // Order is important here, we want to destroy dependents first and only then corresponding // dependencies to avoid unnecessary errors and warnings in logs let networking_fut = networking_fut; @@ -562,6 +572,7 @@ async fn run( process_backend_actions( &config_file_path, backend_action_receiver, + &mut farmer_action_sender, &mut notifications_sender, ) .await @@ -991,6 +1002,7 @@ async fn create_farmer( async fn process_backend_actions( config_file_path: &Path, backend_action_receiver: &mut mpsc::Receiver, + farmer_action_sender: &mut mpsc::Sender, notifications_sender: &mut mpsc::Sender, ) { while let Some(action) = backend_action_receiver.next().await { @@ -1013,6 +1025,11 @@ async fn process_backend_actions( error!(%error, "Failed to send config save result notification"); } } + BackendAction::Farmer(farmer_action) => { + if let Err(error) = farmer_action_sender.send(farmer_action).await { + error!(%error, "Failed to forward farmer action"); + } + } } } } diff --git a/src/backend/farmer.rs b/src/backend/farmer.rs index a946871..b7c015f 100644 --- a/src/backend/farmer.rs +++ b/src/backend/farmer.rs @@ -7,10 +7,10 @@ use crate::PosTable; use anyhow::anyhow; use async_lock::Mutex as AsyncMutex; use event_listener_primitives::HandlerId; -use futures::channel::oneshot; +use futures::channel::{mpsc, oneshot}; use futures::future::BoxFuture; use futures::stream::{FuturesOrdered, FuturesUnordered}; -use futures::{select, FutureExt, StreamExt}; +use futures::{select, FutureExt, StreamExt, TryStreamExt}; use parking_lot::Mutex; use std::num::{NonZeroU8, NonZeroUsize}; use std::path::PathBuf; @@ -19,10 +19,10 @@ use std::{fmt, fs}; use subspace_core_primitives::crypto::kzg::Kzg; use subspace_core_primitives::{PublicKey, Record, SectorIndex}; use subspace_erasure_coding::ErasureCoding; +use subspace_farmer::farm::{Farm, FarmingNotification, SectorPlottingDetails, SectorUpdate}; use subspace_farmer::farmer_cache::{FarmerCache, FarmerCacheWorker}; -use subspace_farmer::single_disk_farm::farming::FarmingNotification; use subspace_farmer::single_disk_farm::{ - SectorPlottingDetails, SectorUpdate, SingleDiskFarm, SingleDiskFarmError, SingleDiskFarmOptions, + SingleDiskFarm, SingleDiskFarmError, SingleDiskFarmOptions, }; use subspace_farmer::utils::plotted_pieces::PlottedPieces; use subspace_farmer::utils::{ @@ -32,8 +32,8 @@ use subspace_farmer::utils::{ use subspace_farmer::NodeClient; use subspace_farmer_components::plotting::PlottedSector; use thread_priority::ThreadPriority; -use tokio::sync::{Barrier, Semaphore}; -use tracing::{error, info, info_span, Instrument}; +use tokio::sync::{watch, Barrier, Semaphore}; +use tracing::{debug, error, info, info_span, Instrument}; /// Minimal cache percentage, there is no need in setting it higher const CACHE_PERCENTAGE: NonZeroU8 = NonZeroU8::MIN; @@ -64,20 +64,42 @@ pub enum FarmerNotification { }, } +#[derive(Debug, Clone)] +pub enum FarmerAction { + /// Pause (or resume) plotting + PausePlotting(bool), +} + type Notifications = Handler; pub(super) struct Farmer { - farm_fut: BoxFuture<'static, anyhow::Result<()>>, + farmer_fut: BoxFuture<'static, anyhow::Result<()>>, farmer_cache_worker_fut: BoxFuture<'static, ()>, initial_farm_states: Vec, farm_during_initial_plotting: bool, notifications: Arc, + action_sender: mpsc::Sender, } impl Farmer { pub(super) async fn run(self) -> anyhow::Result<()> { + let Farmer { + farmer_fut, + farmer_cache_worker_fut, + initial_farm_states, + farm_during_initial_plotting: _, + notifications, + action_sender, + } = self; + + // Explicitly drop unnecessary things, especially senders to make sure farmer can exit + // gracefully when `fn run()`'s future is dropped + drop(initial_farm_states); + drop(notifications); + drop(action_sender); + let farmer_cache_worker_fut = match run_future_in_dedicated_thread( - move || self.farmer_cache_worker_fut, + move || farmer_cache_worker_fut, "farmer-cache-worker".to_string(), ) { Ok(farmer_cache_worker_fut) => farmer_cache_worker_fut, @@ -88,17 +110,15 @@ impl Farmer { } }; - let farm_fut = match run_future_in_dedicated_thread( - move || self.farm_fut, - "farmer-farm".to_string(), - ) { - Ok(farmer_cache_worker_fut) => farmer_cache_worker_fut, - Err(error) => { - return Err(anyhow::anyhow!( - "Failed to spawn farm future in background thread: {error}" - )); - } - }; + let farm_fut = + match run_future_in_dedicated_thread(move || farmer_fut, "farmer-farmer".to_string()) { + Ok(farmer_cache_worker_fut) => farmer_cache_worker_fut, + Err(error) => { + return Err(anyhow::anyhow!( + "Failed to spawn farm future in background thread: {error}" + )); + } + }; select! { _ = farmer_cache_worker_fut.fuse() => { @@ -120,6 +140,10 @@ impl Farmer { self.farm_during_initial_plotting } + pub(super) fn action_sender(&self) -> mpsc::Sender { + self.action_sender.clone() + } + pub(super) fn on_notification(&self, callback: HandlerFn) -> HandlerId { self.notifications.add(callback) } @@ -243,6 +267,8 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu } } + let plotting_thread_pools_count = plotting_thread_pool_core_indices.len(); + let downloading_semaphore = Arc::new(Semaphore::new(plotting_thread_pool_core_indices.len() + 1)); @@ -262,7 +288,7 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu Some(ThreadPriority::Min), )?; - let (single_disk_farms, plotting_delay_senders) = { + let (farms, plotting_delay_senders) = { let global_mutex = Arc::default(); let info_mutex = &AsyncMutex::new(()); let faster_read_sector_record_chunks_mode_barrier = @@ -272,12 +298,12 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu .map(|_| oneshot::channel()) .unzip::<_, _, Vec<_>, Vec<_>>(); - let mut single_disk_farms = Vec::with_capacity(disk_farms.len()); - let mut single_disk_farms_stream = disk_farms + let mut farms = Vec::with_capacity(disk_farms.len()); + let mut farms_stream = disk_farms .into_iter() .zip(plotting_delay_receivers) .enumerate() - .map(|(disk_farm_index, (disk_farm, plotting_delay_receiver))| { + .map(|(farm_index, (disk_farm, plotting_delay_receiver))| { let node_client = node_client.clone(); let farmer_app_info = farmer_app_info.clone(); let max_pieces_in_sector = farmer_app_info.protocol_info.max_pieces_in_sector; @@ -293,7 +319,7 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu Arc::clone(&faster_read_sector_record_chunks_mode_concurrency); async move { - let single_disk_farm_fut = SingleDiskFarm::new::<_, _, PosTable>( + let farm_fut = SingleDiskFarm::new::<_, _, PosTable>( SingleDiskFarmOptions { directory: disk_farm.directory.clone(), farmer_app_info, @@ -316,17 +342,17 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu faster_read_sector_record_chunks_mode_barrier, faster_read_sector_record_chunks_mode_concurrency, }, - disk_farm_index, + farm_index, ); - let single_disk_farm = match single_disk_farm_fut.await { - Ok(single_disk_farm) => single_disk_farm, + let farm = match farm_fut.await { + Ok(farm) => farm, Err(SingleDiskFarmError::InsufficientAllocatedSpace { min_space, allocated_space, }) => { return ( - disk_farm_index, + farm_index, Err(anyhow::anyhow!( "Allocated space {} ({}) is not enough, minimum is ~{} (~{}, \ {} bytes to be exact)", @@ -339,14 +365,14 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu ); } Err(error) => { - return (disk_farm_index, Err(error.into())); + return (farm_index, Err(error.into())); } }; let _info_guard = info_mutex.lock().await; - let info = single_disk_farm.info(); - info!("Single disk farm {disk_farm_index}:"); + let info = farm.info(); + info!("Farm {farm_index}:"); info!(" ID: {}", info.id()); info!(" Genesis hash: 0x{}", hex::encode(info.genesis_hash())); info!(" Public key: 0x{}", hex::encode(info.public_key())); @@ -357,33 +383,31 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu ); info!(" Directory: {}", disk_farm.directory.display()); - (disk_farm_index, Ok(single_disk_farm)) + (farm_index, Ok(Box::new(farm) as Box)) } - .instrument(info_span!("", %disk_farm_index)) + .instrument(info_span!("", %farm_index)) }) .collect::>(); - while let Some((disk_farm_index, single_disk_farm)) = single_disk_farms_stream.next().await - { - if let Err(error) = &single_disk_farm { - let span = info_span!("", %disk_farm_index); + while let Some((farm_index, farm)) = farms_stream.next().await { + if let Err(error) = &farm { + let span = info_span!("", %farm_index); let _span_guard = span.enter(); error!(%error, "Single disk creation failed"); } - single_disk_farms.push((disk_farm_index, single_disk_farm?)); + farms.push((farm_index, farm?)); } // Restore order after unordered initialization - single_disk_farms - .sort_unstable_by_key(|(disk_farm_index, _single_disk_farm)| *disk_farm_index); + farms.sort_unstable_by_key(|(farm_index, _farm)| *farm_index); - let single_disk_farms = single_disk_farms + let farms = farms .into_iter() - .map(|(_disk_farm_index, single_disk_farm)| single_disk_farm) + .map(|(_farm_index, farm)| farm) .collect::>(); - (single_disk_farms, plotting_delay_senders) + (farms, plotting_delay_senders) }; { @@ -409,15 +433,9 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu } farmer_cache .replace_backing_caches( - single_disk_farms - .iter() - .map(|single_disk_farm| single_disk_farm.piece_cache()) - .collect(), + farms.iter().map(|farm| farm.piece_cache()).collect(), if plot_cache { - single_disk_farms - .iter() - .map(|single_disk_farm| single_disk_farm.plot_cache()) - .collect() + farms.iter().map(|farm| farm.plot_cache()).collect() } else { Vec::new() }, @@ -425,9 +443,9 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu .await; // Store piece readers so we can reference them later - let piece_readers = single_disk_farms + let piece_readers = farms .iter() - .map(|single_disk_farm| single_disk_farm.piece_reader()) + .map(|farm| farm.piece_reader()) .collect::>(); info!("Collecting already plotted pieces (this will take some time)..."); @@ -436,31 +454,33 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu { let mut future_plotted_pieces = PlottedPieces::new(piece_readers); - for (disk_farm_index, single_disk_farm) in single_disk_farms.iter().enumerate() { - let disk_farm_index = disk_farm_index.try_into().map_err(|_error| { + for (farm_index, farm) in farms.iter().enumerate() { + let farm_index = farm_index.try_into().map_err(|_error| { anyhow!( "More than 256 plots are not supported, consider running multiple farmer \ instances" ) })?; - (0 as SectorIndex..) - .zip(single_disk_farm.plotted_sectors().await) - .for_each( - |(sector_index, plotted_sector_result)| match plotted_sector_result { + for (sector_index, mut plotted_sectors) in + (0 as SectorIndex..).zip(farm.plotted_sectors().await) + { + while let Some(plotted_sector_result) = plotted_sectors.next().await { + match plotted_sector_result { Ok(plotted_sector) => { - future_plotted_pieces.add_sector(disk_farm_index, &plotted_sector); + future_plotted_pieces.add_sector(farm_index, &plotted_sector); } Err(error) => { error!( %error, - %disk_farm_index, + %farm_index, %sector_index, "Failed reading plotted sector on startup, skipping" ); } - }, - ); + } + } + } } plotted_pieces.lock().replace(future_plotted_pieces); @@ -482,53 +502,57 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu })) .detach(); - let initial_farm_states = single_disk_farms + let initial_farm_states = farms .iter() - .map(|single_disk_farm| async { - InitialFarmState { - total_sectors_count: single_disk_farm.total_sectors_count(), - plotted_sectors_count: single_disk_farm.plotted_sectors_count().await, - } + .enumerate() + .map(|(farm_index, farm)| async move { + anyhow::Ok(InitialFarmState { + total_sectors_count: farm.total_sectors_count(), + plotted_sectors_count: farm.plotted_sectors_count().await.map_err(|error| { + anyhow!( + "Failed to get plotted sectors count from from index {farm_index}: \ + {error}" + ) + })?, + }) }) .collect::>() - .collect() - .await; + .try_collect::>() + .await?; - let mut single_disk_farms_stream = single_disk_farms + let mut farms_stream = farms .into_iter() .enumerate() - .map(|(disk_farm_index, single_disk_farm)| { - let disk_farm_index = u8::try_from(disk_farm_index).expect( + .map(|(farm_index, farm)| { + let farm_index = u8::try_from(farm_index).expect( "More than 256 plots are not supported, this is checked above already; qed", ); let plotted_pieces = Arc::clone(&plotted_pieces); - let span = info_span!("farm", %disk_farm_index); - - single_disk_farm - .on_sector_update(Arc::new({ - let notifications = Arc::clone(¬ifications); - - move |(sector_index, sector_update)| { - notifications.call_simple(&FarmerNotification::SectorUpdate { - farm_index: disk_farm_index, - sector_index: *sector_index, - update: sector_update.clone(), - }); - } - })) - .detach(); - single_disk_farm - .on_farming_notification(Arc::new({ - let notifications = Arc::clone(¬ifications); - - move |notification| { - notifications.call_simple(&FarmerNotification::FarmingNotification { - farm_index: disk_farm_index, - notification: notification.clone(), - }); - } - })) - .detach(); + let span = info_span!("farm", %farm_index); + + farm.on_sector_update(Arc::new({ + let notifications = Arc::clone(¬ifications); + + move |(sector_index, sector_update)| { + notifications.call_simple(&FarmerNotification::SectorUpdate { + farm_index, + sector_index: *sector_index, + update: sector_update.clone(), + }); + } + })) + .detach(); + farm.on_farming_notification(Arc::new({ + let notifications = Arc::clone(¬ifications); + + move |notification| { + notifications.call_simple(&FarmerNotification::FarmingNotification { + farm_index, + notification: notification.clone(), + }); + } + })) + .detach(); // Collect newly plotted pieces let on_plotted_sector_callback = @@ -543,25 +567,24 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu .expect("Initial value was populated above; qed"); if let Some(old_plotted_sector) = &maybe_old_plotted_sector { - plotted_pieces.delete_sector(disk_farm_index, old_plotted_sector); + plotted_pieces.delete_sector(farm_index, old_plotted_sector); } - plotted_pieces.add_sector(disk_farm_index, plotted_sector); + plotted_pieces.add_sector(farm_index, plotted_sector); } }; - single_disk_farm - .on_sector_update(Arc::new(move |(_sector_index, sector_state)| { - if let SectorUpdate::Plotting(SectorPlottingDetails::Finished { - plotted_sector, - old_plotted_sector, - .. - }) = sector_state - { - on_plotted_sector_callback(plotted_sector, old_plotted_sector); - } - })) - .detach(); + farm.on_sector_update(Arc::new(move |(_sector_index, sector_state)| { + if let SectorUpdate::Plotting(SectorPlottingDetails::Finished { + plotted_sector, + old_plotted_sector, + .. + }) = sector_state + { + on_plotted_sector_callback(plotted_sector, old_plotted_sector); + } + })) + .detach(); - single_disk_farm.run() + farm.run() }) .collect::>() .boxed(); @@ -570,28 +593,81 @@ pub(super) async fn create_farmer(farmer_options: FarmerOptions) -> anyhow::Resu // event handlers drop(plotted_pieces); - let farm_fut = Box::pin( - async move { - while let Some(result) = single_disk_farms_stream.next().await { - match result { - Ok(id) => { - info!(%id, "Farm exited successfully"); - } - Err(error) => { - return Err(error); + let (action_sender, mut action_receiver) = mpsc::channel(1); + let (pause_plotting_sender, mut pause_plotting_receiver) = watch::channel(false); + + let pause_plotting_actions_fut = async move { + let mut thread_pools = Vec::with_capacity(plotting_thread_pools_count); + + loop { + if *pause_plotting_receiver.borrow_and_update() { + // Collect all managers so that plotting will be effectively paused + if thread_pools.len() < plotting_thread_pools_count { + thread_pools.push(plotting_thread_pool_manager.get_thread_pools().await); + // Allow to un-pause plotting quickly if user requests it + continue; + } + } else { + // Returns all thread pools back to the manager + thread_pools.clear(); + } + + if pause_plotting_receiver.changed().await.is_err() { + break; + } + } + }; + + let process_actions_fut = async move { + while let Some(action) = action_receiver.next().await { + match action { + FarmerAction::PausePlotting(pause_plotting) => { + if let Err(error) = pause_plotting_sender.send(pause_plotting) { + debug!(%error, "Failed to forward pause plotting"); } } } - anyhow::Ok(()) + } + anyhow::Ok(()) + }; + + let farms_fut = async move { + while let Some(result) = farms_stream.next().await { + match result { + Ok(id) => { + info!(%id, "Farm exited successfully"); + } + Err(error) => { + return Err(error); + } + } + } + anyhow::Ok(()) + }; + + let farmer_fut = Box::pin( + async move { + select! { + _ = pause_plotting_actions_fut.fuse() => { + Ok(()) + } + _ = process_actions_fut.fuse() => { + Ok(()) + } + result = farms_fut.fuse() => { + result + } + } } .in_current_span(), ); anyhow::Ok(Farmer { - farm_fut, + farmer_fut, farmer_cache_worker_fut, initial_farm_states, farm_during_initial_plotting, notifications, + action_sender, }) } diff --git a/src/backend/networking.rs b/src/backend/networking.rs index ce6c18c..f14c2e9 100644 --- a/src/backend/networking.rs +++ b/src/backend/networking.rs @@ -151,7 +151,7 @@ where } }; - plotted_pieces.read_piece(&piece_index)?.in_current_span() + plotted_pieces.read_piece(piece_index)?.in_current_span() }; let piece = read_piece_fut.await; diff --git a/src/frontend/running.rs b/src/frontend/running.rs index c5fa023..ca8d872 100644 --- a/src/frontend/running.rs +++ b/src/frontend/running.rs @@ -13,6 +13,12 @@ use relm4::prelude::*; use relm4_icons::icon_name; use subspace_core_primitives::BlockNumber; use subspace_runtime_primitives::{Balance, SSC}; +use tracing::debug; + +#[derive(Debug)] +pub struct RunningInit { + pub plotting_paused: bool, +} #[derive(Debug)] pub enum RunningInput { @@ -27,6 +33,12 @@ pub enum RunningInput { NodeNotification(NodeNotification), FarmerNotification(FarmerNotification), ToggleFarmDetails, + TogglePausePlotting, +} + +#[derive(Debug)] +pub enum RunningOutput { + PausePlotting(bool), } #[derive(Debug, Default)] @@ -44,13 +56,14 @@ pub struct RunningView { node_synced: bool, farmer_state: FarmerState, farms: FactoryHashMap, + plotting_paused: bool, } #[relm4::component(pub)] impl Component for RunningView { - type Init = (); + type Init = RunningInit; type Input = RunningInput; - type Output = (); + type Output = RunningOutput; type CommandOutput = (); view! { @@ -82,6 +95,13 @@ impl Component for RunningView { set_icon_name: icon_name::GRID_FILLED, set_tooltip: "Expand details about each farm", }, + gtk::ToggleButton { + connect_clicked => RunningInput::TogglePausePlotting, + set_active: model.plotting_paused, + set_has_frame: false, + set_icon_name: icon_name::PAUSE, + set_tooltip: "Pause plotting/replotting, note that currently encoding sectors will not be interrupted", + }, gtk::Box { set_halign: gtk::Align::End, set_hexpand: true, @@ -163,7 +183,7 @@ impl Component for RunningView { } fn init( - _init: Self::Init, + init: Self::Init, _root: Self::Root, _sender: ComponentSender, ) -> ComponentParts { @@ -177,6 +197,7 @@ impl Component for RunningView { node_synced: false, farmer_state: FarmerState::default(), farms, + plotting_paused: init.plotting_paused, }; let farms_box = model.farms.widget(); @@ -185,13 +206,13 @@ impl Component for RunningView { ComponentParts { model, widgets } } - fn update(&mut self, input: Self::Input, _sender: ComponentSender, _root: &Self::Root) { - self.process_input(input); + fn update(&mut self, input: Self::Input, sender: ComponentSender, _root: &Self::Root) { + self.process_input(input, sender); } } impl RunningView { - fn process_input(&mut self, input: RunningInput) { + fn process_input(&mut self, input: RunningInput, sender: ComponentSender) { match input { RunningInput::Initialize { best_block_number, @@ -217,6 +238,7 @@ impl RunningView { total_sectors: initial_farm_state.total_sectors_count, plotted_total_sectors: initial_farm_state.plotted_sectors_count, farm_during_initial_plotting, + plotting_paused: self.plotting_paused, }, ); } @@ -309,6 +331,17 @@ impl RunningView { RunningInput::ToggleFarmDetails => { self.farms.broadcast(FarmWidgetInput::ToggleFarmDetails); } + RunningInput::TogglePausePlotting => { + self.plotting_paused = !self.plotting_paused; + self.farms + .broadcast(FarmWidgetInput::PausePlotting(self.plotting_paused)); + if sender + .output(RunningOutput::PausePlotting(self.plotting_paused)) + .is_err() + { + debug!("Failed to send RunningOutput::TogglePausePlotting"); + } + } } } } diff --git a/src/frontend/running/farm.rs b/src/frontend/running/farm.rs index 90e638b..0ebc9b4 100644 --- a/src/frontend/running/farm.rs +++ b/src/frontend/running/farm.rs @@ -8,10 +8,10 @@ use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; use subspace_core_primitives::SectorIndex; -use subspace_farmer::single_disk_farm::farming::FarmingNotification; -use subspace_farmer::single_disk_farm::{ - FarmingError, SectorExpirationDetails, SectorPlottingDetails, SectorUpdate, +use subspace_farmer::farm::{ + FarmingNotification, SectorExpirationDetails, SectorPlottingDetails, SectorUpdate, }; +use subspace_farmer::single_disk_farm::FarmingError; use tracing::error; /// Experimentally found number that is good for default window size to not have horizontal scroll @@ -76,6 +76,7 @@ pub(super) struct FarmWidgetInit { pub(super) total_sectors: SectorIndex, pub(super) plotted_total_sectors: SectorIndex, pub(super) farm_during_initial_plotting: bool, + pub(super) plotting_paused: bool, } #[derive(Debug, Clone)] @@ -85,6 +86,7 @@ pub(super) enum FarmWidgetInput { update: SectorUpdate, }, FarmingNotification(FarmingNotification), + PausePlotting(bool), OpenFarmFolder, NodeSynced(bool), ToggleFarmDetails, @@ -105,6 +107,8 @@ pub(super) struct FarmWidget { sectors: HashMap, non_fatal_farming_error: Option>, farm_details: bool, + encoding_sectors: usize, + plotting_paused: bool, } #[relm4::factory(pub(super))] @@ -239,31 +243,51 @@ impl FactoryComponent for FarmWidget { match kind { PlottingKind::Initial => { - if self.farm_during_initial_plotting { - let farming = if self.is_node_synced { - "farming" + let initial_plotting = if self.plotting_paused { + if self.encoding_sectors > 0 { + "Pausing initial plotting" } else { - "not farming" - }; - format!( - "Initial plotting {:.2}%{}, {}", - progress, - plotting_speed, - farming - ) + "Paused initial plotting" + } } else { - format!( - "Initial plotting {:.2}%{}, not farming", - progress, - plotting_speed, - ) - } + "Initial plotting" + }; + let farming = if self.is_node_synced && self.farm_during_initial_plotting { + "farming" + } else { + "not farming" + }; + format!( + "{} {:.2}%{}, {}", + initial_plotting, + progress, + plotting_speed, + farming, + ) + }, + PlottingKind::Replotting => { + let replotting = if self.plotting_paused { + if self.encoding_sectors > 0 { + "Pausing replotting" + } else { + "Paused replotting" + } + } else { + "Replotting" + }; + let farming = if self.is_node_synced { + "farming" + } else { + "not farming" + }; + format!( + "{} {:.2}%{}, {}", + replotting, + progress, + plotting_speed, + farming, + ) }, - PlottingKind::Replotting => format!( - "Replotting {:.2}%{}, farming", - progress, - plotting_speed, - ), } }, }, @@ -336,6 +360,8 @@ impl FactoryComponent for FarmWidget { sectors: HashMap::from_iter((SectorIndex::MIN..).zip(sectors)), non_fatal_farming_error: None, farm_details: false, + encoding_sectors: 0, + plotting_paused: init.plotting_paused, } } @@ -377,9 +403,11 @@ impl FarmWidget { self.remove_sector_state(sector_index, SectorState::Downloading); } SectorPlottingDetails::Encoding => { + self.encoding_sectors += 1; self.update_sector_state(sector_index, SectorState::Encoding); } SectorPlottingDetails::Encoded(_) => { + self.encoding_sectors -= 1; self.remove_sector_state(sector_index, SectorState::Encoding); } SectorPlottingDetails::Writing => { @@ -423,6 +451,9 @@ impl FarmWidget { self.non_fatal_farming_error.replace(error); } }, + FarmWidgetInput::PausePlotting(plotting_paused) => { + self.plotting_paused = plotting_paused; + } FarmWidgetInput::OpenFarmFolder => { if let Err(error) = open::that_detached(&self.path) { error!(%error, path = %self.path.display(), "Failed to open farm folder"); diff --git a/src/main.rs b/src/main.rs index c776591..e2d0181 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,12 @@ mod backend; mod frontend; use crate::backend::config::RawConfig; +use crate::backend::farmer::FarmerAction; use crate::backend::{wipe, BackendAction, BackendNotification}; use crate::frontend::configuration::{ConfigurationInput, ConfigurationOutput, ConfigurationView}; use crate::frontend::loading::{LoadingInput, LoadingView}; use crate::frontend::new_version::NewVersion; -use crate::frontend::running::{RunningInput, RunningView}; +use crate::frontend::running::{RunningInit, RunningInput, RunningOutput, RunningView}; use clap::Parser; use duct::cmd; use file_rotate::compression::Compression; @@ -82,6 +83,7 @@ type PosTable = ChiaTable; enum AppInput { BackendNotification(BackendNotification), Configuration(ConfigurationOutput), + Running(RunningOutput), OpenLogFolder, OpenReconfiguration, ShowAboutDialog, @@ -432,7 +434,12 @@ impl AsyncComponent for App { .launch(root.clone()) .forward(sender.input_sender(), AppInput::Configuration); - let running_view = RunningView::builder().launch(()).detach(); + let running_view = RunningView::builder() + .launch(RunningInit { + // Not paused on start + plotting_paused: false, + }) + .forward(sender.input_sender(), AppInput::Running); let about_dialog = gtk::AboutDialog::builder() .title("About") @@ -534,6 +541,9 @@ impl AsyncComponent for App { self.process_configuration_output(configuration_output) .await; } + AppInput::Running(running_output) => { + self.process_running_output(running_output).await; + } AppInput::OpenReconfiguration => { self.menu_popover.hide(); if let Some(raw_config) = self.current_raw_config.clone() { @@ -697,6 +707,24 @@ impl App { } } + async fn process_running_output(&mut self, running_output: RunningOutput) { + match running_output { + RunningOutput::PausePlotting(pause_plotting) => { + if let Err(error) = self + .backend_action_sender + .send(BackendAction::Farmer(FarmerAction::PausePlotting( + pause_plotting, + ))) + .await + { + self.current_view = View::Error(anyhow::anyhow!( + "Failed to send pause plotting to backend: {error}" + )); + } + } + } + } + fn process_command(&mut self, input: AppCommandOutput) { match input { AppCommandOutput::BackendNotification(notification) => {