diff --git a/Cargo.lock b/Cargo.lock index caa4f5a194..d0ffc4c047 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,17 @@ dependencies = [ "subtle 2.6.1", ] +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -497,7 +508,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ - "ahash", + "ahash 0.8.12", "ark-ff 0.5.0", "ark-poly 0.5.0", "ark-serialize 0.5.0", @@ -732,7 +743,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ - "ahash", + "ahash 0.8.12", "ark-ff 0.5.0", "ark-serialize 0.5.0", "ark-std 0.5.0", @@ -1669,6 +1680,29 @@ dependencies = [ "piper", ] +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases 0.2.1", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "bounded-collections" version = "0.1.9" @@ -1955,6 +1989,28 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.24.0" @@ -4085,6 +4141,16 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "endian-cast" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f7a506e5de77a3db9e56fdbed17fa6f3b8d27ede81545dde96107c3d6a1d2" +dependencies = [ + "generic-array 1.3.5", + "typenum", +] + [[package]] name = "enum-as-inner" version = "0.6.1" @@ -5524,6 +5590,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "generic-array" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" +dependencies = [ + "rustversion", + "typenum", +] + [[package]] name = "gethostname" version = "0.2.3" @@ -5737,6 +5813,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] [[package]] name = "hashbrown" @@ -5744,7 +5823,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.12", ] [[package]] @@ -5753,7 +5832,7 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash", + "ahash 0.8.12", "allocator-api2", "serde", ] @@ -5775,6 +5854,8 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", "serde", ] @@ -6908,6 +6989,33 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "lencode" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83dc280ed78264020f986b2539e6a44e0720f98f66c99a48a2f52e4a441e99d8" +dependencies = [ + "endian-cast", + "generic-array 1.3.5", + "hashbrown 0.16.0", + "lencode-macros", + "newt-hype", + "ruint", + "zstd-safe 7.2.4", +] + +[[package]] +name = "lencode-macros" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c57df14b9005d1e4e8e56436e922e2c046ad0be55d7cfb062a303714857508" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "libc" version = "0.2.176" @@ -8210,6 +8318,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "newt-hype" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8b7b69b0eafaa88ec8dc9fe7c3860af0a147517e5207cfbd0ecd21cd7cde18" + [[package]] name = "nix" version = "0.26.4" @@ -13493,6 +13607,26 @@ dependencies = [ "cc", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quanta" version = "0.12.6" @@ -13601,6 +13735,29 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quoth" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d9da82a5dc3ff2fb2eee43d2b434fb197a9bf6a2a243850505b61584f888d2" +dependencies = [ + "quoth-macros", + "regex", + "rust_decimal", + "safe-string", +] + +[[package]] +name = "quoth-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58547202bec9896e773db7ef04b4d47c444f9c97bc4386f36e55718c347db440" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "r-efi" version = "5.3.0" @@ -13896,6 +14053,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "resolv-conf" version = "0.7.5" @@ -13950,6 +14116,35 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rlp" version = "0.5.2" @@ -14185,6 +14380,22 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rust_decimal" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f703d19852dbf87cbc513643fa81428361eb6940f1ac14fd58155d295a3eb0" +dependencies = [ + "arrayvec 0.7.6", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -14440,6 +14651,18 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "safe-bigmath" +version = "0.4.1" +source = "git+https://github.com/sam0x17/safe-bigmath#013c49984910e1c9a23289e8c85e7a856e263a02" +dependencies = [ + "lencode", + "num-bigint", + "num-integer", + "num-traits", + "quoth", +] + [[package]] name = "safe-math" version = "0.1.0" @@ -14459,6 +14682,12 @@ dependencies = [ "rustc_version 0.2.3", ] +[[package]] +name = "safe-string" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fc51f1e562058dee569383bfdb5a58752bfeb7fa7f0823f5c07c4c45381b5a" + [[package]] name = "safe_arch" version = "0.7.4" @@ -14881,7 +15110,7 @@ name = "sc-consensus-grandpa" version = "0.36.0" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=6a3ec6d0b125846a7606e9e27da01ffb9c4c2d05#6a3ec6d0b125846a7606e9e27da01ffb9c4c2d05" dependencies = [ - "ahash", + "ahash 0.8.12", "array-bytes 6.2.3", "async-trait", "dyn-clone", @@ -15184,7 +15413,7 @@ name = "sc-network-gossip" version = "0.51.0" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=6a3ec6d0b125846a7606e9e27da01ffb9c4c2d05#6a3ec6d0b125846a7606e9e27da01ffb9c4c2d05" dependencies = [ - "ahash", + "ahash 0.8.12", "futures", "futures-timer", "log", @@ -15897,7 +16126,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" dependencies = [ - "ahash", + "ahash 0.8.12", "cfg-if", "hashbrown 0.13.2", ] @@ -15961,6 +16190,12 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "sec1" version = "0.7.3" @@ -16349,9 +16584,20 @@ dependencies = [ name = "share-pool" version = "0.1.0" dependencies = [ + "approx", + "lencode", + "log", + "num-traits", + "parity-scale-codec", + "rand 0.8.5", + "rayon", + "safe-bigmath", "safe-math", + "scale-info", + "sp-arithmetic", "sp-std", "substrate-fixed", + "subtensor-macros", ] [[package]] @@ -16392,6 +16638,12 @@ dependencies = [ "wide", ] +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "similar" version = "2.7.0" @@ -17492,7 +17744,7 @@ name = "sp-trie" version = "40.0.0" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=6a3ec6d0b125846a7606e9e27da01ffb9c4c2d05#6a3ec6d0b125846a7606e9e27da01ffb9c4c2d05" dependencies = [ - "ahash", + "ahash 0.8.12", "foldhash 0.1.5", "hash-db", "hashbrown 0.15.5", @@ -18173,7 +18425,7 @@ dependencies = [ name = "subtensor-macros" version = "0.1.0" dependencies = [ - "ahash", + "ahash 0.8.12", "proc-macro2", "quote", "syn 2.0.106", @@ -18259,6 +18511,7 @@ dependencies = [ name = "subtensor-transaction-fee" version = "0.1.0" dependencies = [ + "approx", "frame-executive", "frame-support", "frame-system", @@ -19803,7 +20056,7 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c128c039340ffd50d4195c3f8ce31aac357f06804cfc494c8b9508d4b30dca4" dependencies = [ - "ahash", + "ahash 0.8.12", "hashbrown 0.14.5", "string-interner", ] @@ -21113,12 +21366,954 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "git+https://github.com/gztensor/zstd-safe#42cc34ef6abe5d35d982f6afefb5d7e4e69f5f18" +dependencies = [ + "zstd-sys", +] + [[package]] name = "zstd-sys" version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +source = "git+https://github.com/gztensor/zstd-sys#01e299b6ce8d08af5a3429f7ceb956f8355cf1aa" dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "binary-merkle-tree" +version = "16.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "cumulus-client-parachain-inherent" +version = "0.18.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "cumulus-primitives-core" +version = "0.19.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "cumulus-primitives-parachain-inherent" +version = "0.19.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "cumulus-primitives-proof-size-hostfunction" +version = "0.13.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "cumulus-primitives-storage-weight-reclaim" +version = "12.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "cumulus-relay-chain-interface" +version = "0.24.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "cumulus-test-relay-sproof-builder" +version = "0.20.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "fork-tree" +version = "13.0.1" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-benchmarking" +version = "41.0.1" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-benchmarking-cli" +version = "49.1.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-election-provider-solution-type" +version = "16.1.1" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-election-provider-support" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-executive" +version = "41.0.1" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-metadata-hash-extension" +version = "0.9.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-support" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-support-procedural" +version = "34.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-support-procedural-tools" +version = "13.0.1" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-support-procedural-tools-derive" +version = "12.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-system" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-system-benchmarking" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-system-rpc-runtime-api" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "frame-try-runtime" +version = "0.47.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-asset-conversion" +version = "23.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-asset-rate" +version = "20.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-aura" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-authority-discovery" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-authorship" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-babe" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-bags-list" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-balances" +version = "42.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-broker" +version = "0.20.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-election-provider-multi-phase" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-election-provider-support-benchmarking" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-fast-unstake" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-grandpa" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-identity" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-insecure-randomness-collective-flip" +version = "29.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-membership" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-message-queue" +version = "44.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-mmr" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-multisig" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-nomination-pools" +version = "39.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-nomination-pools-runtime-api" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-offences" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-preimage" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-proxy" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-root-testing" +version = "17.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-safe-mode" +version = "22.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-scheduler" +version = "42.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-session" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-staking" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-staking-reward-curve" +version = "12.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-staking-reward-fn" +version = "23.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-staking-runtime-api" +version = "27.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-sudo" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-timestamp" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-transaction-payment" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-transaction-payment-rpc" +version = "44.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-transaction-payment-rpc-runtime-api" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-treasury" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-utility" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "pallet-vesting" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-core-primitives" +version = "18.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-node-metrics" +version = "24.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-node-network-protocol" +version = "24.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-node-primitives" +version = "20.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-node-subsystem-types" +version = "24.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-overseer" +version = "24.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-parachain-primitives" +version = "17.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-primitives" +version = "19.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-runtime-common" +version = "20.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-runtime-metrics" +version = "21.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-runtime-parachains" +version = "20.0.2" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-sdk" +version = "2506.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-sdk-frame" +version = "0.10.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "polkadot-statement-table" +version = "20.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-allocator" +version = "32.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-authority-discovery" +version = "0.51.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-basic-authorship" +version = "0.50.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-block-builder" +version = "0.45.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-chain-spec" +version = "44.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-chain-spec-derive" +version = "12.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-cli" +version = "0.53.1" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-client-api" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-client-db" +version = "0.47.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-consensus" +version = "0.50.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-consensus-aura" +version = "0.51.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-consensus-babe" +version = "0.51.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-consensus-babe-rpc" +version = "0.51.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-consensus-epochs" +version = "0.50.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-consensus-grandpa" +version = "0.36.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-consensus-grandpa-rpc" +version = "0.36.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-consensus-manual-seal" +version = "0.52.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-consensus-slots" +version = "0.50.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-executor" +version = "0.43.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-executor-common" +version = "0.39.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-executor-polkavm" +version = "0.36.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-executor-wasmtime" +version = "0.39.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-informant" +version = "0.50.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-keystore" +version = "36.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-mixnet" +version = "0.21.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-network" +version = "0.51.1" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-network-common" +version = "0.49.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-network-gossip" +version = "0.51.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-network-light" +version = "0.50.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-network-sync" +version = "0.50.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-network-transactions" +version = "0.50.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-network-types" +version = "0.17.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-offchain" +version = "46.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-proposer-metrics" +version = "0.20.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-rpc" +version = "46.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-rpc-api" +version = "0.50.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-rpc-server" +version = "23.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-rpc-spec-v2" +version = "0.51.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-runtime-utilities" +version = "0.3.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-service" +version = "0.52.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-state-db" +version = "0.39.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-sysinfo" +version = "43.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-telemetry" +version = "29.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-tracing" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-tracing-proc-macro" +version = "11.1.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-transaction-pool" +version = "40.1.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-transaction-pool-api" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sc-utils" +version = "19.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "slot-range-helper" +version = "18.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-api" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-api-proc-macro" +version = "23.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-application-crypto" +version = "41.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-arithmetic" +version = "27.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-authority-discovery" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-block-builder" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-blockchain" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-consensus" +version = "0.43.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-consensus-aura" +version = "0.43.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-consensus-babe" +version = "0.43.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-consensus-grandpa" +version = "24.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-consensus-slots" +version = "0.43.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-core" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-crypto-ec-utils" +version = "0.16.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-crypto-hashing" +version = "0.1.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-crypto-hashing-proc-macro" +version = "0.1.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-database" +version = "10.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-debug-derive" +version = "14.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-externalities" +version = "0.30.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-genesis-builder" +version = "0.18.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-inherents" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-io" +version = "41.0.1" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-keyring" +version = "42.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-keystore" +version = "0.43.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-maybe-compressed-blob" +version = "11.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-metadata-ir" +version = "0.11.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-mixnet" +version = "0.15.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-mmr-primitives" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-npos-elections" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-offchain" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-panic-handler" +version = "13.0.2" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-rpc" +version = "35.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-runtime" +version = "42.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-runtime-interface" +version = "30.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-runtime-interface-proc-macro" +version = "19.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-session" +version = "39.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-staking" +version = "39.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-state-machine" +version = "0.46.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-statement-store" +version = "21.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-std" +version = "14.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-storage" +version = "22.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-timestamp" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-tracing" +version = "17.1.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-transaction-pool" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-transaction-storage-proof" +version = "37.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-trie" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-version" +version = "40.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-version-proc-macro" +version = "15.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-wasm-interface" +version = "22.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "sp-weights" +version = "32.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "staging-xcm" +version = "17.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "staging-xcm-builder" +version = "21.1.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "staging-xcm-executor" +version = "20.0.1" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "substrate-bip39" +version = "0.6.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "substrate-build-script-utils" +version = "11.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "substrate-frame-rpc-system" +version = "45.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "substrate-prometheus-endpoint" +version = "0.17.6" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "substrate-wasm-builder" +version = "27.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "tracing-gum" +version = "20.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "tracing-gum-proc-macro" +version = "5.0.0" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" + +[[patch.unused]] +name = "xcm-procedural" +version = "11.0.2" +source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8#a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" diff --git a/Cargo.toml b/Cargo.toml index 165d570e17..68152ba033 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,7 @@ pallet-subtensor-swap = { path = "pallets/swap", default-features = false } pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default-features = false } pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false } procedural-fork = { path = "support/procedural-fork", default-features = false } +safe-bigmath = { package = "safe-bigmath", default-features = false, git = "https://github.com/sam0x17/safe-bigmath" } safe-math = { path = "primitives/safe-math", default-features = false } share-pool = { path = "primitives/share-pool", default-features = false } subtensor-macros = { path = "support/macros", default-features = false } @@ -87,6 +88,7 @@ hex = { version = "0.4", default-features = false } hex-literal = "0.4.1" jsonrpsee = { version = "0.24.9", default-features = false } libsecp256k1 = { version = "0.7.2", default-features = false } +lencode = "0.1.6" log = { version = "0.4.21", default-features = false } memmap2 = "0.9.8" ndarray = { version = "0.16.1", default-features = false } @@ -315,3 +317,204 @@ pow-faucet = [] [patch.crates-io] w3f-bls = { git = "https://github.com/opentensor/bls", branch = "fix-no-std" } +zstd-sys = { git = "https://github.com/gztensor/zstd-sys" } +zstd-safe = { git = "https://github.com/gztensor/zstd-safe" } + +# Patches automatically generated with `diener`: +# `diener patch --target https://github.com/paritytech/polkadot-sdk --point-to-git https://github.com/opentensor/polkadot-sdk.git --point-to-git-commit 81fa2c54e94f824eba7dabe9dffd063481cb2d80 --crates-to-patch ../polkadot-sdk --ignore-unused` +# +# Using latest commit from `polkadot-stable2506-2-otf-patches`. +# +# View code changes here: +# +# +# NOTE: The Diener will patch unnecesarry crates while this is waiting to be merged: . +# You may install diener from `liamaharon:ignore-unused-flag` if you like in the meantime. +[patch."https://github.com/paritytech/polkadot-sdk"] +frame-support = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +binary-merkle-tree = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-core = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-crypto-hashing = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-crypto-hashing-proc-macro = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-debug-derive = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-externalities = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-storage = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-runtime-interface = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-runtime-interface-proc-macro = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-std = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-tracing = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-wasm-interface = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-io = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-keystore = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-state-machine = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-panic-handler = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-trie = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-runtime = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-application-crypto = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-arithmetic = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-weights = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-api = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-api-proc-macro = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-metadata-ir = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-version = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-version-proc-macro = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-block-builder = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-block-builder = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-inherents = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-blockchain = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-consensus = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-database = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-client-api = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +substrate-prometheus-endpoint = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-executor = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-executor-common = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-allocator = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-maybe-compressed-blob = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-executor-polkavm = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-executor-wasmtime = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +substrate-wasm-builder = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-tracing = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-tracing-proc-macro = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-rpc = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-executive = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-system = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-try-runtime = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-balances = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-benchmarking = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-support-procedural = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-support-procedural-tools = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-support-procedural-tools-derive = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-client-db = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-state-db = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-sdk-frame = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-system-benchmarking = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-system-rpc-runtime-api = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-consensus-aura = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-consensus-slots = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-timestamp = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-consensus-grandpa = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-genesis-builder = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-keyring = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-offchain = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-session = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-staking = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-transaction-pool = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-sdk = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +cumulus-primitives-core = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-core-primitives = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-parachain-primitives = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-primitives = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-authority-discovery = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +staging-xcm = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +xcm-procedural = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +cumulus-primitives-parachain-inherent = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +cumulus-primitives-proof-size-hostfunction = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-message-queue = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-runtime-parachains = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-authority-discovery = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-session = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-timestamp = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-authorship = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-babe = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-consensus-babe = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-election-provider-support = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-election-provider-solution-type = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-npos-elections = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-offences = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-staking = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-bags-list = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-staking-reward-curve = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-broker = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-mmr = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-mmr-primitives = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-runtime-metrics = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +staging-xcm-executor = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-keystore = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +staging-xcm-builder = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-asset-conversion = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-transaction-payment = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-grandpa = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-sudo = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-vesting = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-runtime-common = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-asset-rate = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-election-provider-multi-phase = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-election-provider-support-benchmarking = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-fast-unstake = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-identity = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-staking-reward-fn = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-treasury = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-utility = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-root-testing = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +slot-range-helper = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +cumulus-primitives-storage-weight-reclaim = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-aura = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +cumulus-test-relay-sproof-builder = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-chain-spec = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-chain-spec-derive = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-network = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-network-common = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-network-types = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-utils = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-telemetry = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-cli = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-mixnet = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-transaction-pool-api = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-mixnet = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-service = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-consensus = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-informant = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-network-sync = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +fork-tree = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-network-light = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-network-transactions = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-rpc = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-rpc-api = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-statement-store = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-transaction-pool = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-rpc-server = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-rpc-spec-v2 = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-sysinfo = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-transaction-storage-proof = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +cumulus-relay-chain-interface = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-overseer = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +tracing-gum = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +tracing-gum-proc-macro = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-node-metrics = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-node-primitives = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-node-subsystem-types = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-node-network-protocol = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-authority-discovery = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +polkadot-statement-table = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-benchmarking-cli = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +cumulus-client-parachain-inherent = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-runtime-utilities = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +frame-metadata-hash-extension = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-nomination-pools = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-membership = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-multisig = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-nomination-pools-runtime-api = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-preimage = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-proxy = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-scheduler = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-staking-runtime-api = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-offchain = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-consensus-babe = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-consensus-epochs = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-consensus-slots = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-transaction-payment-rpc = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-consensus-babe-rpc = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-network-gossip = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-consensus-grandpa = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-consensus-grandpa-rpc = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +substrate-frame-rpc-system = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-basic-authorship = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-proposer-metrics = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +substrate-build-script-utils = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-consensus-aura = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-insecure-randomness-collective-flip = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +pallet-safe-mode = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sc-consensus-manual-seal = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +sp-crypto-ec-utils = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } +substrate-bip39 = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "a584a577eeaf31e3f1a65e91b0e0b41f0356f7c8" } diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 46ea71fc39..5c9729250f 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -435,8 +435,8 @@ impl pallet_subtensor_swap::Config for Test { type SubnetInfo = SubtensorModule; type BalanceOps = SubtensorModule; type ProtocolId = SwapProtocolId; - type TaoReserve = TaoCurrencyReserve; - type AlphaReserve = AlphaCurrencyReserve; + type TaoReserve = TaoBalanceReserve; + type AlphaReserve = AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; diff --git a/common/src/lib.rs b/common/src/lib.rs index a143c27824..70fa42c32b 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -259,7 +259,7 @@ pub trait BalanceOps { hotkey: &AccountId, netuid: NetUid, alpha: AlphaBalance, - ) -> Result; + ) -> Result<(), DispatchError>; } /// Allows to query the current block author diff --git a/docs/rust-setup.md b/docs/rust-setup.md index a3e4a952d7..a8f5cf46fb 100644 --- a/docs/rust-setup.md +++ b/docs/rust-setup.md @@ -67,7 +67,21 @@ Open the Terminal application and execute the following commands: # Make sure Homebrew is up-to-date, install protobuf and openssl brew update -brew install protobuf openssl +brew install protobuf openssl llvm@16 +``` + +Also, add the following lines at the end of your ~/.zshrc: + +``` +# LLVM 16 from Homebrew +export PATH="/opt/homebrew/opt/llvm@16/bin:$PATH" + +export CC="/opt/homebrew/opt/llvm@16/bin/clang" +export CXX="/opt/homebrew/opt/llvm@16/bin/clang++" +export LIBCLANG_PATH="/opt/homebrew/opt/llvm@16/lib/libclang.dylib" + +export LDFLAGS="-L/opt/homebrew/opt/llvm@16/lib" +export CPPFLAGS="-I/opt/homebrew/opt/llvm@16/include" ``` ### Windows diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 7f882fcf9c..66c4287be0 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1149,7 +1149,7 @@ pub mod pallet { /// The extrinsic will call the Subtensor pallet to set the minimum stake required for nominators. #[pallet::call_index(43)] #[pallet::weight(Weight::from_parts(28_050_000, 6792) - .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)))] pub fn sudo_set_nominator_min_required_stake( origin: OriginFor, diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 35f8f6f784..f744a4ea39 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -340,8 +340,8 @@ impl pallet_subtensor_swap::Config for Test { type SubnetInfo = SubtensorModule; type BalanceOps = SubtensorModule; type ProtocolId = SwapProtocolId; - type TaoReserve = pallet_subtensor::TaoCurrencyReserve; - type AlphaReserve = pallet_subtensor::AlphaCurrencyReserve; + type TaoReserve = pallet_subtensor::TaoBalanceReserve; + type AlphaReserve = pallet_subtensor::AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index a030607995..a7d41b7f3a 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -33,6 +33,7 @@ impl Pallet { Self::run_auto_claim_root_divs(last_block_hash); // --- 9. Populate root coldkey maps. Self::populate_root_coldkey_staking_maps(); + Self::populate_root_coldkey_staking_maps_v2(); // Return ok. Ok(()) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index d25f7ce170..460a754d45 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -515,7 +515,7 @@ impl Pallet { log::debug!( "owner_hotkey: {owner_hotkey:?} owner_coldkey: {owner_coldkey:?}, owner_cut: {owner_cut:?}" ); - let real_owner_cut = Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &owner_hotkey, &owner_coldkey, netuid, @@ -523,7 +523,7 @@ impl Pallet { ); // If the subnet is leased, notify the lease logic that owner cut has been distributed. if let Some(lease_id) = SubnetUidToLeaseId::::get(netuid) { - Self::distribute_leased_network_dividends(lease_id, real_owner_cut); + Self::distribute_leased_network_dividends(lease_id, owner_cut); } } @@ -617,7 +617,7 @@ impl Pallet { root_alpha = root_alpha.saturating_sub(alpha_take); // Give the validator their take. log::debug!("hotkey: {hotkey:?} alpha_take: {alpha_take:?}"); - let _validator_stake = Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &Owner::::get(hotkey.clone()), netuid, diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index f27019989a..399fcdc6e7 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -92,6 +92,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use pallet_drand::types::RoundNumber; use runtime_common::prod_or_fast; + use share_pool::SafeFloatSerializable; use sp_core::{ConstU32, H160, H256}; use sp_runtime::traits::{Dispatchable, TrailingZeroInput}; use sp_std::collections::btree_map::BTreeMap; @@ -1433,11 +1434,41 @@ pub mod pallet { ValueQuery, >; + /// DMAP ( hot, netuid ) --> total_alpha_shares | Returns the number of alpha shares for a hotkey on a subnet, stores bigmath vector. + #[pallet::storage] + pub type TotalHotkeySharesV2 = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, // hot + Identity, + NetUid, // subnet + SafeFloatSerializable, // Hotkey shares in unlimited precision + ValueQuery, + >; + + /// --- NMAP ( hot, cold, netuid ) --> alpha | Returns the alpha shares for a hotkey, coldkey, netuid triplet, stores bigmath vector. + #[pallet::storage] + pub type AlphaV2 = StorageNMap< + _, + ( + NMapKey, // hot + NMapKey, // cold + NMapKey, // subnet + ), + SafeFloatSerializable, // Shares in unlimited precision + ValueQuery, + >; + /// Contains last Alpha storage map key to iterate (check first) #[pallet::storage] pub type AlphaMapLastKey = StorageValue<_, Option>, ValueQuery, DefaultAlphaIterationLastKey>; + /// Contains last AlphaV2 storage map key to iterate (check first) + #[pallet::storage] + pub type AlphaV2MapLastKey = + StorageValue<_, Option>, ValueQuery, DefaultAlphaIterationLastKey>; + /// --- MAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet. #[pallet::storage] pub type TokenSymbol = @@ -2493,9 +2524,9 @@ use sp_std::vec::Vec; use subtensor_macros::freeze_struct; #[derive(Clone)] -pub struct TaoCurrencyReserve(PhantomData); +pub struct TaoBalanceReserve(PhantomData); -impl TokenReserve for TaoCurrencyReserve { +impl TokenReserve for TaoBalanceReserve { #![deny(clippy::expect_used)] fn reserve(netuid: NetUid) -> TaoBalance { SubnetTAO::::get(netuid).saturating_add(SubnetTaoProvided::::get(netuid)) @@ -2511,9 +2542,9 @@ impl TokenReserve for TaoCurrencyReserve { } #[derive(Clone)] -pub struct AlphaCurrencyReserve(PhantomData); +pub struct AlphaBalanceReserve(PhantomData); -impl TokenReserve for AlphaCurrencyReserve { +impl TokenReserve for AlphaBalanceReserve { #![deny(clippy::expect_used)] fn reserve(netuid: NetUid) -> AlphaBalance { SubnetAlphaIn::::get(netuid).saturating_add(SubnetAlphaInProvided::::get(netuid)) @@ -2529,9 +2560,9 @@ impl TokenReserve for AlphaCurrencyReserve { } pub type GetAlphaForTao = - subtensor_swap_interface::GetAlphaForTao, AlphaCurrencyReserve>; + subtensor_swap_interface::GetAlphaForTao, AlphaBalanceReserve>; pub type GetTaoForAlpha = - subtensor_swap_interface::GetTaoForAlpha, TaoCurrencyReserve>; + subtensor_swap_interface::GetTaoForAlpha, TaoBalanceReserve>; impl> subtensor_runtime_common::SubnetInfo for Pallet @@ -2619,20 +2650,25 @@ impl> hotkey: &T::AccountId, netuid: NetUid, alpha: AlphaBalance, - ) -> Result { + ) -> Result<(), DispatchError> { ensure!( Self::hotkey_account_exists(hotkey), Error::::HotKeyAccountNotExists ); + ensure!( + Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid) >= alpha, + Error::::InsufficientBalance + ); + // Decrese alpha out counter SubnetAlphaOut::::mutate(netuid, |total| { *total = total.saturating_sub(alpha); }); - Ok(Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, coldkey, netuid, alpha, - )) + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha); + + Ok(()) } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index e6c3aa5e78..78e8becb12 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -276,8 +276,6 @@ mod errors { VotingPowerTrackingNotEnabled, /// Invalid voting power EMA alpha value (must be <= 10^18). InvalidVotingPowerEmaAlpha, - /// Unintended precision loss when unstaking alpha - PrecisionLoss, /// Deprecated call. Deprecated, /// "Add stake and burn" exceeded the operation rate limit diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 4363eb3f35..ad62ec7c46 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -528,5 +528,18 @@ mod events { /// Alpha burned alpha: AlphaBalance, }, + + /// Transaction fee was paid in Alpha. + /// + /// Emitted in addition to `TransactionFeePaid` when the fee payment path is Alpha. + /// `alpha_fee` is the exact Alpha amount deducted. + TransactionFeePaidWithAlpha { + /// Account that paid the transaction fee. + who: T::AccountId, + /// Exact fee deducted in Alpha units. + alpha_fee: AlphaBalance, + /// Resulting swapped TAO amount + tao_amount: TaoBalance, + }, } } diff --git a/pallets/subtensor/src/macros/genesis.rs b/pallets/subtensor/src/macros/genesis.rs index d4e269391e..e318273732 100644 --- a/pallets/subtensor/src/macros/genesis.rs +++ b/pallets/subtensor/src/macros/genesis.rs @@ -4,6 +4,7 @@ use frame_support::pallet_macros::pallet_section; /// This can later be imported into the pallet using [`import_section`]. #[pallet_section] mod genesis { + use share_pool::SafeFloat; use sp_core::crypto::Pair; use sp_core::sr25519::Pair as Sr25519Pair; @@ -91,20 +92,20 @@ mod genesis { SubnetOwner::::insert(netuid, hotkey.clone()); SubnetLocked::::insert(netuid, TaoBalance::from(1)); LargestLocked::::insert(netuid, 1); - Alpha::::insert( + AlphaV2::::insert( // Lock the initial funds making this key the owner. (hotkey.clone(), hotkey.clone(), netuid), - U64F64::saturating_from_num(1_000_000_000), + SafeFloatSerializable::from(&SafeFloat::from(1_000_000_000)), ); TotalHotkeyAlpha::::insert( hotkey.clone(), netuid, AlphaBalance::from(1_000_000_000), ); - TotalHotkeyShares::::insert( + TotalHotkeySharesV2::::insert( hotkey.clone(), netuid, - U64F64::saturating_from_num(1_000_000_000), + SafeFloatSerializable::from(&SafeFloat::from(1_000_000_000)), ); SubnetAlphaOut::::insert(netuid, AlphaBalance::from(1_000_000_000)); let mut staking_hotkeys = StakingHotkeys::::get(hotkey.clone()); diff --git a/pallets/subtensor/src/rpc_info/delegate_info.rs b/pallets/subtensor/src/rpc_info/delegate_info.rs index 61c0286e7a..b69ea6f778 100644 --- a/pallets/subtensor/src/rpc_info/delegate_info.rs +++ b/pallets/subtensor/src/rpc_info/delegate_info.rs @@ -65,8 +65,8 @@ impl Pallet { alpha_share_pools.push(alpha_share_pool); } - for ((nominator, netuid), alpha_stake) in Alpha::::iter_prefix((delegate.clone(),)) { - if alpha_stake == 0 { + for (nominator, netuid, alpha_stake) in Self::alpha_iter_single_prefix(&delegate) { + if alpha_stake.is_zero() { continue; } @@ -166,7 +166,7 @@ impl Pallet { )> = Vec::new(); for delegate in as IterableStorageMap>::iter_keys() { // Staked to this delegate, so add to list - for (netuid, _) in Alpha::::iter_prefix((delegate.clone(), delegatee.clone())) { + for (netuid, _) in Self::alpha_iter_prefix((&delegate, &delegatee)) { let delegate_info = Self::get_delegate_by_existing_account(delegate.clone(), true); delegates.push(( delegate_info, diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index bfe34b2ab0..29759fd6eb 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -1,3 +1,4 @@ +use alloc::collections::BTreeMap; use frame_support::traits::{ Imbalance, tokens::{ @@ -6,6 +7,7 @@ use frame_support::traits::{ }, }; use safe_math::*; +use share_pool::SafeFloat; use substrate_fixed::types::U96F32; use subtensor_runtime_common::{NetUid, TaoBalance}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -68,7 +70,7 @@ impl Pallet { hotkeys .iter() .map(|hotkey| { - Alpha::::iter_prefix((hotkey, coldkey)) + Self::alpha_iter_prefix((hotkey, coldkey)) .map(|(netuid, _)| { let alpha_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, netuid, @@ -101,7 +103,7 @@ impl Pallet { hotkeys .iter() .map(|hotkey| { - Alpha::::iter_prefix((hotkey, coldkey)) + Self::alpha_iter_prefix((hotkey, coldkey)) .map(|(netuid_on_storage, _)| { if netuid_on_storage == netuid { let alpha_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -261,7 +263,7 @@ impl Pallet { /// used with caution. pub fn clear_small_nominations() { // Loop through all staking accounts to identify and clear nominations below the minimum stake. - for ((hotkey, coldkey, netuid), _) in Alpha::::iter() { + for ((hotkey, coldkey, netuid), _) in Self::alpha_iter() { Self::clear_small_nomination_if_required(&hotkey, &coldkey, netuid); } } @@ -413,7 +415,158 @@ impl Pallet { } } + // Same thing as populate_root_coldkey_staking_maps, but for AlphaV2 + // TODO: Remove this function and AlphaV2MapLastKey when slow migration is finished + pub fn populate_root_coldkey_staking_maps_v2() { + // Get starting key for the batch. Get the first key if we restart the process. + let mut new_starting_raw_key = AlphaV2MapLastKey::::get(); + let mut starting_key = None; + if new_starting_raw_key.is_none() { + starting_key = AlphaV2::::iter_keys().next(); + new_starting_raw_key = starting_key.as_ref().map(AlphaV2::::hashed_key_for); + } + + if let Some(starting_raw_key) = new_starting_raw_key { + // Get the key batch + let mut keys = AlphaV2::::iter_keys_from(starting_raw_key) + .take(ALPHA_MAP_BATCH_SIZE) + .collect::>(); + + // New iteration: insert the starting key in the batch if it's a new iteration + // iter_keys_from() skips the starting key + if let Some(starting_key) = starting_key { + if keys.len() == ALPHA_MAP_BATCH_SIZE { + keys.remove(keys.len().saturating_sub(1)); + } + keys.insert(0, starting_key); + } + + let mut new_starting_key = None; + let new_iteration = keys.len() < ALPHA_MAP_BATCH_SIZE; + + // Check and remove alphas if necessary + for key in keys { + let (_, coldkey, netuid) = key.clone(); + + if netuid == NetUid::ROOT { + Self::maybe_add_coldkey_index(&coldkey); + } + + new_starting_key = Some(AlphaV2::::hashed_key_for(key)); + } + + // Restart the process if it's the last batch + if new_iteration { + new_starting_key = None; + } + + AlphaV2MapLastKey::::put(new_starting_key); + } + } + pub fn burn_subnet_alpha(_netuid: NetUid, _amount: AlphaBalance) { // Do nothing; TODO: record burned alpha in a tracker } + + /// Several alpha iteration helpers that merge key space from Alpha and AlphaV2 maps + pub fn alpha_iter() -> impl Iterator { + // Old Alpha shares format: U64F64 -> SafeFloat + let legacy = Alpha::::iter().map(|(key, val_u64f64)| { + let sf: SafeFloat = val_u64f64.into(); + (key, sf) + }); + + // New Alpha shares format: SafeFloatSerializable -> SafeFloat + let v2 = AlphaV2::::iter().map(|(key, val_sf_ser)| { + let sf: SafeFloat = SafeFloat::from(&val_sf_ser); + (key, sf) + }); + + // Merge and prefer v2 on duplicates + let merged: BTreeMap<_, SafeFloat> = + legacy + .chain(v2) + .fold(BTreeMap::new(), |mut acc, (key, val)| { + acc.entry(key) + .and_modify(|existing| { + *existing = val.clone(); + }) + .or_insert(val); + acc + }); + + merged.into_iter() + } + + pub fn alpha_iter_prefix( + prefix: (&T::AccountId, &T::AccountId), + ) -> impl Iterator + where + T::AccountId: Clone, + { + // Old Alpha shares format: U64F64 -> SafeFloat + let legacy = Alpha::::iter_prefix(prefix).map(|(netuid, val_u64f64)| { + let sf: SafeFloat = val_u64f64.into(); + (netuid, sf) + }); + + // New Alpha shares format: SafeFloatSerializable -> SafeFloat + let v2 = AlphaV2::::iter_prefix(prefix).map(|(netuid, val_sf_ser)| { + let sf: SafeFloat = SafeFloat::from(&val_sf_ser); + (netuid, sf) + }); + + // Merge by netuid and sum SafeFloat values + let merged: BTreeMap = + legacy + .chain(v2) + .fold(BTreeMap::new(), |mut acc, (netuid, sf)| { + acc.entry(netuid) + .and_modify(|existing| { + *existing = sf.clone(); + }) + .or_insert(sf); + acc + }); + + merged + .into_iter() + .filter(|(_, alpha_share)| !alpha_share.is_zero()) + } + + pub fn alpha_iter_single_prefix( + prefix: &T::AccountId, + ) -> impl Iterator + where + T::AccountId: Clone, + { + // Old Alpha shares format: U64F64 -> SafeFloat + let legacy = + Alpha::::iter_prefix((prefix.clone(),)).map(|((coldkey, netuid), val_u64f64)| { + let sf: SafeFloat = val_u64f64.into(); + ((coldkey, netuid), sf) + }); + + // New Alpha shares format: SafeFloatSerializable -> SafeFloat + let v2 = AlphaV2::::iter_prefix((prefix,)).map(|((coldkey, netuid), val_sf_ser)| { + let sf: SafeFloat = SafeFloat::from(&val_sf_ser); + ((coldkey, netuid), sf) + }); + + let merged: BTreeMap<(T::AccountId, NetUid), SafeFloat> = + legacy + .chain(v2) + .fold(BTreeMap::new(), |mut acc, (key, sf)| { + acc.entry(key) + .and_modify(|existing| { + *existing = sf.clone(); + }) + .or_insert(sf); + acc + }); + + merged + .into_iter() + .map(|((coldkey, netuid), sf)| (coldkey, netuid, sf)) + } } diff --git a/pallets/subtensor/src/staking/recycle_alpha.rs b/pallets/subtensor/src/staking/recycle_alpha.rs index 08627274aa..cd7d92e67e 100644 --- a/pallets/subtensor/src/staking/recycle_alpha.rs +++ b/pallets/subtensor/src/staking/recycle_alpha.rs @@ -51,21 +51,12 @@ impl Pallet { ); // Deduct from the coldkey's stake. - let actual_alpha_decrease = Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, &coldkey, netuid, amount, - ); - - ensure!(actual_alpha_decrease <= amount, Error::::PrecisionLoss); + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid, amount); // Recycle means we should decrease the alpha issuance tracker. - Self::recycle_subnet_alpha(netuid, actual_alpha_decrease); + Self::recycle_subnet_alpha(netuid, amount); - Self::deposit_event(Event::AlphaRecycled( - coldkey, - hotkey, - actual_alpha_decrease, - netuid, - )); + Self::deposit_event(Event::AlphaRecycled(coldkey, hotkey, amount, netuid)); Ok(()) } @@ -118,21 +109,12 @@ impl Pallet { ); // Deduct from the coldkey's stake. - let actual_alpha_decrease = Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, &coldkey, netuid, amount, - ); - - ensure!(actual_alpha_decrease <= amount, Error::::PrecisionLoss); + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid, amount); - Self::burn_subnet_alpha(netuid, actual_alpha_decrease); + Self::burn_subnet_alpha(netuid, amount); // Deposit event - Self::deposit_event(Event::AlphaBurned( - coldkey, - hotkey, - actual_alpha_decrease, - netuid, - )); + Self::deposit_event(Event::AlphaBurned(coldkey, hotkey, amount, netuid)); Ok(()) } diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 1a5238aeb3..75733aeb18 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -483,13 +483,13 @@ impl Pallet { let mut stakers: Vec<(T::AccountId, T::AccountId, u128)> = Vec::new(); let mut total_alpha_value_u128: u128 = 0; - let hotkeys_in_subnet: Vec = TotalHotkeyAlpha::::iter() - .filter(|(_, this_netuid, _)| *this_netuid == netuid) - .map(|(hot, _, _)| hot.clone()) + let hotkeys_in_subnet: Vec = TotalHotkeyAlpha::::iter_keys() + .filter(|(_, this_netuid)| *this_netuid == netuid) + .map(|(hot, _)| hot.clone()) .collect::>(); for hot in hotkeys_in_subnet.iter() { - for ((cold, this_netuid), share_u64f64) in Alpha::::iter_prefix((hot,)) { + for (cold, this_netuid, share_u64f64) in Self::alpha_iter_single_prefix(hot) { if this_netuid != netuid { continue; } @@ -501,7 +501,7 @@ impl Pallet { // Fallback: if pool uninitialized, treat raw Alpha share as value. let val_u64 = if actual_val_u64 == 0 { - share_u64f64.saturating_to_num::() + u64::from(share_u64f64) } else { actual_val_u64 }; @@ -572,12 +572,14 @@ impl Pallet { // 7) Destroy all α-in/α-out state for this subnet. // 7.a) Remove every (hot, cold, netuid) α entry. for (hot, cold) in keys_to_remove { - Alpha::::remove((hot, cold, netuid)); + Alpha::::remove((hot.clone(), cold.clone(), netuid)); + AlphaV2::::remove((hot, cold, netuid)); } // 7.b) Clear share‑pool totals for each hotkey on this subnet. for hot in hotkeys_in_subnet { TotalHotkeyAlpha::::remove(&hot, netuid); TotalHotkeyShares::::remove(&hot, netuid); + TotalHotkeySharesV2::::remove(&hot, netuid); } // 7.c) Remove α‑in/α‑out counters (fully destroyed). SubnetAlphaIn::::remove(netuid); diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index a4987bbd74..e5badc70c4 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1,8 +1,8 @@ use super::*; use safe_math::*; -use share_pool::{SharePool, SharePoolDataOperations}; +use share_pool::{SafeFloat, SafeFloatSerializable, SharePool, SharePoolDataOperations}; use sp_std::ops::Neg; -use substrate_fixed::types::{I64F64, I96F32, U64F64, U96F32}; +use substrate_fixed::types::{I64F64, I96F32, U96F32}; use subtensor_runtime_common::{AlphaBalance, AuthorshipInfo, NetUid, TaoBalance, Token}; use subtensor_swap_interface::{Order, SwapHandler, SwapResult}; @@ -508,7 +508,7 @@ impl Pallet { coldkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance, - ) -> AlphaBalance { + ) { if !amount.is_zero() { let mut staking_hotkeys = StakingHotkeys::::get(coldkey); if !staking_hotkeys.contains(hotkey) { @@ -520,11 +520,7 @@ impl Pallet { let mut alpha_share_pool = Self::get_alpha_share_pool(hotkey.clone(), netuid); // We expect to add a positive amount here. let amount = amount.to_u64() as i64; - let actual_alpha = alpha_share_pool.update_value_for_one(coldkey, amount); - - // We should return a positive amount, or 0 if the operation failed. - // e.g. the stake was removed due to precision issues. - actual_alpha.max(0).unsigned_abs().into() + alpha_share_pool.update_value_for_one(coldkey, amount); } pub fn try_increase_stake_for_hotkey_and_coldkey_on_subnet( @@ -552,22 +548,16 @@ impl Pallet { coldkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance, - ) -> AlphaBalance { + ) { let mut alpha_share_pool = Self::get_alpha_share_pool(hotkey.clone(), netuid); let amount = amount.to_u64(); // We expect a negative value here - let mut actual_alpha = 0; if let Ok(value) = alpha_share_pool.try_get_value(coldkey) && value >= amount { - actual_alpha = alpha_share_pool.update_value_for_one(coldkey, (amount as i64).neg()); + alpha_share_pool.update_value_for_one(coldkey, (amount as i64).neg()); } - - // Get the negation of the removed alpha, and clamp at 0. - // This ensures we return a positive value, but only if - // `actual_alpha` was negative (i.e. a decrease in stake). - actual_alpha.neg().max(0).unsigned_abs().into() } /// Swaps TAO for the alpha token on the subnet. @@ -687,15 +677,13 @@ impl Pallet { drop_fees: bool, ) -> Result { // Decrease alpha on subnet - let actual_alpha_decrease = - Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha); + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha); // Swap the alpha for TAO. - let swap_result = - Self::swap_alpha_for_tao(netuid, actual_alpha_decrease, price_limit, drop_fees)?; + let swap_result = Self::swap_alpha_for_tao(netuid, alpha, price_limit, drop_fees)?; // Refund the unused alpha (in case if limit price is hit) - let refund = actual_alpha_decrease.saturating_sub( + let refund = alpha.saturating_sub( swap_result .amount_paid_in .saturating_add(swap_result.fee_paid) @@ -754,7 +742,7 @@ impl Pallet { coldkey.clone(), hotkey.clone(), swap_result.amount_paid_out.into(), - actual_alpha_decrease, + swap_result.amount_paid_in.into(), netuid, swap_result.fee_paid.to_u64(), )); @@ -764,7 +752,7 @@ impl Pallet { coldkey.clone(), hotkey.clone(), swap_result.amount_paid_out, - actual_alpha_decrease, + swap_result.amount_paid_in, netuid, swap_result.fee_paid ); @@ -802,17 +790,12 @@ impl Pallet { ); // Increase the alpha on the hotkey account. - if Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, netuid, swap_result.amount_paid_out.into(), - ) - .is_zero() - || swap_result.amount_paid_out.is_zero() - { - return Ok(AlphaBalance::ZERO); - } + ); // Step 4: Update the list of hotkeys staking for this coldkey let mut staking_hotkeys = StakingHotkeys::::get(coldkey); @@ -889,7 +872,7 @@ impl Pallet { alpha: AlphaBalance, ) -> Result { // Decrease alpha on origin keys - let actual_alpha_decrease = Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( origin_hotkey, origin_coldkey, netuid, @@ -904,17 +887,17 @@ impl Pallet { } // Increase alpha on destination keys - let actual_alpha_moved = Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( destination_hotkey, destination_coldkey, netuid, - actual_alpha_decrease, + alpha, ); if netuid == NetUid::ROOT { Self::add_stake_adjust_root_claimed_for_hotkey_and_coldkey( destination_hotkey, destination_coldkey, - actual_alpha_decrease.into(), + u64::from(alpha).into(), ); } @@ -923,7 +906,7 @@ impl Pallet { let current_price = ::SwapInterface::current_alpha_price(netuid.into()); let tao_equivalent: TaoBalance = current_price - .saturating_mul(U96F32::saturating_from_num(actual_alpha_moved)) + .saturating_mul(U96F32::saturating_from_num(alpha)) .saturating_to_num::() .into(); @@ -952,7 +935,7 @@ impl Pallet { origin_coldkey.clone(), origin_hotkey.clone(), tao_equivalent, - actual_alpha_decrease, + alpha, netuid, 0_u64, // 0 fee )); @@ -960,7 +943,7 @@ impl Pallet { destination_coldkey.clone(), destination_hotkey.clone(), tao_equivalent, - actual_alpha_moved, + alpha, netuid, 0_u64, // 0 fee )); @@ -1331,47 +1314,84 @@ type AlphaShareKey = ::AccountId; impl SharePoolDataOperations> for HotkeyAlphaSharePoolDataOperations { - fn get_shared_value(&self) -> U64F64 { - U64F64::saturating_from_num(crate::TotalHotkeyAlpha::::get(&self.hotkey, self.netuid)) + fn get_shared_value(&self) -> u64 { + u64::from(TotalHotkeyAlpha::::get(&self.hotkey, self.netuid)) } - fn get_share(&self, key: &AlphaShareKey) -> U64F64 { - crate::Alpha::::get((&(self.hotkey), key, self.netuid)) + fn get_share(&self, key: &AlphaShareKey) -> SafeFloat { + // Read the deprecated Alpha map first and, if value is not available, try new AlphaV2 + let maybe_share_v1 = Alpha::::try_get((&(self.hotkey), key, self.netuid)); + if let Ok(share_v1) = maybe_share_v1 { + return SafeFloat::from(share_v1); + } + + let share_serializable = AlphaV2::::get((&(self.hotkey), key, self.netuid)); + SafeFloat::from(&share_serializable) } - fn try_get_share(&self, key: &AlphaShareKey) -> Result { - crate::Alpha::::try_get((&(self.hotkey), key, self.netuid)) + fn try_get_share(&self, key: &AlphaShareKey) -> Result { + // Read the deprecated Alpha map first and, if value is not available, try new AlphaV2 + let maybe_share_v1 = Alpha::::try_get((&(self.hotkey), key, self.netuid)); + if let Ok(share_v1) = maybe_share_v1 { + return Ok(SafeFloat::from(share_v1)); + } + + let maybe_share_serializable = AlphaV2::::try_get((&(self.hotkey), key, self.netuid)); + if let Ok(share_serializable) = maybe_share_serializable { + Ok(SafeFloat::from(&share_serializable)) + } else { + Err(()) + } } - fn get_denominator(&self) -> U64F64 { - crate::TotalHotkeyShares::::get(&(self.hotkey), self.netuid) + fn get_denominator(&self) -> SafeFloat { + // Read the deprecated TotalHotkeyShares map first and, if value is not available, try new TotalHotkeySharesV2 + let maybe_denomnator_v1 = TotalHotkeyShares::::try_get(&(self.hotkey), self.netuid); + if let Ok(denomnator_v1) = maybe_denomnator_v1 { + return SafeFloat::from(denomnator_v1); + } + + let denominator_serializable = TotalHotkeySharesV2::::get(&(self.hotkey), self.netuid); + SafeFloat::from(&denominator_serializable) } - fn set_shared_value(&mut self, value: U64F64) { + fn set_shared_value(&mut self, value: u64) { if value != 0 { - crate::TotalHotkeyAlpha::::insert( - &(self.hotkey), - self.netuid, - AlphaBalance::from(value.saturating_to_num::()), - ); + TotalHotkeyAlpha::::insert(&(self.hotkey), self.netuid, AlphaBalance::from(value)); } else { - crate::TotalHotkeyAlpha::::remove(&(self.hotkey), self.netuid); + TotalHotkeyAlpha::::remove(&(self.hotkey), self.netuid); } } - fn set_share(&mut self, key: &AlphaShareKey, share: U64F64) { - if share != 0 { - crate::Alpha::::insert((&self.hotkey, key, self.netuid), share); + fn set_share(&mut self, key: &AlphaShareKey, share: SafeFloat) { + // Lazy Alpha -> AlphaV2 migration happens right here + // Delete the Alpha entry, insert into AlphaV2 + let maybe_share_v1 = Alpha::::try_get((&(self.hotkey), key, self.netuid)); + if maybe_share_v1.is_ok() { + Alpha::::remove((&self.hotkey, key, self.netuid)); + } + + if !share.is_zero() { + let float_serializable = SafeFloatSerializable::from(&share); + AlphaV2::::insert((&self.hotkey, key, self.netuid), float_serializable); } else { - crate::Alpha::::remove((&self.hotkey, key, self.netuid)); + AlphaV2::::remove((&self.hotkey, key, self.netuid)); } } - fn set_denominator(&mut self, update: U64F64) { - if update != 0 { - crate::TotalHotkeyShares::::insert(&self.hotkey, self.netuid, update); + fn set_denominator(&mut self, update: SafeFloat) { + // Lazy TotalHotkeyShares -> TotalHotkeySharesV2 migration happens right here + // Delete the TotalHotkeyShares entry, insert into TotalHotkeySharesV2 + let maybe_denominator_v1 = TotalHotkeyShares::::try_get(&(self.hotkey), self.netuid); + if maybe_denominator_v1.is_ok() { + TotalHotkeyShares::::remove(&self.hotkey, self.netuid); + } + + if !update.is_zero() { + let float_serializable = SafeFloatSerializable::from(&update); + TotalHotkeySharesV2::::insert(&self.hotkey, self.netuid, float_serializable); } else { - crate::TotalHotkeyShares::::remove(&self.hotkey, self.netuid); + TotalHotkeySharesV2::::remove(&self.hotkey, self.netuid); } } } diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 401b5989ec..2afd2b8a90 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -1,5 +1,5 @@ use super::*; -use substrate_fixed::types::U64F64; +use share_pool::{SafeFloat, SafeFloatSerializable}; impl Pallet { /// Transfer all assets, stakes, subnet ownerships, and hotkey associations from `old_coldkey` to @@ -98,19 +98,45 @@ impl Pallet { new_coldkey: &T::AccountId, ) { for hotkey in StakingHotkeys::::get(old_coldkey) { - // Get the stake on the old (hot,coldkey) account. - let old_alpha: U64F64 = Alpha::::get((&hotkey, old_coldkey, netuid)); - // Get the stake on the new (hot,coldkey) account. - let new_alpha: U64F64 = Alpha::::get((&hotkey, new_coldkey, netuid)); - // Add the stake to new account. - Alpha::::insert( - (&hotkey, new_coldkey, netuid), - new_alpha.saturating_add(old_alpha), - ); - // Remove the value from the old account. + // Swap and lazy-migrate Alpha to AlphaV2 + // TotalHotkeyShares does not have to be migrated here, these migrations can be independent + + // Get the v1 alpha shares on the old (hot,coldkey) account. + let orig_alpha_v1: SafeFloat = + SafeFloat::from(Alpha::::get((&hotkey, old_coldkey, netuid))); + // Get the v1 alpha shares on the new (hot,coldkey) account. + let dest_alpha_v1: SafeFloat = + SafeFloat::from(Alpha::::get((&hotkey, new_coldkey, netuid))); + // Get the v2 alpha shares on the old (hot,coldkey) account. + let orig_alpha_v2: SafeFloat = + SafeFloat::from(&AlphaV2::::get((&hotkey, old_coldkey, netuid))); + // Get the v2 alpha shares on the new (hot,coldkey) account. + let dest_alpha_v2: SafeFloat = + SafeFloat::from(&AlphaV2::::get((&hotkey, new_coldkey, netuid))); + + // Calculate and save new alpha shares on the destination new_coldkey + let new_dest_alpha = orig_alpha_v1 + .add(&dest_alpha_v1) + .unwrap_or_default() + .add(&orig_alpha_v2) + .unwrap_or_default() + .add(&dest_alpha_v2) + .unwrap_or_default(); + if !new_dest_alpha.is_zero() { + AlphaV2::::insert( + (&hotkey, new_coldkey, netuid), + SafeFloatSerializable::from(&new_dest_alpha), + ); + } + + // Remove shares on the origin old_coldkey in both Alpha and AlphaV2 maps Alpha::::remove((&hotkey, old_coldkey, netuid)); + AlphaV2::::remove((&hotkey, old_coldkey, netuid)); + + // Remove shares on the destination new_coldkey in Alpha map + Alpha::::remove((&hotkey, new_coldkey, netuid)); - if new_alpha.saturating_add(old_alpha) > U64F64::from(0u64) { + if !new_dest_alpha.is_zero() { Self::transfer_root_claimed_for_new_keys( netuid, &hotkey, diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index 1652610afb..f3e1b7fad3 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -1,5 +1,6 @@ use super::*; use frame_support::weights::Weight; +use share_pool::{SafeFloat, SafeFloatSerializable}; use sp_core::Get; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{MechId, NetUid, Token}; @@ -162,8 +163,8 @@ impl Pallet { weight: &mut Weight, ) -> DispatchResult { // 1. keep the old hotkey alpha values for the case where hotkey staked by multiple coldkeys. - let old_alpha_values: Vec<((T::AccountId, NetUid), U64F64)> = - Alpha::::iter_prefix((old_hotkey,)).collect(); + let old_alpha_values: Vec<(T::AccountId, NetUid, SafeFloat)> = + Self::alpha_iter_single_prefix(old_hotkey).collect(); weight.saturating_accrue(T::DbWeight::get().reads(old_alpha_values.len() as u64)); // 2. Swap owner. @@ -217,21 +218,22 @@ impl Pallet { // 10. Alpha already update in perform_hotkey_swap_on_one_subnet // Update the StakingHotkeys for the case where hotkey staked by multiple coldkeys. - for ((coldkey, _netuid), _alpha) in old_alpha_values { + for (coldkey, _netuid, alpha_share) in old_alpha_values { // Swap StakingHotkeys. // StakingHotkeys( coldkey ) --> Vec -- the hotkeys that the coldkey stakes. - let mut staking_hotkeys = StakingHotkeys::::get(&coldkey); - weight.saturating_accrue(T::DbWeight::get().reads(1)); - if staking_hotkeys.contains(old_hotkey) { - staking_hotkeys.retain(|hk| *hk != *old_hotkey && *hk != *new_hotkey); - if !staking_hotkeys.contains(new_hotkey) { - staking_hotkeys.push(new_hotkey.clone()); + if !alpha_share.is_zero() { + let mut staking_hotkeys = StakingHotkeys::::get(&coldkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if staking_hotkeys.contains(old_hotkey) { + staking_hotkeys.retain(|hk| *hk != *old_hotkey && *hk != *new_hotkey); + if !staking_hotkeys.contains(new_hotkey) { + staking_hotkeys.push(new_hotkey.clone()); + } + StakingHotkeys::::insert(&coldkey, staking_hotkeys); + weight.saturating_accrue(T::DbWeight::get().writes(1)); } - StakingHotkeys::::insert(&coldkey, staking_hotkeys); - weight.saturating_accrue(T::DbWeight::get().writes(1)); } } - // Return successful after swapping all the relevant terms. Ok(()) } @@ -334,13 +336,27 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); // 2. Swap total hotkey shares on all subnets it exists on. - // TotalHotkeyShares( hotkey, netuid ) -> alpha -- the total alpha that the hotkey has on a specific subnet. - let share = TotalHotkeyShares::::take(old_hotkey, netuid); - // TotalHotkeyAlpha::::remove(old_hotkey, netuid); - TotalHotkeyShares::::mutate(new_hotkey, netuid, |value| { - *value = value.saturating_add(share) - }); - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + // TotalHotkeyShares( hotkey, netuid ) -> share pool denominator for this hotkey on this subnet. + // Merge v1 and v2 TotalHotkeyShares because TotalHotkeyShares is deprecated + weight.saturating_accrue(T::DbWeight::get().reads(4)); + let old_share_v1 = SafeFloat::from(TotalHotkeyShares::::take(old_hotkey, netuid)); + let old_share_v2 = SafeFloat::from(&TotalHotkeySharesV2::::take(old_hotkey, netuid)); + let total_old_shares = old_share_v1.add(&old_share_v2).unwrap_or_default(); + + let new_share_v1 = SafeFloat::from(TotalHotkeyShares::::take(new_hotkey, netuid)); + let new_share_v2 = SafeFloat::from(&TotalHotkeySharesV2::::take(new_hotkey, netuid)); + let total_new_shares = new_share_v1.add(&new_share_v2).unwrap_or_default(); + + TotalHotkeyShares::::remove(old_hotkey, netuid); + TotalHotkeyShares::::remove(new_hotkey, netuid); + + let total_old_plus_new_shares = total_new_shares.add(&total_old_shares).unwrap_or_default(); + TotalHotkeySharesV2::::insert( + new_hotkey, + netuid, + SafeFloatSerializable::from(&total_old_plus_new_shares), + ); + weight.saturating_accrue(T::DbWeight::get().writes(3)); // 3. Swap all subnet specific info. @@ -505,6 +521,11 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads(old_alpha_values.len() as u64)); weight.saturating_accrue(T::DbWeight::get().writes(old_alpha_values.len() as u64)); + let old_alpha_values_v2: Vec<((T::AccountId, NetUid), SafeFloatSerializable)> = + AlphaV2::::iter_prefix((old_hotkey,)).collect(); + weight.saturating_accrue(T::DbWeight::get().reads(old_alpha_values_v2.len() as u64)); + weight.saturating_accrue(T::DbWeight::get().writes(old_alpha_values_v2.len() as u64)); + // 9.1. Transfer root claimable Self::transfer_root_claimable_for_new_hotkey(old_hotkey, new_hotkey); @@ -518,9 +539,42 @@ impl Pallet { let new_alpha = Alpha::::take((new_hotkey, &coldkey, netuid)); Alpha::::remove((old_hotkey, &coldkey, netuid)); - Alpha::::insert( + + // Insert into AlphaV2 because Alpha is deprecated + AlphaV2::::insert( + (new_hotkey, &coldkey, netuid), + SafeFloatSerializable::from(&SafeFloat::from(alpha.saturating_add(new_alpha))), + ); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); + + // Swap StakingHotkeys. + // StakingHotkeys( coldkey ) --> Vec -- the hotkeys that the coldkey stakes. + let mut staking_hotkeys = StakingHotkeys::::get(&coldkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if staking_hotkeys.contains(old_hotkey) && !staking_hotkeys.contains(new_hotkey) { + staking_hotkeys.push(new_hotkey.clone()); + StakingHotkeys::::insert(&coldkey, staking_hotkeys); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } + } + } + + for ((coldkey, netuid_alpha), alpha) in old_alpha_values_v2 { + if netuid == netuid_alpha { + Self::transfer_root_claimed_for_new_keys( + netuid, old_hotkey, new_hotkey, &coldkey, &coldkey, + ); + + let new_alpha_v2 = + SafeFloat::from(&AlphaV2::::take((new_hotkey, &coldkey, netuid))); + AlphaV2::::remove((old_hotkey, &coldkey, netuid)); + AlphaV2::::insert( (new_hotkey, &coldkey, netuid), - alpha.saturating_add(new_alpha), + SafeFloatSerializable::from( + &SafeFloat::from(&alpha) + .add(&new_alpha_v2) + .unwrap_or_default(), + ), ); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index deb4cd7fc5..07d7e7f33f 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -20,6 +20,7 @@ use frame_system as system; use frame_system::{EnsureRoot, RawOrigin, limits, offchain::CreateTransactionBase}; use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_utility as pallet_utility; +use share_pool::{SafeFloat, SafeFloatSerializable}; use sp_core::{ConstU64, Get, H256, U256, offchain::KeyTypeId}; use sp_runtime::Perbill; use sp_runtime::{ @@ -331,8 +332,8 @@ impl pallet_subtensor_swap::Config for Test { type SubnetInfo = SubtensorModule; type BalanceOps = SubtensorModule; type ProtocolId = SwapProtocolId; - type TaoReserve = TaoCurrencyReserve; - type AlphaReserve = AlphaCurrencyReserve; + type TaoReserve = TaoBalanceReserve; + type AlphaReserve = AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; @@ -1023,3 +1024,14 @@ pub fn commit_dummy(who: U256, netuid: NetUid) { hash )); } + +#[allow(dead_code)] +pub fn sfser_to_u128(sf_ser: &SafeFloatSerializable) -> u128 { + let alpha_f64: f64 = SafeFloat::from(sf_ser).into(); + alpha_f64 as u128 +} + +#[allow(dead_code)] +pub fn sfser_from_u64(val: u64) -> SafeFloatSerializable { + SafeFloatSerializable::from(&SafeFloat::from(val)) +} diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index f6a50cf4ff..417c142c6a 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -1,4 +1,4 @@ -#![allow(clippy::expect_used)] +#![allow(clippy::expect_used, clippy::indexing_slicing)] use super::mock::*; use crate::migrations::migrate_network_immunity_period; @@ -7,7 +7,7 @@ use frame_support::{assert_err, assert_ok}; use frame_system::Config; use sp_core::U256; use sp_std::collections::{btree_map::BTreeMap, vec_deque::VecDeque}; -use substrate_fixed::types::{I96F32, U64F64, U96F32}; +use substrate_fixed::types::{I96F32, U96F32}; use subtensor_runtime_common::{MechId, NetUidStorageIndex, TaoBalance}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -100,7 +100,7 @@ fn dissolve_single_alpha_out_staker_gets_all_tao() { // 2. Single α-out staker let (s_hot, s_cold) = (U256::from(100), U256::from(200)); - Alpha::::insert((s_hot, s_cold, net), U64F64::from_num(5_000u128)); + AlphaV2::::insert((s_hot, s_cold, net), sfser_from_u64(5_000u64)); // Entire TAO pot should be paid to staker's cold-key let pot: u64 = 99_999; @@ -119,7 +119,7 @@ fn dissolve_single_alpha_out_staker_gets_all_tao() { assert_eq!(after, before + pot.into()); // No α entries left for dissolved subnet - assert!(Alpha::::iter().all(|((_h, _c, n), _)| n != net)); + assert!(AlphaV2::::iter().all(|((_h, _c, n), _)| n != net)); assert!(!SubnetTAO::::contains_key(net)); }); } @@ -137,14 +137,14 @@ fn dissolve_two_stakers_pro_rata_distribution() { let reg_at = NetworkRegisteredAt::::get(net); NetworkRegistrationStartBlock::::put(reg_at.saturating_add(1)); - let (s1_hot, s1_cold, a1) = (U256::from(201), U256::from(301), 300u128); - let (s2_hot, s2_cold, a2) = (U256::from(202), U256::from(302), 700u128); + let (s1_hot, s1_cold, a1) = (U256::from(201), U256::from(301), 300u64); + let (s2_hot, s2_cold, a2) = (U256::from(202), U256::from(302), 700u64); - Alpha::::insert((s1_hot, s1_cold, net), U64F64::from_num(a1)); - Alpha::::insert((s2_hot, s2_cold, net), U64F64::from_num(a2)); + AlphaV2::::insert((s1_hot, s1_cold, net), sfser_from_u64(a1)); + AlphaV2::::insert((s2_hot, s2_cold, net), sfser_from_u64(a2)); - TotalHotkeyAlpha::::insert(s1_hot, net, AlphaBalance::from(a1 as u64)); - TotalHotkeyAlpha::::insert(s2_hot, net, AlphaBalance::from(a2 as u64)); + TotalHotkeyAlpha::::insert(s1_hot, net, AlphaBalance::from(a1)); + TotalHotkeyAlpha::::insert(s2_hot, net, AlphaBalance::from(a2)); let pot: u64 = 10_000; SubnetTAO::::insert(net, TaoBalance::from(pot)); @@ -156,9 +156,9 @@ fn dissolve_two_stakers_pro_rata_distribution() { let owner_before = SubtensorModule::get_coldkey_balance(&oc); // Expected τ shares with largest remainder - let total = a1 + a2; - let prod1 = a1 * (pot as u128); - let prod2 = a2 * (pot as u128); + let total = (a1 + a2) as u128; + let prod1 = (a1 as u128) * (pot as u128); + let prod2 = (a2 as u128) * (pot as u128); let share1 = (prod1 / total) as u64; let share2 = (prod2 / total) as u64; let mut distributed = share1 + share2; @@ -202,7 +202,7 @@ fn dissolve_two_stakers_pro_rata_distribution() { ); // α entries for dissolved subnet gone - assert!(Alpha::::iter().all(|((_h, _c, n), _)| n != net)); + assert!(AlphaV2::::iter().all(|((_h, _c, n), _)| n != net)); }); } @@ -635,7 +635,7 @@ fn dissolve_alpha_out_but_zero_tao_no_rewards() { let sh = U256::from(23); let sc = U256::from(24); - Alpha::::insert((sh, sc, net), U64F64::from_num(1_000u64)); + AlphaV2::::insert((sh, sc, net), sfser_from_u64(1_000u64)); SubnetTAO::::insert(net, TaoBalance::from(0)); // zero TAO SubtensorModule::set_subnet_locked_balance(net, TaoBalance::from(0)); Emission::::insert(net, Vec::::new()); @@ -647,7 +647,7 @@ fn dissolve_alpha_out_but_zero_tao_no_rewards() { // No reward distributed, α-out cleared. assert_eq!(after, before); - assert!(Alpha::::iter().next().is_none()); + assert!(AlphaV2::::iter().next().is_none()); }); } @@ -679,8 +679,8 @@ fn dissolve_rounding_remainder_distribution() { let (s1h, s1c) = (U256::from(63), U256::from(64)); let (s2h, s2c) = (U256::from(65), U256::from(66)); - Alpha::::insert((s1h, s1c, net), U64F64::from_num(3u128)); - Alpha::::insert((s2h, s2c, net), U64F64::from_num(2u128)); + AlphaV2::::insert((s1h, s1c, net), sfser_from_u64(3u64)); + AlphaV2::::insert((s2h, s2c, net), sfser_from_u64(2u64)); SubnetTAO::::insert(net, TaoBalance::from(1)); // TAO pot = 1 SubtensorModule::set_subnet_locked_balance(net, TaoBalance::from(0)); @@ -703,7 +703,7 @@ fn dissolve_rounding_remainder_distribution() { assert_eq!(c2_after, c2_before); // α records for subnet gone; TAO key gone - assert!(Alpha::::iter().all(|((_h, _c, n), _)| n != net)); + assert!(AlphaV2::::iter().all(|((_h, _c, n), _)| n != net)); assert!(!SubnetTAO::::contains_key(net)); }); } @@ -748,8 +748,8 @@ fn destroy_alpha_out_multiple_stakers_pro_rata() { )); // 4. α-out snapshot - let a1: u128 = Alpha::::get((h1, c1, netuid)).saturating_to_num(); - let a2: u128 = Alpha::::get((h2, c2, netuid)).saturating_to_num(); + let a1: u128 = sfser_to_u128(&AlphaV2::::get((h1, c1, netuid))); + let a2: u128 = sfser_to_u128(&AlphaV2::::get((h2, c2, netuid))); let atotal = a1 + a2; // 5. TAO pot & lock @@ -799,8 +799,8 @@ fn destroy_alpha_out_multiple_stakers_pro_rata() { ); // 11. α entries cleared for the subnet - assert!(!Alpha::::contains_key((h1, c1, netuid))); - assert!(!Alpha::::contains_key((h2, c2, netuid))); + assert!(!AlphaV2::::contains_key((h1, c1, netuid))); + assert!(!AlphaV2::::contains_key((h2, c2, netuid))); }); } @@ -856,7 +856,7 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { let mut alpha = [0u128; N]; let mut alpha_sum: u128 = 0; for i in 0..N { - alpha[i] = Alpha::::get((hot[i], cold[i], netuid)).saturating_to_num(); + alpha[i] = sfser_to_u128(&AlphaV2::::get((hot[i], cold[i], netuid))); alpha_sum += alpha[i]; } @@ -937,7 +937,7 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { ); // α cleared for dissolved subnet & related counters reset - assert!(Alpha::::iter().all(|((_h, _c, n), _)| n != netuid)); + assert!(AlphaV2::::iter().all(|((_h, _c, n), _)| n != netuid)); assert_eq!(SubnetAlphaIn::::get(netuid), 0.into()); assert_eq!(SubnetAlphaOut::::get(netuid), 0.into()); assert_eq!(SubtensorModule::get_subnet_locked_balance(netuid), 0.into()); @@ -1778,7 +1778,6 @@ fn test_tempo_greater_than_weight_set_rate_limit() { }) } -#[allow(clippy::indexing_slicing)] #[test] fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state() { new_test_ext(0).execute_with(|| { @@ -1816,7 +1815,7 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( ]; // ──────────────────────────────────────────────────────────────────── - // 1) Create many subnets, enable V3, fix price at tick=0 (sqrt≈1) + // 1) Create many subnets, fix price at tick=0 // ──────────────────────────────────────────────────────────────────── let mut nets: Vec = Vec::new(); for i in 0..NUM_NETS { @@ -1828,20 +1827,6 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( Emission::::insert(net, Vec::::new()); SubtensorModule::set_subnet_locked_balance(net, TaoBalance::from(0)); - assert_ok!( - pallet_subtensor_swap::Pallet::::toggle_user_liquidity( - RuntimeOrigin::root(), - net, - true - ) - ); - - // Price/tick pinned so LP math stays stable (sqrt(1)). - let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); - let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1) price"); - pallet_subtensor_swap::CurrentTick::::set(net, ct0); - pallet_subtensor_swap::AlphaSqrtPrice::::set(net, sqrt1); - nets.push(net); } @@ -1922,10 +1907,10 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( } // Capture **pair‑level** α snapshot per net (pre‑LP). - for ((hot, cold, net), amt) in Alpha::::iter() { + for ((hot, cold, net), amt) in AlphaV2::::iter() { if let Some(&ni) = net_index.get(&net) && lp_sets_per_net[ni].contains(&cold) { - let a: u128 = amt.saturating_to_num(); + let a: u128 = sfser_to_u128(&amt); if a > 0 { alpha_pairs_per_net .entry(net) @@ -2050,53 +2035,17 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( // For each dissolved net, check α ledgers gone, network removed, and swap state clean. for &net in nets.iter() { assert!( - Alpha::::iter().all(|((_h, _c, n), _)| n != net), + AlphaV2::::iter().all(|((_h, _c, n), _)| n != net), "alpha ledger not fully cleared for net {net:?}" ); assert!( !SubtensorModule::if_subnet_exist(net), "subnet {net:?} still exists" ); - assert!( - pallet_subtensor_swap::Ticks::::iter_prefix(net) - .next() - .is_none(), - "ticks not cleared for net {net:?}" - ); - assert!( - !pallet_subtensor_swap::Positions::::iter() - .any(|((n, _owner, _pid), _)| n == net), - "swap positions not fully cleared for net {net:?}" - ); - assert_eq!( - pallet_subtensor_swap::FeeGlobalTao::::get(net).saturating_to_num::(), - 0, - "FeeGlobalTao nonzero for net {net:?}" - ); - assert_eq!( - pallet_subtensor_swap::FeeGlobalAlpha::::get(net).saturating_to_num::(), - 0, - "FeeGlobalAlpha nonzero for net {net:?}" - ); - assert_eq!( - pallet_subtensor_swap::CurrentLiquidity::::get(net), - 0, - "CurrentLiquidity not zero for net {net:?}" - ); assert!( !pallet_subtensor_swap::SwapV3Initialized::::get(net), "SwapV3Initialized still set" ); - assert!( - !pallet_subtensor_swap::EnabledUserLiquidity::::get(net), - "EnabledUserLiquidity still set" - ); - assert!( - pallet_subtensor_swap::TickIndexBitmapWords::::iter_prefix((net,)) - .next() - .is_none(), - "TickIndexBitmapWords not cleared for net {net:?}" - ); } // ──────────────────────────────────────────────────────────────────── @@ -2111,18 +2060,6 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( Emission::::insert(net_new, Vec::::new()); SubtensorModule::set_subnet_locked_balance(net_new, TaoBalance::from(0)); - assert_ok!( - pallet_subtensor_swap::Pallet::::toggle_user_liquidity( - RuntimeOrigin::root(), - net_new, - true - ) - ); - let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); - let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1)"); - pallet_subtensor_swap::CurrentTick::::set(net_new, ct0); - pallet_subtensor_swap::AlphaSqrtPrice::::set(net_new, sqrt1); - // Compute the exact min stake per the pallet rule: DefaultMinStake + fee(DefaultMinStake). let min_stake = DefaultMinStake::::get(); let order = GetAlphaForTao::::with_amount(min_stake); @@ -2142,7 +2079,7 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( register_ok_neuron(net_new, hot1, cold, 7777); let before_tao = SubtensorModule::get_coldkey_balance(&cold); - let a_prev: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); + let a_prev: u64 = sfser_to_u128(&AlphaV2::::get((hot1, cold, net_new))) as u64; // Expected α for this exact τ, using the same sim path as the pallet. let order = GetAlphaForTao::::with_amount(min_amount_required); @@ -2161,7 +2098,7 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( )); let after_tao = SubtensorModule::get_coldkey_balance(&cold); - let a_new: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); + let a_new: u64 = sfser_to_u128(&AlphaV2::::get((hot1, cold, net_new))) as u64; let a_delta = a_new.saturating_sub(a_prev); // τ decreased by exactly the amount we sent. diff --git a/pallets/subtensor/src/tests/recycle_alpha.rs b/pallets/subtensor/src/tests/recycle_alpha.rs index 9ae3975744..f7b1f416d6 100644 --- a/pallets/subtensor/src/tests/recycle_alpha.rs +++ b/pallets/subtensor/src/tests/recycle_alpha.rs @@ -3,8 +3,9 @@ use super::mock::*; use crate::*; use approx::assert_abs_diff_eq; use frame_support::{assert_noop, assert_ok, traits::Currency}; +use share_pool::{SafeFloat, SafeFloatSerializable}; use sp_core::U256; -use substrate_fixed::types::{U64F64, U96F32}; +use substrate_fixed::types::U96F32; use subtensor_runtime_common::{AlphaBalance, Token}; use subtensor_swap_interface::SwapHandler; @@ -546,75 +547,107 @@ fn test_burn_errors() { } #[test] -fn test_recycle_precision_loss() { +fn test_recycle_precision() { new_test_ext(1).execute_with(|| { let coldkey = U256::from(1); let hotkey = U256::from(2); let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + let tao_reserve = TaoBalance::from(1_000_000_000_u64); + let alpha_reserve = AlphaBalance::from(1_000_000_000_u64); + SubnetAlphaIn::::insert(netuid, alpha_reserve); + SubnetTAO::::insert(netuid, tao_reserve); Balances::make_free_balance_be(&coldkey, 1_000_000_000.into()); // sanity check assert!(SubtensorModule::if_subnet_exist(netuid)); // add stake to coldkey-hotkey pair so we can recycle it - let stake = 200_000; increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake.into(), netuid); // amount to recycle let recycle_amount = AlphaBalance::from(stake / 2); - // Modify the alpha pool denominator so it's low-precision - let denominator = U64F64::from_num(0.00000001); - TotalHotkeyShares::::insert(hotkey, netuid, denominator); - Alpha::::insert((&hotkey, &coldkey, netuid), denominator); + // Modify the alpha pool denominator so it's low-precision (denominator = share = 1e-9) + let denominator = SafeFloat::from(1) + .div(&SafeFloat::from(1_000_000_000)) + .unwrap_or_default(); + TotalHotkeySharesV2::::insert( + hotkey, + netuid, + SafeFloatSerializable::from(&denominator), + ); + AlphaV2::::insert( + (&hotkey, &coldkey, netuid), + SafeFloatSerializable::from(&denominator), + ); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); // recycle, expect error due to precision loss - assert_noop!( - SubtensorModule::recycle_alpha( - RuntimeOrigin::signed(coldkey), - hotkey, - recycle_amount, - netuid - ), - Error::::PrecisionLoss + assert_ok!(SubtensorModule::recycle_alpha( + RuntimeOrigin::signed(coldkey), + hotkey, + recycle_amount, + netuid + )); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + AlphaBalance::from(stake / 2) ); }); } #[test] -fn test_burn_precision_loss() { +fn test_burn_precision() { new_test_ext(1).execute_with(|| { let coldkey = U256::from(1); let hotkey = U256::from(2); let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + let tao_reserve = TaoBalance::from(1_000_000_000_u64); + let alpha_reserve = AlphaBalance::from(1_000_000_000_u64); + SubnetAlphaIn::::insert(netuid, alpha_reserve); + SubnetTAO::::insert(netuid, tao_reserve); Balances::make_free_balance_be(&coldkey, 1_000_000_000.into()); // sanity check assert!(SubtensorModule::if_subnet_exist(netuid)); // add stake to coldkey-hotkey pair so we can recycle it - let stake = 200_000; increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake.into(), netuid); // amount to recycle let burn_amount = AlphaBalance::from(stake / 2); - // Modify the alpha pool denominator so it's low-precision - let denominator = U64F64::from_num(0.00000001); - TotalHotkeyShares::::insert(hotkey, netuid, denominator); - Alpha::::insert((&hotkey, &coldkey, netuid), denominator); + // Modify the alpha pool denominator so it's low-precision (denominator = share = 1e-9) + let denominator = SafeFloat::from(1) + .div(&SafeFloat::from(1_000_000_000)) + .unwrap_or_default(); + TotalHotkeySharesV2::::insert( + hotkey, + netuid, + SafeFloatSerializable::from(&denominator), + ); + AlphaV2::::insert( + (&hotkey, &coldkey, netuid), + SafeFloatSerializable::from(&denominator), + ); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); // burn, expect error due to precision loss - assert_noop!( - SubtensorModule::burn_alpha( - RuntimeOrigin::signed(coldkey), - hotkey, - burn_amount, - netuid - ), - Error::::PrecisionLoss + assert_ok!(SubtensorModule::burn_alpha( + RuntimeOrigin::signed(coldkey), + hotkey, + burn_amount, + netuid + )); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + AlphaBalance::from(stake / 2) ); }); } diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 0b82fa27eb..57df1c0322 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -8,6 +8,7 @@ use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency}; use frame_system::RawOrigin; use pallet_subtensor_swap::tick::TickIndex; use safe_math::FixedExt; +use share_pool::{SafeFloat, SafeFloatSerializable}; use sp_core::{Get, H256, U256}; use substrate_fixed::traits::FromFixed; use substrate_fixed::types::{I96F32, I110F18, U64F64, U96F32}; @@ -4123,9 +4124,9 @@ fn test_add_stake_specific_stake_into_subnet_fail() { }); } -// cargo test --package pallet-subtensor --lib -- tests::staking::test_remove_99_999_per_cent_stake_removes_all --exact --show-output +// cargo test --package pallet-subtensor --lib -- tests::staking::test_remove_99_999_per_cent_stake_works_precisely --exact --show-output #[test] -fn test_remove_99_9991_per_cent_stake_removes_all() { +fn test_remove_99_9991_per_cent_stake_works_precisely() { new_test_ext(1).execute_with(|| { let subnet_owner_coldkey = U256::from(1); let subnet_owner_hotkey = U256::from(2); @@ -4161,7 +4162,7 @@ fn test_remove_99_9991_per_cent_stake_removes_all() { (U64F64::from_num(alpha) * U64F64::from_num(0.999991)).to_num::(), ); // we expected the entire stake to be returned - let (expected_balance, _) = mock::swap_alpha_to_tao(netuid, alpha); + let (expected_balance, _) = mock::swap_alpha_to_tao(netuid, remove_amount); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, @@ -4175,16 +4176,13 @@ fn test_remove_99_9991_per_cent_stake_removes_all() { expected_balance, epsilon = 10.into(), ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), - TaoBalance::ZERO - ); + assert!(!SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id).is_zero()); let new_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &coldkey_account_id, netuid, ); - assert!(new_alpha.is_zero()); + assert_eq!(new_alpha, alpha - remove_amount); }); } @@ -5653,3 +5651,458 @@ fn test_staking_records_flow() { ); }); } + +// cargo test --package pallet-subtensor --lib -- tests::staking::test_lazy_sharepool_migration_get_stake_reads_from_deprecated_alpha_map --exact --nocapture +#[test] +fn test_lazy_sharepool_migration_get_stake_reads_from_deprecated_alpha_map() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha map + Alpha::::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeyShares::::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + AlphaBalance::from(stake) + ); + }); +} + +#[test] +fn test_lazy_sharepool_migration_get_stake_reads_from_alpha_v2_map() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to AlphaV2 map + AlphaV2::::insert( + (hotkey, coldkey, netuid), + SafeFloatSerializable::from(&SafeFloat::from(1_u64)), + ); + TotalHotkeySharesV2::::insert( + hotkey, + netuid, + SafeFloatSerializable::from(&SafeFloat::from(1_u64)), + ); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + AlphaBalance::from(stake) + ); + }); +} + +#[test] +fn test_lazy_sharepool_migration_get_stake_reads_from_cross_alpha_maps() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to Alpha map + Alpha::::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + // but total shares are in TotalHotkeySharesV2 map (already migrated) + TotalHotkeySharesV2::::insert( + hotkey, + netuid, + SafeFloatSerializable::from(&SafeFloat::from(1_u64)), + ); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + AlphaBalance::from(stake) + ); + }); +} + +#[test] +fn test_lazy_sharepool_migration_staking_causes_migration() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha map + Alpha::::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeyShares::::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Stake more via stake_into_subnet + increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake.into(), netuid); + + // Verify that deprecated v1 map values are gone + assert!(Alpha::::try_get((&hotkey, &coldkey, netuid)).is_err()); + assert!(TotalHotkeyShares::::try_get(hotkey, netuid).is_err()); + + // Verify that v2 map values are present + let migrated_share = SafeFloat::from(&AlphaV2::::get((&hotkey, &coldkey, netuid))); + let migrated_denominator = + SafeFloat::from(&TotalHotkeySharesV2::::get(hotkey, netuid)); + + assert_eq!( + f64::from((migrated_share.div(&migrated_denominator)).unwrap()), + 1.0 + ); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha map + Alpha::::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeyShares::::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to AlphaV2 map + let share = sfser_from_u64(1_u64); + AlphaV2::::insert((hotkey, coldkey, netuid), share.clone()); + TotalHotkeySharesV2::::insert(hotkey, netuid, share); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_mixed_v1_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha map and new THS v2 map + let share = sfser_from_u64(1_u64); + Alpha::::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeySharesV2::::insert(hotkey, netuid, share); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_mixed_v2_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to new AlphaV2 map and deprecated THS map + let share = sfser_from_u64(1_u64); + AlphaV2::::insert((hotkey, coldkey, netuid), share); + TotalHotkeyShares::::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_from_shares_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated THS map + TotalHotkeyShares::::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value_from_shares + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let current_share = SafeFloat::from(U64F64::from(1_u64)); + let actual_value = share_pool.get_value_from_shares(current_share); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_from_shares_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to new THS v2 map + let share = sfser_from_u64(1_u64); + TotalHotkeySharesV2::::insert(hotkey, netuid, share); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value_from_shares + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let current_share = SafeFloat::from(U64F64::from(1_u64)); + let actual_value = share_pool.get_value_from_shares(current_share); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_update_value_for_all() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to new AlphaV2 map + let share = sfser_from_u64(1_u64); + AlphaV2::::insert((hotkey, coldkey, netuid), share.clone()); + TotalHotkeySharesV2::::insert(hotkey, netuid, share); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and call update_value_for_all + let mut share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + share_pool.update_value_for_all(stake as i64); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake * 2); + + share_pool.update_value_for_all(-(stake as i64)); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_update_value_for_one_v1_with_migration() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha and THS maps + Alpha::::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeyShares::::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and call update_value_for_one + let mut share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + share_pool.update_value_for_one(&coldkey, stake as i64); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake * 2); + + // Verify deletion from deprecated + assert!(!Alpha::::contains_key((hotkey, coldkey, netuid))); + assert!(!TotalHotkeyShares::::contains_key(hotkey, netuid)); + }); +} + +#[test] +fn test_sharepool_dataops_update_value_for_one_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to new AlphaV2 and THS maps + let share = sfser_from_u64(1_u64); + AlphaV2::::insert((hotkey, coldkey, netuid), share.clone()); + TotalHotkeySharesV2::::insert(hotkey, netuid, share); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and call update_value_for_one + let mut share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + share_pool.update_value_for_one(&coldkey, stake as i64); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake * 2); + }); +} + +#[test] +fn test_sharepool_dataops_update_value_for_one_mixed_v1_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha and new THS v2 maps + let share = sfser_from_u64(1_u64); + Alpha::::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeySharesV2::::insert(hotkey, netuid, share); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and call update_value_for_one + let mut share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + share_pool.update_value_for_one(&coldkey, stake as i64); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake * 2); + + // Verify deletion from deprecated + assert!(!Alpha::::contains_key((hotkey, coldkey, netuid))); + }); +} + +#[test] +fn test_sharepool_dataops_update_value_for_one_mixed_v2_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to new AlphaV2 and deprecated THS maps + let share = sfser_from_u64(1_u64); + AlphaV2::::insert((hotkey, coldkey, netuid), share); + TotalHotkeyShares::::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and call update_value_for_one + let mut share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + share_pool.update_value_for_one(&coldkey, stake as i64); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake * 2); + + // Verify deletion from deprecated + assert!(!TotalHotkeyShares::::contains_key(hotkey, netuid)); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_returns_zero_on_non_existing_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add to deprecated THS map, but no value in Alpha map + TotalHotkeyShares::::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, 0_u64); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_returns_zero_on_non_existing_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add to THSV2 map, but no value in AlphaV2 map + let share = sfser_from_u64(1_u64); + TotalHotkeySharesV2::::insert(hotkey, netuid, share); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, 0_u64); + }); +} + +#[test] +fn test_sharepool_dataops_try_get_value_returns_err_on_non_existing_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add to deprecated THS map, but no value in Alpha map + TotalHotkeyShares::::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let maybe_actual_value = share_pool.try_get_value(&coldkey); + assert!(maybe_actual_value.is_err()); + }); +} + +#[test] +fn test_sharepool_dataops_try_get_value_returns_err_on_non_existing_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add to THSV2 map, but no value in AlphaV2 map + let share = sfser_from_u64(1_u64); + TotalHotkeySharesV2::::insert(hotkey, netuid, share); + TotalHotkeyAlpha::::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let maybe_actual_value = share_pool.try_get_value(&coldkey); + assert!(maybe_actual_value.is_err()); + }); +} diff --git a/pallets/subtensor/src/tests/staking2.rs b/pallets/subtensor/src/tests/staking2.rs index 7de304e80e..82330289da 100644 --- a/pallets/subtensor/src/tests/staking2.rs +++ b/pallets/subtensor/src/tests/staking2.rs @@ -5,6 +5,7 @@ use frame_support::{ dispatch::{GetDispatchInfo, Pays}, weights::Weight, }; +use share_pool::SafeFloat; use sp_core::U256; use subtensor_runtime_common::{AlphaBalance, TaoBalance, Token}; use subtensor_swap_interface::SwapHandler; @@ -429,12 +430,10 @@ fn test_share_based_staking_denominator_precision() { netuid, stake_amount, ); - assert_eq!( - stake_amount, - Alpha::::get((hotkey1, coldkey1, netuid)) - .to_num::() - .into(), - ); + + let actual_stake: f64 = + SafeFloat::from(&AlphaV2::::get((hotkey1, coldkey1, netuid))).into(); + assert_eq!(stake_amount, (actual_stake as u64).into(),); SubtensorModule::decrease_stake_for_hotkey_and_coldkey_on_subnet( &hotkey1, &coldkey1, @@ -445,15 +444,7 @@ fn test_share_based_staking_denominator_precision() { let stake1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey1, &coldkey1, netuid, ); - let expected_remaining_stake = if (stake_amount.to_u64() as f64 - - unstake_amount.to_u64() as f64) - / (stake_amount.to_u64() as f64) - <= 0.00001 - { - AlphaBalance::ZERO - } else { - stake_amount - unstake_amount - }; + let expected_remaining_stake = stake_amount - unstake_amount; assert_eq!(stake1, expected_remaining_stake); }); }); diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 23488f3370..aca35de6bb 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -16,6 +16,7 @@ use frame_support::traits::schedule::DispatchTime; use frame_support::traits::schedule::v3::Named as ScheduleNamed; use frame_support::{assert_err, assert_noop, assert_ok}; use frame_system::{Config, RawOrigin}; +use share_pool::SafeFloat; use sp_core::{Get, H256, U256}; use sp_runtime::traits::Hash; use sp_runtime::traits::{DispatchInfoOf, DispatchTransaction, TransactionExtension}; @@ -1355,21 +1356,19 @@ fn test_do_swap_coldkey_effect_on_delegations() { approx_total_stake, epsilon = approx_total_stake / 100.into() ); - assert_eq!( - expected_stake, - Alpha::::get((delegate, new_coldkey, netuid)) - .to_num::() - .into(), - ); - assert_eq!(Alpha::::get((delegate, coldkey, netuid)), 0); - - assert_eq!( - expected_stake, - Alpha::::get((delegate, new_coldkey, netuid2)) - .to_num::() - .into() - ); - assert_eq!(Alpha::::get((delegate, coldkey, netuid2)), 0); + let actual_stake_new: u64 = + SafeFloat::from(&AlphaV2::::get((delegate, new_coldkey, netuid))).into(); + assert_eq!(expected_stake, actual_stake_new.into()); + let actual_stake_old: u64 = + SafeFloat::from(&AlphaV2::::get((delegate, coldkey, netuid))).into(); + assert_eq!(actual_stake_old, 0u64); + + let actual_stake_new_2: u64 = + SafeFloat::from(&AlphaV2::::get((delegate, new_coldkey, netuid2))).into(); + assert_eq!(expected_stake, actual_stake_new_2.into()); + let actual_stake_old_2: u64 = + SafeFloat::from(&AlphaV2::::get((delegate, coldkey, netuid2))).into(); + assert_eq!(actual_stake_old_2, 0u64); }); } diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index 87812a32aa..17dfc2f0a8 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -5,6 +5,7 @@ use codec::Encode; use frame_support::weights::Weight; use frame_support::{assert_err, assert_noop, assert_ok}; use frame_system::{Config, RawOrigin}; +use share_pool::{SafeFloat, SafeFloatSerializable}; use sp_core::{Get, H160, H256, U256}; use sp_runtime::SaturatedConversion; use substrate_fixed::types::U64F64; @@ -854,7 +855,7 @@ fn test_swap_owner_new_hotkey_already_exists() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_stake_success --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey::test_swap_stake_success --exact --nocapture #[test] fn test_swap_stake_success() { new_test_ext(1).execute_with(|| { @@ -910,7 +911,13 @@ fn test_swap_stake_success() { ); assert_eq!( TotalHotkeyShares::::get(new_hotkey, netuid), - U64F64::from_num(shares) + U64F64::from_num(0) + ); + assert_eq!( + f64::from(SafeFloat::from(&TotalHotkeySharesV2::::get( + new_hotkey, netuid + ))), + shares.to_num::() ); assert_eq!( Alpha::::get((old_hotkey, coldkey, netuid)), @@ -918,7 +925,104 @@ fn test_swap_stake_success() { ); assert_eq!( Alpha::::get((new_hotkey, coldkey, netuid)), - U64F64::from_num(amount) + U64F64::from_num(0) + ); + assert_eq!( + f64::from(SafeFloat::from(&AlphaV2::::get(( + new_hotkey, coldkey, netuid + )))), + amount as f64 + ); + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, old_hotkey), + AlphaBalance::ZERO + ); + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, new_hotkey), + amount.into() + ); + }); +} + +#[test] +fn test_swap_stake_v2_success() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(1); + let new_hotkey = U256::from(2); + let coldkey = U256::from(3); + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let amount = 10_000; + let shares = U64F64::from_num(123456); + let mut weight = Weight::zero(); + + // Initialize staking variables for old_hotkey + TotalHotkeyAlpha::::insert(old_hotkey, netuid, AlphaBalance::from(amount)); + TotalHotkeyAlphaLastEpoch::::insert( + old_hotkey, + netuid, + AlphaBalance::from(amount * 2), + ); + TotalHotkeySharesV2::::insert( + old_hotkey, + netuid, + SafeFloatSerializable::from(&SafeFloat::from(shares)), + ); + AlphaV2::::insert( + (old_hotkey, coldkey, netuid), + SafeFloatSerializable::from(&SafeFloat::from(U64F64::from_num(amount))), + ); + AlphaDividendsPerSubnet::::insert(netuid, old_hotkey, AlphaBalance::from(amount)); + + // Perform the swap + SubtensorModule::perform_hotkey_swap_on_all_subnets( + &old_hotkey, + &new_hotkey, + &coldkey, + &mut weight, + ); + + // Verify the swap + assert_eq!( + TotalHotkeyAlpha::::get(old_hotkey, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + TotalHotkeyAlpha::::get(new_hotkey, netuid), + amount.into() + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(old_hotkey, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(new_hotkey, netuid), + AlphaBalance::from(amount * 2) + ); + assert_eq!( + f64::from(SafeFloat::from(&TotalHotkeySharesV2::::get( + old_hotkey, netuid + ))), + 0_f64 + ); + assert_eq!( + f64::from(SafeFloat::from(&TotalHotkeySharesV2::::get( + new_hotkey, netuid + ))), + shares.to_num::() + ); + assert_eq!( + f64::from(SafeFloat::from(&AlphaV2::::get(( + old_hotkey, coldkey, netuid + )))), + 0_f64 + ); + assert_eq!( + f64::from(SafeFloat::from(&AlphaV2::::get(( + new_hotkey, coldkey, netuid + )))), + amount as f64 ); assert_eq!( AlphaDividendsPerSubnet::::get(netuid, old_hotkey), @@ -960,8 +1064,8 @@ fn test_swap_stake_old_hotkey_not_exist() { &mut weight, ); - // Verify that new_hotkey has the stake and old_hotkey does not - assert!(Alpha::::contains_key((new_hotkey, coldkey, netuid))); + // Verify that new_hotkey has the stake (in new AlphaV2 map) and old_hotkey does not + assert!(AlphaV2::::contains_key((new_hotkey, coldkey, netuid))); assert!(!Alpha::::contains_key((old_hotkey, coldkey, netuid))); }); } diff --git a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs index f71133859c..d134e30591 100644 --- a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs +++ b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs @@ -9,6 +9,7 @@ use subtensor_runtime_common::{AlphaBalance, NetUidStorageIndex, TaoBalance, Tok use super::mock::*; use crate::*; +use share_pool::{SafeFloat, SafeFloatSerializable}; use sp_core::{Get, H160, H256, U256}; use sp_runtime::SaturatedConversion; use substrate_fixed::types::U64F64; @@ -906,7 +907,7 @@ fn test_swap_owner_new_hotkey_already_exists() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey_with_subnet -- test_swap_stake_success --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_swap_stake_success --exact --nocapture #[test] fn test_swap_stake_success() { new_test_ext(1).execute_with(|| { @@ -963,7 +964,13 @@ fn test_swap_stake_success() { ); assert_eq!( TotalHotkeyShares::::get(new_hotkey, netuid), - U64F64::from_num(shares) + U64F64::from_num(0) + ); + assert_eq!( + f64::from(SafeFloat::from(&TotalHotkeySharesV2::::get( + new_hotkey, netuid + ))), + shares.to_num::() ); assert_eq!( Alpha::::get((old_hotkey, coldkey, netuid)), @@ -971,7 +978,105 @@ fn test_swap_stake_success() { ); assert_eq!( Alpha::::get((new_hotkey, coldkey, netuid)), - U64F64::from_num(amount) + U64F64::from_num(0) + ); + assert_eq!( + f64::from(SafeFloat::from(&AlphaV2::::get(( + new_hotkey, coldkey, netuid + )))), + amount as f64 + ); + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, old_hotkey), + AlphaBalance::ZERO + ); + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, new_hotkey), + AlphaBalance::from(amount) + ); + }); +} + +#[test] +fn test_swap_stake_v2_success() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(1); + let new_hotkey = U256::from(2); + let coldkey = U256::from(3); + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&old_hotkey, &coldkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + let amount = 10_000; + let shares = U64F64::from_num(123456); + + // Initialize staking variables for old_hotkey + TotalHotkeyAlpha::::insert(old_hotkey, netuid, AlphaBalance::from(amount)); + TotalHotkeyAlphaLastEpoch::::insert( + old_hotkey, + netuid, + AlphaBalance::from(amount * 2), + ); + TotalHotkeySharesV2::::insert( + old_hotkey, + netuid, + SafeFloatSerializable::from(&SafeFloat::from(shares)), + ); + AlphaV2::::insert( + (old_hotkey, coldkey, netuid), + SafeFloatSerializable::from(&SafeFloat::from(U64F64::from_num(amount))), + ); + AlphaDividendsPerSubnet::::insert(netuid, old_hotkey, AlphaBalance::from(amount)); + + // Perform the swap + System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &old_hotkey, + &new_hotkey, + Some(netuid) + ),); + + // Verify the swap + assert_eq!( + TotalHotkeyAlpha::::get(old_hotkey, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + TotalHotkeyAlpha::::get(new_hotkey, netuid), + AlphaBalance::from(amount) + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(old_hotkey, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(new_hotkey, netuid), + AlphaBalance::from(amount * 2) + ); + assert_eq!( + f64::from(SafeFloat::from(&TotalHotkeySharesV2::::get( + old_hotkey, netuid + ))), + 0_f64 + ); + assert_eq!( + f64::from(SafeFloat::from(&TotalHotkeySharesV2::::get( + new_hotkey, netuid + ))), + shares.to_num::() + ); + assert_eq!( + f64::from(SafeFloat::from(&AlphaV2::::get(( + old_hotkey, coldkey, netuid + )))), + 0_f64 + ); + assert_eq!( + f64::from(SafeFloat::from(&AlphaV2::::get(( + new_hotkey, coldkey, netuid + )))), + amount as f64 ); assert_eq!( AlphaDividendsPerSubnet::::get(netuid, old_hotkey), diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index 9fbeefd3b6..1a1cd0156e 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -47,6 +47,7 @@ pub trait SwapHandler { fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResult; fn toggle_user_liquidity(netuid: NetUid, enabled: bool); fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; + fn get_alpha_amount_for_tao(netuid: NetUid, tao_amount: TaoBalance) -> AlphaBalance; } pub trait DefaultPriceLimit diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index b12fd4d567..5063972082 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -289,9 +289,9 @@ impl BalanceOps for MockBalanceOps { _coldkey: &AccountId, _hotkey: &AccountId, _netuid: NetUid, - alpha: AlphaBalance, - ) -> Result { - Ok(alpha) + _alpha: AlphaBalance, + ) -> Result<(), DispatchError> { + Ok(()) } } diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 1ef6009ce4..1a912dcc24 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -1157,4 +1157,23 @@ impl SwapHandler for Pallet { fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { Self::do_clear_protocol_liquidity(netuid) } + + /// Get the amount of Alpha that needs to be sold to get a given amount of Tao + fn get_alpha_amount_for_tao(netuid: NetUid, tao_amount: TaoBalance) -> AlphaBalance { + match T::SubnetInfo::mechanism(netuid.into()) { + 1 => { + // For uniswap v3: Use no-slippage method. Amount is supposed to be small, + // hence we can neglect slippage and return slightly lower amount. + let alpha_price = Self::current_price(netuid.into()); + AlphaBalance::from( + U96F32::from(u64::from(tao_amount)) + .safe_div(alpha_price) + .saturating_to_num::(), + ) + } + + // Static subnet, alpha == tao + _ => u64::from(tao_amount).into(), + } + } } diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 281467277e..f096c80210 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -533,12 +533,7 @@ mod pallet { let tao_provided = T::BalanceOps::decrease_balance(&coldkey, result.tao)?; ensure!(tao_provided == result.tao, Error::::InsufficientBalance); - let alpha_provided = - T::BalanceOps::decrease_stake(&coldkey, &hotkey, netuid.into(), result.alpha)?; - ensure!( - alpha_provided == result.alpha, - Error::::InsufficientBalance - ); + T::BalanceOps::decrease_stake(&coldkey, &hotkey, netuid.into(), result.alpha)?; // Emit an event Self::deposit_event(Event::LiquidityModified { diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 211020cbba..e2b53c5142 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -2230,15 +2230,10 @@ fn liquidate_v3_refunds_user_funds_and_clears_state() { // Mirror extrinsic bookkeeping: withdraw funds & bump provided‑reserve counters. let tao_taken = ::BalanceOps::decrease_balance(&cold, need_tao.into()) .expect("decrease TAO"); - let alpha_taken = ::BalanceOps::decrease_stake( - &cold, - &hot, - netuid.into(), - need_alpha.into(), - ) - .expect("decrease ALPHA"); + ::BalanceOps::decrease_stake(&cold, &hot, netuid.into(), need_alpha.into()) + .expect("decrease ALPHA"); TaoReserve::increase_provided(netuid.into(), tao_taken); - AlphaReserve::increase_provided(netuid.into(), alpha_taken); + AlphaReserve::increase_provided(netuid.into(), need_alpha.into()); // Users‑only liquidation. assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); @@ -2297,14 +2292,14 @@ fn refund_alpha_single_provider_exact() { let alpha_before_total = alpha_before_hot + alpha_before_owner; // --- Mimic extrinsic bookkeeping: withdraw α and record provided reserve. - let alpha_taken = ::BalanceOps::decrease_stake( + ::BalanceOps::decrease_stake( &cold, &hot, netuid.into(), alpha_needed.into(), ) .expect("decrease ALPHA"); - AlphaReserve::increase_provided(netuid.into(), alpha_taken); + AlphaReserve::increase_provided(netuid.into(), alpha_needed.into()); // --- Act: users‑only dissolve. assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); @@ -2371,16 +2366,14 @@ fn refund_alpha_multiple_providers_proportional_to_principal() { let a2_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); let a2_before = a2_before_hot + a2_before_owner; - // Withdraw α and account reserves for each provider. - let a1_taken = - ::BalanceOps::decrease_stake(&c1, &h1, netuid.into(), a1.into()) - .expect("decrease α #1"); - AlphaReserve::increase_provided(netuid.into(), a1_taken); + // Withdraw alpha and account reserves for each provider. + ::BalanceOps::decrease_stake(&c1, &h1, netuid.into(), a1.into()) + .expect("decrease alpha #1"); + AlphaReserve::increase_provided(netuid.into(), a1.into()); - let a2_taken = - ::BalanceOps::decrease_stake(&c2, &h2, netuid.into(), a2.into()) - .expect("decrease α #2"); - AlphaReserve::increase_provided(netuid.into(), a2_taken); + ::BalanceOps::decrease_stake(&c2, &h2, netuid.into(), a2.into()) + .expect("decrease alpha #2"); + AlphaReserve::increase_provided(netuid.into(), a2.into()); // Act assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); @@ -2433,16 +2426,14 @@ fn refund_alpha_same_cold_multiple_hotkeys_conserved_to_owner() { let before_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); let before_total = before_hot1 + before_hot2 + before_owner; - // Withdraw α from both hotkeys; track provided‑reserve. - let t1 = - ::BalanceOps::decrease_stake(&cold, &hot1, netuid.into(), a1.into()) - .expect("decr α #hot1"); - AlphaReserve::increase_provided(netuid.into(), t1); + // Withdraw alpha from both hotkeys; track provided‑reserve. + ::BalanceOps::decrease_stake(&cold, &hot1, netuid.into(), a1.into()) + .expect("decr alpha #hot1"); + AlphaReserve::increase_provided(netuid.into(), a1.into()); - let t2 = - ::BalanceOps::decrease_stake(&cold, &hot2, netuid.into(), a2.into()) - .expect("decr α #hot2"); - AlphaReserve::increase_provided(netuid.into(), t2); + ::BalanceOps::decrease_stake(&cold, &hot2, netuid.into(), a2.into()) + .expect("decr alpha #hot2"); + AlphaReserve::increase_provided(netuid.into(), a2.into()); // Act assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); @@ -2524,7 +2515,7 @@ fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() { // --- Mirror extrinsic bookkeeping: withdraw τ & α; bump provided reserves --- let tao_taken = ::BalanceOps::decrease_balance(&cold, tao_needed.into()) .expect("decrease TAO"); - let alpha_taken = ::BalanceOps::decrease_stake( + ::BalanceOps::decrease_stake( &cold, &hot, netuid.into(), @@ -2533,7 +2524,7 @@ fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() { .expect("decrease ALPHA"); TaoReserve::increase_provided(netuid.into(), tao_taken); - AlphaReserve::increase_provided(netuid.into(), alpha_taken); + AlphaReserve::increase_provided(netuid.into(), alpha_needed.into()); // --- Act: dissolve (GREEN PATH: permitted validators exist) --- assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); diff --git a/pallets/transaction-fee/Cargo.toml b/pallets/transaction-fee/Cargo.toml index 2180732677..d5a5c2f418 100644 --- a/pallets/transaction-fee/Cargo.toml +++ b/pallets/transaction-fee/Cargo.toml @@ -25,6 +25,7 @@ pallet-preimage = { workspace = true, default-features = false, optional = true pallet-scheduler = { workspace = true, default-features = false, optional = true } [dev-dependencies] +approx.workspace = true frame-executive.workspace = true pallet-evm-chain-id.workspace = true scale-info.workspace = true diff --git a/pallets/transaction-fee/src/lib.rs b/pallets/transaction-fee/src/lib.rs index 53e6cb816b..ec9eadf773 100644 --- a/pallets/transaction-fee/src/lib.rs +++ b/pallets/transaction-fee/src/lib.rs @@ -29,9 +29,9 @@ use subtensor_swap_interface::SwapHandler; // Misc use core::marker::PhantomData; use smallvec::smallvec; +use sp_runtime::traits::SaturatedConversion; use sp_std::vec::Vec; -use substrate_fixed::types::U96F32; -use subtensor_runtime_common::{AuthorshipInfo, NetUid, TaoBalance, Token}; +use subtensor_runtime_common::{AlphaBalance, AuthorshipInfo, NetUid, TaoBalance}; // Tests #[cfg(test)] @@ -67,7 +67,7 @@ pub trait AlphaFeeHandler { coldkey: &AccountIdOf, alpha_vec: &[(AccountIdOf, NetUid)], tao_amount: TaoBalance, - ); + ) -> (AlphaBalance, TaoBalance); fn get_all_netuids_for_coldkey_and_hotkey( coldkey: &AccountIdOf, hotkey: &AccountIdOf, @@ -99,12 +99,7 @@ where { fn on_nonzero_unbalanced(imbalance: BalancesImbalanceOf) { if let Some(author) = T::author() { - // Pay block author instead of burning. - // One of these is the right call depending on your exact fungible API: - // let _ = pallet_balances::Pallet::::resolve(&author, imbalance); - // or: let _ = pallet_balances::Pallet::::deposit(&author, imbalance.peek(), Precision::BestEffort); - // - // Prefer "resolve" (moves the actual imbalance) if available: + // Pay block author let _ = as Balanced<_>>::resolve(&author, imbalance); } else { // Fallback: if no author, burn (or just drop). @@ -121,9 +116,9 @@ where T: pallet_subtensor_swap::Config, { /// This function checks if tao_amount fee can be withdraw in Alpha currency - /// by converting Alpha to TAO at the current price and ignoring slippage. + /// by converting Alpha to TAO using the current pool conditions. /// - /// If this function returns true, the transaction will be included in the block + /// If this function returns true, the transaction will be added to the mempool /// and Alpha will be withdraw from the account, no matter whether transaction /// is successful or not. /// @@ -135,64 +130,67 @@ where alpha_vec: &[(AccountIdOf, NetUid)], tao_amount: TaoBalance, ) -> bool { - if alpha_vec.is_empty() { - // Alpha vector is empty, nothing to withdraw + if alpha_vec.len() != 1 { + // Multi-subnet alpha fee deduction is prohibited. return false; } - // Divide tao_amount among all alpha entries - let tao_per_entry = tao_amount - .checked_div(&TaoBalance::from(alpha_vec.len())) - .unwrap_or(TaoBalance::ZERO); - - // The rule here is that we should be able to withdraw at least from one entry. - // This is not ideal because it may not pay all fees, but UX is the priority - // and this approach still provides spam protection. - alpha_vec.iter().any(|(hotkey, netuid)| { - let alpha_balance = U96F32::saturating_from_num( + if let Some((hotkey, netuid)) = alpha_vec.first() { + let alpha_balance = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, *netuid, - ), + ); + let alpha_fee = pallet_subtensor_swap::Pallet::::get_alpha_amount_for_tao( + *netuid, + tao_amount.into(), ); - let alpha_price = pallet_subtensor_swap::Pallet::::current_alpha_price(*netuid); - alpha_price.saturating_mul(alpha_balance) >= u64::from(tao_per_entry) - }) + alpha_balance >= alpha_fee + } else { + false + } } fn withdraw_in_alpha( coldkey: &AccountIdOf, alpha_vec: &[(AccountIdOf, NetUid)], tao_amount: TaoBalance, - ) { - if alpha_vec.is_empty() { - return; + ) -> (AlphaBalance, TaoBalance) { + if alpha_vec.len() != 1 { + return (0.into(), 0.into()); } - let tao_per_entry = tao_amount - .checked_div(&TaoBalance::from(alpha_vec.len())) - .unwrap_or(TaoBalance::ZERO); - - alpha_vec.iter().for_each(|(hotkey, netuid)| { - // Divide tao_amount evenly among all alpha entries - let alpha_balance = U96F32::saturating_from_num( + if let Some((hotkey, netuid)) = alpha_vec.first() { + let alpha_balance = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, *netuid, - ), + ); + let mut alpha_equivalent = pallet_subtensor_swap::Pallet::::get_alpha_amount_for_tao( + *netuid, + tao_amount.into(), ); - let alpha_price = pallet_subtensor_swap::Pallet::::current_alpha_price(*netuid); - let alpha_fee = U96F32::saturating_from_num(tao_per_entry) - .checked_div(alpha_price) - .unwrap_or(alpha_balance) - .min(alpha_balance) - .saturating_to_num::(); - - pallet_subtensor::Pallet::::decrease_stake_for_hotkey_and_coldkey_on_subnet( + if alpha_equivalent.is_zero() { + alpha_equivalent = alpha_balance; + } + let alpha_fee = alpha_equivalent.min(alpha_balance); + + // Sell alpha_fee and burn received tao (ignore unstake_from_subnet return). + let swap_result = pallet_subtensor::Pallet::::unstake_from_subnet( hotkey, coldkey, *netuid, - alpha_fee.into(), + alpha_fee, + 0.into(), + true, ); - }); + + if let Ok(tao_amount) = swap_result { + (alpha_fee, tao_amount) + } else { + (0.into(), 0.into()) + } + } else { + (0.into(), 0.into()) + } } fn get_all_netuids_for_coldkey_and_hotkey( @@ -211,11 +209,13 @@ where } } -/// Enum that describes either a withdrawn amount of transaction fee in TAO or the -/// fact that fee was charged in Alpha (without an amount because it is not needed) +/// Enum that describes either a withdrawn amount of transaction fee in TAO or +/// the exact charged Alpha amount. pub enum WithdrawnFee>> { + // Contains withdrawn TAO amount Tao(Credit, F>), - Alpha, + // Contains withdrawn Alpha amount and resulting swapped TAO + Alpha((AlphaBalance, TaoBalance)), } /// Custom OnChargeTransaction implementation based on standard FungibleAdapter from transaction_payment @@ -231,7 +231,7 @@ impl SubtensorTxFeeHandler { /// distributed evenly between subnets in case of multiple subnets. pub fn fees_in_alpha(who: &AccountIdOf, call: &CallOf) -> Vec<(AccountIdOf, NetUid)> where - T: frame_system::Config + pallet_subtensor::Config, + T: frame_system::Config + pallet_subtensor::Config + AuthorshipInfo>, CallOf: IsSubType>, OU: AlphaFeeHandler, { @@ -303,18 +303,18 @@ impl SubtensorTxFeeHandler { impl OnChargeTransaction for SubtensorTxFeeHandler where - T: PTPConfig + pallet_subtensor::Config, + T: PTPConfig + pallet_subtensor::Config + AuthorshipInfo>, CallOf: IsSubType>, F: Balanced, OU: OnUnbalanced> + AlphaFeeHandler, - >>::Balance: Into, + >>::Balance: Into + From, { type LiquidityInfo = Option>; type Balance = ::AccountId>>::Balance; fn withdraw_fee( who: &AccountIdOf, - _call: &CallOf, + call: &CallOf, _dispatch_info: &DispatchInfoOf>, fee: Self::Balance, _tip: Self::Balance, @@ -333,12 +333,13 @@ where ) { Ok(imbalance) => Ok(Some(WithdrawnFee::Tao(imbalance))), Err(_) => { - // let alpha_vec = Self::fees_in_alpha::(who, call); - // if !alpha_vec.is_empty() { - // let fee_u64: u64 = fee.into(); - // OU::withdraw_in_alpha(who, &alpha_vec, fee_u64); - // return Ok(Some(WithdrawnFee::Alpha)); - // } + let alpha_vec = Self::fees_in_alpha::(who, call); + if !alpha_vec.is_empty() { + let fee_u64: u64 = fee.saturated_into::(); + let (alpha_fee, tao_amount) = + OU::withdraw_in_alpha(who, &alpha_vec, fee_u64.into()); + return Ok(Some(WithdrawnFee::Alpha((alpha_fee, tao_amount)))); + } Err(InvalidTransaction::Payment.into()) } } @@ -346,7 +347,7 @@ where fn can_withdraw_fee( who: &AccountIdOf, - _call: &CallOf, + call: &CallOf, _dispatch_info: &DispatchInfoOf>, fee: Self::Balance, _tip: Self::Balance, @@ -359,14 +360,14 @@ where match F::can_withdraw(who, fee) { WithdrawConsequence::Success => Ok(()), _ => { - // // Fallback to fees in Alpha if possible - // let alpha_vec = Self::fees_in_alpha::(who, call); - // if !alpha_vec.is_empty() { - // let fee_u64: u64 = fee.into(); - // if OU::can_withdraw_in_alpha(who, &alpha_vec, fee_u64) { - // return Ok(()); - // } - // } + // Fallback to fees in Alpha if possible + let alpha_vec = Self::fees_in_alpha::(who, call); + if !alpha_vec.is_empty() { + let fee_u64: u64 = fee.saturated_into::(); + if OU::can_withdraw_in_alpha(who, &alpha_vec, fee_u64.into()) { + return Ok(()); + } + } Err(InvalidTransaction::Payment.into()) } } @@ -404,7 +405,21 @@ where let (tip, fee) = adjusted_paid.split(tip); OU::on_unbalanceds(Some(fee).into_iter().chain(Some(tip))); } - WithdrawnFee::Alpha => { + WithdrawnFee::Alpha((alpha_fee, tao_amount)) => { + if let Some(author) = T::author() { + // Pay block author + let _ = F::deposit(&author, tao_amount.into(), Precision::BestEffort) + .unwrap_or_else(|_| Debt::::zero()); + } else { + // Fallback: no author => do nothing + } + frame_system::Pallet::::deposit_event( + pallet_subtensor::Event::::TransactionFeePaidWithAlpha { + who: who.clone(), + alpha_fee, + tao_amount, + }, + ); // Subtensor does not refund Alpha fees, charges are final } } diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index f0ad323168..1b4eac0706 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -413,8 +413,8 @@ impl pallet_subtensor_swap::Config for Test { type SubnetInfo = SubtensorModule; type BalanceOps = SubtensorModule; type ProtocolId = SwapProtocolId; - type TaoReserve = pallet_subtensor::TaoCurrencyReserve; - type AlphaReserve = pallet_subtensor::AlphaCurrencyReserve; + type TaoReserve = pallet_subtensor::TaoBalanceReserve; + type AlphaReserve = pallet_subtensor::AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; diff --git a/pallets/transaction-fee/src/tests/mod.rs b/pallets/transaction-fee/src/tests/mod.rs index 5dd353dcde..c7c39eb030 100644 --- a/pallets/transaction-fee/src/tests/mod.rs +++ b/pallets/transaction-fee/src/tests/mod.rs @@ -1,8 +1,9 @@ -#![allow(clippy::indexing_slicing, clippy::unwrap_used)] -use crate::TransactionSource; -use frame_support::assert_ok; +#![allow(clippy::expect_used, clippy::indexing_slicing, clippy::unwrap_used)] +use crate::{AlphaFeeHandler, SubtensorTxFeeHandler, TransactionFeeHandler, TransactionSource}; +use approx::assert_abs_diff_eq; use frame_support::dispatch::GetDispatchInfo; use frame_support::pallet_prelude::Zero; +use frame_support::{assert_err, assert_ok}; use pallet_subtensor_swap::AlphaSqrtPrice; use sp_runtime::{ traits::{DispatchTransaction, TransactionExtension, TxBaseImplication}, @@ -71,12 +72,99 @@ fn test_remove_stake_fees_tao() { // Remove stake extrinsic should pay fees in TAO because ck has sufficient TAO balance assert!(actual_tao_fee > 0.into()); assert_eq!(actual_alpha_fee, AlphaBalance::from(0)); + + let events = System::events(); + assert!(events.iter().any(|event_record| { + matches!( + &event_record.event, + RuntimeEvent::TransactionPayment( + pallet_transaction_payment::Event::TransactionFeePaid { .. } + ) + ) + })); + assert!(!events.iter().any(|event_record| { + matches!( + &event_record.event, + RuntimeEvent::SubtensorModule(SubtensorEvent::TransactionFeePaidWithAlpha { .. }) + ) + })); + }); +} + +// cargo test --package subtensor-transaction-fee --lib -- tests::test_rejects_multi_subnet_alpha_fee_deduction --exact --show-output +#[test] +fn test_rejects_multi_subnet_alpha_fee_deduction() { + new_test_ext().execute_with(|| { + let sn = setup_subnets(2, 1); + let stake_amount = TAO; + setup_stake( + sn.subnets[0].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + setup_stake( + sn.subnets[1].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + + let alpha_before_0 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + let alpha_before_1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[1].netuid, + ); + + let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::unstake_all { + hotkey: sn.hotkeys[0], + }); + let alpha_vec = + SubtensorTxFeeHandler::>::fees_in_alpha::( + &sn.coldkey, + &call, + ); + assert_eq!(alpha_vec.len(), 2); + + assert!( + ! as AlphaFeeHandler>::can_withdraw_in_alpha( + &sn.coldkey, + &alpha_vec, + 1.into(), + ) + ); + assert_eq!( + as AlphaFeeHandler>::withdraw_in_alpha( + &sn.coldkey, + &alpha_vec, + 1.into(), + ), + (0.into(), 0.into()) + ); + + let alpha_after_0 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + let alpha_after_1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[1].netuid, + ); + + assert_eq!(alpha_before_0, alpha_after_0); + assert_eq!(alpha_before_1, alpha_after_1); }); } // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_fees_alpha --exact --show-output #[test] -#[ignore] fn test_remove_stake_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -90,7 +178,7 @@ fn test_remove_stake_fees_alpha() { ); // Simulate stake removal to get how much TAO should we get for unstaked Alpha - let (expected_unstaked_tao, _swap_fee) = + let (expected_unstaked_tao, swap_fee) = mock::swap_alpha_to_tao(sn.subnets[0].netuid, unstake_amount); // Forse-set signer balance to ED @@ -100,6 +188,10 @@ fn test_remove_stake_fees_alpha() { current_balance - ExistentialDeposit::get(), ); + // Get the block builder balance + let block_builder = U256::from(MOCK_BLOCK_BUILDER); + let block_builder_balance_before = Balances::free_balance(block_builder); + // Remove stake let balance_before = Balances::free_balance(sn.coldkey); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -113,6 +205,8 @@ fn test_remove_stake_fees_alpha() { amount_unstaked: unstake_amount, }); + System::reset_events(); + // Dispatch the extrinsic with ChargeTransactionPayment extension let info = call.get_dispatch_info(); let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0.into()); @@ -136,8 +230,51 @@ fn test_remove_stake_fees_alpha() { let actual_alpha_fee = alpha_before - alpha_after - unstake_amount; // Remove stake extrinsic should pay fees in Alpha - assert_eq!(actual_tao_fee, 0.into()); + assert_abs_diff_eq!(actual_tao_fee, 0.into(), epsilon = 10.into()); assert!(actual_alpha_fee > 0.into()); + + // Assert that swapped TAO from alpha fee goes to block author + let block_builder_fee_portion = 1.; + let expected_block_builder_swap_reward = swap_fee as f64 * block_builder_fee_portion; + let expected_tx_fee = 14000.; // Use very low value (0.000014) for less test flakiness, value before we 10x tx fees + let block_builder_balance_after = Balances::free_balance(block_builder); + let actual_block_builder_reward = + block_builder_balance_after - block_builder_balance_before; + assert!( + u64::from(actual_block_builder_reward) as f64 + >= expected_block_builder_swap_reward + expected_tx_fee + ); + + let events = System::events(); + let alpha_event = events + .iter() + .position(|event_record| { + matches!( + &event_record.event, + RuntimeEvent::SubtensorModule(SubtensorEvent::TransactionFeePaidWithAlpha { + who, + alpha_fee, + tao_amount: _, + }) if who == &sn.coldkey && *alpha_fee == actual_alpha_fee + ) + }) + .expect("expected TransactionFeePaidWithAlpha event"); + let tao_event = events + .iter() + .position(|event_record| { + matches!( + &event_record.event, + RuntimeEvent::TransactionPayment( + pallet_transaction_payment::Event::TransactionFeePaid { who, .. } + ) if who == &sn.coldkey + ) + }) + .expect("expected TransactionFeePaid event"); + + assert!( + alpha_event < tao_event, + "expected TransactionFeePaidWithAlpha before TransactionFeePaid" + ); }); } @@ -146,7 +283,6 @@ fn test_remove_stake_fees_alpha() { // // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_root --exact --show-output #[test] -#[ignore] fn test_remove_stake_root() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -205,7 +341,6 @@ fn test_remove_stake_root() { // // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_completely_root --exact --show-output #[test] -#[ignore] fn test_remove_stake_completely_root() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -257,7 +392,6 @@ fn test_remove_stake_completely_root() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_completely_fees_alpha --exact --show-output #[test] -#[ignore] fn test_remove_stake_completely_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -388,7 +522,6 @@ fn test_remove_stake_not_enough_balance_for_fees() { // // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_edge_alpha --exact --show-output #[test] -#[ignore] fn test_remove_stake_edge_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -523,11 +656,11 @@ fn test_remove_stake_failing_transaction_tao_fees() { }); } -// Validation passes, but transaction fails => Alpha fees are paid +// Validation passes, but transaction fails (artificially disable subtoken) => +// Alpha fees are still paid // // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_failing_transaction_alpha_fees --exact --show-output #[test] -#[ignore] fn test_remove_stake_failing_transaction_alpha_fees() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -540,8 +673,11 @@ fn test_remove_stake_failing_transaction_alpha_fees() { stake_amount, ); - // Make unstaking fail by reducing liquidity to critical - SubnetTAO::::insert(sn.subnets[0].netuid, TaoBalance::from(1)); + // Provide adequate TAO reserve so that sim swap works ok in validation + SubnetTAO::::insert(sn.subnets[0].netuid, TaoBalance::from(1_000_000_000_u64)); + + // Provide Alpha reserve so that price is about 1.0 + SubnetAlphaIn::::insert(sn.subnets[0].netuid, AlphaBalance::from(1_000_000_000_u64)); // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); @@ -550,6 +686,9 @@ fn test_remove_stake_failing_transaction_alpha_fees() { current_balance - ExistentialDeposit::get(), ); + // Disable subtoken so that removing stake tx fails (still allows the validation to pass) + pallet_subtensor::SubtokenEnabled::::insert(sn.subnets[0].netuid, false); + // Remove stake let balance_before = Balances::free_balance(sn.coldkey); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -560,7 +699,7 @@ fn test_remove_stake_failing_transaction_alpha_fees() { let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::remove_stake { hotkey: sn.hotkeys[0], netuid: sn.subnets[0].netuid, - amount_unstaked: unstake_amount, + amount_unstaked: alpha_before, }); // Dispatch the extrinsic with ChargeTransactionPayment extension @@ -593,7 +732,6 @@ fn test_remove_stake_failing_transaction_alpha_fees() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_limit_fees_alpha --exact --show-output #[test] -#[ignore] fn test_remove_stake_limit_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -607,8 +745,12 @@ fn test_remove_stake_limit_fees_alpha() { ); // Simulate stake removal to get how much TAO should we get for unstaked Alpha - let (expected_unstaked_tao, _swap_fee) = - mock::swap_alpha_to_tao(sn.subnets[0].netuid, unstake_amount); + let alpha_fee = AlphaBalance::from(24229); // This is measured alpha fee that matches the withdrawn tx fee + let (expected_burned_tao_fees, _swap_fee) = + mock::swap_alpha_to_tao(sn.subnets[0].netuid, alpha_fee); + let (expected_unstaked_tao_plus_fees, _swap_fee) = + mock::swap_alpha_to_tao(sn.subnets[0].netuid, unstake_amount + alpha_fee); + let expected_unstaked_tao = expected_unstaked_tao_plus_fees - expected_burned_tao_fees; // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); @@ -654,14 +796,13 @@ fn test_remove_stake_limit_fees_alpha() { let actual_alpha_fee = alpha_before - alpha_after - unstake_amount; // Remove stake extrinsic should pay fees in Alpha - assert_eq!(actual_tao_fee, 0.into()); + assert_abs_diff_eq!(actual_tao_fee, 0.into(), epsilon = 100.into()); assert!(actual_alpha_fee > 0.into()); }); } // cargo test --package subtensor-transaction-fee --lib -- tests::test_unstake_all_fees_alpha --exact --show-output #[test] -#[ignore] fn test_unstake_all_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -703,8 +844,22 @@ fn test_unstake_all_fees_alpha() { }); // Dispatch the extrinsic with ChargeTransactionPayment extension + // Get invalid payment because we cannot pay fees in multiple alphas let info = call.get_dispatch_info(); let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0.into()); + assert_err!( + ext.clone().dispatch_transaction( + RuntimeOrigin::signed(coldkey).into(), + call.clone(), + &info, + 0, + 0, + ), + TransactionValidityError::Invalid(InvalidTransaction::Payment), + ); + + // Give the coldkey TAO balance - now should unstake ok + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_u64.into()); assert_ok!(ext.dispatch_transaction( RuntimeOrigin::signed(coldkey).into(), call, @@ -734,7 +889,6 @@ fn test_unstake_all_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_unstake_all_alpha_fees_alpha --exact --show-output #[test] -#[ignore] fn test_unstake_all_alpha_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -771,8 +925,22 @@ fn test_unstake_all_alpha_fees_alpha() { }); // Dispatch the extrinsic with ChargeTransactionPayment extension + // Get invalid payment because we cannot pay fees in multiple alphas let info = call.get_dispatch_info(); let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0.into()); + assert_err!( + ext.clone().dispatch_transaction( + RuntimeOrigin::signed(coldkey).into(), + call.clone(), + &info, + 0, + 0, + ), + TransactionValidityError::Invalid(InvalidTransaction::Payment), + ); + + // Give the coldkey TAO balance - now should unstake ok + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_u64.into()); assert_ok!(ext.dispatch_transaction( RuntimeOrigin::signed(coldkey).into(), call, @@ -802,7 +970,6 @@ fn test_unstake_all_alpha_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_move_stake_fees_alpha --exact --show-output #[test] -#[ignore] fn test_move_stake_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -874,7 +1041,6 @@ fn test_move_stake_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_transfer_stake_fees_alpha --exact --show-output #[test] -#[ignore] fn test_transfer_stake_fees_alpha() { new_test_ext().execute_with(|| { let destination_coldkey = U256::from(100000); @@ -947,7 +1113,6 @@ fn test_transfer_stake_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_swap_stake_fees_alpha --exact --show-output #[test] -#[ignore] fn test_swap_stake_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -1018,7 +1183,6 @@ fn test_swap_stake_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_swap_stake_limit_fees_alpha --exact --show-output #[test] -#[ignore] fn test_swap_stake_limit_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -1091,7 +1255,6 @@ fn test_swap_stake_limit_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_burn_alpha_fees_alpha --exact --show-output #[test] -#[ignore] fn test_burn_alpha_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -1153,7 +1316,6 @@ fn test_burn_alpha_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_recycle_alpha_fees_alpha --exact --show-output #[test] -#[ignore] fn test_recycle_alpha_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -1258,7 +1420,7 @@ fn test_add_stake_fees_go_to_block_builder() { // Expect that block builder balance has increased by both the swap fee and the transaction fee let expected_block_builder_swap_reward = swap_fee as f64 * block_builder_fee_portion; - let expected_tx_fee = 0.000136; // Use very low value for less test flakiness + let expected_tx_fee = 14000.; // Use very low value (0.000014) for less test flakiness, value before we 10x tx fees let block_builder_balance_after = Balances::free_balance(block_builder); let actual_reward = block_builder_balance_after - block_builder_balance_before; assert!( diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index 93bc9be3a7..8ec79b4092 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -315,7 +315,8 @@ where let hotkey = R::AccountId::from(hotkey.0); let mut coldkeys: Vec = vec![]; let netuid = NetUid::from(try_u16_from_u256(netuid)?); - for ((coldkey, netuid_in_alpha), _) in pallet_subtensor::Alpha::::iter_prefix((hotkey,)) + for (coldkey, netuid_in_alpha, _) in + pallet_subtensor::Pallet::::alpha_iter_single_prefix(&hotkey) { if netuid == netuid_in_alpha { let key: [u8; 32] = coldkey.into(); diff --git a/primitives/share-pool/Cargo.toml b/primitives/share-pool/Cargo.toml index ba42b0d77d..4fdaaffd6c 100644 --- a/primitives/share-pool/Cargo.toml +++ b/primitives/share-pool/Cargo.toml @@ -4,13 +4,38 @@ version = "0.1.0" edition.workspace = true [dependencies] +approx.workspace = true +codec.workspace = true +lencode.workspace = true +log.workspace = true +scale-info.workspace = true substrate-fixed.workspace = true +subtensor-macros.workspace = true sp-std.workspace = true +num-traits.workspace = true +safe-bigmath.workspace = true safe-math.workspace = true +[dev-dependencies] +sp-arithmetic.workspace = true +rand = { version = "0.8", features = ["std", "std_rng"] } +rayon = "1.10" + [lints] workspace = true [features] default = ["std"] -std = ["substrate-fixed/std", "sp-std/std", "safe-math/std"] +std = [ + "codec/std", + "lencode/std", + "log/std", + "rand/std", + "scale-info/std", + "substrate-fixed/std", + "sp-arithmetic/std", + "sp-std/std", + "num-traits/std", + "safe-math/std", + "safe-bigmath/std" +] diff --git a/primitives/share-pool/src/lib.rs b/primitives/share-pool/src/lib.rs index d43f36259c..f0aee427b3 100644 --- a/primitives/share-pool/src/lib.rs +++ b/primitives/share-pool/src/lib.rs @@ -1,26 +1,427 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::result_unit_err)] -use safe_math::*; +use codec::{Decode, Encode}; +use lencode::io::Cursor; +use lencode::{Decode as LenDecode, Encode as LenEncode}; +#[cfg(not(feature = "std"))] +use num_traits::float::FloatCore as _; +use safe_bigmath::*; +use scale_info::TypeInfo; +use sp_arithmetic::Perquintill; use sp_std::marker; use sp_std::ops::Neg; -use substrate_fixed::types::{I64F64, U64F64}; +use sp_std::vec::Vec; +use substrate_fixed::types::U64F64; +use subtensor_macros::freeze_struct; + +// Maximum value that can be represented with SafeFloat +pub const SAFE_FLOAT_MAX: u128 = 1_000_000_000_000_000_000_000_u128; +pub const SAFE_FLOAT_MAX_EXP: i64 = 21_i64; + +/// Controlled precision floating point number with efficient storage +/// +/// Precision is controlled in a way that keeps enough mantissa digits so +/// that updating hotkey stake by 1 rao makes difference in the resulting shared +/// pool variables (both coldkey share and share pool denominator), but also +/// precision should be limited so that updating by 0.1 rao does not make the +/// difference (because there's no such thing as 0.1 rao, rao is integer). +#[derive(Clone, Debug)] +pub struct SafeFloat { + mantissa: SafeInt, + exponent: i64, +} + +#[freeze_struct("2e6be279168faadf")] +#[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] +pub struct SafeFloatSerializable { + mantissa: Vec, + exponent: i64, +} + +/// Power of 10 in SafeInt +/// Uses SafeInt pow function that accepts u32 argument +/// and the formula: 10^(a*b) = (10^a)^b +#[allow( + clippy::arithmetic_side_effects, + reason = "SafeInt never overflows and never panics" +)] +fn pow10(e: u64) -> SafeInt { + if e == 0 { + return SafeInt::one(); + } + let exp_high = ((e & 0xFFFFFFFF00000000) >> 32) as u32; + let exp_low = (e & 0xFFFFFFFF) as u32; + let ten_exp_low = SafeInt::from(10u32).pow(exp_low); + let ten_exp_high = SafeInt::from(10u32).pow(exp_high); + let two_exp_16 = 1u32 << 16; + + ten_exp_high.pow(two_exp_16).pow(two_exp_16) * ten_exp_low +} + +fn intlog10(a: &SafeInt) -> u64 { + let scale = SafeInt::from(1_000_000_000_000_000_000i128); + let precision = 256u32; + let max_iters = Some(4096); + (a.log10(&scale, precision, max_iters)) + .unwrap_or_default() + .to_u64() + .unwrap_or_default() +} + +impl SafeFloat { + pub fn zero() -> Self { + SafeFloat { + mantissa: SafeInt::zero(), + exponent: 0_i64, + } + } + + #[allow( + clippy::arithmetic_side_effects, + reason = "SafeInt never overflows and never panics" + )] + pub fn new(mantissa: SafeInt, exponent: i64) -> Option { + // Cap at SAFE_FLOAT_MAX + let max_value = SafeInt::from(SAFE_FLOAT_MAX) + SafeInt::one(); + if !(mantissa.clone() / max_value).unwrap_or_default().is_zero() { + return None; + } + + let mut safe_float = SafeFloat { mantissa, exponent }; + + if safe_float.normalize() { + Some(safe_float) + } else { + None + } + } + + /// Adjusts mantissa and exponent of this floating point number so that + /// SAFE_FLOAT_MAX <= mantissa < 10 * SAFE_FLOAT_MAX + /// + /// Returns true in case of success or false if exponent over- or underflows + #[allow( + clippy::arithmetic_side_effects, + reason = "SafeInt never overflows and never panics" + )] + pub(crate) fn normalize(&mut self) -> bool { + let max_value = SafeInt::from(SAFE_FLOAT_MAX); + let max_value_div10 = SafeInt::from(SAFE_FLOAT_MAX.checked_div(10).unwrap_or_default()); + let mantissa_abs = self.mantissa.clone().abs(); + + let exponent_adjustment: i64 = if mantissa_abs.is_zero() { + 0i64 + } else if max_value_div10 >= mantissa_abs { + // Mantissa is too low, upscale mantissa + reduce exponent + let scale = (max_value_div10 / mantissa_abs).unwrap_or_default(); + ((intlog10(&scale).saturating_add(1)) as i64).neg() + } else if max_value < mantissa_abs { + // Mantissa is too high, downscale mantissa + increase exponent + let scale = (mantissa_abs / max_value).unwrap_or_default(); + (intlog10(&scale).saturating_add(1)) as i64 + } else { + 0i64 + }; + + // Check exponent over- or underflows + let new_exponent_i128 = (self.exponent as i128).saturating_add(exponent_adjustment as i128); + if (i64::MIN as i128 <= new_exponent_i128) && (new_exponent_i128 <= i64::MAX as i128) { + self.exponent = new_exponent_i128 as i64; + } else { + return false; + } + + if exponent_adjustment > 0 { + let mantissa_adjustment = pow10(exponent_adjustment as u64); + self.mantissa = (self.mantissa.clone() / mantissa_adjustment).unwrap_or_default(); + } else { + let mantissa_adjustment = pow10(exponent_adjustment.neg() as u64); + self.mantissa = self.mantissa.clone() * mantissa_adjustment + } + + // Check if adjusted mantissa turned into zero, in which case set exponent to 0. + if self.mantissa.is_zero() { + self.exponent = 0; + } + + true + } + + /// Divide current value by a preserving precision (SAFE_FLOAT_MAX digits in mantissa) + /// result = m1 * 10^e1 / m2 * 10^e2 + #[allow( + clippy::arithmetic_side_effects, + reason = "SafeInt never overflows and never panics" + )] + pub fn div(&self, a: &SafeFloat) -> Option { + // We need to offset exponent so that + // 1. e1 - e2 is non-negative + // 2. We have enough precision after division + let redundant_exponent = SAFE_FLOAT_MAX_EXP.saturating_mul(2); + + let maybe_new_mantissa = + self.mantissa.clone() * pow10(redundant_exponent as u64) / a.mantissa.clone(); + if let Some(new_mantissa) = maybe_new_mantissa { + let mut safe_float = SafeFloat { + mantissa: new_mantissa, + exponent: self + .exponent + .saturating_sub(a.exponent) + .saturating_sub(redundant_exponent), + }; + if safe_float.normalize() { + Some(safe_float) + } else { + None + } + } else { + None + } + } + + #[allow( + clippy::arithmetic_side_effects, + reason = "SafeInt never overflows and never panics" + )] + pub fn add(&self, a: &SafeFloat) -> Option { + // Multiply both operands by 10^exponent_offset so that both are above 1. + // (lowest exponent becomes 0) + let exponent_offset = self.exponent.min(a.exponent).neg(); + let unnormalized_mantissa = self.mantissa.clone() + * pow10(self.exponent.saturating_add(exponent_offset) as u64) + + a.mantissa.clone() * pow10(a.exponent.saturating_add(exponent_offset) as u64); + + let mut safe_float = SafeFloat { + mantissa: unnormalized_mantissa, + exponent: exponent_offset.neg(), + }; + if safe_float.normalize() { + Some(safe_float) + } else { + None + } + } + + /// Calculate self * a / b without loss of precision + #[allow( + clippy::arithmetic_side_effects, + reason = "SafeInt never overflows and never panics" + )] + pub fn mul_div(&self, a: &SafeFloat, b: &SafeFloat) -> Option { + let self_a_mantissa = self.mantissa.clone() * a.mantissa.clone(); + let self_a_exponent = self.exponent.saturating_add(a.exponent); + + // Divide by b without adjusting precision first (preserve higher precision + // of multiplication result) + SafeFloat { + mantissa: self_a_mantissa, + exponent: self_a_exponent, + } + .div(b) + } + + pub fn is_zero(&self) -> bool { + self.mantissa.is_zero() + } + + /// Returns true if self > a + #[allow( + clippy::arithmetic_side_effects, + reason = "SafeInt never overflows and never panics" + )] + pub fn gt(&self, a: &SafeFloat) -> bool { + // Shortcut: same exponent → compare mantissas directly + if self.exponent == a.exponent { + return self.mantissa > a.mantissa; + } + + // Bring both to the same exponent = max(exponents) + let max_e = self.exponent.max(a.exponent); + let k1 = max_e - self.exponent; + let k2 = max_e - a.exponent; + + let scale1 = pow10(k1 as u64); + let scale2 = pow10(k2 as u64); + + let lhs = &self.mantissa * &scale1; + let rhs = &a.mantissa * &scale2; + + lhs - rhs > 0 + } +} + +// Saturating conversion: negatives -> 0, overflow -> u64::MAX +impl From<&SafeFloat> for u64 { + #[allow( + clippy::arithmetic_side_effects, + reason = "SafeInt never overflows and never panics" + )] + fn from(value: &SafeFloat) -> Self { + // Negative values are clamped to 0 + if value.mantissa.is_negative() { + return 0; + } + + // If exponent is zero, it's just an integer mantissa + if value.exponent == 0 { + return value.mantissa.to_u64().unwrap_or(u64::MAX); + } + + // scale = 10^exponent + let scale = pow10(value.exponent.unsigned_abs()); + + // mantissa * 10^exponent + let q: SafeInt = if value.exponent > 0 { + &value.mantissa * &scale + } else { + (&value.mantissa / &scale).unwrap_or_else(SafeInt::zero) + }; + + // Convert quotient to u64, saturating on overflow + if q.is_zero() { + 0 + } else { + q.to_u64().unwrap_or(u64::MAX) + } + } +} + +// Convenience impl for owning values +impl From for u64 { + fn from(value: SafeFloat) -> Self { + u64::from(&value) + } +} + +impl From for SafeFloat { + fn from(value: u64) -> Self { + SafeFloat::new(SafeInt::from(value), 0).unwrap_or_default() + } +} + +impl From for SafeFloat { + fn from(value: U64F64) -> Self { + let bits = value.to_bits(); + // High 64 bits = integer part + let int = (bits >> 64) as u64; + // Low 64 bits = fractional part + let frac = (bits & 0xFFFF_FFFF_FFFF_FFFF) as u64; + + // If strictly zero, shortcut + if bits == 0 { + return SafeFloat::zero(); + } + + // SafeFloat for integer part: int * 10^0 + let safe_int = SafeFloat::new(SafeInt::from(int), 0).unwrap_or_default(); + + // Numerator of fractional part: frac * 10^0 + let safe_frac_num = SafeFloat::new(SafeInt::from(frac), 0).unwrap_or_default(); + + // Denominator = 2^64 as an integer SafeFloat: (2^64) * 10^0 + let two64: u128 = 1u128 << 64; + let safe_two64 = SafeFloat::new(SafeInt::from(two64), 0).unwrap_or_default(); + + // frac_part = frac / 2^64 + let safe_frac = safe_frac_num.div(&safe_two64).unwrap_or_default(); + + // int + frac/2^64, with all mantissa/exponent normalization + safe_int.add(&safe_frac).unwrap_or_default() + } +} + +impl From<&SafeFloat> for SafeFloatSerializable { + fn from(value: &SafeFloat) -> Self { + let mut mantissa_serializable = Vec::new(); + value + .mantissa + .encode(&mut mantissa_serializable) + .unwrap_or_default(); + + SafeFloatSerializable { + mantissa: mantissa_serializable, + exponent: value.exponent, + } + } +} + +impl From<&SafeFloatSerializable> for SafeFloat { + fn from(value: &SafeFloatSerializable) -> Self { + let decoded = SafeInt::decode(&mut Cursor::new(&value.mantissa)).unwrap_or_default(); + SafeFloat { + mantissa: decoded, + exponent: value.exponent, + } + } +} + +impl From<&SafeFloat> for f64 { + #[allow( + clippy::arithmetic_side_effects, + reason = "This code is only used in tests" + )] + fn from(value: &SafeFloat) -> Self { + // Zero shortcut + if value.mantissa.is_zero() { + return 0.0; + } + + // If you ever allow negative mantissas, handle sign here. + // For now we assume mantissa >= 0 per your spec. + let mut mant = value.mantissa.clone(); + let mut exp_i32 = value.exponent as i32; + + let ten = SafeInt::from(10); + + // Max integer exactly representable in f64: 2^53 - 1 + let max_exact = SafeInt::from((1u64 << 53) - 1); + + // While mantissa is too large to be exactly represented, + // discard right decimal digits: mant /= 10, and adjust exponent + // so that mant * 10^exp stays the same value. + while mant > max_exact { + mant = (&mant / &ten).unwrap_or_default(); + exp_i32 += 1; // because value = mant * 10^exp, and we did mant /= 10 + } + + // Now mant <= max_exact, so we can convert mant to u64 then to f64 exactly. + let mant_u64 = mant.to_u64().unwrap_or_default(); + + let mant_f = mant_u64 as f64; + let scale = 10f64.powi(exp_i32); + + mant_f * scale + } +} + +impl From for f64 { + fn from(value: SafeFloat) -> Self { + f64::from(&value) + } +} + +impl Default for SafeFloat { + fn default() -> Self { + SafeFloat::zero() + } +} pub trait SharePoolDataOperations { - /// Gets shared value - fn get_shared_value(&self) -> U64F64; + /// Gets shared value (always "the real thing" measured in rao, not fractional) + fn get_shared_value(&self) -> u64; /// Gets single share for a given key - fn get_share(&self, key: &Key) -> U64F64; + fn get_share(&self, key: &Key) -> SafeFloat; // Tries to get a single share for a given key, as a result. - fn try_get_share(&self, key: &Key) -> Result; + fn try_get_share(&self, key: &Key) -> Result; /// Gets share pool denominator - fn get_denominator(&self) -> U64F64; + fn get_denominator(&self) -> SafeFloat; /// Updates shared value by provided signed value - fn set_shared_value(&mut self, value: U64F64); + fn set_shared_value(&mut self, value: u64); /// Update single share for a given key by provided signed value - fn set_share(&mut self, key: &Key, share: U64F64); + fn set_share(&mut self, key: &Key, share: SafeFloat); /// Update share pool denominator by provided signed value - fn set_denominator(&mut self, update: U64F64); + fn set_denominator(&mut self, update: SafeFloat); } /// SharePool struct that depends on the Key type and uses the SharePoolDataOperations @@ -47,36 +448,24 @@ where } pub fn get_value(&self, key: &K) -> u64 { - let shared_value: U64F64 = self.state_ops.get_shared_value(); - let current_share: U64F64 = self.state_ops.get_share(key); - let denominator: U64F64 = self.state_ops.get_denominator(); - - let maybe_value_per_share = shared_value.checked_div(denominator); - (if let Some(value_per_share) = maybe_value_per_share { - value_per_share.saturating_mul(current_share) - } else { - shared_value - .saturating_mul(current_share) - .checked_div(denominator) - .unwrap_or(U64F64::saturating_from_num(0)) - }) - .saturating_to_num::() + let shared_value: SafeFloat = + SafeFloat::new(SafeInt::from(self.state_ops.get_shared_value()), 0).unwrap_or_default(); + let current_share: SafeFloat = self.state_ops.get_share(key); + let denominator: SafeFloat = self.state_ops.get_denominator(); + shared_value + .mul_div(¤t_share, &denominator) + .unwrap_or_default() + .into() } - pub fn get_value_from_shares(&self, current_share: U64F64) -> u64 { - let shared_value: U64F64 = self.state_ops.get_shared_value(); - let denominator: U64F64 = self.state_ops.get_denominator(); - - let maybe_value_per_share = shared_value.checked_div(denominator); - (if let Some(value_per_share) = maybe_value_per_share { - value_per_share.saturating_mul(current_share) - } else { - shared_value - .saturating_mul(current_share) - .checked_div(denominator) - .unwrap_or(U64F64::saturating_from_num(0)) - }) - .saturating_to_num::() + pub fn get_value_from_shares(&self, current_share: SafeFloat) -> u64 { + let shared_value: SafeFloat = + SafeFloat::new(SafeInt::from(self.state_ops.get_shared_value()), 0).unwrap_or_default(); + let denominator: SafeFloat = self.state_ops.get_denominator(); + shared_value + .mul_div(¤t_share, &denominator) + .unwrap_or_default() + .into() } pub fn try_get_value(&self, key: &K) -> Result { @@ -89,164 +478,155 @@ where /// Update the total shared value. /// Every key's associated value effectively updates with this operation pub fn update_value_for_all(&mut self, update: i64) { - let shared_value: U64F64 = self.state_ops.get_shared_value(); + let shared_value: u64 = self.state_ops.get_shared_value(); self.state_ops.set_shared_value(if update >= 0 { - shared_value.saturating_add(U64F64::saturating_from_num(update)) + shared_value.saturating_add(update as u64) } else { - shared_value.saturating_sub(U64F64::saturating_from_num(update.neg())) + shared_value.saturating_sub(update.neg() as u64) }); } pub fn sim_update_value_for_one(&mut self, update: i64) -> bool { - let shared_value: U64F64 = self.state_ops.get_shared_value(); - let denominator: U64F64 = self.state_ops.get_denominator(); + let shared_value: u64 = self.state_ops.get_shared_value(); + let denominator: SafeFloat = self.state_ops.get_denominator(); // Then, update this key's share - if denominator == 0 { + if denominator.mantissa == 0 { true } else { // There are already keys in the pool, set or update this key - let shares_per_update: I64F64 = - self.get_shares_per_update(update, &shared_value, &denominator); + let shares_per_update = self.get_shares_per_update(update, shared_value, &denominator); - shares_per_update != 0 + !shares_per_update.is_zero() } } fn get_shares_per_update( &self, update: i64, - shared_value: &U64F64, - denominator: &U64F64, - ) -> I64F64 { - let maybe_value_per_share = shared_value.checked_div(*denominator); - if let Some(value_per_share) = maybe_value_per_share { - I64F64::saturating_from_num(update) - .checked_div(I64F64::saturating_from_num(value_per_share)) - .unwrap_or(I64F64::saturating_from_num(0)) - } else { - I64F64::saturating_from_num(update) - .checked_div(I64F64::saturating_from_num(*shared_value)) - .unwrap_or(I64F64::saturating_from_num(0)) - .saturating_mul(I64F64::saturating_from_num(*denominator)) - } + shared_value: u64, + denominator: &SafeFloat, + ) -> SafeFloat { + let shared_value: SafeFloat = + SafeFloat::new(SafeInt::from(shared_value), 0).unwrap_or_default(); + let update: SafeFloat = SafeFloat::new(SafeInt::from(update), 0).unwrap_or_default(); + update + .mul_div(denominator, &shared_value) + .unwrap_or_default() } /// Update the value associated with an item identified by the Key /// Returns actual update /// - pub fn update_value_for_one(&mut self, key: &K, update: i64) -> i64 { - let shared_value: U64F64 = self.state_ops.get_shared_value(); - let current_share: U64F64 = self.state_ops.get_share(key); - let denominator: U64F64 = self.state_ops.get_denominator(); - let initial_value: i64 = self.get_value(key) as i64; - let mut actual_update: i64 = update; + pub fn update_value_for_one(&mut self, key: &K, update: i64) { + let shared_value: u64 = self.state_ops.get_shared_value(); + let current_share: SafeFloat = self.state_ops.get_share(key); + let denominator: SafeFloat = self.state_ops.get_denominator(); // Then, update this key's share - if denominator == 0 { + if denominator.is_zero() { // Initialize the pool. The first key gets all. - let update_fixed: U64F64 = U64F64::saturating_from_num(update); - self.state_ops.set_denominator(update_fixed); - self.state_ops.set_share(key, update_fixed); + let update_float: SafeFloat = + SafeFloat::new(SafeInt::from(update), 0).unwrap_or_default(); + self.state_ops.set_denominator(update_float.clone()); + self.state_ops.set_share(key, update_float); } else { - let shares_per_update: I64F64 = - self.get_shares_per_update(update, &shared_value, &denominator); - - if shares_per_update >= 0 { - self.state_ops.set_denominator( - denominator.saturating_add(U64F64::saturating_from_num(shares_per_update)), - ); - self.state_ops.set_share( - key, - current_share.saturating_add(U64F64::saturating_from_num(shares_per_update)), - ); - } else { - // Check if this entry is about to break precision - let mut new_denominator = denominator - .saturating_sub(U64F64::saturating_from_num(shares_per_update.neg())); - let mut new_share = current_share - .saturating_sub(U64F64::saturating_from_num(shares_per_update.neg())); - - // The condition here is either the share remainder is too little OR - // the new_denominator is too low compared to what shared_value + year worth of emissions would be - if (new_share.safe_div(current_share) < U64F64::saturating_from_num(0.00001)) - || shared_value - .saturating_add(U64F64::saturating_from_num(2_628_000_000_000_000_u64)) - .checked_div(new_denominator) - .is_none() - { - // yes, precision is low, just remove all - new_share = U64F64::saturating_from_num(0); - new_denominator = denominator.saturating_sub(current_share); - actual_update = initial_value.neg(); + let shares_per_update: SafeFloat = + self.get_shares_per_update(update, shared_value, &denominator); + + // Handle SafeFloat overflows quietly here because this overflow of i64 exponent + // is extremely hypothetical and should never happen in practice. + let new_denominator = match denominator.add(&shares_per_update) { + Some(new_denominator) => new_denominator, + None => { + log::error!( + "SafeFloat::add overflow when adding {:?} to {:?}; keeping old denominator", + shares_per_update, + denominator, + ); + // Return the value as it was before the failed addition + denominator + } + }; + + let new_current_share = match current_share.add(&shares_per_update) { + Some(new_current_share) => new_current_share, + None => { + log::error!( + "SafeFloat::add overflow when adding {:?} to {:?}; keeping old current_share", + shares_per_update, + current_share, + ); + // Return the value as it was before the failed addition + current_share } + }; - self.state_ops.set_denominator(new_denominator); - self.state_ops.set_share(key, new_share); - } + self.state_ops.set_denominator(new_denominator); + self.state_ops.set_share(key, new_current_share); } // Update shared value - self.update_value_for_all(actual_update); - - // Return actual udate - actual_update + self.update_value_for_all(update); } } +// cargo test --package share-pool --lib -- tests --nocapture #[cfg(test)] +#[allow(clippy::unwrap_used)] mod tests { use super::*; + use approx::assert_abs_diff_eq; + use lencode::io::Cursor; + use lencode::{Decode, Encode}; use std::collections::BTreeMap; + use substrate_fixed::types::U64F64; struct MockSharePoolDataOperations { - shared_value: U64F64, - share: BTreeMap, - denominator: U64F64, + shared_value: u64, + share: BTreeMap, + denominator: SafeFloat, } impl MockSharePoolDataOperations { fn new() -> Self { MockSharePoolDataOperations { - shared_value: U64F64::saturating_from_num(0), + shared_value: 0u64, share: BTreeMap::new(), - denominator: U64F64::saturating_from_num(0), + denominator: SafeFloat::zero(), } } } impl SharePoolDataOperations for MockSharePoolDataOperations { - fn get_shared_value(&self) -> U64F64 { + fn get_shared_value(&self) -> u64 { self.shared_value } - fn get_share(&self, key: &u16) -> U64F64 { - *self - .share - .get(key) - .unwrap_or(&U64F64::saturating_from_num(0)) + fn get_share(&self, key: &u16) -> SafeFloat { + self.share.get(key).cloned().unwrap_or_else(SafeFloat::zero) } - fn try_get_share(&self, key: &u16) -> Result { - match self.share.get(key) { - Some(&value) => Ok(value), + fn try_get_share(&self, key: &u16) -> Result { + match self.share.get(key).cloned() { + Some(value) => Ok(value), None => Err(()), } } - fn get_denominator(&self) -> U64F64 { - self.denominator + fn get_denominator(&self) -> SafeFloat { + self.denominator.clone() } - fn set_shared_value(&mut self, value: U64F64) { + fn set_shared_value(&mut self, value: u64) { self.shared_value = value; } - fn set_share(&mut self, key: &u16, share: U64F64) { + fn set_share(&mut self, key: &u16, share: SafeFloat) { self.share.insert(*key, share); } - fn set_denominator(&mut self, update: U64F64) { + fn set_denominator(&mut self, update: SafeFloat) { self.denominator = update; } } @@ -254,10 +634,10 @@ mod tests { #[test] fn test_get_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_denominator(U64F64::saturating_from_num(10)); - mock_ops.set_share(&1_u16, U64F64::saturating_from_num(3)); - mock_ops.set_share(&2_u16, U64F64::saturating_from_num(7)); - mock_ops.set_shared_value(U64F64::saturating_from_num(100)); + mock_ops.set_denominator(10u64.into()); + mock_ops.set_share(&1_u16, 3u64.into()); + mock_ops.set_share(&2_u16, 7u64.into()); + mock_ops.set_shared_value(100u64.into()); let share_pool = SharePool::new(mock_ops); let result1 = share_pool.get_value(&1); let result2 = share_pool.get_value(&2); @@ -268,7 +648,7 @@ mod tests { #[test] fn test_division_by_zero() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_denominator(U64F64::saturating_from_num(0)); // Zero denominator + mock_ops.set_denominator(SafeFloat::zero()); // Zero denominator let pool = SharePool::::new(mock_ops); let value = pool.get_value(&1); @@ -278,10 +658,10 @@ mod tests { #[test] fn test_max_shared_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_shared_value(U64F64::saturating_from_num(u64::MAX)); - mock_ops.set_share(&1, U64F64::saturating_from_num(3)); // Use a neutral value for share - mock_ops.set_share(&2, U64F64::saturating_from_num(7)); // Use a neutral value for share - mock_ops.set_denominator(U64F64::saturating_from_num(10)); // Neutral value to see max effect + mock_ops.set_shared_value(u64::MAX.into()); + mock_ops.set_share(&1, 3u64.into()); // Use a neutral value for share + mock_ops.set_share(&2, 7u64.into()); // Use a neutral value for share + mock_ops.set_denominator(10u64.into()); // Neutral value to see max effect let pool = SharePool::::new(mock_ops); let max_value = pool.get_value(&1) + pool.get_value(&2); @@ -291,16 +671,16 @@ mod tests { #[test] fn test_max_share_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_shared_value(U64F64::saturating_from_num(1_000_000_000)); // Use a neutral value for shared value - mock_ops.set_share(&1, U64F64::saturating_from_num(u64::MAX / 2)); - mock_ops.set_share(&2, U64F64::saturating_from_num(u64::MAX / 2)); - mock_ops.set_denominator(U64F64::saturating_from_num(u64::MAX)); + mock_ops.set_shared_value(1_000_000_000u64); // Use a neutral value for shared value + mock_ops.set_share(&1, (u64::MAX / 2).into()); + mock_ops.set_share(&2, (u64::MAX / 2).into()); + mock_ops.set_denominator((u64::MAX).into()); let pool = SharePool::::new(mock_ops); let value1 = pool.get_value(&1) as i128; let value2 = pool.get_value(&2) as i128; - assert!((value1 - 500_000_000).abs() <= 1); + assert_abs_diff_eq!(value1 as f64, 500_000_000_f64, epsilon = 1.); assert!((value2 - 500_000_000).abs() <= 1); } @@ -331,26 +711,30 @@ mod tests { let mock_ops = MockSharePoolDataOperations::new(); let mut pool = SharePool::::new(mock_ops); + // 50%/50% stakes consisting of 1 rao each pool.update_value_for_one(&1, 1); pool.update_value_for_one(&2, 1); + // Huge emission resulting in 1M Alpha + // Both stakers should have 500k Alpha each pool.update_value_for_all(999_999_999_999_998); + // Everyone unstakes almost everything, leaving 10 rao in the stake pool.update_value_for_one(&1, -499_999_999_999_990); pool.update_value_for_one(&2, -499_999_999_999_990); + // Huge emission resulting in 1M Alpha + // Both stakers should have 500k Alpha each pool.update_value_for_all(999_999_999_999_980); + // Stakers add 1k Alpha each pool.update_value_for_one(&1, 1_000_000_000_000); pool.update_value_for_one(&2, 1_000_000_000_000); - let value1 = pool.get_value(&1) as i128; - let value2 = pool.get_value(&2) as i128; - - // First to stake gets all accumulated emission if there are no other stakers - // (which is artificial situation because there will be no emissions if there is no stake) - assert!((value1 - 1_001_000_000_000_000).abs() < 100); - assert!((value2 - 1_000_000_000_000).abs() < 100); + let value1 = pool.get_value(&1) as f64; + let value2 = pool.get_value(&2) as f64; + assert_abs_diff_eq!(value1, 501_000_000_000_000_f64, epsilon = 1.); + assert_abs_diff_eq!(value2, 501_000_000_000_000_f64, epsilon = 1.); } // cargo test --package share-pool --lib -- tests::test_denom_high_precision_many_small_unstakes --exact --show-output @@ -359,26 +743,37 @@ mod tests { let mock_ops = MockSharePoolDataOperations::new(); let mut pool = SharePool::::new(mock_ops); + // 50%/50% stakes consisting of 1 rao each pool.update_value_for_one(&1, 1); pool.update_value_for_one(&2, 1); + // Huge emission resulting in 1M Alpha + // Both stakers should have 500k Alpha + 1 rao each pool.update_value_for_all(1_000_000_000_000_000); - for _ in 0..1_000_000 { - pool.update_value_for_one(&1, -500_000_000); - pool.update_value_for_one(&2, -500_000_000); + // Run X number of small unstake transactions + let tx_count = 1000; + let unstake_amount = -500_000_000; + for _ in 0..tx_count { + pool.update_value_for_one(&1, unstake_amount); + pool.update_value_for_one(&2, unstake_amount); } + // Emit 1M - each gets 500k Alpha pool.update_value_for_all(1_000_000_000_000_000); + // Each adds 1k Alpha pool.update_value_for_one(&1, 1_000_000_000_000); pool.update_value_for_one(&2, 1_000_000_000_000); + // Result, each should get + // (500k+1) + tx_count * unstake_amount + 500k + 1k let value1 = pool.get_value(&1) as i128; let value2 = pool.get_value(&2) as i128; + let expected = 1_001_000_000_000_000 + tx_count * unstake_amount; - assert!((value1 - 1_001_000_000_000_000).abs() < 10); - assert!((value2 - 1_000_000_000_000).abs() < 10); + assert_abs_diff_eq!(value1 as f64, expected as f64, epsilon = 1.); + assert_abs_diff_eq!(value2 as f64, expected as f64, epsilon = 1.); } #[test] @@ -407,46 +802,1039 @@ mod tests { // cargo test --package share-pool --lib -- tests::test_get_shares_per_update --exact --show-output #[test] fn test_get_shares_per_update() { + // Test case (update, shared_value, denominator_mantissa, denominator_exponent) + [ + (1_i64, 1_u64, 1_u64, 0_i64), + (1, 1_000_000_000_000_000_000, 1, 0), + (1, 21_000_000_000_000_000, 1, 5), + (1, 21_000_000_000_000_000, 1, -1_000_000), + (1, 21_000_000_000_000_000, 1, -1_000_000_000), + (1, 21_000_000_000_000_000, 1, -1_000_000_001), + (1_000, 21_000_000_000_000_000, 1, 5), + (21_000_000_000_000_000, 21_000_000_000_000_000, 1, 5), + (21_000_000_000_000_000, 21_000_000_000_000_000, 1, -5), + (21_000_000_000_000_000, 21_000_000_000_000_000, 1, -100), + (21_000_000_000_000_000, 21_000_000_000_000_000, 1, 100), + (210_000_000_000_000_000, 21_000_000_000_000_000, 1, 5), + (1_000, 1_000, 21_000_000_000_000_000, 0), + (1_000, 1_000, 21_000_000_000_000_000, -1), + ] + .into_iter() + .for_each( + |(update, shared_value, denominator_mantissa, denominator_exponent)| { + let mock_ops = MockSharePoolDataOperations::new(); + let pool = SharePool::::new(mock_ops); + + let denominator_float = + SafeFloat::new(SafeInt::from(denominator_mantissa), denominator_exponent) + .unwrap_or_default(); + let denominator_f64: f64 = denominator_float.clone().into(); + let spu: f64 = pool + .get_shares_per_update(update, shared_value, &denominator_float) + .into(); + let expected = update as f64 * denominator_f64 / shared_value as f64; + let precision = 1000.; + assert_abs_diff_eq!(expected, spu, epsilon = expected / precision); + }, + ); + } + + #[test] + fn test_safeint_serialization() { + let safe_int = SafeInt::from(12345); + let mut buf = Vec::new(); + safe_int.encode(&mut buf).unwrap(); + + let decoded = SafeInt::decode(&mut Cursor::new(&buf)).unwrap(); + assert_eq!(decoded, safe_int); + } + + #[test] + fn test_safefloat_normalize() { + // Test case: mantissa, exponent, expected mantissa, expected exponent + [ + (1_u128, 0, 100_000_000_000_000_000_000_u128, -20_i64), + (0, 0, 0, 0), + (10_u128, 0, 100_000_000_000_000_000_000_u128, -19), + (1_000_u128, 0, 100_000_000_000_000_000_000_u128, -17), + ( + 100_000_000_000_000_000_000_u128, + 0, + 1_000_000_000_000_000_000_000_u128, + -1, + ), + (SAFE_FLOAT_MAX, 0, SAFE_FLOAT_MAX, 0), + ] + .into_iter() + .for_each(|(m, e, expected_m, expected_e)| { + let a = SafeFloat::new(SafeInt::from(m), e).unwrap(); + assert_eq!(a.mantissa, SafeInt::from(expected_m)); + assert_eq!(a.exponent, SafeInt::from(expected_e)); + }); + } + + #[test] + fn test_safefloat_add() { + // Test case: man_a, exp_a, man_b, exp_b, expected mantissa of a+b, expected exponent of a+b + [ + // 1 + 1 = 2 + ( + 1_u128, + 0, + 1_u128, + 0, + 200_000_000_000_000_000_000_u128, + -20_i64, + ), + // SAFE_FLOAT_MAX + SAFE_FLOAT_MAX + ( + SAFE_FLOAT_MAX, + 0, + SAFE_FLOAT_MAX, + 0, + SAFE_FLOAT_MAX * 2 / 10, + 1_i64, + ), + // Expected loss of precision: tiny + huge + ( + 1_u128, + 0, + 1_000_000_000_000_000_000_000_u128, + 1, + 1_000_000_000_000_000_000_000_u128, + 1_i64, + ), + ( + 1_u128, + 0, + 1_u128, + 22, + 1_000_000_000_000_000_000_000_u128, + 1_i64, + ), + ( + 1_u128, + 0, + 1_u128, + 23, + 1_000_000_000_000_000_000_000_u128, + 2_i64, + ), + ( + 123_u128, + 0, + 1_u128, + 23, + 1_000_000_000_000_000_000_001_u128, + 2_i64, + ), + ( + 123_u128, + 1, + 1_u128, + 23, + 1_000_000_000_000_000_000_012_u128, + 2_i64, + ), + // Small-ish + very large (10^22 + 42) + // 42 * 10^0 + 1 * 10^22 ≈ 1e22 + 42 + // Normalized ≈ (1e21 + 4) * 10^1 + ( + 42_u128, + 0, + 1_u128, + 22, + 1_000_000_000_000_000_000_004_u128, + 1_i64, + ), + // "Almost 10^21" + 10^22 + // (10^21 - 1) + 10^22 → floor((10^22 + 10^21 - 1) / 100) * 10^2 + ( + 999_999_999_999_999_999_999_u128, + 0, + 1_u128, + 22, + 109_999_999_999_999_999_999_u128, + 2_i64, + ), + // Small-ish + 10^23 where the small part is completely lost + // 42 + 10^23 -> floor((10^23 + 42)/100) * 10^2 ≈ 1e21 * 10^2 + ( + 42_u128, + 0, + 1_u128, + 23, + 1_000_000_000_000_000_000_000_u128, + 2_i64, + ), + // Small-ish + 10^23 where tiny part slightly affects mantissa + // 4200 + 10^23 -> floor((10^23 + 4200)/100) * 10^2 = (1e21 + 42) * 10^2 + ( + 4_200_u128, + 0, + 1_u128, + 23, + 1_000_000_000_000_000_000_042_u128, + 2_i64, + ), + // (10^21 - 1) + 10^23 + // -> floor((10^23 + 10^21 - 1)/100) = 1e21 + 1e19 - 1 + ( + 999_999_999_999_999_999_999_u128, + 0, + 1_u128, + 23, + 1_009_999_999_999_999_999_999_u128, + 2_i64, + ), + // Medium + 10^23 with exponent 1 on the smaller term + // 999_999 * 10^1 + 1 * 10^23 -> (10^22 + 999_999) * 10^1 + // Normalized ≈ (1e21 + 99_999) * 10^2 + ( + 999_999_u128, + 1, + 1_u128, + 23, + 1_000_000_000_000_000_099_999_u128, + 2_i64, + ), + // Check behaviour with exponent 24, tiny second term + // 1 * 10^24 + 1 -> floor((10^24 + 1)/1000) * 10^3 ≈ 1e21 * 10^3 + ( + 1_u128, + 24, + 1_u128, + 0, + 1_000_000_000_000_000_000_000_u128, + 3_i64, + ), + // 1 * 10^24 + a non-trivial small mantissa + // 1e24 + 123456789012345678901 -> floor(/1000) = 1e21 + 123456789012345678 + ( + 1_u128, + 24, + 123_456_789_012_345_678_901_u128, + 0, + 1_000_123_456_789_012_345_678_u128, + 3_i64, + ), + // 10^22 and 10^23 combined: + // 1 * 10^22 + 1 * 10^23 = 11 * 10^22 = (1.1 * 10^23) + // Normalized → (1.1e20) * 10^3 + ( + 1_u128, + 22, + 1_u128, + 23, + 110_000_000_000_000_000_000_u128, + 3_i64, + ), + // Both operands already aligned at a huge scale: + // (10^21 - 1) * 10^22 + 1 * 10^22 = 10^21 * 10^22 = 10^43 + // Canonical form: (1e21) * 10^22 + ( + 999_999_999_999_999_999_999_u128, + 22, + 1_u128, + 22, + 1_000_000_000_000_000_000_000_u128, + 22_i64, + ), + ] + .into_iter() + .for_each(|(m_a, e_a, m_b, e_b, expected_m, expected_e)| { + let a = SafeFloat::new(SafeInt::from(m_a), e_a).unwrap(); + let b = SafeFloat::new(SafeInt::from(m_b), e_b).unwrap(); + + let a_plus_b = a.add(&b).unwrap(); + let b_plus_a = b.add(&a).unwrap(); + + assert_eq!(a_plus_b.mantissa, SafeInt::from(expected_m)); + assert_eq!(a_plus_b.exponent, SafeInt::from(expected_e)); + assert_eq!(b_plus_a.mantissa, SafeInt::from(expected_m)); + assert_eq!(b_plus_a.exponent, SafeInt::from(expected_e)); + }); + } + + #[test] + fn test_safefloat_div_by_zero_is_none() { + let a = SafeFloat::new(SafeInt::from(1), 0).unwrap(); + assert!(a.div(&SafeFloat::zero()).is_none()); + } + + #[test] + fn test_safefloat_div() { + // Test case: man_a, exp_a, man_b, exp_b [ - (1_i64, 1_u64, 1.0, 1.0), + (1_u128, 0_i64, 100_000_000_000_000_000_000_u128, -20_i64), + (1_u128, 0, 1_u128, 0), + (1_u128, 1, 1_u128, 0), + (1_u128, 7, 1_u128, 0), + (1_u128, 50, 1_u128, 0), + (1_u128, 100, 1_u128, 0), + (1_u128, 0, 7_u128, 0), + (1_u128, 1, 7_u128, 0), + (1_u128, 7, 7_u128, 0), + (1_u128, 50, 7_u128, 0), + (1_u128, 100, 7_u128, 0), + (1_u128, 0, 3_u128, 0), + (1_u128, 1, 3_u128, 0), + (1_u128, 7, 3_u128, 0), + (1_u128, 50, 3_u128, 0), + (1_u128, 100, 3_u128, 0), + (2_u128, 0, 3_u128, 0), + (2_u128, 1, 3_u128, 0), + (2_u128, 7, 3_u128, 0), + (2_u128, 50, 3_u128, 0), + (2_u128, 100, 3_u128, 0), + (5_u128, 0, 3_u128, 0), + (5_u128, 1, 3_u128, 0), + (5_u128, 7, 3_u128, 0), + (5_u128, 50, 3_u128, 0), + (5_u128, 100, 3_u128, 0), + (10_u128, 0, 100_000_000_000_000_000_000_u128, -19), + (1_000_u128, 0, 100_000_000_000_000_000_000_u128, -17), + ( + 100_000_000_000_000_000_000_u128, + 0, + 1_000_000_000_000_000_000_000_u128, + -1, + ), + (SAFE_FLOAT_MAX, 0, SAFE_FLOAT_MAX, 0), + (SAFE_FLOAT_MAX, 100, SAFE_FLOAT_MAX, -100), + (SAFE_FLOAT_MAX, 100, SAFE_FLOAT_MAX - 1, -100), + (SAFE_FLOAT_MAX - 1, 100, SAFE_FLOAT_MAX, -100), + (SAFE_FLOAT_MAX - 2, 100, SAFE_FLOAT_MAX, -100), + (SAFE_FLOAT_MAX, 100, SAFE_FLOAT_MAX / 2 - 1, -100), + (SAFE_FLOAT_MAX, 100, SAFE_FLOAT_MAX / 2 - 1, 100), + (1_u128, 0, 100_000_000_000_000_000_000_u128, -20_i64), + ( + 123_456_789_123_456_789_123_u128, + 20_i64, + 87_654_321_987_654_321_987_u128, + -20_i64, + ), + ( + 123_456_789_123_456_789_123_u128, + 100_i64, + 87_654_321_987_654_321_987_u128, + -100_i64, + ), ( - 1_000, - 21_000_000_000_000_000, - 0.00001, - 0.00000000000000000043, + 123_456_789_123_456_789_123_u128, + -100_i64, + 87_654_321_987_654_321_987_u128, + 100_i64, ), ( - 21_000_000_000_000_000, - 21_000_000_000_000_000, - 0.00001, - 0.00001, + 123_456_789_123_456_789_123_u128, + -99_i64, + 87_654_321_987_654_321_987_u128, + 99_i64, ), ( - 210_000_000_000_000_000, - 21_000_000_000_000_000, - 0.00001, - 0.0001, + 123_456_789_123_456_789_123_u128, + 123_i64, + 87_654_321_987_654_321_987_u128, + -32_i64, ), ( - 1_000, - 1_000, - 21_000_000_000_000_000_f64, - 21_000_000_000_000_000_f64, + 123_456_789_123_456_789_123_u128, + -123_i64, + 87_654_321_987_654_321_987_u128, + 32_i64, ), ] - .iter() - .for_each(|(update, shared_value, denominator, expected)| { - let mock_ops = MockSharePoolDataOperations::new(); - let pool = SharePool::::new(mock_ops); - - let shared_fixed = U64F64::from_num(*shared_value); - let denominator_fixed = U64F64::from_num(*denominator); - let expected_fixed = I64F64::from_num(*expected); - - let spu: I64F64 = - pool.get_shares_per_update(*update, &shared_fixed, &denominator_fixed); - let precision: I64F64 = I64F64::from_num(1000.); - assert!((spu - expected_fixed).abs() <= expected_fixed / precision,); + .into_iter() + .for_each(|(ma, ea, mb, eb)| { + let a = SafeFloat::new(SafeInt::from(ma), ea).unwrap(); + let b = SafeFloat::new(SafeInt::from(mb), eb).unwrap(); + + let actual: f64 = a.div(&b).unwrap().into(); + let expected = + ma as f64 * (10_f64).powi(ea as i32) / (mb as f64 * (10_f64).powi(eb as i32)); + + assert_abs_diff_eq!(actual, expected, epsilon = actual / 100_000_000_000_000_f64); }); } + + #[test] + fn test_safefloat_mul_div() { + // result = a * b / c + // should not lose precision gained in a * b + // Test case: man_a, exp_a, man_b, exp_b, man_c, exp_c + [ + (1_u128, -20_i64, 1_u128, -20_i64, 1_u128, -20_i64), + (123_u128, 20_i64, 123_u128, -20_i64, 321_u128, 0_i64), + ( + 123_123_123_123_123_123_u128, + 20_i64, + 321_321_321_321_321_321_u128, + -20_i64, + 777_777_777_777_777_777_u128, + 0_i64, + ), + ( + 11_111_111_111_111_111_111_u128, + 20_i64, + 99_321_321_321_321_321_321_u128, + -20_i64, + 77_777_777_777_777_777_777_u128, + 0_i64, + ), + ] + .into_iter() + .for_each(|(ma, ea, mb, eb, mc, ec)| { + let a = SafeFloat::new(SafeInt::from(ma), ea).unwrap(); + let b = SafeFloat::new(SafeInt::from(mb), eb).unwrap(); + let c = SafeFloat::new(SafeInt::from(mc), ec).unwrap(); + + let actual: f64 = a.mul_div(&b, &c).unwrap().into(); + let expected = (ma as f64 * (10_f64).powi(ea as i32)) + * (mb as f64 * (10_f64).powi(eb as i32)) + / (mc as f64 * (10_f64).powi(ec as i32)); + + assert_abs_diff_eq!(actual, expected, epsilon = actual / 100_000_000_000_000_f64); + }); + } + + #[test] + fn test_safefloat_from_u64f64() { + [ + U64F64::from_num(1000.0), + U64F64::from_num(10.0), + U64F64::from_num(1.0), + U64F64::from_num(0.1), + U64F64::from_num(0.00000001), + U64F64::from_num(123_456_789_123_456u128), + // Exact zero + U64F64::from_num(0.0), + // Very small positive value (well above Q64.64 resolution) + U64F64::from_num(1e-18), + // Value just below 1 + U64F64::from_num(0.999_999_999_999_999_f64), + // Value just above 1 + U64F64::from_num(1.000_000_000_000_001_f64), + // "Random-looking" fractional with many digits + U64F64::from_num(1.234_567_890_123_45_f64), + // Large integer, but smaller than the max integer part of U64F64 + U64F64::from_num(999_999_999_999_999_999u128), + // Very large integer near the upper bound of integer range + U64F64::from_num(u64::MAX as u128), + // Large number with fractional part + U64F64::from_num(123_456_789_123_456.78_f64), + // Medium-large with tiny fractional part to test precision on tail digits + U64F64::from_num(1_000_000_000_000.000_001_f64), + // Smallish with long fractional part + U64F64::from_num(0.123_456_789_012_345_f64), + ] + .into_iter() + .for_each(|f| { + let safe_float: SafeFloat = f.into(); + let actual: f64 = safe_float.into(); + let expected = f.to_num::(); + + // Relative epsilon ~1e-14 of the magnitude + let epsilon = if actual == 0.0 { + 0.0 + } else { + actual.abs() / 100_000_000_000_000_f64 + }; + + assert_abs_diff_eq!(actual, expected, epsilon = epsilon); + }); + } + + /// This is a real-life scenario test when someone lost 7 TAO on Chutes (SN64) + /// when paying fees in Alpha. The scenario occured because the update of share value + /// of one coldkey (update_value_for_one) hit the scenario of full unstake. + /// + /// Specifically, the following condition was triggered: + /// + /// `(shared_value + 2_628_000_000_000_000_u64).checked_div(new_denominator)` + /// + /// returned None because new_denominator was too low and division of + /// `shared_value + 2_628_000_000_000_000_u64` by new_denominator has overflown U64F64. + /// + /// This test fails on the old version of share pool (with much lower tolerances). + /// + /// cargo test --package share-pool --lib -- tests::test_loss_due_to_precision --exact --nocapture + #[test] + fn test_loss_due_to_precision() { + let mock_ops = MockSharePoolDataOperations::new(); + let mut pool = SharePool::::new(mock_ops); + + // Setup pool so that initial coldkey's alpha is 10% of 1e12 = 1e11 rao. + let low_denominator = SafeFloat::new(SafeInt::from(1), -14).unwrap(); + let low_share = SafeFloat::new(SafeInt::from(1), -15).unwrap(); + pool.state_ops.set_denominator(low_denominator); + pool.state_ops.set_shared_value(1_000_000_000_000_u64); + pool.state_ops.set_share(&1, low_share); + + let value_before = pool.get_value(&1) as i128; + assert_abs_diff_eq!(value_before as f64, 100_000_000_000., epsilon = 0.1); + + // Remove a little stake + let unstake_amount = 1000i64; + pool.update_value_for_one(&1, unstake_amount.neg()); + + let value_after = pool.get_value(&1) as i128; + assert_abs_diff_eq!( + (value_before - value_after) as f64, + unstake_amount as f64, + epsilon = unstake_amount as f64 / 1_000_000_000. + ); + } + + // Below are the tests transplanted from balancer plus some new wide-range SafeFloat tests, + // all to ensure safety of bigmath crate + + const ACCURACY: u64 = 1_000_000_000_000_000_000_u64; + + fn exp_scaled( + w_base: Perquintill, + w_quote: Perquintill, + x: u64, + dx: i128, + base_quote: bool, + ) -> U64F64 { + let x_plus_dx = if dx >= 0 { + x.saturating_add(dx as u64) + } else { + x.saturating_sub(dx.neg() as u64) + }; + + if x_plus_dx == 0 { + return U64F64::saturating_from_num(0); + } + + let w1: u128 = w_base.deconstruct() as u128; + let w2: u128 = w_quote.deconstruct() as u128; + + let precision = 1024; + let x_safe = SafeInt::from(x); + let w1_safe = SafeInt::from(w1); + let w2_safe = SafeInt::from(w2); + let perquintill_scale = SafeInt::from(ACCURACY as u128); + let denominator = SafeInt::from(x_plus_dx); + log::debug!("x = {:?}", x); + log::debug!("dx = {:?}", dx); + log::debug!("x_safe = {:?}", x_safe); + log::debug!("denominator = {:?}", denominator); + log::debug!("w1_safe = {:?}", w1_safe); + log::debug!("w2_safe = {:?}", w2_safe); + log::debug!("precision = {:?}", precision); + log::debug!("perquintill_scale = {:?}", perquintill_scale); + + let maybe_result_safe_int = if base_quote { + SafeInt::pow_ratio_scaled( + &x_safe, + &denominator, + &w1_safe, + &w2_safe, + precision, + &perquintill_scale, + ) + } else { + SafeInt::pow_ratio_scaled( + &x_safe, + &denominator, + &w2_safe, + &w1_safe, + precision, + &perquintill_scale, + ) + }; + + if let Some(result_safe_int) = maybe_result_safe_int + && let Some(result_u64) = result_safe_int.to_u64() + { + return U64F64::saturating_from_num(result_u64) + .checked_div(U64F64::saturating_from_num(ACCURACY)) + .unwrap(); + } + U64F64::saturating_from_num(0) + } + + fn exp_base_quote(w_base: Perquintill, w_quote: Perquintill, x: u64, dx: u64) -> U64F64 { + exp_scaled(w_base, w_quote, x, dx as i128, true) + } + + // Helper: convert Perquintill to f64 for comparison + fn perquintill_to_f64(p: Perquintill) -> f64 { + let parts = p.deconstruct() as f64; + parts / ACCURACY as f64 + } + + /// Test the broad range of w_quote values, usually should be ignored + // cargo test --package share-pool --lib -- tests::test_exp_quote_broad_range --exact --include-ignored --show-output + #[ignore] + #[test] + fn test_exp_quote_broad_range() { + let y = 1_000_000_000_000_u64; + let x = 100_000_000_000_000_u64; + let dx = 10_000_000_u64; + + let mut prev = U64F64::from_num(1_000_000_000); + let mut last_progress = 0.; + let start = 100_000_000_000_u128; + let stop = 900_000_000_000_u128; + for num in (start..=stop).step_by(1000_usize) { + let w_base = + Perquintill::from_rational(1_000_000_000_000_u128 - num, 1_000_000_000_000_u128); + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let e = exp_base_quote(w_base, w_quote, x, dx); + + let one = U64F64::from_num(1); + let dy = U64F64::from_num(y) * (one - e); + + let progress = (num as f64 - start as f64) / (stop as f64 - start as f64); + if progress - last_progress >= 0.0001 { + // Replace with println for real-time progress + log::debug!("progress = {:?}%", progress * 100.); + log::debug!("dy = {:?}", dy); + last_progress = progress; + } + + assert!(dy != U64F64::from_num(0)); + assert!(dy <= prev); + prev = dy; + } + } + + // cargo test --package share-pool --lib -- tests::test_exp_quote_fuzzy --exact --include-ignored --show-output + #[ignore] + #[test] + fn test_exp_quote_fuzzy() { + use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; + use rayon::prelude::*; + use std::sync::Arc; + use std::sync::atomic::{AtomicUsize, Ordering}; + + const ITERATIONS: usize = 1_000_000_000; + let counter = Arc::new(AtomicUsize::new(0)); + + (0..ITERATIONS) + .into_par_iter() + .for_each(|i| { + // Each iteration gets its own deterministic RNG. + // Seed depends on i, so runs are reproducible. + let mut rng = StdRng::seed_from_u64(42 + i as u64); + let max_supply: u64 = 21_000_000_000_000_000; + let full_range = true; + + let x: u64 = rng.gen_range(1_000..=max_supply); // Alpha reserve + let y: u64 = if full_range { + // TAO reserve (allow huge prices) + rng.gen_range(1_000..=max_supply) + } else { + // TAO reserve (limit prices with 0-1000) + rng.gen_range(1_000..x.saturating_mul(1000).min(max_supply)) + }; + let dx: u64 = if full_range { + // Alhpa sold (allow huge values) + rng.gen_range(1_000..=21_000_000_000_000_000) + } else { + // Alhpa sold (do not sell more than 100% of what's in alpha reserve) + rng.gen_range(1_000..=x) + }; + let w_numerator: u64 = rng.gen_range(ACCURACY / 10..=ACCURACY / 10 * 9); + let w_base = Perquintill::from_rational(ACCURACY - w_numerator, ACCURACY); + let w_quote = Perquintill::from_rational(w_numerator, ACCURACY); + let e = exp_base_quote(w_base, w_quote, x, dx); + + let one = U64F64::from_num(1); + let dy = U64F64::from_num(y) * (one - e); + + // Calculate expected in f64 and approx-assert + let w1 = perquintill_to_f64(w_base); + let w2 = perquintill_to_f64(w_quote); + let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); + let dy_expected = y as f64 * (1. - e_expected); + + let actual = dy.to_num::(); + let eps = (dy_expected / 1_000_000.).clamp(1.0, 1000.0); + + assert!( + (actual - dy_expected).abs() <= eps, + "dy mismatch:\n actual: {}\n expected: {}\n eps: {}\nParameters:\n x: {}\n y: {}\n dx: {}\n w_numerator: {}\n", + actual, dy_expected, eps, x, y, dx, w_numerator, + ); + + // Assert that we aren't giving out more than reserve y + assert!(dy <= y, "dy = {},\ny = {}", dy, y,); + + // Print progress + let done = counter.fetch_add(1, Ordering::Relaxed) + 1; + if done % 100_000_000 == 0 { + let progress = done as f64 / ITERATIONS as f64 * 100.0; + // Replace with println for real-time progress + log::debug!("progress = {progress:.4}%"); + } + }); + } + + fn rel_err(a: f64, b: f64) -> f64 { + let denom = a.abs().max(b.abs()).max(1.0); + (a - b).abs() / denom + } + + fn push_unique(v: &mut Vec, x: u128) { + if x != 0 && !v.contains(&x) { + v.push(x); + } + } + + // cargo test --package share-pool --lib -- tests::test_safefloat_mul_div_wide_range --exact --include-ignored --show-output + #[test] + #[ignore = "long-running sweep test; run explicitly when needed"] + fn test_safefloat_mul_div_wide_range() { + use rayon::prelude::*; + use std::sync::Arc; + use std::sync::atomic::{AtomicUsize, Ordering}; + + // Build mantissa corpus + let mut mantissas = Vec::::new(); + + let linear_steps: u128 = 200; + let linear_step = (SAFE_FLOAT_MAX / linear_steps).max(1); + let mut m = 1u128; + while m <= SAFE_FLOAT_MAX { + push_unique(&mut mantissas, m); + match m.checked_add(linear_step) { + Some(next) if next > m => m = next, + _ => break, + } + } + push_unique(&mut mantissas, SAFE_FLOAT_MAX); + + let mut p = 1u128; + while p <= SAFE_FLOAT_MAX { + push_unique(&mut mantissas, p); + if p > 1 { + push_unique(&mut mantissas, p - 1); + } + if let Some(next) = p.checked_add(1) { + if next <= SAFE_FLOAT_MAX { + push_unique(&mut mantissas, next); + } + } + + match p.checked_mul(10) { + Some(next) if next > p && next <= SAFE_FLOAT_MAX => p = next, + _ => break, + } + } + + for delta in [ + 0u128, 1, 2, 3, 7, 9, 10, 11, 99, 100, 101, 999, 1_000, 10_000, + ] { + if SAFE_FLOAT_MAX > delta { + push_unique(&mut mantissas, SAFE_FLOAT_MAX - delta); + } + } + + mantissas.sort_unstable(); + mantissas.dedup(); + + let exp_min: i64 = -120; + let exp_max: i64 = 120; + let exp_step: usize = 5; + let exponents: Vec = (exp_min..=exp_max).step_by(exp_step).collect(); + + // Precompute all (a, b) pairs as outer work items. + // Each Rayon task will then iterate all c's sequentially. + let mut outer_cases: Vec<(u128, i64, u128, i64)> = Vec::new(); + + for &ma in &mantissas { + for &ea in &exponents { + for &mb in &mantissas { + for &eb in &exponents { + outer_cases.push((ma, ea, mb, eb)); + } + } + } + } + + let checked = Arc::new(AtomicUsize::new(0)); + let skipped_non_finite = Arc::new(AtomicUsize::new(0)); + let skipped_invalid_sf = Arc::new(AtomicUsize::new(0)); + + let progress_step = 10_000usize; + let total_outer = outer_cases.len(); + + outer_cases.into_par_iter().for_each(|(ma, ea, mb, eb)| { + let a = match SafeFloat::new(SafeInt::from(ma), ea) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + return; + } + }; + + let b = match SafeFloat::new(SafeInt::from(mb), eb) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + return; + } + }; + + for &mc in &mantissas { + for &ec in &exponents { + let c = match SafeFloat::new(SafeInt::from(mc), ec) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + continue; + } + }; + + let actual_sf = a.mul_div(&b, &c).unwrap(); + let actual: f64 = actual_sf.into(); + + let expected = + (ma as f64 * 10_f64.powi(ea as i32)) + * (mb as f64 * 10_f64.powi(eb as i32)) + / (mc as f64 * 10_f64.powi(ec as i32)); + + if !expected.is_finite() || !actual.is_finite() { + skipped_non_finite.fetch_add(1, Ordering::Relaxed); + continue; + } + + let err = rel_err(actual, expected); + + assert!( + err <= 1e-12, + concat!( + "mul_div mismatch:\n", + " a = {}e{}\n", + " b = {}e{}\n", + " c = {}e{}\n", + " actual = {:.20e}\n", + " expected = {:.20e}\n", + " rel_err = {:.20e}" + ), + ma, ea, mb, eb, mc, ec, actual, expected, err + ); + + checked.fetch_add(1, Ordering::Relaxed); + } + } + + let done_outer = checked.load(Ordering::Relaxed); + if done_outer % progress_step == 0 { + let invalid = skipped_invalid_sf.load(Ordering::Relaxed); + let non_finite = skipped_non_finite.load(Ordering::Relaxed); + log::debug!( + "progress: checked={}, skipped_invalid_sf={}, skipped_non_finite={}, outer_total={}", + done_outer, + invalid, + non_finite, + total_outer, + ); + } + }); + + let checked = checked.load(Ordering::Relaxed); + let skipped_non_finite = skipped_non_finite.load(Ordering::Relaxed); + let skipped_invalid_sf = skipped_invalid_sf.load(Ordering::Relaxed); + + println!( + "checked={}, skipped_non_finite={}, skipped_invalid_sf={}, mantissas={}, exponents={}, outer_cases={}", + checked, + skipped_non_finite, + skipped_invalid_sf, + mantissas.len(), + exponents.len(), + total_outer, + ); + + assert!(checked > 0, "test did not validate any finite cases"); + } + + #[test] + #[ignore = "long-running broad-range test; run explicitly when needed"] + fn test_safefloat_div_wide_range() { + use rayon::prelude::*; + use std::sync::Arc; + use std::sync::atomic::{AtomicUsize, Ordering}; + + fn rel_err(a: f64, b: f64) -> f64 { + let denom = a.abs().max(b.abs()).max(1.0); + (a - b).abs() / denom + } + + fn push_unique(v: &mut Vec, x: u128) { + if x != 0 && !v.contains(&x) { + v.push(x); + } + } + + // Build a broad mantissa corpus: + // - coarse linear sweep + // - powers of 10 and neighbors + // - values near SAFE_FLOAT_MAX + let mut mantissas = Vec::::new(); + + let linear_steps: u128 = 200; + let linear_step = (SAFE_FLOAT_MAX / linear_steps).max(1); + let mut m = 1u128; + while m <= SAFE_FLOAT_MAX { + push_unique(&mut mantissas, m); + match m.checked_add(linear_step) { + Some(next) if next > m => m = next, + _ => break, + } + } + push_unique(&mut mantissas, SAFE_FLOAT_MAX); + + let mut p = 1u128; + while p <= SAFE_FLOAT_MAX { + push_unique(&mut mantissas, p); + if p > 1 { + push_unique(&mut mantissas, p - 1); + } + if let Some(next) = p.checked_add(1) { + if next <= SAFE_FLOAT_MAX { + push_unique(&mut mantissas, next); + } + } + + match p.checked_mul(10) { + Some(next) if next > p && next <= SAFE_FLOAT_MAX => p = next, + _ => break, + } + } + + for delta in [ + 0u128, 1, 2, 3, 7, 9, 10, 11, 99, 100, 101, 999, 1_000, 10_000, + ] { + if SAFE_FLOAT_MAX > delta { + push_unique(&mut mantissas, SAFE_FLOAT_MAX - delta); + } + } + + mantissas.sort_unstable(); + mantissas.dedup(); + + // Exponent sweep. + // Keep it large enough to stress normalization / exponent math, + // but still practical for f64 reference calculations. + let exp_min: i64 = -120; + let exp_max: i64 = 120; + let exp_step: usize = 5; + let exponents: Vec = (exp_min..=exp_max).step_by(exp_step).collect(); + + let m_len = mantissas.len(); + let e_len = exponents.len(); + let total_cases = m_len * e_len * m_len * e_len; + + let checked = Arc::new(AtomicUsize::new(0)); + let skipped_non_finite = Arc::new(AtomicUsize::new(0)); + let skipped_invalid_sf = Arc::new(AtomicUsize::new(0)); + let done_counter = Arc::new(AtomicUsize::new(0)); + + (0..total_cases).into_par_iter().for_each(|idx| { + let mut rem = idx; + + let eb_idx = rem % e_len; + rem /= e_len; + + let mb_idx = rem % m_len; + rem /= m_len; + + let ea_idx = rem % e_len; + rem /= e_len; + + let ma_idx = rem % m_len; + + let ma = mantissas[ma_idx]; + let ea = exponents[ea_idx]; + let mb = mantissas[mb_idx]; + let eb = exponents[eb_idx]; + + let a = match SafeFloat::new(SafeInt::from(ma), ea) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + done_counter.fetch_add(1, Ordering::Relaxed); + return; + } + }; + + let b = match SafeFloat::new(SafeInt::from(mb), eb) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + done_counter.fetch_add(1, Ordering::Relaxed); + return; + } + }; + + let actual_sf = match a.div(&b) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + done_counter.fetch_add(1, Ordering::Relaxed); + return; + } + }; + + let actual: f64 = actual_sf.into(); + let expected = + (ma as f64 * 10_f64.powi(ea as i32)) / (mb as f64 * 10_f64.powi(eb as i32)); + + if !actual.is_finite() || !expected.is_finite() { + skipped_non_finite.fetch_add(1, Ordering::Relaxed); + } else { + let err = rel_err(actual, expected); + + assert!( + err <= 1e-12, + concat!( + "div mismatch:\n", + " a = {}e{}\n", + " b = {}e{}\n", + " actual = {:.20e}\n", + " expected = {:.20e}\n", + " rel_err = {:.20e}" + ), + ma, + ea, + mb, + eb, + actual, + expected, + err + ); + + checked.fetch_add(1, Ordering::Relaxed); + } + + let done = done_counter.fetch_add(1, Ordering::Relaxed) + 1; + if done % 10_000 == 0 { + let progress = done as f64 / total_cases as f64 * 100.0; + log::debug!("div progress = {progress:.4}%"); + } + }); + + let checked = checked.load(Ordering::Relaxed); + let skipped_non_finite = skipped_non_finite.load(Ordering::Relaxed); + let skipped_invalid_sf = skipped_invalid_sf.load(Ordering::Relaxed); + + println!( + "div checked={}, skipped_non_finite={}, skipped_invalid_sf={}, mantissas={}, exponents={}, total_cases={}", + checked, + skipped_non_finite, + skipped_invalid_sf, + mantissas.len(), + exponents.len(), + total_cases, + ); + + assert!(checked > 0, "div test did not validate any finite cases"); + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index e554ce0b25..19bde50331 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1209,8 +1209,8 @@ impl pallet_subtensor_swap::Config for Runtime { type SubnetInfo = SubtensorModule; type BalanceOps = SubtensorModule; type ProtocolId = SwapProtocolId; - type TaoReserve = pallet_subtensor::TaoCurrencyReserve; - type AlphaReserve = pallet_subtensor::AlphaCurrencyReserve; + type TaoReserve = pallet_subtensor::TaoBalanceReserve; + type AlphaReserve = pallet_subtensor::AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity;