Implement adjust_priority to match Salvium C++ wallet2 behavior

Automatically downgrade fee priority from Normal (5x) to Low (1x)
  when the network isn't busy, saving users fees. Matches the C++
  wallet2::adjust_priority logic: check mempool backlog and recent
  block fullness (>80% threshold) before deciding.

  - Add FeePriority::Default variant that resolves at runtime
  - Add adjust_priority() in both FFI and CLI paths
  - Refactor CLI TxPipeline to use shared NodePool from AppContext
  - Wire adjustment into all transfer commands (FFI and CLI)
This commit is contained in:
Matt Hess
2026-02-28 14:34:25 +00:00
parent 0331a99283
commit 272fbfffde
11 changed files with 508 additions and 91 deletions
Generated
+284 -73
View File
@@ -108,6 +108,12 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "anyhow"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "argon2"
version = "0.5.3"
@@ -216,9 +222,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.19.1"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
name = "bytemuck"
@@ -289,9 +295,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.58"
version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806"
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
dependencies = [
"clap_builder",
"clap_derive",
@@ -299,9 +305,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.58"
version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2"
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
dependencies = [
"anstream",
"anstyle",
@@ -383,9 +389,9 @@ dependencies = [
[[package]]
name = "crypto-common"
version = "0.1.6"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"rand_core 0.6.4",
@@ -555,6 +561,12 @@ dependencies = [
"log",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
@@ -605,6 +617,12 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
@@ -665,9 +683,9 @@ dependencies = [
[[package]]
name = "generic-array"
version = "0.14.9"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
@@ -701,6 +719,19 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
"wasip3",
]
[[package]]
name = "ghash"
version = "0.5.1"
@@ -731,13 +762,28 @@ dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "hashlink"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown",
"hashbrown 0.14.5",
]
[[package]]
@@ -760,15 +806,15 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hidapi"
version = "2.6.4"
version = "2.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "565dd4c730b8f8b2c0fb36df6be12e5470ae10895ddcc4e9dcfbfb495de202b0"
checksum = "d1b71e1f4791fb9e93b9d7ee03d70b501ab48f6151432fbcadeabc30fe15396e"
dependencies = [
"cc",
"cfg-if",
"libc",
"pkg-config",
"windows-sys 0.48.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -979,6 +1025,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "idna"
version = "1.1.0"
@@ -1012,6 +1064,18 @@ dependencies = [
"num-traits",
]
[[package]]
name = "indexmap"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
"serde",
"serde_core",
]
[[package]]
name = "inout"
version = "0.1.4"
@@ -1051,9 +1115,9 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "jiff"
version = "0.2.20"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c867c356cc096b33f4981825ab281ecba3db0acefe60329f044c1789d94c6543"
checksum = "b3e3d65f018c6ae946ab16e80944b97096ed73c35b221d1c478a6c81d8f57940"
dependencies = [
"jiff-static",
"log",
@@ -1064,9 +1128,9 @@ dependencies = [
[[package]]
name = "jiff-static"
version = "0.2.20"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7946b4325269738f270bb55b3c19ab5c5040525f83fd625259422a9d25d9be5"
checksum = "a17c2b211d863c7fde02cbea8a3c1a439b98e109286554f2860bdded7ff83818"
dependencies = [
"proc-macro2",
"quote",
@@ -1075,9 +1139,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.85"
version = "0.3.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -1111,6 +1175,12 @@ dependencies = [
"spin",
]
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.182"
@@ -1147,9 +1217,9 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
version = "0.11.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "litemap"
@@ -1207,9 +1277,9 @@ dependencies = [
[[package]]
name = "ml-kem"
version = "0.2.2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcaee19a45f916d98f24a551cc9a2cdae705a040e66f3cbc4f3a282ea6a2e982"
checksum = "8de49b3df74c35498c0232031bb7e85f9389f913e2796169c8ab47a53993a18f"
dependencies = [
"hybrid-array",
"kem",
@@ -1407,9 +1477,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "pin-utils"
@@ -1478,6 +1548,16 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "primeorder"
version = "0.13.6"
@@ -1700,9 +1780,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.9"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "reqwest"
@@ -1820,9 +1900,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.1.3"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags 2.11.0",
"errno",
@@ -1833,9 +1913,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.36"
version = "0.23.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
dependencies = [
"once_cell",
"ring",
@@ -1880,7 +1960,7 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "salvium-cli"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"clap",
"dirs",
@@ -1902,7 +1982,7 @@ dependencies = [
[[package]]
name = "salvium-consensus"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"hex",
"salvium-types",
@@ -1914,7 +1994,7 @@ dependencies = [
[[package]]
name = "salvium-crypto"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"aes-gcm",
"argon2",
@@ -1940,7 +2020,7 @@ dependencies = [
[[package]]
name = "salvium-ffi"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"hex",
"log",
@@ -1958,7 +2038,7 @@ dependencies = [
[[package]]
name = "salvium-miner"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"clap",
"hex",
@@ -1973,7 +2053,7 @@ dependencies = [
[[package]]
name = "salvium-miner-gr"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"cc",
"clap",
@@ -1985,7 +2065,7 @@ dependencies = [
[[package]]
name = "salvium-miner-v2"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"cc",
"clap",
@@ -2000,7 +2080,7 @@ dependencies = [
[[package]]
name = "salvium-multisig"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"curve25519-dalek",
"hex",
@@ -2016,7 +2096,7 @@ dependencies = [
[[package]]
name = "salvium-rpc"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"base64",
"hex",
@@ -2031,7 +2111,7 @@ dependencies = [
[[package]]
name = "salvium-sync-bench"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"clap",
"env_logger",
@@ -2048,7 +2128,7 @@ dependencies = [
[[package]]
name = "salvium-tx"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"curve25519-dalek",
"getrandom 0.2.17",
@@ -2066,7 +2146,7 @@ dependencies = [
[[package]]
name = "salvium-types"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"hex",
"serde",
@@ -2076,7 +2156,7 @@ dependencies = [
[[package]]
name = "salvium-wallet"
version = "1.0.7-r013"
version = "1.0.7-r014"
dependencies = [
"aes-gcm",
"chacha20",
@@ -2297,9 +2377,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.116"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -2328,12 +2408,12 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.24.0"
version = "3.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
dependencies = [
"fastrand",
"getrandom 0.3.4",
"getrandom 0.4.1",
"once_cell",
"rustix",
"windows-sys 0.61.2",
@@ -2533,6 +2613,12 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "universal-hash"
version = "0.5.1"
@@ -2620,10 +2706,19 @@ dependencies = [
]
[[package]]
name = "wasm-bindgen"
version = "0.2.108"
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
dependencies = [
"cfg-if",
"once_cell",
@@ -2634,9 +2729,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.58"
version = "0.4.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f"
checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8"
dependencies = [
"cfg-if",
"futures-util",
@@ -2648,9 +2743,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.108"
version = "0.2.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -2658,9 +2753,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.108"
version = "0.2.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -2671,18 +2766,18 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.108"
version = "0.2.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-bindgen-test"
version = "0.3.58"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45649196a53b0b7a15101d845d44d2dda7374fc1b5b5e2bbf58b7577ff4b346d"
checksum = "6311c867385cc7d5602463b31825d454d0837a3aba7cdb5e56d5201792a3f7fe"
dependencies = [
"async-trait",
"cast",
@@ -2702,9 +2797,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.58"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f579cdd0123ac74b94e1a4a72bd963cf30ebac343f2df347da0b8df24cdebed2"
checksum = "67008cdde4769831958536b0f11b3bdd0380bde882be17fff9c2f34bb4549abd"
dependencies = [
"proc-macro2",
"quote",
@@ -2713,15 +2808,49 @@ dependencies = [
[[package]]
name = "wasm-bindgen-test-shared"
version = "0.2.108"
version = "0.2.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8145dd1593bf0fb137dbfa85b8be79ec560a447298955877804640e40c2d6ea"
checksum = "cfe29135b180b72b04c74aa97b2b4a2ef275161eff9a6c7955ea9eaedc7e1d4e"
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags 2.11.0",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "web-sys"
version = "0.3.85"
version = "0.3.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -2997,6 +3126,88 @@ name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags 2.11.0",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "writeable"
@@ -3029,18 +3240,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.39"
version = "0.8.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.39"
version = "0.8.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953"
dependencies = [
"proc-macro2",
"quote",
+1 -1
View File
@@ -20,7 +20,7 @@ members = [
]
[workspace.package]
version = "1.0.7-r013"
version = "1.0.7-r014"
edition = "2021"
license = "LicenseRef-Salvium-RS"
repository = "https://github.com/salvium/salvium-rs"
+2 -1
View File
@@ -264,7 +264,8 @@ pub async fn transfer_multisig(ctx: &AppContext, address: &str, amount_str: &str
println!(" Amount: {} SAL", format_sal_u64(amount));
println!();
let fee_priority = salvium_tx::fee::FeePriority::Normal;
let fee_priority =
tx_common::adjust_priority(salvium_tx::fee::FeePriority::Default, &ctx.pool).await;
let est_fee = salvium_tx::estimate_tx_fee(2, 2, 16, true, 0x04, fee_priority);
println!(" Estimated fee: {} SAL", format_sal_u64(est_fee));
+15 -2
View File
@@ -16,6 +16,7 @@ pub async fn transfer(ctx: &AppContext, address: &str, amount_str: &str, priorit
let parsed_addr = salvium_types::address::parse_address(address)
.map_err(|e| format!("invalid destination address: {}", e))?;
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
println!("Transfer:");
println!(" To: {}", address);
@@ -91,7 +92,8 @@ pub async fn stake(ctx: &AppContext, amount_str: &str) -> Result {
println!(" Amount: {} SAL", format_sal_u64(amount));
println!();
let fee_priority = salvium_tx::fee::FeePriority::Normal;
let fee_priority =
tx_common::adjust_priority(salvium_tx::fee::FeePriority::Default, &ctx.pool).await;
let est_fee = salvium_tx::estimate_tx_fee(2, 2, 16, true, 0x04, fee_priority);
println!(" Estimated fee: {} SAL", format_sal_u64(est_fee));
println!();
@@ -155,6 +157,7 @@ pub async fn burn(ctx: &AppContext, amount_str: &str, priority: &str) -> Result
let amount = parse_sal_amount(amount_str)?;
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
println!("Burn:");
println!(" Amount: {} SAL", format_sal_u64(amount));
@@ -221,6 +224,7 @@ pub async fn convert(
let amount = parse_sal_amount(amount_str)?;
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
println!("Convert:");
println!(" Amount: {} {}", format_sal_u64(amount), source_asset);
@@ -290,6 +294,7 @@ pub async fn audit(ctx: &AppContext, priority: &str) -> Result {
}
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
// Audit sweeps all funds back to self as a verifiable on-chain proof.
let balance = wallet.get_balance("SAL", 0)?;
@@ -360,6 +365,7 @@ pub async fn locked_transfer(
let parsed_addr = salvium_types::address::parse_address(address)
.map_err(|e| format!("invalid destination address: {}", e))?;
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
println!("Locked Transfer:");
println!(" To: {}", address);
@@ -421,6 +427,7 @@ pub async fn sweep_all(ctx: &AppContext, address: &str, priority: &str) -> Resul
let parsed_addr = salvium_types::address::parse_address(address)
.map_err(|e| format!("invalid destination address: {}", e))?;
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
let balance = wallet.get_balance("SAL", 0)?;
let unlocked: u64 = balance.unlocked_balance.parse().unwrap_or(0);
@@ -492,6 +499,7 @@ pub async fn sweep_below(
let parsed_addr = salvium_types::address::parse_address(address)
.map_err(|e| format!("invalid destination address: {}", e))?;
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
// Get outputs below threshold.
let query = salvium_crypto::storage::OutputQuery {
@@ -580,6 +588,7 @@ pub async fn sweep_single(
let parsed_addr = salvium_types::address::parse_address(address)
.map_err(|e| format!("invalid destination address: {}", e))?;
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
let output = wallet
.get_output(key_image)?
@@ -669,7 +678,8 @@ pub async fn sweep_unmixable(ctx: &AppContext) -> Result {
return Ok(());
}
let fee_priority = salvium_tx::fee::FeePriority::Normal;
let fee_priority =
tx_common::adjust_priority(salvium_tx::fee::FeePriority::Default, &ctx.pool).await;
let pipeline = TxPipeline::new(&wallet, ctx, fee_priority);
let est_fee = salvium_tx::estimate_tx_fee(unmixable.len(), 1, 16, true, 0x04, fee_priority);
@@ -724,6 +734,7 @@ pub async fn locked_sweep_all(
let parsed_addr = salvium_types::address::parse_address(address)
.map_err(|e| format!("invalid destination address: {}", e))?;
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
let balance = wallet.get_balance("SAL", 0)?;
let unlocked: u64 = balance.unlocked_balance.parse().unwrap_or(0);
@@ -784,6 +795,7 @@ pub async fn return_payment(ctx: &AppContext, tx_hash: &str, priority: &str) ->
}
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
// Look up the incoming TX to find the sender's info.
let query = salvium_crypto::storage::TxQuery {
@@ -888,6 +900,7 @@ pub async fn sweep_account(
let parsed_addr = salvium_types::address::parse_address(address)
.map_err(|e| format!("invalid destination address: {}", e))?;
let fee_priority = tx_common::parse_fee_priority(priority);
let fee_priority = tx_common::adjust_priority(fee_priority, &ctx.pool).await;
// Get outputs for the specific account (and optional subaddress filter).
let query = salvium_crypto::storage::OutputQuery {
+8 -1
View File
@@ -830,6 +830,7 @@ pub struct AppContext {
pub daemon_url: String,
pub extra_nodes: Vec<String>,
pub wallet_path: PathBuf,
pub pool: salvium_rpc::NodePool,
}
impl AppContext {
@@ -843,7 +844,13 @@ impl AppContext {
default_wallet_dir(&cli.network).join("wallet.db")
};
Self { network, daemon_url, extra_nodes: cli.nodes.clone(), wallet_path }
let pool = salvium_rpc::NodePool::new(salvium_rpc::PoolConfig {
network,
primary_url: Some(daemon_url.clone()),
..Default::default()
});
Self { network, daemon_url, extra_nodes: cli.nodes.clone(), wallet_path, pool }
}
}
+79 -9
View File
@@ -4,7 +4,7 @@
//! → sign → submit logic used by transfer, stake, burn, convert, sweep, etc.
use crate::AppContext;
use salvium_rpc::DaemonRpc;
use salvium_rpc::NodePool;
use salvium_wallet::Wallet;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
@@ -20,7 +20,7 @@ pub struct SignedResult {
/// Shared pipeline for building, signing, and optionally submitting transactions.
pub struct TxPipeline<'a> {
pub wallet: &'a Wallet,
pub daemon: DaemonRpc,
pub pool: NodePool,
pub fee_priority: salvium_tx::fee::FeePriority,
}
@@ -30,7 +30,7 @@ impl<'a> TxPipeline<'a> {
ctx: &AppContext,
fee_priority: salvium_tx::fee::FeePriority,
) -> Self {
Self { wallet, daemon: DaemonRpc::new(&ctx.daemon_url), fee_priority }
Self { wallet, pool: ctx.pool.clone(), fee_priority }
}
/// Select UTXOs for the given amount + fee, derive spend keys, and return
@@ -135,8 +135,8 @@ impl<'a> TxPipeline<'a> {
inputs: &[InputData],
) -> Result<Vec<salvium_tx::builder::PreparedInput>> {
println!("Fetching decoy data from daemon...");
let info = self.daemon.get_info().await?;
let dist = self.daemon.get_output_distribution(&[0], 0, info.height, true, "").await?;
let info = self.pool.get_info().await?;
let dist = self.pool.get_output_distribution(&[0], 0, info.height, true, "").await?;
let rct_offsets =
dist.first().ok_or("no output distribution returned from daemon")?.distribution.clone();
let decoy_selector = salvium_tx::DecoySelector::new(rct_offsets)
@@ -154,7 +154,7 @@ impl<'a> TxPipeline<'a> {
.iter()
.map(|&idx| salvium_rpc::daemon::OutputRequest { amount: 0, index: idx })
.collect();
let outs_info = self.daemon.get_outs(&requests, false, "").await?;
let outs_info = self.pool.get_outs(&requests, false, "").await?;
let mut ring_keys = Vec::with_capacity(ring_size);
let mut ring_commitments = Vec::with_capacity(ring_size);
@@ -199,8 +199,8 @@ impl<'a> TxPipeline<'a> {
println!("Submitting transaction...");
let result = self
.daemon
.send_raw_transaction(&tx_hex, false)
.pool
.send_raw_transaction_ex(&tx_hex, false, true, "SAL")
.await
.map_err(|e| format!("submission: {}", e))?;
@@ -245,9 +245,79 @@ pub fn hex_to_32(s: &str) -> std::result::Result<[u8; 32], Box<dyn std::error::E
pub fn parse_fee_priority(s: &str) -> salvium_tx::fee::FeePriority {
match s {
"low" => salvium_tx::fee::FeePriority::Low,
"normal" => salvium_tx::fee::FeePriority::Normal,
"high" => salvium_tx::fee::FeePriority::High,
"urgent" | "highest" => salvium_tx::fee::FeePriority::Highest,
_ => salvium_tx::fee::FeePriority::Normal,
_ => salvium_tx::fee::FeePriority::Default,
}
}
/// Adjust fee priority based on network conditions (matches wallet2::adjust_priority).
///
/// When priority is `Default` (user didn't explicitly choose), queries the daemon:
/// 1. If mempool has pending transactions -> Normal (5x)
/// 2. If recent blocks are >80% full -> Normal (5x)
/// 3. Otherwise -> Low (1x)
///
/// Explicit priorities (Low/Normal/High/Highest) pass through unchanged.
pub async fn adjust_priority(
priority: salvium_tx::fee::FeePriority,
pool: &NodePool,
) -> salvium_tx::fee::FeePriority {
use salvium_tx::fee::FeePriority;
if priority != FeePriority::Default {
return priority;
}
match try_adjust_priority(pool).await {
Ok(adjusted) => adjusted,
Err(e) => {
log::debug!("adjust_priority failed, using Normal: {e}");
FeePriority::Normal
}
}
}
async fn try_adjust_priority(
pool: &NodePool,
) -> std::result::Result<salvium_tx::fee::FeePriority, String> {
use salvium_tx::fee::FeePriority;
let info = pool.get_info().await.map_err(|e| e.to_string())?;
// 1. Mempool backlog -> Normal
if info.tx_pool_size > 0 {
log::info!("adjust_priority: mempool has {} txs, using Normal", info.tx_pool_size);
return Ok(FeePriority::Normal);
}
// 2. Block fullness check
let block_weight_limit = info
.extra
.get("block_weight_limit")
.and_then(|v| v.as_u64())
.ok_or("block_weight_limit not in get_info")?;
let full_reward_zone = block_weight_limit / 2;
if full_reward_zone == 0 {
return Ok(FeePriority::Normal);
}
let height = info.height;
if height < 10 {
return Ok(FeePriority::Normal);
}
let headers =
pool.get_block_headers_range(height - 10, height - 1).await.map_err(|e| e.to_string())?;
let weight_sum: u64 = headers.iter().map(|h| h.block_weight).sum();
let fullness_pct = 100 * weight_sum / (10 * full_reward_zone);
if fullness_pct > 80 {
log::info!("adjust_priority: blocks {fullness_pct}% full, using Normal");
Ok(FeePriority::Normal)
} else {
log::info!("adjust_priority: blocks {fullness_pct}% full, using Low");
Ok(FeePriority::Low)
}
}
+14
View File
@@ -148,6 +148,20 @@ pub unsafe extern "C" fn salvium_daemon_force_race(handle: *mut c_void) -> i32 {
})
}
/// Get per-node status as JSON.
///
/// Returns a JSON array with each node's health, latency, and failure info.
/// Caller must free with `salvium_string_free()`.
#[no_mangle]
pub unsafe extern "C" fn salvium_daemon_node_status(handle: *mut c_void) -> *mut c_char {
ffi_try_string(|| {
let dh = unsafe { borrow_handle::<DaemonHandle>(handle) }?;
let rt = crate::runtime();
let status = rt.block_on(dh.pool.get_node_status());
serde_json::to_string(&status).map_err(|e| e.to_string())
})
}
/// Close a daemon handle.
///
/// If the daemon is in use by a sync operation, this blocks until the sync
+68 -3
View File
@@ -73,7 +73,7 @@ struct SweepParams {
}
fn default_priority() -> String {
"normal".into()
"default".into()
}
fn default_ring_size() -> usize {
16
@@ -82,10 +82,70 @@ fn default_ring_size() -> usize {
fn parse_priority(s: &str) -> FeePriority {
match s.to_lowercase().as_str() {
"low" => FeePriority::Low,
"normal" | "default" => FeePriority::Normal,
"normal" => FeePriority::Normal,
"high" | "elevated" => FeePriority::High,
"highest" | "urgent" => FeePriority::Highest,
_ => FeePriority::Normal,
_ => FeePriority::Default,
}
}
/// Adjust fee priority based on network conditions (matches wallet2::adjust_priority).
///
/// When priority is `Default` (user didn't explicitly choose), queries the daemon:
/// 1. If mempool has pending transactions -> Normal (5x)
/// 2. If recent blocks are >80% full -> Normal (5x)
/// 3. Otherwise -> Low (1x)
///
/// Explicit priorities (Low/Normal/High/Highest) pass through unchanged.
async fn adjust_priority(priority: FeePriority, pool: &salvium_rpc::NodePool) -> FeePriority {
if priority != FeePriority::Default {
return priority;
}
match try_adjust_priority(pool).await {
Ok(adjusted) => adjusted,
Err(e) => {
log::debug!("adjust_priority failed, using Normal: {e}");
FeePriority::Normal
}
}
}
async fn try_adjust_priority(pool: &salvium_rpc::NodePool) -> Result<FeePriority, String> {
let info = pool.get_info().await.map_err(|e| e.to_string())?;
// 1. Mempool backlog -> Normal
if info.tx_pool_size > 0 {
log::info!("adjust_priority: mempool has {} txs, using Normal", info.tx_pool_size);
return Ok(FeePriority::Normal);
}
// 2. Block fullness check
let block_weight_limit = info
.extra
.get("block_weight_limit")
.and_then(|v| v.as_u64())
.ok_or("block_weight_limit not in get_info")?;
let full_reward_zone = block_weight_limit / 2;
if full_reward_zone == 0 {
return Ok(FeePriority::Normal);
}
let height = info.height;
if height < 10 {
return Ok(FeePriority::Normal);
}
let headers =
pool.get_block_headers_range(height - 10, height - 1).await.map_err(|e| e.to_string())?;
let weight_sum: u64 = headers.iter().map(|h| h.block_weight).sum();
let fullness_pct = 100 * weight_sum / (10 * full_reward_zone);
if fullness_pct > 80 {
log::info!("adjust_priority: blocks {fullness_pct}% full, using Normal");
Ok(FeePriority::Normal)
} else {
log::info!("adjust_priority: blocks {fullness_pct}% full, using Low");
Ok(FeePriority::Low)
}
}
@@ -130,6 +190,7 @@ pub unsafe extern "C" fn salvium_wallet_transfer(
let rt = crate::runtime();
rt.block_on(async {
let priority = adjust_priority(priority, &dh.pool).await;
let daemon = dh.pool.active_daemon().await;
do_transfer(&wh.wallet, &daemon, &params, priority).await
})
@@ -173,6 +234,7 @@ pub unsafe extern "C" fn salvium_wallet_stake(
let rt = crate::runtime();
rt.block_on(async {
let priority = adjust_priority(priority, &dh.pool).await;
let daemon = dh.pool.active_daemon().await;
do_stake(&wh.wallet, &daemon, &params, priority, false).await
})
@@ -209,6 +271,7 @@ pub unsafe extern "C" fn salvium_wallet_stake_dry_run(
let rt = crate::runtime();
rt.block_on(async {
let priority = adjust_priority(priority, &dh.pool).await;
let daemon = dh.pool.active_daemon().await;
do_stake(&wh.wallet, &daemon, &params, priority, true).await
})
@@ -254,6 +317,7 @@ pub unsafe extern "C" fn salvium_wallet_sweep(
let rt = crate::runtime();
rt.block_on(async {
let priority = adjust_priority(priority, &dh.pool).await;
let daemon = dh.pool.active_daemon().await;
do_sweep(&wh.wallet, &daemon, &params, priority).await
})
@@ -294,6 +358,7 @@ pub unsafe extern "C" fn salvium_wallet_transfer_dry_run(
let rt = crate::runtime();
rt.block_on(async {
let priority = adjust_priority(priority, &dh.pool).await;
let daemon = dh.pool.active_daemon().await;
do_transfer(&wh.wallet, &daemon, &params, priority).await
})
+1 -1
View File
@@ -27,7 +27,7 @@ pub mod wallet_rpc;
pub use client::RpcClient;
pub use daemon::DaemonRpc;
pub use error::RpcError;
pub use pool::{DistributedBatchResult, NodePool, PoolConfig};
pub use pool::{DistributedBatchResult, NodePool, NodeStatusInfo, PoolConfig};
pub use wallet_rpc::WalletRpc;
/// Seed nodes per network.
+31
View File
@@ -18,6 +18,18 @@ use tokio::sync::RwLock;
// Types
// ─────────────────────────────────────────────────────────────────────────────
/// Per-node status snapshot returned by [`NodePool::get_node_status()`].
#[derive(Debug, Clone, serde::Serialize)]
pub struct NodeStatusInfo {
pub url: String,
pub is_seed: bool,
pub is_active: bool,
pub is_healthy: bool,
pub latency_ms: Option<u64>,
pub last_probed_secs_ago: Option<u64>,
pub consecutive_failures: u32,
}
struct NodeState {
daemon: DaemonRpc,
url: String,
@@ -180,6 +192,25 @@ impl NodePool {
inner.nodes[inner.active_index].url.clone()
}
/// Return a snapshot of every node's health, latency, and failure status.
pub async fn get_node_status(&self) -> Vec<NodeStatusInfo> {
let inner = self.inner.read().await;
inner
.nodes
.iter()
.enumerate()
.map(|(idx, node)| NodeStatusInfo {
url: node.url.clone(),
is_seed: node.is_seed,
is_active: idx == inner.active_index,
is_healthy: node.is_healthy,
latency_ms: node.last_latency.map(|d| d.as_millis() as u64),
last_probed_secs_ago: node.last_probed.map(|i| i.elapsed().as_secs()),
consecutive_failures: node.consecutive_failures,
})
.collect()
}
// ── Racing ──────────────────────────────────────────────────────────
/// Check if enough time has elapsed since the last race; if so, run one.
+5
View File
@@ -17,6 +17,9 @@ pub const DEFAULT_RING_SIZE: usize = 16;
/// Fee priority levels.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FeePriority {
/// Unset — resolves to Low or Normal based on network load via `adjust_priority`.
/// Falls back to Normal (5x) if adjustment fails.
Default,
Low,
Normal,
High,
@@ -27,6 +30,7 @@ impl FeePriority {
/// Priority multiplier applied to the base fee.
pub fn multiplier(&self) -> u64 {
match self {
FeePriority::Default => 5, // safe fallback = Normal
FeePriority::Low => 1,
FeePriority::Normal => 5,
FeePriority::High => 25,
@@ -226,6 +230,7 @@ mod tests {
#[test]
fn test_fee_priority_multipliers() {
assert_eq!(FeePriority::Default.multiplier(), 5);
assert_eq!(FeePriority::Low.multiplier(), 1);
assert_eq!(FeePriority::Normal.multiplier(), 5);
assert_eq!(FeePriority::High.multiplier(), 25);