diff --git a/Cargo.lock b/Cargo.lock index d134d115..d5681169 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -125,9 +125,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -140,43 +140,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.90" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "approx" @@ -198,7 +198,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -360,7 +360,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -376,7 +376,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -400,7 +400,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", "synstructure 0.13.1", ] @@ -423,7 +423,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -445,9 +445,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", @@ -456,7 +456,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 0.38.37", + "rustix 0.38.42", "slab", "tracing", "windows-sys 0.59.0", @@ -481,7 +481,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -531,7 +531,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -1174,13 +1174,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.24", + "prettyplease 0.2.25", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -1284,9 +1284,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" +checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" dependencies = [ "arrayref", "arrayvec", @@ -1315,9 +1315,9 @@ dependencies = [ [[package]] name = "bounded-collections" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db436177db0d505b1507f03aca56a41442ae6efdf8b6eaa855d73e52c5b078dc" +checksum = "3d077619e9c237a5d1875166f5e8033e8f6bff0c96f8caf81e1c2d7738c431bf" dependencies = [ "log", "parity-scale-codec", @@ -1402,9 +1402,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" [[package]] name = "byteorder" @@ -1414,9 +1414,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "bzip2-sys" @@ -1450,9 +1450,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -1465,10 +1465,10 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1479,9 +1479,9 @@ checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" [[package]] name = "cc" -version = "1.1.31" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" dependencies = [ "jobserver", "libc", @@ -1554,9 +1554,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1625,9 +1625,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", "clap_derive", @@ -1635,9 +1635,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", @@ -1655,14 +1655,14 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "codespan-reporting" @@ -1671,14 +1671,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "combine" @@ -1692,13 +1692,13 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" +checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] @@ -1725,7 +1725,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.14", "windows-sys 0.52.0", ] @@ -1818,9 +1818,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -1949,9 +1949,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1968,18 +1968,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" @@ -2063,51 +2063,66 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "cxx" -version = "1.0.129" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdc8cca144dce1c4981b5c9ab748761619979e515c3d53b5df385c677d1d007" +checksum = "a5a32d755fe20281b46118ee4b507233311fb7a48a0cfd42f554b93640521a2f" dependencies = [ "cc", + "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", + "foldhash", "link-cplusplus", ] [[package]] name = "cxx-build" -version = "1.0.129" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5764c3142ab44fcf857101d12c0ddf09c34499900557c764f5ad0597159d1fc" +checksum = "11645536ada5d1c8804312cbffc9ab950f2216154de431de930da47ca6955199" dependencies = [ "cc", "codespan-reporting", - "once_cell", "proc-macro2", "quote", "scratch", - "syn 2.0.82", + "syn 2.0.90", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcc9c78e3c7289665aab921a2b394eaffe8bdb369aa18d81ffc0f534fd49385" +dependencies = [ + "clap", + "codespan-reporting", + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] name = "cxxbridge-flags" -version = "1.0.129" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d422aff542b4fa28c2ce8e5cc202d42dbf24702345c1fba3087b2d3f8a1b90ff" +checksum = "3a22a87bd9e78d7204d793261470a4c9d585154fddd251828d8aefbb5f74c3bf" [[package]] name = "cxxbridge-macro" -version = "1.0.129" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1719100f31492cd6adeeab9a0f46cdbc846e615fdb66d7b398aa46ec7fdd06f" +checksum = "1dfdb020ff8787c5daf6e0dca743005cc8782868faeadfbabb8824ede5cb1c72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "rustversion", + "syn 2.0.90", ] [[package]] @@ -2215,7 +2230,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -2228,7 +2243,27 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.82", + "syn 2.0.90", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -2317,23 +2352,23 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "docify" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2f138ad521dc4a2ced1a4576148a6a610b4c5923933b062a263130a6802ce" +checksum = "a772b62b1837c8f060432ddcc10b17aae1453ef17617a99bc07789252d2a5896" dependencies = [ "docify_macros", ] [[package]] name = "docify_macros" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a081e51fb188742f5a7a1164ad752121abcb22874b21e2c3b0dd040c515fdad" +checksum = "60e6be249b0a462a14784a99b19bf35a667bb5e09de611738bb7362fa4c95ff7" dependencies = [ "common-path", "derive-syn-parse", @@ -2341,7 +2376,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.82", + "syn 2.0.90", "termcolor", "toml 0.8.19", "walkdir", @@ -2503,7 +2538,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -2523,7 +2558,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -2553,12 +2588,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2639,9 +2674,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener 5.3.1", "pin-project-lite", @@ -2739,10 +2774,10 @@ dependencies = [ "blake2 0.10.6", "file-guard", "fs-err", - "prettyplease 0.2.24", + "prettyplease 0.2.25", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -2759,9 +2794,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fc-api" @@ -2788,7 +2823,7 @@ dependencies = [ "sp-block-builder", "sp-consensus", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2912,7 +2947,7 @@ dependencies = [ "sp-timestamp", "substrate-prometheus-endpoint", "target_info", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -3099,7 +3134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182f7dbc2ef73d9ef67351c5fbbea084729c48362d3ce9dd44c28e32e277fe5" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3255,7 +3290,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" dependencies = [ "nonempty", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3488,7 +3523,7 @@ dependencies = [ "sp-storage", "sp-trie", "sp-wasm-interface", - "thiserror", + "thiserror 1.0.69", "thousands", ] @@ -3579,7 +3614,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -3591,7 +3626,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -3601,7 +3636,7 @@ source = "git+https://github.com/bifrost-platform/polkadot-sdk?branch=bifrost-po dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -3762,9 +3797,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "futures-core", "pin-project-lite", @@ -3778,7 +3813,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -3974,7 +4009,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.6.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -3983,17 +4018,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.6.0", + "http 1.2.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -4011,7 +4046,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4056,9 +4091,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", "equivalent", @@ -4203,9 +4238,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -4230,7 +4265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -4241,7 +4276,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "pin-project-lite", ] @@ -4281,7 +4316,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.5.8", "tokio", "tower-service", "tracing", @@ -4290,15 +4325,15 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", - "http 1.1.0", + "h2 0.4.7", + "http 1.2.0", "http-body 1.0.1", "httparse", "httpdate", @@ -4326,15 +4361,15 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.0", + "hyper 1.5.1", "pin-project-lite", "tokio", "tower-service", @@ -4363,6 +4398,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "idna" version = "0.2.3" @@ -4386,12 +4539,23 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -4406,9 +4570,9 @@ dependencies = [ [[package]] name = "if-watch" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" +checksum = "cdf9d64cfcf380606e64f9a0bcf493616b65331199f984151a6fa11a7b3cde38" dependencies = [ "async-io", "core-foundation", @@ -4417,6 +4581,10 @@ dependencies = [ "if-addrs", "ipnet", "log", + "netlink-packet-core", + "netlink-packet-route", + "netlink-proto", + "netlink-sys", "rtnetlink", "system-configuration", "tokio", @@ -4471,13 +4639,13 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.90", ] [[package]] @@ -4512,12 +4680,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.2", ] [[package]] @@ -4570,7 +4738,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.7", + "socket2 0.5.8", "widestring", "windows-sys 0.48.0", "winreg", @@ -4628,9 +4796,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" @@ -4643,10 +4811,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -4675,7 +4844,7 @@ dependencies = [ "beef", "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "jsonrpsee-types", @@ -4684,7 +4853,7 @@ dependencies = [ "rustc-hash", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -4699,7 +4868,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -4710,10 +4879,10 @@ checksum = "654afab2e92e5d88ebd8a39d6074483f3f2bfdf91c5ac57fe285e7127cdd4f51" dependencies = [ "anyhow", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", @@ -4722,7 +4891,7 @@ dependencies = [ "serde", "serde_json", "soketto", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tokio-util", @@ -4737,10 +4906,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c465fbe385238e861fdc4d1c85e04ada6c1fd246161d26385c1b311724d2af" dependencies = [ "beef", - "http 1.1.0", + "http 1.2.0", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4822,15 +4991,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -4838,9 +5007,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libp2p" @@ -4876,7 +5045,7 @@ dependencies = [ "multiaddr 0.18.2", "pin-project", "rw-stream-sink", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4917,7 +5086,7 @@ dependencies = [ "libp2p-identity", "log", "multiaddr 0.18.2", - "multihash 0.19.1", + "multihash 0.19.3", "multistream-select", "once_cell", "parking_lot 0.12.3", @@ -4926,7 +5095,7 @@ dependencies = [ "rand", "rw-stream-sink", "smallvec", - "thiserror", + "thiserror 1.0.69", "unsigned-varint 0.7.2", "void", ] @@ -4966,24 +5135,24 @@ dependencies = [ "quick-protobuf", "quick-protobuf-codec", "smallvec", - "thiserror", + "thiserror 1.0.69", "void", ] [[package]] name = "libp2p-identity" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cca1eb2bc1fd29f099f3daaab7effd01e1a54b7c577d0ed082521034d912e8" +checksum = "257b5621d159b32282eac446bed6670c39c7dc68a200a992d8f056afa0066f6d" dependencies = [ "bs58 0.5.1", "ed25519-dalek", "hkdf", - "multihash 0.19.1", + "multihash 0.19.3", "quick-protobuf", "rand", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.69", "tracing", "zeroize", ] @@ -5011,7 +5180,7 @@ dependencies = [ "rand", "sha2 0.10.8", "smallvec", - "thiserror", + "thiserror 1.0.69", "uint", "unsigned-varint 0.7.2", "void", @@ -5032,7 +5201,7 @@ dependencies = [ "log", "rand", "smallvec", - "socket2 0.5.7", + "socket2 0.5.8", "tokio", "trust-dns-proto 0.22.0", "void", @@ -5068,14 +5237,14 @@ dependencies = [ "libp2p-identity", "log", "multiaddr 0.18.2", - "multihash 0.19.1", + "multihash 0.19.3", "once_cell", "quick-protobuf", "rand", "sha2 0.10.8", "snow", "static_assertions", - "thiserror", + "thiserror 1.0.69", "x25519-dalek", "zeroize", ] @@ -5117,8 +5286,8 @@ dependencies = [ "rand", "ring 0.16.20", "rustls 0.21.12", - "socket2 0.5.7", - "thiserror", + "socket2 0.5.8", + "thiserror 1.0.69", "tokio", ] @@ -5173,7 +5342,7 @@ dependencies = [ "proc-macro-warning 0.4.2", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -5189,7 +5358,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "log", - "socket2 0.5.7", + "socket2 0.5.8", "tokio", ] @@ -5207,7 +5376,7 @@ dependencies = [ "ring 0.16.20", "rustls 0.21.12", "rustls-webpki", - "thiserror", + "thiserror 1.0.69", "x509-parser 0.15.1", "yasna", ] @@ -5258,7 +5427,7 @@ dependencies = [ "pin-project-lite", "rw-stream-sink", "soketto", - "thiserror", + "thiserror 1.0.69", "url", "webpki-roots", ] @@ -5272,7 +5441,7 @@ dependencies = [ "futures 0.3.31", "libp2p-core", "log", - "thiserror", + "thiserror 1.0.69", "yamux", ] @@ -5284,7 +5453,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", ] [[package]] @@ -5429,6 +5598,12 @@ dependencies = [ "keystream", ] +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "litep2p" version = "0.6.2" @@ -5443,7 +5618,7 @@ dependencies = [ "futures 0.3.31", "futures-timer", "hex-literal", - "indexmap 2.6.0", + "indexmap 2.7.0", "libc", "mockall 0.12.1", "multiaddr 0.17.1", @@ -5464,10 +5639,10 @@ dependencies = [ "simple-dns", "smallvec", "snow", - "socket2 0.5.7", + "socket2 0.5.8", "static_assertions", "str0m", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tokio-tungstenite", @@ -5506,7 +5681,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.0", + "hashbrown 0.15.2", ] [[package]] @@ -5555,7 +5730,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -5569,7 +5744,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -5580,7 +5755,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -5591,7 +5766,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -5637,7 +5812,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.37", + "rustix 0.38.42", ] [[package]] @@ -5716,11 +5891,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -5747,7 +5921,7 @@ dependencies = [ "rand_chacha", "rand_distr", "subtle 2.6.1", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -5802,7 +5976,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -5835,7 +6009,7 @@ dependencies = [ "data-encoding", "libp2p-identity", "multibase", - "multihash 0.19.1", + "multihash 0.19.3", "percent-encoding", "serde", "static_assertions", @@ -5890,12 +6064,12 @@ dependencies = [ [[package]] name = "multihash" -version = "0.19.1" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076d548d76a0e2a0d4ab471d0b1c36c577786dfc4471242035d97a12a735c492" +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" dependencies = [ "core2 0.4.0", - "unsigned-varint 0.7.2", + "unsigned-varint 0.8.0", ] [[package]] @@ -5940,9 +6114,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.33.1" +version = "0.33.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf139e93ad757869338ad85239cb1d6c067b23b94e5846e637ca6328ee4be60" +checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b" dependencies = [ "approx", "matrixmultiply", @@ -5981,21 +6155,20 @@ dependencies = [ [[package]] name = "netlink-packet-core" -version = "0.4.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" dependencies = [ "anyhow", "byteorder", - "libc", "netlink-packet-utils", ] [[package]] name = "netlink-packet-route" -version = "0.12.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" dependencies = [ "anyhow", "bitflags 1.3.2", @@ -6014,29 +6187,29 @@ dependencies = [ "anyhow", "byteorder", "paste", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "netlink-proto" -version = "0.10.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +checksum = "86b33524dc0968bfad349684447bfce6db937a9ac3332a1fe60c0c5a5ce63f21" dependencies = [ "bytes", "futures 0.3.31", "log", "netlink-packet-core", "netlink-sys", - "thiserror", + "thiserror 1.0.69", "tokio", ] [[package]] name = "netlink-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" +checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" dependencies = [ "bytes", "futures 0.3.31", @@ -6053,15 +6226,15 @@ checksum = "a4a43439bf756eed340bdf8feba761e2d50c7d47175d87545cd5cbe4a137c4d1" dependencies = [ "cc", "libc", - "thiserror", + "thiserror 1.0.69", "winapi", ] [[package]] name = "nix" -version = "0.24.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", @@ -6236,7 +6409,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -6328,7 +6501,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -6339,9 +6512,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.3.2+3.3.2" +version = "300.4.1+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" dependencies = [ "cc", ] @@ -7081,7 +7254,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -7142,20 +7315,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror", + "thiserror 2.0.7", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" dependencies = [ "pest", "pest_generator", @@ -7163,22 +7336,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "pest_meta" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" dependencies = [ "once_cell", "pest", @@ -7192,34 +7365,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.6.0", + "indexmap 2.7.0", ] [[package]] name = "pin-project" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -7292,7 +7465,7 @@ dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -7302,7 +7475,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -7328,15 +7501,15 @@ checksum = "26e85d3456948e650dff0cfc85603915847faf893ed1e66b020bb82ef4557120" [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.37", + "rustix 0.38.42", "tracing", "windows-sys 0.59.0", ] @@ -7366,9 +7539,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "powerfmt" @@ -7545,7 +7718,7 @@ source = "git+https://github.com/bifrost-platform/bifrost-frontier?branch=bifros dependencies = [ "case", "num_enum", - "prettyplease 0.2.24", + "prettyplease 0.2.25", "proc-macro2", "quote", "sp-crypto-hashing", @@ -7604,12 +7777,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -7632,7 +7805,7 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ - "thiserror", + "thiserror 1.0.69", "toml 0.5.11", ] @@ -7677,7 +7850,7 @@ checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -7688,14 +7861,14 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -7711,7 +7884,7 @@ dependencies = [ "lazy_static", "memchr", "parking_lot 0.12.3", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7734,7 +7907,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -7792,11 +7965,11 @@ dependencies = [ "multimap 0.10.0", "once_cell", "petgraph", - "prettyplease 0.2.24", + "prettyplease 0.2.25", "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.82", + "syn 2.0.90", "tempfile", ] @@ -7823,7 +7996,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -7846,18 +8019,18 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] [[package]] name = "quanta" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +checksum = "773ce68d0bb9bc7ef20be3536ffe94e223e1f365bd374108b2659fac0c65cfe6" dependencies = [ "crossbeam-utils", "libc", @@ -7892,7 +8065,7 @@ dependencies = [ "asynchronous-codec", "bytes", "quick-protobuf", - "thiserror", + "thiserror 1.0.69", "unsigned-varint 0.7.2", ] @@ -7908,7 +8081,7 @@ dependencies = [ "quinn-udp 0.3.2", "rustc-hash", "rustls 0.20.9", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "webpki", @@ -7927,7 +8100,7 @@ dependencies = [ "quinn-udp 0.4.1", "rustc-hash", "rustls 0.21.12", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -7944,7 +8117,7 @@ dependencies = [ "rustc-hash", "rustls 0.20.9", "slab", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tracing", "webpki", @@ -7962,7 +8135,7 @@ dependencies = [ "rustc-hash", "rustls 0.21.12", "slab", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tracing", ] @@ -7988,7 +8161,7 @@ checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" dependencies = [ "bytes", "libc", - "socket2 0.5.7", + "socket2 0.5.8", "tracing", "windows-sys 0.48.0", ] @@ -8115,9 +8288,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -8130,7 +8303,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8150,7 +8323,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -8180,13 +8353,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -8201,9 +8374,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -8332,16 +8505,19 @@ dependencies = [ [[package]] name = "rtnetlink" -version = "0.10.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" +checksum = "7a552eb82d19f38c3beed3f786bd23aa434ceb9ac43ab44419ca6d67a7e186c0" dependencies = [ "futures 0.3.31", "log", + "netlink-packet-core", "netlink-packet-route", + "netlink-packet-utils", "netlink-proto", + "netlink-sys", "nix", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -8379,7 +8555,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.23", + "semver 1.0.24", ] [[package]] @@ -8407,15 +8583,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8521,7 +8697,7 @@ dependencies = [ "log", "sp-core", "sp-wasm-interface", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8596,7 +8772,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -8636,7 +8812,7 @@ dependencies = [ "sp-panic-handler", "sp-runtime", "sp-version", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -8714,7 +8890,7 @@ dependencies = [ "sp-runtime", "sp-state-machine", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8743,7 +8919,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8779,7 +8955,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8836,7 +9012,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8856,7 +9032,7 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8891,7 +9067,7 @@ dependencies = [ "sp-runtime", "sp-timestamp", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8949,7 +9125,7 @@ dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", "sp-wasm-interface", - "thiserror", + "thiserror 1.0.69", "wasm-instrument", ] @@ -9010,7 +9186,7 @@ dependencies = [ "sp-application-crypto", "sp-core", "sp-keystore", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9039,7 +9215,7 @@ dependencies = [ "sp-keystore", "sp-mixnet", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9084,7 +9260,7 @@ dependencies = [ "sp-core", "sp-runtime", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "unsigned-varint 0.7.2", @@ -9148,7 +9324,7 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9183,7 +9359,7 @@ dependencies = [ "sp-core", "sp-runtime", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -9218,9 +9394,9 @@ dependencies = [ "litep2p", "log", "multiaddr 0.18.2", - "multihash 0.19.1", + "multihash 0.19.3", "rand", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -9316,7 +9492,7 @@ dependencies = [ "sp-rpc", "sp-runtime", "sp-version", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9327,9 +9503,9 @@ dependencies = [ "forwarded-header-value", "futures 0.3.31", "governor", - "http 1.1.0", + "http 1.2.0", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "ip_network", "jsonrpsee", "log", @@ -9368,7 +9544,7 @@ dependencies = [ "sp-rpc", "sp-runtime", "sp-version", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -9431,7 +9607,7 @@ dependencies = [ "static_init", "substrate-prometheus-endpoint", "tempfile", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "tracing-futures", @@ -9453,7 +9629,7 @@ name = "sc-sysinfo" version = "37.0.0" source = "git+https://github.com/bifrost-platform/polkadot-sdk?branch=bifrost-polkadot-stable2407#d8e2d11864ca21deb9a753b86cb2396b1c531acb" dependencies = [ - "derive_more", + "derive_more 0.99.18", "futures 0.3.31", "libc", "log", @@ -9485,7 +9661,7 @@ dependencies = [ "sc-utils", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "wasm-timer", ] @@ -9513,7 +9689,7 @@ dependencies = [ "sp-rpc", "sp-runtime", "sp-tracing", - "thiserror", + "thiserror 1.0.69", "tracing", "tracing-log", "tracing-subscriber", @@ -9527,7 +9703,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -9554,7 +9730,7 @@ dependencies = [ "sp-tracing", "sp-transaction-pool", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9570,7 +9746,7 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9590,13 +9766,13 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.3" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "bitvec", "cfg-if", - "derive_more", + "derive_more 1.0.0", "parity-scale-codec", "scale-info-derive", "serde", @@ -9604,21 +9780,21 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.3" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.90", ] [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -9687,7 +9863,7 @@ dependencies = [ "log", "rand", "slab", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9748,9 +9924,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -9767,9 +9943,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] @@ -9788,9 +9964,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] @@ -9806,20 +9982,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -10036,9 +10212,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -10046,14 +10222,14 @@ dependencies = [ [[package]] name = "soketto" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" dependencies = [ "base64 0.22.1", "bytes", "futures 0.3.31", - "http 1.1.0", + "http 1.2.0", "httparse", "log", "rand", @@ -10079,7 +10255,7 @@ dependencies = [ "sp-state-machine", "sp-trie", "sp-version", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -10093,7 +10269,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -10147,7 +10323,7 @@ dependencies = [ "sp-database", "sp-runtime", "sp-state-machine", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -10163,7 +10339,7 @@ dependencies = [ "sp-inherents", "sp-runtime", "sp-state-machine", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -10268,7 +10444,7 @@ dependencies = [ "sp-storage", "ss58-registry", "substrate-bip39", - "thiserror", + "thiserror 1.0.69", "tracing", "w3f-bls", "zeroize", @@ -10294,7 +10470,7 @@ source = "git+https://github.com/bifrost-platform/polkadot-sdk?branch=bifrost-po dependencies = [ "quote", "sp-crypto-hashing", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -10313,7 +10489,7 @@ source = "git+https://github.com/bifrost-platform/polkadot-sdk?branch=bifrost-po dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -10348,7 +10524,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -10403,7 +10579,7 @@ name = "sp-maybe-compressed-blob" version = "11.0.0" source = "git+https://github.com/bifrost-platform/polkadot-sdk?branch=bifrost-polkadot-stable2407#d8e2d11864ca21deb9a753b86cb2396b1c531acb" dependencies = [ - "thiserror", + "thiserror 1.0.69", "zstd 0.12.4", ] @@ -10513,7 +10689,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -10558,7 +10734,7 @@ dependencies = [ "sp-externalities", "sp-panic-handler", "sp-trie", - "thiserror", + "thiserror 1.0.69", "tracing", "trie-db", ] @@ -10583,7 +10759,7 @@ dependencies = [ "sp-externalities", "sp-runtime", "sp-runtime-interface", - "thiserror", + "thiserror 1.0.69", "x25519-dalek", ] @@ -10613,7 +10789,7 @@ dependencies = [ "parity-scale-codec", "sp-inherents", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -10667,7 +10843,7 @@ dependencies = [ "schnellru", "sp-core", "sp-externalities", - "thiserror", + "thiserror 1.0.69", "tracing", "trie-db", "trie-root", @@ -10687,7 +10863,7 @@ dependencies = [ "sp-runtime", "sp-std", "sp-version-proc-macro", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -10698,7 +10874,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -10803,7 +10979,7 @@ dependencies = [ "futures-util", "hashlink", "hex", - "indexmap 2.6.0", + "indexmap 2.7.0", "log", "memchr", "native-tls", @@ -10814,7 +10990,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "sqlformat", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -10943,9 +11119,9 @@ dependencies = [ [[package]] name = "static_init_macro" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" +checksum = "1389c88ddd739ec6d3f8f83343764a0e944cd23cfbf126a9796a714b0b6edd6f" dependencies = [ "cfg_aliases", "memchr", @@ -10970,7 +11146,7 @@ dependencies = [ "sctp-proto", "serde", "sha-1", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -11018,7 +11194,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -11088,11 +11264,11 @@ version = "0.17.0" source = "git+https://github.com/bifrost-platform/polkadot-sdk?branch=bifrost-polkadot-stable2407#d8e2d11864ca21deb9a753b86cb2396b1c531acb" dependencies = [ "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", "log", "prometheus", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -11150,9 +11326,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.82" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -11179,25 +11355,25 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -11223,14 +11399,14 @@ checksum = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", "once_cell", - "rustix 0.38.37", + "rustix 0.38.42", "windows-sys 0.59.0", ] @@ -11245,11 +11421,11 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" dependencies = [ - "rustix 0.38.37", + "rustix 0.38.42", "windows-sys 0.59.0", ] @@ -11261,22 +11437,42 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767" +dependencies = [ + "thiserror-impl 2.0.7", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -11316,9 +11512,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -11337,9 +11533,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -11354,6 +11550,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -11371,9 +11577,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -11382,7 +11588,7 @@ dependencies = [ "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2 0.5.8", "tokio-macros", "windows-sys 0.52.0", ] @@ -11395,7 +11601,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -11410,9 +11616,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -11437,9 +11643,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -11485,7 +11691,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", @@ -11515,7 +11721,7 @@ checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "bitflags 2.6.0", "bytes", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "pin-project-lite", @@ -11537,9 +11743,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -11549,20 +11755,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -11591,9 +11797,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -11648,7 +11854,7 @@ dependencies = [ "rand", "smallvec", "socket2 0.4.10", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -11673,7 +11879,7 @@ dependencies = [ "once_cell", "rand", "smallvec", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -11695,7 +11901,7 @@ dependencies = [ "rand", "resolv-conf", "smallvec", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "trust-dns-proto 0.23.2", @@ -11728,7 +11934,7 @@ dependencies = [ "rand", "rustls 0.21.12", "sha1", - "thiserror", + "thiserror 1.0.69", "url", "utf-8", ] @@ -11777,9 +11983,9 @@ checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" @@ -11802,6 +12008,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -11860,12 +12072,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", ] @@ -11881,6 +12093,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -11913,9 +12137,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "w3f-bls" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a48c48447120a85b0bdb897ba9426a7aa15b4229498a2e19103e8c9368dd4b2" +checksum = "70a3028804c8bbae2a97a15b71ffc0e308c4b01a520994aafa77d56e94e19024" dependencies = [ "ark-bls12-377", "ark-bls12-381", @@ -11931,7 +12155,7 @@ dependencies = [ "rand_core", "sha2 0.10.8", "sha3", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -11962,9 +12186,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -11973,36 +12197,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12010,22 +12234,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-instrument" @@ -12047,7 +12271,7 @@ dependencies = [ "strum 0.24.1", "strum_macros 0.24.3", "tempfile", - "thiserror", + "thiserror 1.0.69", "wasm-opt-cxx-sys", "wasm-opt-sys", ] @@ -12174,7 +12398,7 @@ dependencies = [ "log", "object 0.30.4", "target-lexicon", - "thiserror", + "thiserror 1.0.69", "wasmparser", "wasmtime-cranelift-shared", "wasmtime-environ", @@ -12209,7 +12433,7 @@ dependencies = [ "object 0.30.4", "serde", "target-lexicon", - "thiserror", + "thiserror 1.0.69", "wasmparser", "wasmtime-types", ] @@ -12292,15 +12516,15 @@ checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" dependencies = [ "cranelift-entity", "serde", - "thiserror", + "thiserror 1.0.69", "wasmparser", ] [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -12331,14 +12555,14 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.37", + "rustix 0.38.42", ] [[package]] name = "wide" -version = "0.7.28" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690" +checksum = "58e6db2670d2be78525979e9a5f9c69d296fd7d670549fe9ebf70f8708cb5019" dependencies = [ "bytemuck", "safe_arch", @@ -12383,28 +12607,38 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.51.1" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" dependencies = [ - "windows-core 0.51.1", - "windows-targets 0.48.5", + "windows-core 0.53.0", + "windows-targets 0.52.6", ] [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] name = "windows-core" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ "windows-targets 0.52.6", ] @@ -12657,6 +12891,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -12691,7 +12937,7 @@ dependencies = [ "nom", "oid-registry 0.6.1", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -12708,7 +12954,7 @@ dependencies = [ "nom", "oid-registry 0.7.1", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -12720,14 +12966,14 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "xml-rs" -version = "0.8.22" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" +checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" [[package]] name = "xmltree" @@ -12762,6 +13008,30 @@ dependencies = [ "time", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure 0.13.1", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -12780,7 +13050,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure 0.13.1", ] [[package]] @@ -12800,7 +13091,29 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] diff --git a/pallets/bfc-staking/src/lib.rs b/pallets/bfc-staking/src/lib.rs index 148e7bee..5fa0d521 100644 --- a/pallets/bfc-staking/src/lib.rs +++ b/pallets/bfc-staking/src/lib.rs @@ -1,35 +1,3 @@ -//! # Bfc Staking -//! Minimal staking pallet that implements validator selection by total backed stake. -//! The main difference between this pallet and `frame/pallet-staking` is that this pallet -//! uses direct nomination. Nominators choose exactly who they nominate and with what stake. -//! This is different from `frame/pallet-staking` where nominators approval vote and run Phragmen. -//! -//! ### Rules -//! There is a new round every `>::get().length` blocks. -//! -//! At the start of every round, -//! * issuance is calculated for validators (and their nominators) for block authoring -//! `T::RewardPaymentDelay` rounds ago -//! * a new set of validators is chosen from the candidates -//! -//! Immediately following a round change, payments are made once-per-block until all payments have -//! been made. In each such block, one validator is chosen for a rewards payment and is paid along -//! with each of its top `T::MaxTopNominationsPerCandidate` nominators. -//! -//! To join the set of candidates, call `join_candidates` with `bond >= MinCandidateStk`. -//! To leave the set of candidates, call `schedule_leave_candidates`. If the call succeeds, -//! the validator is removed from the pool of candidates so they cannot be selected for future -//! validator sets, but they are not unbonded until their exit request is executed. Any signed -//! account may trigger the exit `T::LeaveCandidatesDelay` rounds after the round in which the -//! original request was made. -//! -//! To join the set of nominators, call `nominate` and pass in an account that is -//! already a validator candidate and `bond >= MinNominatorStk`. Each nominator can nominate up to -//! `T::MaxNominationsPerNominator` validator candidates by calling `nominate`. -//! -//! To revoke a nomination, call `revoke_nomination` with the validator candidate's account. -//! To leave the set of nominators and revoke all nominations, call `leave_nominators`. - #![cfg_attr(not(feature = "std"), no_std)] pub mod inflation; @@ -513,22 +481,6 @@ impl< pub fn highest_nomination_amount(&self) -> Balance { self.nominations.first().map(|x| x.amount).unwrap_or(Balance::zero()) } - - /// Slash nominator's bonding and total amount with given slashing value. - pub fn slash_nomination_amount( - &mut self, - nominator: &AccountId, - value: Balance, - ) -> bool { - for bond in self.nominations.iter_mut() { - if bond.owner == *nominator { - bond.amount = bond.amount.saturating_sub(value); - self.total = self.total.saturating_sub(value); - return true; - } - } - false - } } #[derive( @@ -966,7 +918,7 @@ impl< let mut top_nominations = >::get(candidate).ok_or(>::TopNominationDNE)?; let max_top_nominations_per_candidate = T::MaxTopNominationsPerCandidate::get(); - if top_nominations.nominations.len() as u32 == max_top_nominations_per_candidate { + if top_nominations.count() == max_top_nominations_per_candidate { // pop lowest top nomination let new_bottom_nomination = top_nominations.nominations.pop().ok_or(>::TopNominationDNE)?; @@ -989,10 +941,9 @@ impl< Ok(less_total_staked) } - /// Add nomination to bottom nominations - /// Check before call that if capacity is full, inserted nomination is higher than lowest - /// bottom nomination (and if so, need to adjust the total storage item) - /// CALLER MUST ensure(lowest_bottom_to_be_kicked.amount < nomination.amount) + /// Add a nomination to the `BottomNominations`. + /// If the nomination bumps a nomination from the top, `bumped_from_top` is set to true. + /// If the maximum capacity is reached, the lowest nomination is kicked and removed from storage. pub fn add_bottom_nomination( &mut self, bumped_from_top: bool, @@ -1004,31 +955,37 @@ impl< { let mut bottom_nominations = >::get(candidate).ok_or(>::BottomNominationDNE)?; - // if bottom is full, kick the lowest bottom (which is expected to be lower than input - // as per check) - let increase_nomination_count = if bottom_nominations.nominations.len() as u32 + // if bottom is full, kick the lowest bottom (which is expected to be lower than input. + // previously checked before function call) + let increase_nomination_count = if bottom_nominations.count() == T::MaxBottomNominationsPerCandidate::get() { let lowest_bottom_to_be_kicked = bottom_nominations.nominations.pop().ok_or(>::BottomNominationDNE)?; - // EXPECT lowest_bottom_to_be_kicked.amount < nomination.amount enforced by caller - // if lowest_bottom_to_be_kicked.amount == nomination.amount, we will still kick - // the lowest bottom to enforce first come first served bottom_nominations.total = bottom_nominations.total.saturating_sub(lowest_bottom_to_be_kicked.amount); - // update nominator state - // unreserve kicked bottom - T::Currency::unreserve( - &lowest_bottom_to_be_kicked.owner, - lowest_bottom_to_be_kicked.amount, - ); - // total staked is updated via propagation of lowest bottom nomination amount prior - // to call + + // unreserve the kicked bottom nomination + // we also have to ensure that any existing pending requests are also unreserved + let mut unreserved_amount = lowest_bottom_to_be_kicked.amount; let mut nominator_state = >::get(&lowest_bottom_to_be_kicked.owner) .ok_or(>::NominatorDNE)?; - let leaving = nominator_state.nominations.len() == 1usize; + if let Some(request) = nominator_state.requests.requests.get(&candidate) { + Pallet::::remove_unstaking_nomination( + candidate.clone(), + Bond { + owner: lowest_bottom_to_be_kicked.owner.clone(), + amount: request.amount, + }, + )?; + unreserved_amount = unreserved_amount.saturating_add(request.amount); + } + T::Currency::unreserve(&lowest_bottom_to_be_kicked.owner, unreserved_amount); + + let leaving = nominator_state.nomination_count(false) == 1; nominator_state.rm_nomination(candidate); nominator_state.requests.remove_request(&candidate); + Pallet::::deposit_event(Event::NominationKicked { nominator: lowest_bottom_to_be_kicked.owner.clone(), candidate: candidate.clone(), @@ -1050,7 +1007,7 @@ impl< // only increase nomination count if new bottom nomination (1) doesn't come from top && // (2) doesn't pop the lowest nomination from the bottom if increase_nomination_count { - self.nomination_count += 1u32; + self.nomination_count = self.nomination_count.saturating_add(1); } bottom_nominations.insert_sorted_greatest_to_least(nomination); self.reset_bottom_data::(&bottom_nominations); @@ -1456,6 +1413,7 @@ impl< }) .collect(); ensure!(in_bottom, Error::::NominationDNE); + bottom_nominations.total = bottom_nominations.total.saturating_sub(less); bottom_nominations.sort_greatest_to_least(); self.reset_bottom_data::(&bottom_nominations); >::insert(candidate, bottom_nominations); @@ -1490,7 +1448,7 @@ pub struct Nominator { pub initial_nominations: BTreeMap, /// Total balance locked for this nominator pub total: Balance, - /// Requests to change nominations, relevant if active + /// Requests to change nominations (decrease, revoke, and leave) pub requests: PendingNominationRequests, /// Status for this nominator pub status: NominatorStatus, @@ -1534,14 +1492,26 @@ impl< } } + /// Get the number of nominations for the nominator. + pub fn nomination_count(&self, is_active: bool) -> u32 { + if is_active { + self.nominations.iter().filter(|(_, amount)| *amount > &Zero::zero()).count() as u32 + } else { + self.nominations.clone().len() as u32 + } + } + + /// Get the pending requests for the nominator. pub fn requests(&self) -> BTreeMap> { self.requests.requests.clone() } + /// Check if the nominator is active. pub fn is_active(&self) -> bool { matches!(self.status, NominatorStatus::Active) } + /// Check if the nominator is scheduled to revoke a nomination. pub fn is_revoking(&self, candidate: &AccountId) -> bool { if let Some(request) = self.requests().get(candidate) { if request.action == NominationChange::Revoke { @@ -1551,29 +1521,25 @@ impl< false } - pub fn is_decreasing(&self, candidate: &AccountId) -> bool { - if let Some(request) = self.requests().get(candidate) { - if request.action == NominationChange::Decrease { - return true; - } - } - false - } - + /// Check if the nominator is scheduled to leave. pub fn is_leaving(&self) -> bool { matches!(self.status, NominatorStatus::Leaving(_)) } + /// Replace the mapped validator address in the nominations, due to a controller re-set. pub fn replace_nominations(&mut self, old: &AccountId, new: &AccountId) { if let Some(amount) = self.nominations.remove(old) { self.nominations.insert(new.clone(), amount); } - if let Some(amount) = self.initial_nominations.remove(old) { self.initial_nominations.insert(new.clone(), amount); } + if let Some(amount) = self.awarded_tokens_per_candidate.remove(old) { + self.awarded_tokens_per_candidate.insert(new.clone(), amount); + } } + /// Replace the mapped validator address in the pending requests, due to a controller re-set. pub fn replace_requests(&mut self, old: &AccountId, new: &AccountId) { if let Some(request) = self.requests.requests.get(old) { let request_clone = request.clone(); @@ -1590,18 +1556,18 @@ impl< } } + /// Increment the amount of awarded tokens for a validator. pub fn increment_awarded_tokens(&mut self, validator: &AccountId, tokens: Balance) { if let Some(x) = self.awarded_tokens_per_candidate.get_mut(validator) { - *x += tokens; + *x = x.saturating_add(tokens); } - self.awarded_tokens += tokens; + self.awarded_tokens = self.awarded_tokens.saturating_add(tokens); } - /// Can only leave if the current round is less than or equal to scheduled execution round - /// - returns None if not in leaving state + /// Check if the nominator can leave. pub fn can_execute_leave(&self, nomination_weight_hint: u32) -> DispatchResult { ensure!( - nomination_weight_hint >= (self.nominations.len() as u32), + nomination_weight_hint >= self.nomination_count(false), Error::::TooLowNominationCountToLeaveNominators ); if let NominatorStatus::Leaving(when) = self.status { @@ -1615,16 +1581,17 @@ impl< } } - /// Set status to leaving + /// Set the nominator's status to leaving. pub(crate) fn set_leaving(&mut self, when: RoundIndex) { self.status = NominatorStatus::Leaving(when); } + /// Set the destination for round rewards. pub fn set_reward_dst(&mut self, reward_dst: RewardDestination) { self.reward_dst = reward_dst; } - /// Schedule status to exit + /// Schedule the nominator to leave with a delay. pub fn schedule_leave(&mut self) -> (RoundIndex, RoundIndex) { let now = >::get().current_round_index; let when = now + T::LeaveNominatorsDelay::get(); @@ -1632,12 +1599,12 @@ impl< (now, when) } - /// Set nominator status to active + /// Cancel the leave state of the nominator and reset to active. pub fn cancel_leave(&mut self) { self.status = NominatorStatus::Active } - // pub fn add_nomination(&mut self, bond: Bond) -> bool { + /// Add a nomination to the nominator's state. pub fn add_nomination( &mut self, candidate: AccountId, @@ -1647,20 +1614,18 @@ impl< Err(>::AlreadyNominatedCandidate.into()) } else { self.nominations.insert(candidate.clone(), amount); - self.total += amount; + self.total = self.total.saturating_add(amount); self.initial_nominations.insert(candidate.clone(), amount); self.awarded_tokens_per_candidate.insert(candidate, Zero::zero()); Ok(()) } } - // Return Some(remaining balance), must be more than MinNominatorStk - // Return None if nomination not found + /// Remove a nomination from the nominator's state and return the remaining balance. pub fn rm_nomination(&mut self, validator: &AccountId) -> Option { if let Some(amount) = self.nominations.remove(validator) { self.initial_nominations.remove(validator); self.awarded_tokens_per_candidate.remove(validator); - self.total = self.total.saturating_sub(amount); Some(self.total) } else { @@ -1668,10 +1633,13 @@ impl< } } + /// Increase the nomination for a candidate. `do_reserve` exists to allow for skipping the + /// reserve step in the case where the nomination is already reserved. pub fn increase_nomination( &mut self, candidate: AccountId, - amount: Balance, + more: Balance, + do_reserve: bool, ) -> DispatchResult where BalanceOf: From, @@ -1680,137 +1648,183 @@ impl< { let nominator_id: T::AccountId = self.id.clone().into(); let candidate_id: T::AccountId = candidate.clone().into(); - let balance_amt: BalanceOf = amount.into(); - // increase nomination - if let Some(candidate_amount) = self.nominations.get_mut(&candidate) { - let before_amount = candidate_amount.clone(); - *candidate_amount += amount; - self.total += amount; - // update validator state nomination - let mut validator_state = - >::get(&candidate_id).ok_or(Error::::CandidateDNE)?; - T::Currency::reserve(&self.id.clone().into(), balance_amt)?; - let in_top = validator_state.increase_nomination::( - &candidate_id, - nominator_id.clone(), - before_amount.into(), - balance_amt, - )?; - let after = validator_state.voting_power; - Pallet::::update_active(&candidate_id, after)?; - let new_total_staked = >::get().saturating_add(balance_amt); - let nom_st: Nominator> = self.clone().into(); - >::put(new_total_staked); - >::insert(&candidate_id, validator_state); - >::insert(&nominator_id, nom_st); - Pallet::::deposit_event(Event::NominationIncreased { - nominator: nominator_id, - candidate: candidate_id, - amount: balance_amt, - in_top, - }); - return Ok(()); + let balance_more: BalanceOf = more.into(); + + let amount = self.nominations.get_mut(&candidate).ok_or(Error::::NominationDNE)?; + let before_amount = amount.clone(); + *amount = amount.saturating_add(more); + self.total = self.total.saturating_add(more); + + let mut validator_state = + >::get(&candidate_id).ok_or(Error::::CandidateDNE)?; + if do_reserve { + T::Currency::reserve(&self.id.clone().into(), balance_more)?; } - Err(Error::::NominationDNE.into()) + let _ = validator_state.increase_nomination::( + &candidate_id, + nominator_id.clone(), + before_amount.into(), + balance_more, + )?; + >::insert(&candidate_id, validator_state); + >::mutate(|total| { + *total = total.saturating_add(balance_more); + }); + + let nom_st: Nominator> = self.clone().into(); + >::insert(&nominator_id, nom_st); + + Ok(()) } - /// Schedule decrease nomination + /// Schedule a decrease for a candidate. This adds a pending request to the + /// nominator's state and also adds a nomination to the `UnstakingNominations`. pub fn schedule_decrease_nomination( &mut self, - validator: AccountId, + candidate: AccountId, less: Balance, ) -> Result where BalanceOf: Into + From, + T::AccountId: From, { // get nomination amount - return if let Some(amount) = self.nominations.get(&validator) { - ensure!(*amount > less, Error::::NominatorBondBelowMin); - let expected_amt: BalanceOf = (*amount - less).into(); - ensure!(expected_amt >= T::MinNomination::get(), Error::::NominationBelowMin); - // Net Total is total after pending orders are executed - let net_total = self.total - self.requests.less_total; - // Net Total is always >= MinNominatorStk - let max_subtracted_amount = net_total - T::MinNominatorStk::get().into(); - ensure!(less <= max_subtracted_amount, Error::::NominatorBondBelowMin); + return if let Some(amount) = self.nominations.get(&candidate) { + let after_less = amount.saturating_sub(less.into()); + let candidate_id: T::AccountId = candidate.clone().into(); + let validator = + >::get(&candidate_id).ok_or(Error::::CandidateDNE)?; + ensure!( + *amount > validator.highest_bottom_nomination_amount.into(), + Error::::CannotDecreaseWhenInvolvedInBottom + ); + ensure!( + after_less > validator.highest_bottom_nomination_amount.into(), + Error::::CannotDecreaseLessThanHighestBottom + ); + ensure!(after_less >= T::MinNomination::get().into(), Error::::NominationBelowMin); + ensure!( + self.total.saturating_sub(less) >= T::MinNominatorStk::get().into(), + Error::::NominatorBondBelowMin + ); let when = >::get().current_round_index + T::NominationBondLessDelay::get(); - self.requests.bond_less::(validator, less, when)?; + self.requests.bond_less::(candidate.clone(), less, when)?; + Pallet::::add_to_unstaking_nominations( + candidate.into(), + Bond { owner: self.id.clone().into(), amount: less.into() }, + )?; Ok(when) } else { Err(Error::::NominationDNE.into()) }; } - /// Schedule revocation for the given validator + /// Schedule a revocation for a candidate. This adds a pending request to the nominator's state + /// and also adds a nomination to the `UnstakingNominations`. It also removes the nomination + /// from the `TopNominations`/`BottomNominations`. pub fn schedule_revoke( &mut self, - validator: AccountId, + candidate: AccountId, ) -> Result<(RoundIndex, RoundIndex), DispatchError> where - BalanceOf: Into, + BalanceOf: From + Into, + T::AccountId: From, { - // get nomination amount - return if let Some(amount) = self.nominations.get(&validator) { - let now = >::get().current_round_index; - let when = now + T::RevokeNominationDelay::get(); - // add revocation to pending requests - self.requests.revoke::(validator, *amount, when)?; - Ok((now, when)) - } else { - Err(Error::::NominationDNE.into()) - }; + let nomination_count = self.nomination_count(true); + let amount = self.nominations.get_mut(&candidate).ok_or(Error::::NominationDNE)?; + // if there is more than one active nomination, ensure the nominator has enough bond to revoke + if nomination_count > 1 { + ensure!( + self.total.saturating_sub(*amount) >= T::MinNominatorStk::get().into(), + Error::::NominatorBondBelowMin + ); + } + let now = >::get().current_round_index; + let when = now + T::RevokeNominationDelay::get(); + self.requests.revoke::(candidate.clone(), *amount, when)?; + Pallet::::nominator_leaves_candidate( + candidate.clone().into(), + self.id.clone().into(), + amount.clone().into(), + )?; + Pallet::::add_to_unstaking_nominations( + candidate.into(), + Bond { owner: self.id.clone().into(), amount: amount.clone().into() }, + )?; + self.total = self.total.saturating_sub(*amount); + *amount = Balance::zero(); + + Ok((now, when)) } - /// Execute pending nomination change request - pub fn execute_pending_request(&mut self, candidate: AccountId) -> DispatchResult + /// Execute a pending request (decrease or revoke) that has reached its executable round. + /// For revocations, this also removes the nomination from storage. + pub fn execute_pending_request( + &mut self, + candidate: AccountId, + when: RoundIndex, + ) -> DispatchResult where BalanceOf: From + Into, T::AccountId: From, Nominator>: From>, { - let now = >::get().current_round_index; - let NominationRequest { amount, action, when_executable, .. } = self + let order = self .requests .requests - .remove(&candidate) + .get(&candidate) + .ok_or(Error::::PendingNominationRequestDNE)?; + let order_amount = order + .when_executable + .get(&when) .ok_or(Error::::PendingNominationRequestDNE)?; - ensure!(when_executable <= now, Error::::PendingNominationRequestNotDueYet); - let (balance_amt, candidate_id, nominator_id): (BalanceOf, T::AccountId, T::AccountId) = - (amount.into(), candidate.clone().into(), self.id.clone().into()); + + let now = >::get().current_round_index; + ensure!(when <= now, Error::::PendingNominationRequestNotDueYet); + + let action = order.action.clone(); + let order_amount = *order_amount; + let should_remove = order.when_executable.len() == 1; + + if should_remove { + // remove the entire request if it's the last one + self.requests.requests.remove(&candidate); + } else { + // remove the specific when_executable if it's not the last one + if let Some(req) = self.requests.requests.get_mut(&candidate) { + req.when_executable.remove(&when); + req.amount = req.amount.saturating_sub(order_amount); + } + } + + let (balance_amount, candidate_id, nominator_id): ( + BalanceOf, + T::AccountId, + T::AccountId, + ) = (order_amount.into(), candidate.clone().into(), self.id.clone().into()); + Pallet::::remove_unstaking_nomination( + candidate_id.clone(), + Bond { owner: nominator_id.clone(), amount: balance_amount }, + )?; + match action { NominationChange::Revoke => { - // revoking last nomination => leaving set of nominators - let leaving = if self.nominations.len() == 1usize { - true - } else { - ensure!( - self.total - T::MinNominatorStk::get().into() >= amount, - Error::::NominatorBondBelowMin - ); - false - }; - // remove from pending requests - self.requests.less_total = self.requests.less_total.saturating_sub(amount); - self.requests.revocations_count = - self.requests.revocations_count.saturating_sub(1u32); - // remove nomination from nominator state + let leaving = self.nomination_count(false) == 1; + self.requests.less_total = self.requests.less_total.saturating_sub(order_amount); + self.rm_nomination(&candidate); - // remove nomination from validator state nominations - Pallet::::nominator_leaves_candidate( - candidate_id.clone(), - nominator_id.clone(), - balance_amt, - )?; + T::Currency::unreserve(&nominator_id, balance_amount); + Pallet::::deposit_event(Event::NominationRevoked { nominator: nominator_id.clone(), - candidate: candidate_id, - unstaked_amount: balance_amt, + candidate: candidate_id.clone(), + unstaked_amount: balance_amount, }); if leaving { >::remove(&nominator_id); Pallet::::deposit_event(Event::NominatorLeft { nominator: nominator_id, - unstaked_amount: balance_amt, + unstaked_amount: balance_amount, }); } else { let nom_st: Nominator> = self.clone().into(); @@ -1819,99 +1833,126 @@ impl< Ok(()) }, NominationChange::Decrease => { - // remove from pending requests - self.requests.less_total = self.requests.less_total.saturating_sub(amount); - // decrease nomination - return if let Some(candidate_amount) = self.nominations.get_mut(&candidate) { - return if *candidate_amount > amount { - let amount_before = candidate_amount.clone(); - *candidate_amount = candidate_amount.saturating_sub(amount); - self.total = self.total.saturating_sub(amount); - let new_total: BalanceOf = self.total.into(); - ensure!( - new_total >= T::MinNomination::get(), - Error::::NominationBelowMin - ); - ensure!( - new_total >= T::MinNominatorStk::get(), - Error::::NominatorBondBelowMin - ); - let mut validator = >::get(&candidate_id) - .ok_or(Error::::CandidateDNE)?; - T::Currency::unreserve(&nominator_id, balance_amt); - // need to go into decrease_nomination - let in_top = validator.decrease_nomination::( - &candidate_id, - nominator_id.clone(), - amount_before.into(), - balance_amt, - )?; - >::insert(&candidate_id, validator); - let new_total_staked = >::get().saturating_sub(balance_amt); - >::put(new_total_staked); - let nom_st: Nominator> = self.clone().into(); - >::insert(&nominator_id, nom_st); - Pallet::::deposit_event(Event::NominationDecreased { - nominator: nominator_id, - candidate: candidate_id, - amount: balance_amt, - in_top, - }); - Ok(()) - } else { - // must rm entire nomination if x.amount <= less or cancel request - Err(Error::::NominationBelowMin.into()) - }; - } else { - Err(Error::::NominationDNE.into()) - }; + self.requests.less_total = self.requests.less_total.saturating_sub(order_amount); + T::Currency::unreserve(&nominator_id, balance_amount); + + Pallet::::deposit_event(Event::NominationDecreased { + nominator: nominator_id.clone(), + candidate: candidate_id, + amount: balance_amount, + }); + let nom_st: Nominator> = self.clone().into(); + >::insert(&nominator_id, nom_st); + Ok(()) }, + NominationChange::Leave => Err(Error::::NominatorCannotLeaveYet.into()), } } - /// Cancel pending nomination change request + /// Cancel a pending request (decrease or revoke) that is executable in the given round. + /// For revocations and leaves, this will reset the nomination back to the nominator's state. + /// And for decreases, this will increase the existing nomination without reserving. pub fn cancel_pending_request( &mut self, candidate: AccountId, - ) -> Result, DispatchError> { + when: RoundIndex, + ) -> Result, DispatchError> + where + BalanceOf: From, + T::AccountId: From, + Nominator>: From>, + { let order = self .requests .requests - .remove(&candidate) + .get(&candidate) .ok_or(Error::::PendingNominationRequestDNE)?; - match order.action { - NominationChange::Revoke => { - self.requests.revocations_count = - self.requests.revocations_count.saturating_sub(1u32); - self.requests.less_total = self.requests.less_total.saturating_sub(order.amount); + let order_amount = order + .when_executable + .get(&when) + .ok_or(Error::::PendingNominationRequestDNE)?; + + let order_cloned = order.clone(); + let action = order.action.clone(); + let order_amount = *order_amount; + + self.requests.less_total = self.requests.less_total.saturating_sub(order_amount); + if let Some(request) = self.requests.requests.get_mut(&candidate) { + request.when_executable.remove(&when); + if request.when_executable.is_empty() { + self.requests.requests.remove(&candidate); + } else { + request.amount = request.amount.saturating_sub(order_amount); + } + } + + let candidate_id: T::AccountId = candidate.clone().into(); + let balance_amount: BalanceOf = order_amount.into(); + + match action { + NominationChange::Revoke | NominationChange::Leave => { + // add nomination back to nominator state + let amount = + self.nominations.get_mut(&candidate).ok_or(Error::::NominationDNE)?; + *amount = amount.saturating_add(order_amount); + self.total = self.total.saturating_add(order_amount); + + // add nomination back to validator state (top or bottom) + let mut validator_state = + >::get(&candidate_id).ok_or(Error::::CandidateDNE)?; + let (_, less_total_staked) = validator_state.add_nomination::( + &candidate_id, + Bond { owner: self.id.clone().into(), amount: balance_amount }, + )?; + + // after adding the nomination back, update the total staked + // the lowest bottom could possibly be kicked as a consequence of this new nomination + let net_total_increase = if let Some(less) = less_total_staked { + balance_amount - less + } else { + balance_amount + }; + >::mutate(|total| { + *total = total.saturating_add(net_total_increase); + }); + >::insert(&candidate_id, validator_state); }, NominationChange::Decrease => { - self.requests.less_total = self.requests.less_total.saturating_sub(order.amount); + // increase nomination without reserving + self.increase_nomination::(candidate, order_amount, false)?; }, - } - Ok(order) + }; + Pallet::::remove_unstaking_nomination( + candidate_id, + Bond { owner: self.id.clone().into(), amount: balance_amount }, + )?; + + Ok(order_cloned) } } #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -/// Changes requested by the nominator -/// - limit of 1 ongoing change per nomination +/// Nomination changes requested by the nominator. Limits to 1 ongoing change per nomination. pub enum NominationChange { - /// Requests to unbond the entire nomination + /// Requests to unbond the entire nomination. Revoke, - /// Requests to unbond a certain amount of nomination + /// Requests to unbond a certain amount of nomination. + /// Multiple decrease requests are allowed to be pending for a single nomination. Decrease, + /// Requests to leave the set of nominators. + Leave, } #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -/// The nomination unbonding request of a specific nominator +/// The change request for a specific nomination. pub struct NominationRequest { /// The validator who owns this nomination pub validator: AccountId, - /// The unbonding amount + /// The total unbonding amount of this request pub amount: Balance, - /// The round index when this request is executable - pub when_executable: RoundIndex, + /// The unbonding amount for each round. + /// `Decrease` requests are allowed to be pending for multiple rounds. + pub when_executable: BTreeMap, /// The requested unbonding action pub action: NominationChange, } @@ -1919,21 +1960,15 @@ pub struct NominationRequest { #[derive(Clone, Encode, PartialEq, Decode, RuntimeDebug, TypeInfo)] /// Pending requests to mutate nominations for each nominator pub struct PendingNominationRequests { - /// Number of pending revocations (necessary for determining whether revoke is exit) - pub revocations_count: u32, /// Map from validator -> Request (enforces at most 1 pending request per nomination) pub requests: BTreeMap>, - /// Sum of pending revocation amounts + bond less amounts + /// Total amount of pending requests. pub less_total: Balance, } impl Default for PendingNominationRequests { fn default() -> PendingNominationRequests { - PendingNominationRequests { - revocations_count: 0u32, - requests: BTreeMap::new(), - less_total: B::zero(), - } + PendingNominationRequests { requests: BTreeMap::new(), less_total: B::zero() } } } @@ -1945,9 +1980,6 @@ impl< pub fn remove_request(&mut self, address: &A) { if let Some(request) = self.requests.remove(address) { self.less_total = self.less_total.saturating_sub(request.amount); - if matches!(request.action, NominationChange::Revoke) { - self.revocations_count = self.revocations_count.saturating_sub(1u32); - } } } } @@ -1961,7 +1993,8 @@ impl< + sp_std::ops::AddAssign + sp_std::ops::Add + sp_std::ops::SubAssign - + sp_std::ops::Sub, + + sp_std::ops::Sub + + FixedPointOperand, > PendingNominationRequests { /// New default (empty) pending requests @@ -1969,14 +2002,12 @@ impl< PendingNominationRequests::default() } - /// Add bond less order to pending requests, only succeeds if returns true - /// - limit is the maximum amount allowed that can be subtracted from the nomination - /// before it would be below the minimum nomination amount - pub fn bond_less( + /// Add a leave request to pending requests. + pub fn leave( &mut self, validator: A, amount: B, - when_executable: RoundIndex, + when: RoundIndex, ) -> DispatchResult { ensure!( self.requests.get(&validator).is_none(), @@ -1987,22 +2018,51 @@ impl< NominationRequest { validator, amount, - when_executable, - action: NominationChange::Decrease, + when_executable: BTreeMap::from([(when, amount)]), + action: NominationChange::Leave, }, ); - self.less_total += amount; + self.less_total = self.less_total.saturating_add(amount); + Ok(()) + } + + /// Add a decrease request to pending requests. + /// If the nomination is already scheduled for a decrease in the given round, the amount is + /// added to the existing request. + pub fn bond_less( + &mut self, + validator: A, + amount: B, + when: RoundIndex, + ) -> DispatchResult { + if let Some(request) = self.requests.get_mut(&validator) { + if let Some(existing_amount) = request.when_executable.get_mut(&when) { + *existing_amount = existing_amount.saturating_add(amount); + } else { + request.when_executable.insert(when, amount); + } + request.amount = request.amount.saturating_add(amount); + } else { + self.requests.insert( + validator.clone(), + NominationRequest { + validator, + amount, + when_executable: BTreeMap::from([(when, amount)]), + action: NominationChange::Decrease, + }, + ); + } + self.less_total = self.less_total.saturating_add(amount); Ok(()) } - /// Add revoke order to pending requests - /// - limit is the maximum amount allowed that can be subtracted from the nomination - /// before it would be below the minimum nomination amount + /// Add a revoke request to pending requests. pub fn revoke( &mut self, validator: A, amount: B, - when_executable: RoundIndex, + when: RoundIndex, ) -> DispatchResult { ensure!( self.requests.get(&validator).is_none(), @@ -2013,12 +2073,11 @@ impl< NominationRequest { validator, amount, - when_executable, + when_executable: BTreeMap::from([(when, amount)]), action: NominationChange::Revoke, }, ); - self.revocations_count += 1u32; - self.less_total += amount; + self.less_total = self.less_total.saturating_add(amount); Ok(()) } } diff --git a/pallets/bfc-staking/src/migrations.rs b/pallets/bfc-staking/src/migrations.rs index 1c8df9ec..1cc5230a 100644 --- a/pallets/bfc-staking/src/migrations.rs +++ b/pallets/bfc-staking/src/migrations.rs @@ -1,6 +1,216 @@ use super::*; use crate::set::OrderedSet; +pub mod v6 { + use frame_support::traits::OnRuntimeUpgrade; + + use super::*; + + pub struct MigrateToV6(PhantomData); + + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] + /// The change request for a specific nomination. + pub struct OldNominationRequest { + /// The validator who owns this nomination + pub validator: AccountId, + /// The total unbonding amount of this request + pub amount: Balance, + /// The unbonding amount for each round. + /// `Decrease` requests are allowed to be pending for multiple rounds. + pub when_executable: RoundIndex, + /// The requested unbonding action + pub action: NominationChange, + } + + #[derive(Clone, Encode, PartialEq, Decode, RuntimeDebug, TypeInfo)] + /// Pending requests to mutate nominations for each nominator + pub struct OldPendingNominationRequests { + /// Number of pending revocations (necessary for determining whether revoke is exit) + pub revocations_count: u32, + /// Map from validator -> Request (enforces at most 1 pending request per nomination) + pub requests: BTreeMap>, + /// Total amount of pending requests. + pub less_total: Balance, + } + + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] + /// Nominator state + pub struct OldNominator { + /// Nominator account + pub id: AccountId, + /// Current state of all nominations + pub nominations: BTreeMap, + /// Initial state of all nominations + pub initial_nominations: BTreeMap, + /// Total balance locked for this nominator + pub total: Balance, + /// Requests to change nominations (decrease, revoke, and leave) + pub requests: OldPendingNominationRequests, + /// Status for this nominator + pub status: NominatorStatus, + /// The destination for round rewards + pub reward_dst: RewardDestination, + /// The total amount of awarded tokens to this nominator + pub awarded_tokens: Balance, + /// The amount of awarded tokens to this nominator per candidate + pub awarded_tokens_per_candidate: BTreeMap, + } + + impl OnRuntimeUpgrade for MigrateToV6 { + fn on_runtime_upgrade() -> Weight { + let mut weight = Weight::zero(); + + let current = Pallet::::in_code_storage_version(); + let onchain = Pallet::::on_chain_storage_version(); + + if current == 6 && onchain == 5 { + // 1. create `UnstakingNominations` for each validator + for (who, _) in CandidateInfo::::iter() { + >::insert(&who, Nominations::default()); + } + + // 2. translate `NominatorState` & add requests to `UnstakingNominations` + NominatorState::::translate( + |_, old: OldNominator>| { + // 2.1. remove `requests.revocations_count` field + // 2.2. update `requests.requests.when_executable` from `RoundIndex` to `BTreeMap` + let mut new_requests: BTreeMap< + T::AccountId, + NominationRequest>, + > = old.requests + .requests + .into_iter() + .map(|(validator, request)| { + ( + validator.clone(), + NominationRequest { + validator, + amount: request.amount, + when_executable: BTreeMap::from([( + request.when_executable, + request.amount, + )]), + action: request.action, + }, + ) + }) + .collect(); + + // 2.3. add leave requests to `requests.requests` + match old.status { + NominatorStatus::Leaving(round_index) => { + for (validator, amount) in old.nominations.clone() { + new_requests.insert( + validator.clone(), + NominationRequest { + validator, + amount, + when_executable: BTreeMap::from([( + round_index, + amount, + )]), + action: NominationChange::Leave, + }, + ); + } + }, + _ => (), + } + + // 2.4. decrease `nominations`, `total`, `requests.less_total` + let mut new_nominations = old.nominations.clone(); + let mut new_total = old.total.clone(); + let mut new_less_total = BalanceOf::::zero(); + for (validator, request) in new_requests.iter() { + if let Some(amount) = new_nominations.remove(validator) { + new_nominations.insert( + validator.clone(), + amount.saturating_sub(request.amount), + ); + new_total = new_total.saturating_sub(request.amount); + new_less_total = new_less_total.saturating_add(request.amount); + + // 2.5. add to `UnstakingNominations` + let _ = Pallet::::add_to_unstaking_nominations( + validator.clone(), + Bond { owner: old.id.clone(), amount: request.amount }, + ); + + // 2.6. decrease `Total` + >::mutate(|total| { + *total = total.saturating_sub(request.amount); + }); + + // 2.7. decrease `CandidateInfo.voting_power` + let mut candidate_info = + CandidateInfo::::get(&validator).expect("CandidateInfo DNE"); + candidate_info.voting_power = candidate_info + .clone() + .voting_power + .saturating_sub(request.amount); + + // 2.8. decrease or remove from `TopNominations` | `BottomNominations` + // this will reorganize the following fields + // - `lowest_top_nomination_amount` + // - `highest_bottom_nomination_amount` + // - `lowest_bottom_nomination_amount` + // - `top_capacity` + // - `bottom_capacity` + // - `CandidatePool` + match request.action { + NominationChange::Decrease => { + candidate_info + .decrease_nomination::( + &validator, + old.id.clone(), + amount, + request.amount, + ) + .expect("decrease_nomination failed"); + }, + NominationChange::Revoke | NominationChange::Leave => { + candidate_info + .rm_nomination_if_exists::( + &validator, + old.id.clone(), + request.amount, + ) + .expect("rm_nomination_if_exists failed"); + }, + } + >::insert(&validator, candidate_info); + } + } + + Some(Nominator { + id: old.id, + nominations: new_nominations, + initial_nominations: old.initial_nominations.clone(), + total: new_total, + requests: PendingNominationRequests { + requests: new_requests, + less_total: new_less_total, + }, + status: old.status, + reward_dst: old.reward_dst, + awarded_tokens: old.awarded_tokens, + awarded_tokens_per_candidate: old.awarded_tokens_per_candidate, + }) + }, + ); + current.put::>(); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log!(info, "bfc-staking storage migration v6 completed successfully ✅"); + } else { + log!(warn, "Skipping bfc-staking storage migration v6 💤"); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + } + weight + } + } +} + pub mod v5 { use frame_support::traits::OnRuntimeUpgrade; diff --git a/pallets/bfc-staking/src/pallet/impls.rs b/pallets/bfc-staking/src/pallet/impls.rs index 8f93593c..155b3bbf 100644 --- a/pallets/bfc-staking/src/pallet/impls.rs +++ b/pallets/bfc-staking/src/pallet/impls.rs @@ -25,7 +25,7 @@ use sp_std::{collections::btree_set::BTreeSet, vec, vec::Vec}; use frame_support::{ pallet_prelude::*, - traits::{Currency, EstimateNextSessionRotation, Get, Imbalance, ReservableCurrency}, + traits::{Currency, EstimateNextSessionRotation, Get, Imbalance}, weights::Weight, BoundedBTreeSet, }; @@ -77,6 +77,62 @@ impl Pallet { return commission_sets.into_iter().any(|c| c.who == *who); } + /// Adds a new nomination to the unstaking nominations for the given candidate. + /// If the nomination already exists, it will increase the amount + pub fn add_to_unstaking_nominations( + candidate: T::AccountId, + nomination: Bond>, + ) -> DispatchResult { + let mut unstaking_nominations = + >::get(&candidate).ok_or(Error::::UnstakingNominationDNE)?; + let mut is_exist = false; + for n in &mut unstaking_nominations.nominations { + if n.owner == nomination.owner { + n.amount = n.amount.saturating_add(nomination.amount); + is_exist = true; + break; + } + } + if !is_exist { + unstaking_nominations.nominations.push(nomination.clone()); + } + unstaking_nominations.total = unstaking_nominations.total.saturating_add(nomination.amount); + unstaking_nominations.sort_greatest_to_least(); + >::insert(&candidate, unstaking_nominations); + Ok(()) + } + + /// Removes a nomination from the unstaking nominations for the given candidate. + /// If the nomination amount is zero, it will remove the nomination + pub fn remove_unstaking_nomination( + candidate: T::AccountId, + nomination: Bond>, + ) -> DispatchResult { + let mut unstaking_nominations = + >::get(&candidate).ok_or(Error::::UnstakingNominationDNE)?; + unstaking_nominations.nominations = unstaking_nominations + .nominations + .clone() + .into_iter() + .filter_map(|n| { + if n.owner == nomination.owner { + let amount = n.amount.saturating_sub(nomination.amount); + if amount.is_zero() { + None + } else { + Some(Bond { owner: n.owner, amount }) + } + } else { + Some(n) + } + }) + .collect(); + unstaking_nominations.total = unstaking_nominations.total.saturating_sub(nomination.amount); + unstaking_nominations.sort_greatest_to_least(); + >::insert(&candidate, unstaking_nominations); + Ok(()) + } + /// Adds a new controller set request. The state reflection will be applied in the next round. pub fn add_to_controller_sets( stash: T::AccountId, @@ -256,17 +312,10 @@ impl Pallet { ) -> DispatchResult { let mut state = CandidateInfo::::get(&candidate).ok_or(Error::::CandidateDNE)?; state.rm_nomination_if_exists::(&candidate, nominator.clone(), amount)?; - T::Currency::unreserve(&nominator, amount); - let new_total_locked = Total::::get().saturating_sub(amount); - >::put(new_total_locked); - let new_total = state.voting_power; - >::insert(&candidate, state); - Self::deposit_event(Event::NominatorLeftCandidate { - nominator, - candidate, - unstaked_amount: amount, - total_candidate_staked: new_total, + >::mutate(|total| { + *total = total.saturating_sub(amount); }); + >::insert(&candidate, state); Ok(()) } @@ -331,12 +380,8 @@ impl Pallet { reward: BalanceOf, ) -> Result<(), DispatchError> { if let Some(mut nominator_state) = NominatorState::::get(&nominator) { - // the nominator must be active (not leaving) - // and not revoking/decreasing the current validator - if nominator_state.is_active() - && !nominator_state.is_revoking(&controller) - && !nominator_state.is_decreasing(&controller) - { + // the nominator must not be leaving and not revoking the current validator + if nominator_state.is_active() && !nominator_state.is_revoking(&controller) { // mint rewards to the nominator account Self::mint_reward(reward, nominator.clone()); @@ -347,7 +392,7 @@ impl Pallet { nominator_state.increment_awarded_tokens(&controller, reward); // auto-compound nomination if nominator_state - .increase_nomination::(controller.clone(), reward) + .increase_nomination::(controller.clone(), reward, true) .is_ok() { >::insert(&nominator, nominator_state); @@ -459,6 +504,15 @@ impl Pallet { ); >::insert(&c.new, bottom_nominations); } + // replace `UnstakingNominations` + if let Some(unstaking_nominations) = >::take(&c.old) { + Self::replace_nominator_nominations( + &unstaking_nominations.nominators(), + &c.old, + &c.new, + ); + >::insert(&c.new, unstaking_nominations); + } // replace `AwardedPts` let points = >::take(now, &c.old); >::insert(now, &c.new, points); diff --git a/pallets/bfc-staking/src/pallet/mod.rs b/pallets/bfc-staking/src/pallet/mod.rs index 1481224d..ca77bc79 100644 --- a/pallets/bfc-staking/src/pallet/mod.rs +++ b/pallets/bfc-staking/src/pallet/mod.rs @@ -2,7 +2,7 @@ mod impls; use crate::{ migrations, BalanceOf, BlockNumberFor, Bond, CandidateMetadata, DelayedCommissionSet, - DelayedControllerSet, DelayedPayout, InflationInfo, NominationRequest, Nominations, Nominator, + DelayedControllerSet, DelayedPayout, InflationInfo, NominationChange, Nominations, Nominator, NominatorAdded, Range, RewardDestination, RewardPoint, RoundIndex, RoundInfo, TierType, TotalSnapshot, ValidatorSnapshot, WeightInfo, }; @@ -30,7 +30,7 @@ pub mod pallet { use super::*; /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(6); /// Pallet for bfc staking #[pallet::pallet] @@ -145,6 +145,8 @@ pub mod pallet { TopNominationDNE, /// A bottom nomination does not exist with the target nominator and candidate account. BottomNominationDNE, + /// An unstaking nomination does not exist with the target nominator and candidate account. + UnstakingNominationDNE, /// A commission set request does not exist with the target controller account. CommissionSetDNE, /// A controller set request does not exist with the target controller account. @@ -249,6 +251,10 @@ pub mod pallet { PendingNominationRequestNotDueYet, /// Cannot nominate if the given amount is less than the lowest bottom. CannotNominateLessThanLowestBottomWhenBottomIsFull, + /// Cannot decrease nomination less than the highest bottom. + CannotDecreaseLessThanHighestBottom, + /// Cannot decrease nomination when involved in bottom nominations. + CannotDecreaseWhenInvolvedInBottom, } #[pallet::event] @@ -327,14 +333,12 @@ pub mod pallet { nominator: T::AccountId, candidate: T::AccountId, amount: BalanceOf, - in_top: bool, }, /// Nomination decreased. NominationDecreased { nominator: T::AccountId, candidate: T::AccountId, amount: BalanceOf, - in_top: bool, }, /// Nominator requested to leave the set of nominators. NominatorExitScheduled { @@ -368,7 +372,10 @@ pub mod pallet { /// Cancelled request to change an existing nomination. CancelledNominationRequest { nominator: T::AccountId, - cancelled_request: NominationRequest>, + candidate: T::AccountId, + amount: BalanceOf, + scheduled_at: RoundIndex, + action: NominationChange, }, /// New nomination (increase of the existing one). Nomination { @@ -536,6 +543,18 @@ pub mod pallet { OptionQuery, >; + #[pallet::storage] + #[pallet::unbounded] + /// Unstaking nominations for a certain validator candidate. + /// This contains pending decrease, revoke, and leave requests. + pub type UnstakingNominations = StorageMap< + _, + Twox64Concat, + T::AccountId, + Nominations>, + OptionQuery, + >; + #[pallet::storage] /// The active validator set (full and basic) selected for the current round. This storage is sorted by address. pub type SelectedCandidates = @@ -672,7 +691,7 @@ pub mod pallet { } fn on_runtime_upgrade() -> Weight { - migrations::v5::MigrateToV5::::on_runtime_upgrade() + migrations::v6::MigrateToV6::::on_runtime_upgrade() } } @@ -1193,7 +1212,9 @@ pub mod pallet { // insert empty top nominations >::insert(&controller, empty_nominations.clone()); // insert empty bottom nominations - >::insert(&controller, empty_nominations); + >::insert(&controller, empty_nominations.clone()); + // insert empty unstaking nominations + >::insert(&controller, empty_nominations); candidates .try_insert(controller.clone(), bond) .map_err(|_| Error::::TooManyCandidates)?; @@ -1269,13 +1290,12 @@ pub mod pallet { mut nominator: Nominator>| { T::Currency::unreserve(&bond.owner, bond.amount); // remove nomination from nominator state - if let Some(remaining) = nominator.rm_nomination(&controller) { - if remaining.is_zero() { - >::remove(&bond.owner); - } else { - nominator.requests.remove_request(&controller); - >::insert(&bond.owner, nominator); - } + nominator.rm_nomination(&controller); + if nominator.nominations.is_empty() { + >::remove(&bond.owner); + } else { + nominator.requests.remove_request(&controller); + >::insert(&bond.owner, nominator); } }; // total backing stake is at least the candidate self bond @@ -1300,12 +1320,24 @@ pub mod pallet { ); } total_backing += bottom_nominations.total; + // return all unstaking nominations + // we do not increment total_backing here because it is already subtracted from Total + let unstaking_nominations = >::take(&controller) + .ok_or(>::UnstakingNominationDNE)?; + for bond in unstaking_nominations.nominations { + if let Some(nominator) = NominatorState::::get(&bond.owner) { + return_stake(bond.clone(), nominator); + } else { + T::Currency::unreserve(&bond.owner, bond.amount); + } + } // return stake to stash account T::Currency::unreserve(&stash, state.bond); >::remove(&controller); >::remove(&stash); >::remove(&controller); >::remove(&controller); + >::remove(&controller); let new_total_staked = >::get().saturating_sub(total_backing); >::put(new_total_staked); @@ -1589,11 +1621,11 @@ pub mod pallet { // nomination after first ensure!(amount >= T::MinNomination::get(), Error::::NominationBelowMin); ensure!( - nomination_count >= state.nominations.len() as u32, + nomination_count >= state.nomination_count(false), Error::::TooLowNominationCountToNominate ); ensure!( - (state.nominations.len() as u32) < T::MaxNominationsPerNominator::get(), + state.nomination_count(false) < T::MaxNominationsPerNominator::get(), Error::::ExceedMaxNominationsPerNominator ); state.add_nomination::(candidate.clone(), amount)?; @@ -1635,18 +1667,31 @@ pub mod pallet { #[pallet::call_index(26)] #[pallet::weight(::WeightInfo::schedule_leave_nominators())] /// Request to leave the set of nominators. If successful, the caller is scheduled - /// to be allowed to exit. Success forbids future nominator actions until the request is - /// invoked or cancelled. + /// to be allowed to exit. Every nomination will be set to zero and it will be removed + /// from `TopNominations`/`BottomNominations` and moved to `UnstakingNominations`. + /// The actual exit is available after `LeaveNominatorsDelay` rounds. pub fn schedule_leave_nominators(origin: OriginFor) -> DispatchResultWithPostInfo { - let acc = ensure_signed(origin)?; - let mut state = >::get(&acc).ok_or(Error::::NominatorDNE)?; + let nominator = ensure_signed(origin)?; + let mut state = >::get(&nominator).ok_or(Error::::NominatorDNE)?; ensure!(!state.is_leaving(), Error::::NominatorAlreadyLeaving); ensure!(state.requests().is_empty(), Error::::PendingNominationRequestAlreadyExists); let (now, when) = state.schedule_leave::(); - >::insert(&acc, state); + + for (candidate, amount) in &mut state.nominations { + state.requests.leave::(candidate.clone(), *amount, when)?; + Self::nominator_leaves_candidate(candidate.clone(), nominator.clone(), *amount)?; + Self::add_to_unstaking_nominations( + candidate.clone(), + Bond { owner: nominator.clone(), amount: *amount }, + )?; + *amount = Zero::zero(); + } + state.total = Zero::zero(); + >::insert(&nominator, state); + Self::deposit_event(Event::NominatorExitScheduled { round: now, - nominator: acc, + nominator, scheduled_exit: when, }); Ok(().into()) @@ -1654,7 +1699,8 @@ pub mod pallet { #[pallet::call_index(27)] #[pallet::weight(::WeightInfo::execute_leave_nominators(*nomination_count))] - /// Execute the right to exit the set of nominators and revoke all ongoing nominations. + /// Execute the pending leave request. All nominations will be revoked and removed from storage. + /// The actual balance unreservation is done if successful. pub fn execute_leave_nominators( origin: OriginFor, nomination_count: u32, @@ -1662,34 +1708,48 @@ pub mod pallet { let nominator = ensure_signed(origin)?; let state = >::get(&nominator).ok_or(Error::::NominatorDNE)?; state.can_execute_leave::(nomination_count)?; - for bond in state.nominations { - if let Err(error) = - Self::nominator_leaves_candidate(bond.0.clone(), nominator.clone(), bond.1) - { - log::warn!( - "STORAGE CORRUPTED \nNominator leaving validator failed with error: {:?}", - error - ); - } + + for (candidate, request) in state.requests.requests { + Self::remove_unstaking_nomination( + candidate.clone(), + Bond { owner: nominator.clone(), amount: request.amount }, + )?; } + + T::Currency::unreserve(&nominator, state.requests.less_total); >::remove(&nominator); - Self::deposit_event(Event::NominatorLeft { nominator, unstaked_amount: state.total }); + + Self::deposit_event(Event::NominatorLeft { + nominator, + unstaked_amount: state.requests.less_total, + }); Ok(().into()) } #[pallet::call_index(28)] #[pallet::weight(::WeightInfo::cancel_leave_nominators())] - /// Cancel a pending request to exit the set of nominators. Success clears the pending exit - /// request (thereby resetting the delay upon another `leave_nominators` call). + /// Cancel a pending request to exit the set of nominators. If successful, it clears the pending exit + /// request, thereby resetting the priorly reduced and removed nominations. pub fn cancel_leave_nominators(origin: OriginFor) -> DispatchResultWithPostInfo { let nominator = ensure_signed(origin)?; - // ensure nominator state exists let mut state = >::get(&nominator).ok_or(Error::::NominatorDNE)?; - // ensure state is leaving ensure!(state.is_leaving(), Error::::NominatorDNE); - // cancel exit request + + let to_cancel: Vec<_> = state + .requests + .requests + .iter() + .map(|(candidate, request)| { + let when = request.when_executable.keys().next().unwrap(); + (candidate.clone(), when.clone()) + }) + .collect(); + for (candidate, when) in to_cancel { + state.cancel_pending_request::(candidate, when)?; + } state.cancel_leave(); >::insert(&nominator, state); + Self::deposit_event(Event::NominatorExitCancelled { nominator }); Ok(().into()) } @@ -1698,6 +1758,9 @@ pub mod pallet { #[pallet::weight(::WeightInfo::schedule_revoke_nomination())] /// Request to revoke an existing nomination. If successful, the nomination is scheduled /// to be allowed to be revoked via the `execute_nomination_request` extrinsic. + /// The nomination will be set to zero and it will be removed + /// from `TopNominations`/`BottomNominations` and moved to `UnstakingNominations`. + /// The actual exit is available after `RevokeNominationDelay` rounds. pub fn schedule_revoke_nomination( origin: OriginFor, validator: T::AccountId, @@ -1718,7 +1781,9 @@ pub mod pallet { #[pallet::call_index(30)] #[pallet::weight(::WeightInfo::nominator_bond_more())] - /// Bond more for nominators wrt a specific validator candidate. + /// Bond more for a specific validator candidate. + /// If a pending revoke or leave request exists, it will be cancelled and then increased. + /// For leave requests, all other requests will be switched to revoke. pub fn nominator_bond_more( origin: OriginFor, candidate: T::AccountId, @@ -1726,25 +1791,83 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let nominator = ensure_signed(origin)?; let mut state = >::get(&nominator).ok_or(Error::::NominatorDNE)?; - state.increase_nomination::(candidate.clone(), more)?; + + if let Some(request) = state.requests.requests.get_mut(&candidate) { + // for revoke and leave requests, we have to increase the pending revoke amount to + // check & try if it can be cancelled and increased + let action = request.action.clone(); + match action { + NominationChange::Revoke | NominationChange::Leave => { + // increase and try to cancel + let when = *request.when_executable.keys().next().unwrap(); + let amount = request.when_executable.get_mut(&when).unwrap(); + *amount = amount.saturating_add(more); + request.amount = request.amount.saturating_add(more); + state.cancel_pending_request::(candidate.clone(), when)?; + + if matches!(action, NominationChange::Leave) { + // cancel leave request and switch any other requests to revoke + state.cancel_leave(); + state.requests.requests.iter_mut().for_each(|(_, request)| { + request.action = NominationChange::Revoke; + }); + } + T::Currency::reserve(&nominator, more)?; + }, + NominationChange::Decrease => { + state.increase_nomination::(candidate.clone(), more, true)?; + }, + } + } else { + state.increase_nomination::(candidate.clone(), more, true)?; + } + >::insert(&nominator, state); + + Pallet::::deposit_event(Event::NominationIncreased { + nominator, + candidate, + amount: more, + }); Ok(().into()) } #[pallet::call_index(31)] #[pallet::weight(::WeightInfo::schedule_nominator_bond_less())] - /// Request bond less for nominators wrt a specific validator candidate. + /// Request to decrease bond for a specific validator candidate. If successful, the nomination + /// will be immediately decreased and moved to `UnstakingNominations`. The actual exit is + /// available after `NominationBondLessDelay` rounds. pub fn schedule_nominator_bond_less( origin: OriginFor, candidate: T::AccountId, less: BalanceOf, ) -> DispatchResultWithPostInfo { - let caller = ensure_signed(origin)?; - let mut state = >::get(&caller).ok_or(Error::::NominatorDNE)?; + let nominator = ensure_signed(origin)?; + let mut state = >::get(&nominator).ok_or(Error::::NominatorDNE)?; ensure!(!state.is_leaving(), Error::::NominatorAlreadyLeaving); let when = state.schedule_decrease_nomination::(candidate.clone(), less)?; - >::insert(&caller, state); + + // immediately decrease nomination and apply to state (without unreserving) + let nomination = + state.nominations.get_mut(&candidate).ok_or(Error::::NominationDNE)?; + let amount_before = nomination.clone(); + *nomination = nomination.saturating_sub(less); + state.total = state.total.saturating_sub(less); + let mut validator = + >::get(&candidate).ok_or(Error::::CandidateDNE)?; + let _ = validator.decrease_nomination::( + &candidate, + nominator.clone(), + amount_before, + less, + )?; + >::insert(&candidate, validator); + >::mutate(|total| { + *total = total.saturating_sub(less); + }); + + >::insert(&nominator, state); Self::deposit_event(Event::NominationDecreaseScheduled { - nominator: caller, + nominator, candidate, amount_to_decrease: less, execute_round: when, @@ -1754,31 +1877,38 @@ pub mod pallet { #[pallet::call_index(32)] #[pallet::weight(::WeightInfo::execute_nominator_bond_less())] - /// Execute pending request to change an existing nomination + /// Execute a pending request (Decrease, Revoke) to modify an existing nomination. + /// - `when` is the round index when the request is executable pub fn execute_nomination_request( origin: OriginFor, candidate: T::AccountId, + when: RoundIndex, ) -> DispatchResultWithPostInfo { let nominator = ensure_signed(origin)?; let mut state = >::get(&nominator).ok_or(Error::::NominatorDNE)?; - state.execute_pending_request::(candidate)?; + state.execute_pending_request::(candidate, when)?; Ok(().into()) } #[pallet::call_index(33)] #[pallet::weight(::WeightInfo::cancel_nominator_bond_less())] - /// Cancel request to change an existing nomination. + /// Cancel a pending request (Decrease, Revoke) that modifies an existing nomination. + /// - `when` is the round index when the request is executable pub fn cancel_nomination_request( origin: OriginFor, candidate: T::AccountId, + when: RoundIndex, ) -> DispatchResultWithPostInfo { let nominator = ensure_signed(origin)?; let mut state = >::get(&nominator).ok_or(Error::::NominatorDNE)?; - let request = state.cancel_pending_request::(candidate)?; + let request = state.cancel_pending_request::(candidate.clone(), when)?; >::insert(&nominator, state); Self::deposit_event(Event::CancelledNominationRequest { nominator, - cancelled_request: request, + candidate, + amount: request.amount, + scheduled_at: when, + action: request.action, }); Ok(().into()) } diff --git a/precompiles/bfc-staking/src/interface.sol b/precompiles/bfc-staking/src/interface.sol index 0e4b526f..a0d4d2ce 100644 --- a/precompiles/bfc-staking/src/interface.sol +++ b/precompiles/bfc-staking/src/interface.sol @@ -81,30 +81,30 @@ interface BfcStaking { /// @param candidate the address that we want to confirm is a validator andidate /// @param tier the type of the validator candidate (0: All, 1: Basic, 2: Full) /// @return A boolean confirming whether the address is a validator candidate - function is_candidate(address candidate, uint256 tier) - external - view - returns (bool); + function is_candidate( + address candidate, + uint256 tier + ) external view returns (bool); /// @dev Check whether the specified address is currently a part of the active set (full or basic) /// Selector: 4a079cfd /// @param candidate the address that we want to confirm is a part of the active set /// @param tier the type of the validator candidate (0: All, 1: Basic, 2: Full) /// @return A boolean confirming whether the address is a part of the active set - function is_selected_candidate(address candidate, uint256 tier) - external - view - returns (bool); + function is_selected_candidate( + address candidate, + uint256 tier + ) external view returns (bool); /// @dev Check whether the specified address elements is currently a part of the active set /// Selector: 044527bd /// @param candidates the address array that we want to confirm is a part of the active set /// @param tier the type of the validator candidate (0: All, 1: Basic, 2: Full) /// @return A boolean confirming whether the address array is a part of the active set - function is_selected_candidates(address[] calldata candidates, uint256 tier) - external - view - returns (bool); + function is_selected_candidates( + address[] calldata candidates, + uint256 tier + ) external view returns (bool); /// @dev Check whether every specified address element is currently the active set (full or basic) /// Selector: 2e8c2a6a @@ -176,10 +176,9 @@ interface BfcStaking { /// @dev Get the given rounds active validator sets majority /// Selector: e0f9ab40 /// @return The given rounds majority - function previous_majority(uint256 round_index) - external - view - returns (uint256); + function previous_majority( + uint256 round_index + ) external view returns (uint256); /// @dev Total points awarded to all validators in a particular round /// Selector: 9799b4e7 @@ -191,10 +190,10 @@ interface BfcStaking { /// Selector: 59a595fb /// @param round_index the round for which we are querying the points /// @return The awarded points to the validator in the given round - function validator_points(uint256 round_index, address validator) - external - view - returns (uint256); + function validator_points( + uint256 round_index, + address validator + ) external view returns (uint256); /// @dev The amount of awarded tokens to validators and nominators since genesis /// Selector: 9ec5a894 @@ -204,10 +203,9 @@ interface BfcStaking { /// @dev Total capital locked information of self-bonds and nominations of the given round /// Selector: b119ebfe /// @return The total locked information - function total(uint256 round_index) - external - view - returns (total_stake memory); + function total( + uint256 round_index + ) external view returns (total_stake memory); /// @dev Stake annual inflation parameters /// Selector: 10db2de9 @@ -215,11 +213,7 @@ interface BfcStaking { function inflation_config() external view - returns ( - uint256, - uint256, - uint256 - ); + returns (uint256, uint256, uint256); /// @dev Stake annual inflation rate /// Selector: 180692d0 @@ -266,11 +260,7 @@ interface BfcStaking { function nominator_bond_less_delay() external view - returns ( - uint256, - uint256, - uint256 - ); + returns (uint256, uint256, uint256); /// @dev Get the CandidateCount weight hint /// Selector: 4b1c4c29 @@ -281,18 +271,16 @@ interface BfcStaking { /// Selector: a5542eea /// @param tier the type of the validator candidate (0: All, 1: Basic, 2: Full) /// @return The list of the selected candidates - function selected_candidates(uint256 tier) - external - view - returns (address[] memory); + function selected_candidates( + uint256 tier + ) external view returns (address[] memory); /// @dev Get the previous selected candidates of the given round index /// Selector: d9c62dc8 /// @return The list of the previous selected candidates - function previous_selected_candidates(uint256 round_index) - external - view - returns (address[] memory); + function previous_selected_candidates( + uint256 round_index + ) external view returns (address[] memory); /// @dev Get the current state of joined validator candidates /// Selector: 96b41b5b @@ -306,16 +294,17 @@ interface BfcStaking { /// Selector: 36f3b497 /// @param candidate the address for which we are querying the state /// @return The current state of the queried candidate - function candidate_state(address candidate) - external - view - returns (candidate_meta_data memory); + function candidate_state( + address candidate + ) external view returns (candidate_meta_data memory); /// @dev Get every candidate states /// Selector: 3b368c8c /// @param tier the type of the validator candidate (0: All, 1: Basic, 2: Full) /// @return An array of every candidate states - function candidate_states(uint256 tier) + function candidate_states( + uint256 tier + ) external view returns ( @@ -346,7 +335,10 @@ interface BfcStaking { /// @param tier the type of the validator candidate (0: All, 1: Basic, 2: Full) /// @param is_selected the boolean for which it is selected for the current round /// @return An array of every candidate states that matches the selector - function candidate_states_by_selection(uint256 tier, bool is_selected) + function candidate_states_by_selection( + uint256 tier, + bool is_selected + ) external view returns ( @@ -376,53 +368,47 @@ interface BfcStaking { /// Selector: 2e388768 /// @param candidate the address for which we are querying the state /// @return The current status of the queried candidate - function candidate_request(address candidate) - external - view - returns (candidate_request_data memory); + function candidate_request( + address candidate + ) external view returns (candidate_request_data memory); /// @dev Get the top nominations of the given candidate /// Selector: 2a9cdf2b /// @param candidate the address for which we are querying the state /// @return The current status of the queried candidate - function candidate_top_nominations(address candidate) + function candidate_top_nominations( + address candidate + ) external view - returns ( - address, - uint256, - address[] memory, - uint256[] memory - ); + returns (address, uint256, address[] memory, uint256[] memory); /// @dev Get the bottom nominations of the given candidate /// Selector: 9be794c0 /// @param candidate the address for which we are querying the state /// @return The current status of the queried candidate - function candidate_bottom_nominations(address candidate) + function candidate_bottom_nominations( + address candidate + ) external view - returns ( - address, - uint256, - address[] memory, - uint256[] memory - ); + returns (address, uint256, address[] memory, uint256[] memory); /// @dev Get the CandidateNominationCount weight hint /// Selector: 1c8ad6fe /// @param candidate The address for which we are querying the nomination count /// @return The number of nominations backing the validator - function candidate_nomination_count(address candidate) - external - view - returns (uint256); + function candidate_nomination_count( + address candidate + ) external view returns (uint256); /// @dev Get the current state of the given nominator /// Selector: 3f97be51 /// @param nominator the address for which we are querying the state /// @return The current state of the queried nominator - function nominator_state(address nominator) + function nominator_state( + address nominator + ) external view returns ( @@ -430,7 +416,6 @@ interface BfcStaking { uint256, uint256, uint256, - uint256, address[] memory, uint256[] memory, uint256[] memory, @@ -443,16 +428,17 @@ interface BfcStaking { /// Selector: 24f81326 /// @param nominator the address for which we are querying the state /// @return The pending requests of the queried nominator - function nominator_requests(address nominator) + function nominator_requests( + address nominator + ) external view returns ( address, uint256, - uint256, address[] memory, - uint256[] memory, - uint256[] memory, + uint256[][] memory, + uint256[][] memory, uint256[] memory ); @@ -460,10 +446,9 @@ interface BfcStaking { /// Selector: dae5659b /// @param nominator The address for which we are querying the nomination count /// @return The number of nominations made by the nominator - function nominator_nomination_count(address nominator) - external - view - returns (uint256); + function nominator_nomination_count( + address nominator + ) external view returns (uint256); /// @dev Temporarily leave the set of validator candidates without unbonding /// Selector: 767e0450 @@ -504,8 +489,9 @@ interface BfcStaking { /// @dev Execute due request to leave the set of validator candidates /// Selector: e33a8f25 /// @param candidateNominationCount The number of nominations for the candidate to be revoked - function execute_leave_candidates(uint256 candidateNominationCount) - external; + function execute_leave_candidates( + uint256 candidateNominationCount + ) external; /// @dev Execute pending candidate bond request /// Selector: 6c76b502 @@ -566,28 +552,37 @@ interface BfcStaking { /// Selector: 774bef4d /// @param candidate The address of the validator candidate for which nomination shall decrease /// @param less The amount by which the nomination is decreased (upon execution) - function schedule_nominator_bond_less(address candidate, uint256 less) - external; + function schedule_nominator_bond_less( + address candidate, + uint256 less + ) external; /// @dev Execute request to leave the set of nominators and revoke all nominations /// Selector: 4480de22 /// @param nominatorNominationCount The number of active nominations to be revoked by nominator - function execute_leave_nominators(uint256 nominatorNominationCount) - external; + function execute_leave_nominators( + uint256 nominatorNominationCount + ) external; /// @dev Execute pending nomination request (if exists && is due) - /// Selector: bfb13332 + /// Selector: 29c0e286 /// @param candidate The address of the candidate - function execute_nomination_request(address candidate) external; + function execute_nomination_request( + address candidate, + uint256 when + ) external; /// @dev Cancel request to leave the set of nominators /// Selector: e48105f0 function cancel_leave_nominators() external; /// @dev Cancel pending nomination request (already made in support of input by caller) - /// Selector: bdb20cae + /// Selector: 74d430a5 /// @param candidate The address of the candidate - function cancel_nomination_request(address candidate) external; + function cancel_nomination_request( + address candidate, + uint256 when + ) external; /// @dev Set the nominator reward destination /// Selector: 5706390d diff --git a/precompiles/bfc-staking/src/lib.rs b/precompiles/bfc-staking/src/lib.rs index df5b251c..2dc89b3c 100644 --- a/precompiles/bfc-staking/src/lib.rs +++ b/precompiles/bfc-staking/src/lib.rs @@ -920,39 +920,37 @@ where handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let zero = 0u32; - let mut revocations_count: u32 = zero.into(); let mut less_total: U256 = zero.into(); let mut candidates: Vec
= vec![]; - let mut amounts: Vec = vec![]; - let mut when_executables: Vec = vec![]; + let mut amounts: Vec> = vec![]; + let mut when_executables: Vec> = vec![]; let mut actions: Vec = vec![]; if let Some(state) = pallet_bfc_staking::NominatorState::::get(&nominator) { - revocations_count = state.requests.revocations_count.into(); less_total = state.requests.less_total.into(); for (candidate, request) in state.requests.requests { candidates.push(Address(candidate.into())); - amounts.push(request.amount.into()); - when_executables.push(request.when_executable.into()); + + let mut inner_when = vec![]; + let mut inner_amount = vec![]; + for (when, amount) in request.when_executable { + inner_when.push(when.into()); + inner_amount.push(amount.into()); + } + when_executables.push(inner_when); + amounts.push(inner_amount); let action: u32 = match request.action { NominationChange::Revoke => 1u32.into(), NominationChange::Decrease => 2u32.into(), + NominationChange::Leave => 3u32.into(), }; actions.push(action.into()); } } - Ok(( - Address(nominator.into()), - revocations_count, - less_total, - candidates, - amounts.into(), - when_executables, - actions, - )) + Ok((Address(nominator.into()), less_total, candidates, amounts, when_executables, actions)) } /// Returns the count of nominations of the given `nominator` @@ -970,8 +968,7 @@ where handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let result = if let Some(state) = pallet_bfc_staking::NominatorState::::get(&nominator) { - let nominator_nomination_count: u32 = state.nominations.len() as u32; - nominator_nomination_count + state.nomination_count(false) } else { 0u32 }; @@ -1282,11 +1279,12 @@ where fn execute_nomination_request( handle: &mut impl PrecompileHandle, candidate: Address, + when: u32, ) -> EvmResult { let candidate = Runtime::AddressMapping::into_account_id(candidate.0); let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); - let call = StakingCall::::execute_nomination_request { candidate }; + let call = StakingCall::::execute_nomination_request { candidate, when }; RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; @@ -1309,11 +1307,12 @@ where fn cancel_nomination_request( handle: &mut impl PrecompileHandle, candidate: Address, + when: u32, ) -> EvmResult { let candidate = Runtime::AddressMapping::into_account_id(candidate.0); let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); - let call = StakingCall::::cancel_nomination_request { candidate }; + let call = StakingCall::::cancel_nomination_request { candidate, when }; RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; diff --git a/precompiles/bfc-staking/src/types.rs b/precompiles/bfc-staking/src/types.rs index dbc4cf66..f5913227 100644 --- a/precompiles/bfc-staking/src/types.rs +++ b/precompiles/bfc-staking/src/types.rs @@ -90,9 +90,10 @@ pub type EvmCandidateStatesOf = ( ); pub type EvmNominatorStateOf = - (Address, U256, u32, u32, U256, Vec
, Vec, Vec, u32, U256, Vec); + (Address, U256, u32, U256, Vec
, Vec, Vec, u32, U256, Vec); -pub type EvmNominatorRequestsOf = (Address, u32, U256, Vec
, Vec, Vec, Vec); +pub type EvmNominatorRequestsOf = + (Address, U256, Vec
, Vec>, Vec>, Vec); /// EVM struct for candidate states pub struct CandidateStates { @@ -445,8 +446,6 @@ pub struct NominatorState { pub initial_nominations: Vec>, /// The total balance locked for this nominator pub total: BalanceOf, - /// The number of pending revocations - pub request_revocations_count: u32, /// The sum of pending revocation amounts + bond less amounts pub request_less_total: BalanceOf, /// The status of this nominator @@ -472,7 +471,6 @@ where nominations: vec![], initial_nominations: vec![], total: zero.into(), - request_revocations_count: zero.into(), request_less_total: zero.into(), status: zero.into(), reward_dst: zero.into(), @@ -491,7 +489,6 @@ where }); self.total = state.total; - self.request_revocations_count = state.requests.revocations_count.into(); self.request_less_total = state.requests.less_total; self.status = match state.status { @@ -516,7 +513,6 @@ where owner, self.total.into(), self.status, - self.request_revocations_count, self.request_less_total.into(), self.candidates.clone(), self.nominations.clone().into_iter().map(|n| n.into()).collect::>(), diff --git a/runtime/common/src/apis.rs b/runtime/common/src/apis.rs index c11c4b21..0bcbcc18 100644 --- a/runtime/common/src/apis.rs +++ b/runtime/common/src/apis.rs @@ -302,6 +302,8 @@ macro_rules! impl_common_runtime_apis { estimate: bool, access_list: Option)>>, ) -> Result { + use pallet_evm::GasWeightMapping as _; + let config = if estimate { let mut config = ::config().clone(); config.estimate = true; @@ -310,20 +312,44 @@ macro_rules! impl_common_runtime_apis { None }; - let gas_limit = gas_limit.min(u64::MAX.into()); - let transaction_data = TransactionData::new( - TransactionAction::Call(to), - data.clone(), - nonce.unwrap_or_default(), - gas_limit, - None, - max_fee_per_gas, - max_priority_fee_per_gas, - value, - Some(::ChainId::get()), - access_list.clone().unwrap_or_default(), - ); - let (weight_limit, proof_size_base_cost) = pallet_ethereum::Pallet::::transaction_weight(&transaction_data); + // Estimated encoded transaction size must be based on the heaviest transaction + // type (EIP1559Transaction) to be compatible with all transaction types. + let mut estimated_transaction_len = data.len() + + // pallet ethereum index: 1 + // transact call index: 1 + // Transaction enum variant: 1 + // chain_id 8 bytes + // nonce: 32 + // max_priority_fee_per_gas: 32 + // max_fee_per_gas: 32 + // gas_limit: 32 + // action: 21 (enum variant + call address) + // value: 32 + // access_list: 1 (empty vec size) + // 65 bytes signature + 258; + + if access_list.is_some() { + estimated_transaction_len += access_list.encoded_size(); + } + + let gas_limit = if gas_limit > U256::from(u64::MAX) { + u64::MAX + } else { + gas_limit.low_u64() + }; + let without_base_extrinsic_weight = true; + + let (weight_limit, proof_size_base_cost) = + match ::GasWeightMapping::gas_to_weight( + gas_limit, + without_base_extrinsic_weight + ) { + weight_limit if weight_limit.proof_size() > 0 => { + (Some(weight_limit), Some(estimated_transaction_len as u64)) + } + _ => (None, None), + }; ::Runner::call( from, @@ -353,6 +379,8 @@ macro_rules! impl_common_runtime_apis { estimate: bool, access_list: Option)>>, ) -> Result { + use pallet_evm::GasWeightMapping as _; + let config = if estimate { let mut config = ::config().clone(); config.estimate = true; @@ -361,19 +389,43 @@ macro_rules! impl_common_runtime_apis { None }; - let transaction_data = TransactionData::new( - TransactionAction::Create, - data.clone(), - nonce.unwrap_or_default(), - gas_limit, - None, - max_fee_per_gas, - max_priority_fee_per_gas, - value, - Some(::ChainId::get()), - access_list.clone().unwrap_or_default(), - ); - let (weight_limit, proof_size_base_cost) = pallet_ethereum::Pallet::::transaction_weight(&transaction_data); + let mut estimated_transaction_len = data.len() + + // from: 20 + // value: 32 + // gas_limit: 32 + // nonce: 32 + // 1 byte transaction action variant + // chain id 8 bytes + // 65 bytes signature + 190; + + if max_fee_per_gas.is_some() { + estimated_transaction_len += 32; + } + if max_priority_fee_per_gas.is_some() { + estimated_transaction_len += 32; + } + if access_list.is_some() { + estimated_transaction_len += access_list.encoded_size(); + } + + let gas_limit = if gas_limit > U256::from(u64::MAX) { + u64::MAX + } else { + gas_limit.low_u64() + }; + let without_base_extrinsic_weight = true; + + let (weight_limit, proof_size_base_cost) = + match ::GasWeightMapping::gas_to_weight( + gas_limit, + without_base_extrinsic_weight + ) { + weight_limit if weight_limit.proof_size() > 0 => { + (Some(weight_limit), Some(estimated_transaction_len as u64)) + } + _ => (None, None), + }; ::Runner::create( from, diff --git a/runtime/dev/src/lib.rs b/runtime/dev/src/lib.rs index d42dad2b..96bc9f66 100644 --- a/runtime/dev/src/lib.rs +++ b/runtime/dev/src/lib.rs @@ -46,7 +46,6 @@ pub use pallet_balances::{Call as BalancesCall, NegativeImbalance}; pub use pallet_bfc_staking::{InflationInfo, Range}; use pallet_ethereum::{ Call::transact, EthereumBlockHashMapping, PostLogContent, Transaction as EthereumTransaction, - TransactionAction, TransactionData, }; use pallet_evm::{ Account as EVMAccount, EVMCurrencyAdapter, EnsureAddressNever, EnsureAddressRoot, @@ -112,10 +111,7 @@ pub type UncheckedExtrinsic = fp_self_contained::UncheckedExtrinsic; /// All migrations executed on runtime upgrade as a nested tuple of types implementing `OnRuntimeUpgrade`. -type Migrations = ( - pallet_identity::migration::versioned::V0ToV1, - pallet_grandpa::migrations::MigrateV4ToV5, -); +type Migrations = (); /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -155,7 +151,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // The version of the authorship interface. authoring_version: 1, // The version of the runtime spec. - spec_version: 371, + spec_version: 373, // The version of the implementation of the spec. impl_version: 1, // A list of supported runtime APIs along with their versions. @@ -829,9 +825,9 @@ parameter_types! { /// Default maximum basicvalidators selected per round, default at genesis. pub const DefaultMaxSelectedBasicCandidates: u32 = 10; /// Maximum top nominations per candidate. - pub const MaxTopNominationsPerCandidate: u32 = 10; + pub const MaxTopNominationsPerCandidate: u32 = 2; /// Maximum bottom nominations per candidate. - pub const MaxBottomNominationsPerCandidate: u32 = 2; + pub const MaxBottomNominationsPerCandidate: u32 = 1; /// Maximum nominations per nominator. pub const MaxNominationsPerNominator: u32 = 3; /// Default commission rate for full validators. diff --git a/runtime/mainnet/src/lib.rs b/runtime/mainnet/src/lib.rs index 619ce05f..31b4a3f9 100644 --- a/runtime/mainnet/src/lib.rs +++ b/runtime/mainnet/src/lib.rs @@ -44,7 +44,6 @@ pub use pallet_balances::{Call as BalancesCall, NegativeImbalance}; pub use pallet_bfc_staking::{InflationInfo, Range}; use pallet_ethereum::{ Call::transact, EthereumBlockHashMapping, PostLogContent, Transaction as EthereumTransaction, - TransactionAction, TransactionData, }; use pallet_evm::{ Account as EVMAccount, EVMCurrencyAdapter, EnsureAddressNever, EnsureAddressRoot, @@ -149,7 +148,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // The version of the authorship interface. authoring_version: 1, // The version of the runtime spec. - spec_version: 2027, + spec_version: 2028, // The version of the implementation of the spec. impl_version: 1, // A list of supported runtime APIs along with their versions. diff --git a/runtime/testnet/src/lib.rs b/runtime/testnet/src/lib.rs index 85ce25dc..5c538c93 100644 --- a/runtime/testnet/src/lib.rs +++ b/runtime/testnet/src/lib.rs @@ -44,7 +44,6 @@ pub use pallet_balances::{Call as BalancesCall, NegativeImbalance}; pub use pallet_bfc_staking::{InflationInfo, Range}; use pallet_ethereum::{ Call::transact, EthereumBlockHashMapping, PostLogContent, Transaction as EthereumTransaction, - TransactionAction, TransactionData, }; use pallet_evm::{ Account as EVMAccount, EVMCurrencyAdapter, EnsureAddressNever, EnsureAddressRoot, @@ -153,7 +152,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // The version of the authorship interface. authoring_version: 1, // The version of the runtime spec. - spec_version: 477, + spec_version: 478, // The version of the implementation of the spec. impl_version: 1, // A list of supported runtime APIs along with their versions. diff --git a/tests/runtime/test_local_runtime.ts b/tests/runtime/test_local_runtime.ts index 4963ad6d..2b63194e 100644 --- a/tests/runtime/test_local_runtime.ts +++ b/tests/runtime/test_local_runtime.ts @@ -60,18 +60,32 @@ const sendTransaction = async (signedTx: string): Promise => { return txHash; }; -const createErc20Transfer = async (): Promise => { +const createErc20Transfer = async (): Promise<[number, string]> => { const erc20: any = new web3.eth.Contract(ERC20_ABI, erc20Address); const gas = await erc20.methods.transfer(baltathar, web3.utils.toWei(1, 'ether')).estimateGas({ from: alith }); expect(gas).is.ok; - return (await web3.eth.accounts.signTransaction({ + return [gas, (await web3.eth.accounts.signTransaction({ from: alith, to: erc20Address, gas, gasPrice: web3.utils.toWei(1000, 'gwei'), data: erc20.methods.transfer(baltathar, web3.utils.toWei(1, 'ether')).encodeABI() - }, alithPk)).rawTransaction; + }, alithPk)).rawTransaction]; +}; + +const createErc20Approve = async (): Promise<[number, string]> => { + const erc20: any = new web3.eth.Contract(ERC20_ABI, erc20Address); + const gas = await erc20.methods.approve(baltathar, web3.utils.toWei(1, 'ether')).estimateGas({ from: alith }); + expect(gas).is.ok; + + return [gas, (await web3.eth.accounts.signTransaction({ + from: alith, + to: erc20Address, + gas, + gasPrice: web3.utils.toWei(1000, 'gwei'), + data: erc20.methods.approve(baltathar, web3.utils.toWei(1, 'ether')).encodeABI() + }, alithPk)).rawTransaction]; }; describe('test_runtime - evm interactions', function () { @@ -217,7 +231,7 @@ describe('test_runtime - ethapi', function () { const receipt = await deployDemo(deployTx); erc20Address = receipt?.contractAddress; - const signedTx_2 = await createErc20Transfer(); + const [_, signedTx_2] = await createErc20Transfer(); const txHash = await sendTransaction(signedTx_2); const receipt_2 = await web3.requestManager.send({ method: 'eth_getTransactionReceipt', params: [txHash] }); @@ -236,6 +250,28 @@ describe('test_runtime - ethapi', function () { expect(logs).is.ok; }); + it('should successfully calculate estimated gas - transfer', async function () { + const [gasLimit, signedTx] = await createErc20Transfer(); + console.log(gasLimit); + const txHash = await sendTransaction(signedTx); + const receipt = await web3.requestManager.send({ method: 'eth_getTransactionReceipt', params: [txHash] }); + console.log(receipt); + expect(receipt).is.ok; + expect(receipt?.gasUsed).is.ok; + expect(Number(receipt?.gasUsed)).lte(Number(gasLimit)); + }); + + it('should successfully calculate estimated gas - approve', async function () { + const [gasLimit, signedTx] = await createErc20Approve(); + console.log(gasLimit); + const txHash = await sendTransaction(signedTx); + const receipt = await web3.requestManager.send({ method: 'eth_getTransactionReceipt', params: [txHash] }); + console.log(receipt); + expect(receipt).is.ok; + expect(receipt?.gasUsed).is.ok; + expect(Number(receipt?.gasUsed)).lte(Number(gasLimit)); + }); + it('should successfully request txpool namespace methods', async function () { // verify txpool_status const status = await web3.requestManager.send({ method: 'txpool_status', params: [] }); @@ -257,7 +293,7 @@ describe('test_runtime - ethapi', function () { }); it('should successfully request debug namespace methods', async function () { - const signedTx = await createErc20Transfer(); + const [_, signedTx] = await createErc20Transfer(); const txHash = await sendTransaction(signedTx); diff --git a/tests/tests/pallets/test_bfc_staking.ts b/tests/tests/pallets/test_bfc_staking.ts index 9f68d773..d3a07da4 100644 --- a/tests/tests/pallets/test_bfc_staking.ts +++ b/tests/tests/pallets/test_bfc_staking.ts @@ -57,7 +57,7 @@ describeDevNode('pallet_bfc_staking - set controller', (context) => { await context.createBlock(); await context.polkadotApi.tx.bfcStaking - .scheduleRevokeNomination(alith.address) + .scheduleLeaveNominators() .signAndSend(charleth); await context.createBlock(); @@ -101,17 +101,17 @@ describeDevNode('pallet_bfc_staking - set controller', (context) => { const atStake = rawAtStake.toJSON(); expect(atStake).is.not.null; - const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(newAlith.address); - const topNominations = rawTopNominations.unwrap().toJSON(); - expect(topNominations.nominations.length).equal(1); - let isTopNominationFound = false; - for (const nomination of topNominations.nominations) { + const rawUnstakingNominations: any = await context.polkadotApi.query.bfcStaking.unstakingNominations(newAlith.address); + const unstakingNominations = rawUnstakingNominations.unwrap().toJSON(); + expect(unstakingNominations.nominations.length).equal(1); + let isUnstakingNominationFound = false; + for (const nomination of unstakingNominations.nominations) { if (nomination.owner === charleth.address) { - isTopNominationFound = true; + isUnstakingNominationFound = true; break; } } - expect(isTopNominationFound).equal(true); + expect(isUnstakingNominationFound).equal(true); const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); const nominatorState = rawNominatorState.unwrap().toJSON(); @@ -120,7 +120,6 @@ describeDevNode('pallet_bfc_staking - set controller', (context) => { expect(nominatorState.nominations).has.key(newAlith.address); expect(nominatorState.initialNominations).has.key(newAlith.address); - expect(nominatorState.requests.revocationsCount).equal(1); expect(nominatorState.requests.requests).has.key(newAlith.address); expect(Object.keys(nominatorState.requests.requests).length).equal(1); expect(nominatorState.requests.requests[newAlith.address].validator).equal(newAlith.address); @@ -1733,481 +1732,3 @@ describeDevNode('pallet_bfc_staking - candidate leave', (context) => { expect(isRelayerFound).equal(false); }); }); - -describeDevNode('pallet_bfc_staking - join nominators', (context) => { - const keyring = new Keyring({ type: 'ethereum' }); - const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); - const charleth = keyring.addFromUri(TEST_CONTROLLERS[2].private); - - it('should fail due to minimum amount constraint', async function () { - const stakeBelowMin = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT).minus(10 ** 15); - - await context.polkadotApi.tx.bfcStaking - .nominate(alith.address, stakeBelowMin.toFixed(), 0, 0) - .signAndSend(charleth); - - await context.createBlock(); - }); - - it('should fail due to wrong candidate', async function () { - const stake = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT); - - await context.polkadotApi.tx.bfcStaking - .nominate(charleth.address, stake.toFixed(), 0, 0) - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'nominate'); - expect(extrinsicResult).equal('CandidateDNE'); - }); - - it('should successfully nominate to alith', async function () { - const stake = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT); - - await context.polkadotApi.tx.bfcStaking - .nominate(alith.address, stake.toFixed(), 0, 0) - .signAndSend(charleth); - - await context.createBlock(); - - const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorState = rawNominatorState.unwrap().toJSON(); - - expect(nominatorState.nominations).has.key(alith.address); - expect(parseInt(nominatorState.nominations[alith.address].toString(), 16).toString()).equal(stake.toFixed()); - - const rawCandidateState: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); - const candidateState = rawCandidateState.unwrap(); - - expect(candidateState.nominationCount.toString()).equal('1'); - - const selfBond = new BigNumber(candidateState.bond.toString()); - const expectedStake = selfBond.plus(stake); - expect(candidateState.votingPower.toString()).equal(expectedStake.toFixed()); - - const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); - const topNominations = rawTopNominations.unwrap(); - - expect(topNominations.nominations[0].owner.toString().toLowerCase()).equal(charleth.address.toLowerCase()); - expect(topNominations.nominations[0].amount.toString()).equal(stake.toFixed()); - }); - - it('should fail due to calling nominate function twice', async function () { - const stake = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT); - - await context.polkadotApi.tx.bfcStaking - .nominate(alith.address, stake.toFixed(), 1, 1) - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'nominate'); - expect(extrinsicResult).equal('AlreadyNominatedCandidate'); - }); -}); - -describeDevNode('pallet_bfc_staking - nominator stake management', (context) => { - const keyring = new Keyring({ type: 'ethereum' }); - const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); - const baltathar = keyring.addFromUri(TEST_CONTROLLERS[1].private); - const charleth = keyring.addFromUri(TEST_CONTROLLERS[2].private); - - before('should successfully nominate to alith', async function () { - const stake = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT); - - await context.polkadotApi.tx.bfcStaking - .nominate(alith.address, stake.toFixed(), 0, 0) - .signAndSend(charleth); - - await context.createBlock(); - }); - - it('should fail due to calling nominatorBondMore before nominate', async function () { - const stake = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT); - - await context.polkadotApi.tx.bfcStaking - .nominatorBondMore(baltathar.address, stake.toFixed()) - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'nominatorBondMore'); - expect(extrinsicResult).equal('NominationDNE'); - }); - - it('should successfully request nominator bond more', async function () { - const stake = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT); - - await context.polkadotApi.tx.bfcStaking - .nominatorBondMore(alith.address, stake.toFixed()) - .signAndSend(charleth); - - await context.createBlock(); - - const stakeAfter = stake.multipliedBy(2); - - const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorState = rawNominatorState.unwrap().toJSON(); - - expect(nominatorState.nominations).has.key(alith.address); - expect(parseInt(nominatorState.nominations[alith.address].toString(), 16).toString()).equal(stakeAfter.toFixed()); - - const rawCandidateState: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); - const candidateState = rawCandidateState.unwrap(); - - expect(candidateState.nominationCount.toString()).equal('1'); - - const selfBond = new BigNumber(candidateState.bond.toString()); - const expectedStake = selfBond.plus(stakeAfter); - expect(candidateState.votingPower.toString()).equal(expectedStake.toFixed()); - - const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); - const topNominations = rawTopNominations.unwrap(); - - expect(topNominations.nominations[0].owner.toString().toLowerCase()).equal(charleth.address.toLowerCase()); - expect(topNominations.nominations[0].amount.toString()).equal(stakeAfter.toFixed()); - }); - - it('should fail due to minimum nominator stake constraint', async function () { - const stake = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT).plus(AMOUNT_FACTOR); - - await context.polkadotApi.tx.bfcStaking - .scheduleNominatorBondLess(alith.address, stake.toFixed()) - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'scheduleNominatorBondLess'); - expect(extrinsicResult).equal('NominatorBondBelowMin'); - }); - - it('should successfully schedule nominator bond less', async function () { - const stake = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT); - - await context.polkadotApi.tx.bfcStaking - .scheduleNominatorBondLess(alith.address, stake.toFixed()) - .signAndSend(charleth); - - await context.createBlock(); - - const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorState = rawNominatorState.unwrap(); - const nominatorRequests = nominatorState.requests.toJSON(); - - let validator = null; - Object.keys(nominatorRequests['requests']).forEach(function (key) { - validator = key.toLowerCase(); - }); - - expect(validator).equal(alith.address.toLowerCase()); - - let amount = null; - let whenExecutable = null; - let action = null; - Object.values(nominatorRequests['requests']).forEach(function (value: any) { - amount = context.web3.utils.hexToNumberString(value.amount); - whenExecutable = value.whenExecutable; - action = value.action; - }); - - const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); - const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); - const rawRoundDelay: any = context.polkadotApi.consts.bfcStaking.nominationBondLessDelay; - const roundDelay = rawRoundDelay.toNumber(); - - expect(amount).equal(stake.toFixed()); - expect(whenExecutable).equal(currentRound + roundDelay); - expect(action).equal('Decrease'); - }); - - it('should fail due to wrong round to execute schedule nominator bond less', async function () { - await context.polkadotApi.tx.bfcStaking - .executeNominationRequest(alith.address) - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'executeNominationRequest'); - expect(extrinsicResult).equal('PendingNominationRequestNotDueYet'); - }); - - it('should successfully execute scheduled nominator bond less', async function () { - this.timeout(20000); - - const rawNominatorStateBefore: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorStateBefore = rawNominatorStateBefore.unwrap(); - const nominatorRequestsBefore = nominatorStateBefore.requests.toJSON(); - - let validator = null; - Object.keys(nominatorRequestsBefore['requests']).forEach(function (key) { - validator = key.toLowerCase(); - }); - - expect(validator).equal(alith.address.toLowerCase()); - - let whenExecutable = null; - Object.values(nominatorRequestsBefore['requests']).forEach(function (value: any) { - whenExecutable = value.whenExecutable; - }); - expect(whenExecutable).to.be.not.null; - - await jumpToRound(context, Number(whenExecutable)); - - await context.polkadotApi.tx.bfcStaking - .executeNominationRequest(alith.address) - .signAndSend(charleth); - - await context.createBlock(); - await context.createBlock(); - - const rawNominatorStateAfter: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorStateAfter = rawNominatorStateAfter.unwrap().toJSON(); - const nominatorRequestsAfter = nominatorStateAfter.requests; - - let validatorAfter = null; - Object.keys(nominatorRequestsAfter['requests']).forEach(function (key) { - validator = key.toLowerCase(); - }); - expect(validatorAfter).to.be.null; - expect(parseInt(nominatorStateAfter.nominations[alith.address].toString(), 16).toString()).equal(MIN_NOMINATOR_STAKING_AMOUNT); - }); -}); - -describeDevNode('pallet_bfc_staking - revoke nomination', (context) => { - const keyring = new Keyring({ type: 'ethereum' }); - const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); - const baltathar = keyring.addFromUri(TEST_CONTROLLERS[1].private); - const charleth = keyring.addFromUri(TEST_CONTROLLERS[2].private); - - before('should successfully nominate to alith', async function () { - const stake = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT); - - await context.polkadotApi.tx.bfcStaking - .nominate(alith.address, stake.toFixed(), 0, 0) - .signAndSend(charleth); - - await context.createBlock(); - }); - - // even though candidate does not exist in pool - // the returned error will be NominationDNE - it('should fail due to nomination not found', async function () { - await context.polkadotApi.tx.bfcStaking - .scheduleRevokeNomination(baltathar.address) - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'scheduleRevokeNomination'); - expect(extrinsicResult).equal('NominationDNE'); - }); - - it('should successfully schedule revoke nomination', async function () { - await context.polkadotApi.tx.bfcStaking - .scheduleRevokeNomination(alith.address) - .signAndSend(charleth); - - await context.createBlock(); - - const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorState = rawNominatorState.unwrap(); - const nominatorRequests = nominatorState.requests.toJSON(); - - expect(nominatorRequests['revocationsCount']).equal(1); - - let validator = null; - Object.keys(nominatorRequests['requests']).forEach(function (key) { - validator = key.toLowerCase(); - }); - - expect(validator).equal(alith.address.toLowerCase()); - - let amount = null; - let whenExecutable = null; - let action = null; - Object.values(nominatorRequests['requests']).forEach(function (value: any) { - amount = context.web3.utils.hexToNumberString(value.amount); - whenExecutable = value.whenExecutable; - action = value.action; - }); - - const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); - const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); - const rawRoundDelay: any = context.polkadotApi.consts.bfcStaking.nominationBondLessDelay; - const roundDelay = rawRoundDelay.toNumber(); - - expect(amount).equal(MIN_NOMINATOR_STAKING_AMOUNT); - expect(whenExecutable).equal(currentRound + roundDelay); - expect(action).equal('Revoke'); - }); - - it('should fail due to duplicate requests', async function () { - await context.polkadotApi.tx.bfcStaking - .scheduleRevokeNomination(alith.address) - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'scheduleRevokeNomination'); - expect(extrinsicResult).equal('PendingNominationRequestAlreadyExists'); - }); - - it('should fail to execute due to wrong round', async function () { - await context.polkadotApi.tx.bfcStaking - .executeNominationRequest(alith.address) - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'executeNominationRequest'); - expect(extrinsicResult).equal('PendingNominationRequestNotDueYet'); - }); - - it('should successfully execute scheduled revoke nomination', async function () { - this.timeout(20000); - - const balanceBefore = new BigNumber((await context.web3.eth.getBalance(charleth.address)).toString()); - - const rawNominatorStateBefore: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorStateBefore = rawNominatorStateBefore.unwrap(); - const nominatorRequestsBefore = nominatorStateBefore.requests.toJSON(); - - let validator = null; - Object.keys(nominatorRequestsBefore['requests']).forEach(function (key) { - validator = key.toLowerCase(); - }); - - expect(validator).equal(alith.address.toLowerCase()); - - let whenExecutable = null; - Object.values(nominatorRequestsBefore['requests']).forEach(function (value: any) { - whenExecutable = value.whenExecutable; - }); - expect(whenExecutable).to.be.not.null; - - await jumpToRound(context, Number(whenExecutable)); - - await context.polkadotApi.tx.bfcStaking - .executeNominationRequest(alith.address) - .signAndSend(charleth); - - await context.createBlock(); - await context.createBlock(); - - const rawNominatorStateAfter: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorStateAfter = rawNominatorStateAfter.toHuman(); - expect(nominatorStateAfter).to.be.null; - - const balanceAfter = new BigNumber((await context.web3.eth.getBalance(charleth.address)).toString()); - expect(balanceAfter.isGreaterThan(balanceBefore)).equal(true); - }); -}); - -describeDevNode('pallet_bfc_staking - leave nominators', (context) => { - const keyring = new Keyring({ type: 'ethereum' }); - const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); - const baltathar = keyring.addFromUri(TEST_CONTROLLERS[1].private); - const charleth = keyring.addFromUri(TEST_CONTROLLERS[2].private); - - before('should successfully nominate to alith', async function () { - const stake = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT); - - await context.polkadotApi.tx.bfcStaking - .nominate(alith.address, stake.toFixed(), 0, 0) - .signAndSend(charleth); - - await context.createBlock(); - }); - - it('should fail due to empty nominations', async function () { - await context.polkadotApi.tx.bfcStaking - .scheduleLeaveNominators() - .signAndSend(baltathar); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'scheduleLeaveNominators'); - expect(extrinsicResult).equal('NominatorDNE'); - }); - - it('should successfully schedule leave nominators', async function () { - await context.polkadotApi.tx.bfcStaking - .scheduleLeaveNominators() - .signAndSend(charleth); - - await context.createBlock(); - - const rawRoundDelay: any = context.polkadotApi.consts.bfcStaking.leaveNominatorsDelay; - const roundDelay = rawRoundDelay.toNumber(); - - const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); - const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); - - const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorState = rawNominatorState.unwrap(); - - expect(nominatorState.status.isLeaving).equal(true); - expect(nominatorState.status.asLeaving.toNumber()).equal(currentRound + roundDelay); - }); - - it('should fail due to duplicate requests', async function () { - await context.polkadotApi.tx.bfcStaking - .scheduleLeaveNominators() - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'scheduleLeaveNominators'); - expect(extrinsicResult).equal('NominatorAlreadyLeaving'); - }); - - // it only cares if the nominationCount is below to the actual amount - // if the requested amount is over the actual amount it will pass - it('should fail to execute scheduled leave nominators due to invalid nominationCount', async function () { - await context.polkadotApi.tx.bfcStaking - .executeLeaveNominators(0) - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'executeLeaveNominators'); - expect(extrinsicResult).equal('TooLowNominationCountToLeaveNominators'); - }); - - it('should fail execute due to wrong round', async function () { - await context.polkadotApi.tx.bfcStaking - .executeLeaveNominators(1) - .signAndSend(charleth); - - await context.createBlock(); - - const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'executeLeaveNominators'); - expect(extrinsicResult).equal('NominatorCannotLeaveYet'); - }); - - it('should successfully execute scheduled leave nominators', async function () { - this.timeout(20000); - - const balanceBefore = new BigNumber((await context.web3.eth.getBalance(charleth.address)).toString()); - - const rawNominatorStateBefore: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorStateBefore = rawNominatorStateBefore.unwrap(); - - await jumpToRound(context, nominatorStateBefore.status.asLeaving.toNumber()); - - await context.polkadotApi.tx.bfcStaking - .executeLeaveNominators(1) - .signAndSend(charleth); - - await context.createBlock(); - await context.createBlock(); - - const rawNominatorStateAfter: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); - const nominatorStateAfter = rawNominatorStateAfter.toHuman(); - expect(nominatorStateAfter).to.be.null; - - const balanceAfter = new BigNumber((await context.web3.eth.getBalance(charleth.address)).toString()); - expect(balanceAfter.isGreaterThan(balanceBefore)).equal(true); - }); -}); diff --git a/tests/tests/pallets/test_bfc_staking_nominations.ts b/tests/tests/pallets/test_bfc_staking_nominations.ts new file mode 100644 index 00000000..c23fdfc6 --- /dev/null +++ b/tests/tests/pallets/test_bfc_staking_nominations.ts @@ -0,0 +1,1675 @@ +import BigNumber from 'bignumber.js'; +import { expect } from 'chai'; + +import { Keyring } from '@polkadot/api'; + +import { + DEFAULT_STAKING_AMOUNT, MIN_NOMINATOR_STAKING_AMOUNT +} from '../../constants/currency'; +import { TEST_CONTROLLERS, TEST_STASHES } from '../../constants/keys'; +import { getExtrinsicResult } from '../extrinsics'; +import { describeDevNode } from '../set_dev_node'; +import { jumpToRound } from '../utils'; + +describeDevNode('pallet_bfc_staking - nominations', (context) => { + const keyring = new Keyring({ type: 'ethereum' }); + const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); + const baltathar = keyring.addFromUri(TEST_CONTROLLERS[1].private); + const charleth = keyring.addFromUri(TEST_CONTROLLERS[2].private); + const dorothy = keyring.addFromUri(TEST_CONTROLLERS[3].private); + const ethan = keyring.addFromUri(TEST_CONTROLLERS[4].private); + + const faith = keyring.addFromUri(TEST_CONTROLLERS[5].private); + const faithStash = keyring.addFromUri(TEST_STASHES[5].private); + + it('should fail due to minimum amount constraint', async function () { + const stakeBelowMin = new BigNumber(MIN_NOMINATOR_STAKING_AMOUNT).minus(10 ** 15); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stakeBelowMin.toFixed(), 0, 0) + .signAndSend(baltathar); + + await context.createBlock(); + }); + + it('should fail due to unknown candidate', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .nominate(charleth.address, stake.toFixed(), 0, 0) + .signAndSend(baltathar); + + await context.createBlock(); + + const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'nominate'); + expect(extrinsicResult).equal('CandidateDNE'); + }); + + it('should successfully nominate to alith - baltathar', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); // 1000 BFC + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 0, 0) + .signAndSend(baltathar); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(baltathar.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + + expect(nominatorState.nominations).has.key(alith.address); + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorState.initialNominations[alith.address].toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(stake.toFixed()); + + const rawCandidateState: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); + const candidateState = rawCandidateState.unwrap(); + expect(candidateState.nominationCount.toString()).equal('1'); + expect(candidateState.lowestTopNominationAmount.toString()).equal(stake.toFixed()); + + const selfBond = new BigNumber(candidateState.bond.toString()); + const expectedStake = selfBond.plus(stake); + expect(candidateState.votingPower.toString()).equal(expectedStake.toFixed()); + + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap(); + expect(topNominations.nominations.length).equal(1); + expect(topNominations.nominations[0].owner.toString().toLowerCase()).equal(baltathar.address.toLowerCase()); + expect(topNominations.nominations[0].amount.toString()).equal(stake.toFixed()); + }); + + it('should successfully nominate to alith - charleth', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); // 1000 BFC + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(charleth); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + + expect(nominatorState.nominations).has.key(alith.address); + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorState.initialNominations[alith.address].toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(stake.toFixed()); + + const rawCandidateState: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); + const candidateState = rawCandidateState.unwrap(); + expect(candidateState.nominationCount.toString()).equal('2'); + expect(candidateState.lowestTopNominationAmount.toString()).equal(stake.toFixed()); + + const selfBond = new BigNumber(candidateState.bond.toString()); + const expectedStake = selfBond.plus(stake.multipliedBy(2)); // for baltathar and charleth + expect(candidateState.votingPower.toString()).equal(expectedStake.toFixed()); + + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap(); + expect(topNominations.nominations.length).equal(2); + expect(topNominations.nominations[0].owner.toString().toLowerCase()).equal(baltathar.address.toLowerCase()); + expect(topNominations.nominations[0].amount.toString()).equal(stake.toFixed()); + expect(topNominations.nominations[1].owner.toString().toLowerCase()).equal(charleth.address.toLowerCase()); + expect(topNominations.nominations[1].amount.toString()).equal(stake.toFixed()); + }); + + it('should fail due to calling nominate function twice', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 1, 1) + .signAndSend(baltathar); + + await context.createBlock(); + + const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'nominate'); + expect(extrinsicResult).equal('AlreadyNominatedCandidate'); + }); + + it('should fail due to calling nominatorBondMore before nominate', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .nominatorBondMore(alith.address, stake.toFixed()) + .signAndSend(dorothy); + + await context.createBlock(); + + const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'nominatorBondMore'); + expect(extrinsicResult).equal('NominatorDNE'); + }); + + it('should successfully bond more', async function () { + const more = new BigNumber(DEFAULT_STAKING_AMOUNT); // 1000 BFC + const stakeBefore = more; + + await context.polkadotApi.tx.bfcStaking + .nominatorBondMore(alith.address, more.toFixed()) + .signAndSend(baltathar); + + await context.createBlock(); + + const stakeAfter = more.multipliedBy(2); // we nominated twice with the same amount + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(baltathar.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + + expect(nominatorState.nominations).has.key(alith.address); + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(stakeAfter.toFixed()); + expect(new BigNumber(nominatorState.initialNominations[alith.address].toString()).toFixed()).equal(stakeBefore.toFixed()); + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(stakeAfter.toFixed()); + + const rawCandidateState: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); + const candidateState = rawCandidateState.unwrap(); + expect(candidateState.nominationCount.toString()).equal('2'); + expect(candidateState.lowestTopNominationAmount.toString()).equal(more.toFixed()); + + const selfBond = new BigNumber(candidateState.bond.toString()); + const expectedStake = selfBond.plus(more.multipliedBy(3)); // for baltathar (2) and charleth (1) + expect(candidateState.votingPower.toString()).equal(expectedStake.toFixed()); + + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap(); + expect(topNominations.nominations.length).equal(2); + expect(topNominations.nominations[0].owner.toString().toLowerCase()).equal(baltathar.address.toLowerCase()); + expect(topNominations.nominations[0].amount.toString()).equal(stakeAfter.toFixed()); + }); + + it('should successfully join bottom nominations', async function () { + const defaultStake = new BigNumber(DEFAULT_STAKING_AMOUNT); + const stake = new BigNumber(900).multipliedBy(10 ** 18); // 900 BFC + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(dorothy); + + await context.createBlock(); + + const rawCandidateState: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); + const candidateState = rawCandidateState.unwrap(); + expect(candidateState.nominationCount.toString()).equal('3'); + expect(candidateState.lowestTopNominationAmount.toString()).equal(defaultStake.toFixed()); + expect(candidateState.highestBottomNominationAmount.toString()).equal(stake.toFixed()); + expect(candidateState.lowestBottomNominationAmount.toString()).equal(stake.toFixed()); + + const selfBond = new BigNumber(candidateState.bond.toString()); + const expectedStake = selfBond.plus(defaultStake.multipliedBy(3)); // for baltathar (2) and charleth (1) + expect(candidateState.votingPower.toString()).equal(expectedStake.toFixed()); // voting power includes top only + + const rawCandidatePool: any = await context.polkadotApi.query.bfcStaking.candidatePool(); + const candidatePool = rawCandidatePool.toJSON(); + expect(new BigNumber(candidatePool[alith.address].toString()).toFixed()).equal(expectedStake.toFixed()); // CandidatePool includes top only + + const rawTotal: any = await context.polkadotApi.query.bfcStaking.total(); + const total = rawTotal.toJSON(); + expect(new BigNumber(total.toString()).toFixed()).equal(expectedStake.plus(stake).toFixed()); // Total includes both top and bottom + + const rawBottomNominations: any = await context.polkadotApi.query.bfcStaking.bottomNominations(alith.address); + const bottomNominations = rawBottomNominations.unwrap(); + expect(bottomNominations.nominations.length).equal(1); + expect(bottomNominations.nominations[0].owner.toString().toLowerCase()).equal(dorothy.address.toLowerCase()); + expect(bottomNominations.nominations[0].amount.toString()).equal(stake.toFixed()); + }); + + it('should successfully schedule nominator bond less', async function () { + const less = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(2); // 500 BFC + const stakeAfter = new BigNumber(1500).multipliedBy(10 ** 18); // 1500 BFC + + const rawCandidateStateBefore: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); + const candidateStateBefore = rawCandidateStateBefore.unwrap(); + const votingPowerBefore = new BigNumber(candidateStateBefore.votingPower.toString()); + + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(alith.address, less.toFixed()) + .signAndSend(baltathar); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(baltathar.address); + const nominatorRequests = rawNominatorState.unwrap().requests.toJSON(); + + const nominatorState = rawNominatorState.unwrap().toJSON(); + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(stakeAfter.toFixed()); + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(stakeAfter.toFixed()); + + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap(); + expect(topNominations.nominations[0].amount.toString()).equal(stakeAfter.toFixed()); + + const rawCandidateStateAfter: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); + const candidateStateAfter = rawCandidateStateAfter.unwrap(); + let votingPowerAfter = new BigNumber(candidateStateAfter.votingPower.toString()); + expect(votingPowerAfter.toFixed()).equal(votingPowerBefore.minus(less).toFixed()); + + const rawCandidatePool: any = await context.polkadotApi.query.bfcStaking.candidatePool(); + const candidatePool = rawCandidatePool.toJSON(); + expect(new BigNumber(candidatePool[alith.address].toString()).toFixed()).equal(votingPowerBefore.minus(less).toFixed()); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + expect(new BigNumber(nominatorRequests.lessTotal.toString()).toFixed()).equal(less.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].amount.toString()).toFixed()).equal(less.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(less.toFixed()); + expect(nominatorRequests.requests[alith.address].action).equal('Decrease'); + }); + + it('should fail to schedule decrease due to nomination in bottom', async function () { + const less = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(2); // 500 BFC + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(alith.address, less.toFixed()) + .signAndSend(dorothy); + + await context.createBlock(); + + const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'scheduleNominatorBondLess'); + expect(extrinsicResult).equal('CannotDecreaseWhenInvolvedInBottom'); + }); + + it('should fail to schedule decrease due to nomination below lowest top', async function () { + const less = new BigNumber(DEFAULT_STAKING_AMOUNT); // 1000 BFC + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(alith.address, less.toFixed()) + .signAndSend(baltathar); + + await context.createBlock(); + + const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'scheduleNominatorBondLess'); + expect(extrinsicResult).equal('CannotDecreaseLessThanHighestBottom'); + }); + + it('should successfully schedule nominator bond less multiple times - same round', async function () { + const prevLess = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(2); // 500 BFC + const less = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(10); // 100 BFC -> 1400 BFC + + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(alith.address, less.toFixed()) + .signAndSend(baltathar); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(baltathar.address); + const nominatorState = rawNominatorState.unwrap(); + const nominatorRequests = nominatorState.requests.toJSON(); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + // if requested multiple times in the same round, the amount should be cumulative + expect(new BigNumber(nominatorRequests.lessTotal.toString()).toFixed()).equal(less.plus(prevLess).toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].amount.toString()).toFixed()).equal(less.plus(prevLess).toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(less.plus(prevLess).toFixed()); + expect(nominatorRequests.requests[alith.address].action).equal('Decrease'); + }); + + it('should successfully schedule nominator bond less multiple times - different rounds', async function () { + const prevLess = new BigNumber(600).multipliedBy(10 ** 18); // 600 BFC + const less = new BigNumber(450).multipliedBy(10 ** 18); // 450 BFC -> 950 BFC + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + await jumpToRound(context, currentRound + 1); + + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(alith.address, less.toFixed()) + .signAndSend(baltathar); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(baltathar.address); + const nominatorState = rawNominatorState.unwrap(); + const nominatorRequests = nominatorState.requests.toJSON(); + + // if requested multiple times in different rounds, it is individually stored + expect(new BigNumber(nominatorRequests.lessTotal.toString()).toFixed()).equal(less.plus(prevLess).toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].amount.toString()).toFixed()).equal(less.plus(prevLess).toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].whenExecutable[currentRound + 2].toString()).toFixed()).equal(less.toFixed()); + expect(Object.keys(nominatorRequests.requests[alith.address].whenExecutable).length).equal(2); + expect(nominatorRequests.requests[alith.address].action).equal('Decrease'); + + // it should be moved to the lowest top nomination + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap(); + expect(topNominations.nominations.length).equal(2); + expect(topNominations.nominations[1].owner.toString().toLowerCase()).equal(baltathar.address.toLowerCase()); + + // it should be moved to the unstaking nominations + const rawUnstakingNominations: any = await context.polkadotApi.query.bfcStaking.unstakingNominations(alith.address); + const unstakingNominations = rawUnstakingNominations.unwrap().toJSON(); + expect(unstakingNominations.nominations.length).equal(1); + expect(unstakingNominations.nominations[0].owner.toString().toLowerCase()).equal(baltathar.address.toLowerCase()); + expect(new BigNumber(unstakingNominations.nominations[0].amount.toString()).toFixed()).equal(less.plus(prevLess).toFixed()); + }); + + it('should fail to execute nominator bond less due to unknown when', async function () { + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + const when = currentRound + 10; + + await context.polkadotApi.tx.bfcStaking + .executeNominationRequest(alith.address, when) + .signAndSend(baltathar); + + await context.createBlock(); + + const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'executeNominationRequest'); + expect(extrinsicResult).equal('PendingNominationRequestDNE'); + }); + + it('should fail to execute nominator bond less due still pending', async function () { + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + const when = currentRound + 1; // round 3 + + await context.polkadotApi.tx.bfcStaking + .executeNominationRequest(alith.address, when) + .signAndSend(baltathar); + + await context.createBlock(); + + const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'executeNominationRequest'); + expect(extrinsicResult).equal('PendingNominationRequestNotDueYet'); + }); + + it('should successfully execute nominator bond less', async function () { + const reserved = new BigNumber(DEFAULT_STAKING_AMOUNT).multipliedBy(2); + const orderAmount = new BigNumber(600).multipliedBy(10 ** 18); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + const when = currentRound; // round 2 + + const accountBefore = await context.polkadotApi.query.system.account(baltathar.address); + expect(accountBefore['data'].reserved.toString()).equal(reserved.toFixed()); + + await context.polkadotApi.tx.bfcStaking + .executeNominationRequest(alith.address, when) + .signAndSend(baltathar); + + await context.createBlock(); + + const accountAfter = await context.polkadotApi.query.system.account(baltathar.address); + expect(accountAfter['data'].reserved.toString()).equal(reserved.minus(orderAmount).toFixed()); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(baltathar.address); + const nominatorState = rawNominatorState.unwrap(); + const nominatorRequests = nominatorState.requests.toJSON(); + + const lessTotal = new BigNumber(450).multipliedBy(10 ** 18); // 450 BFC + expect(new BigNumber(nominatorRequests.lessTotal.toString()).toFixed()).equal(lessTotal.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].amount.toString()).toFixed()).equal(lessTotal.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(lessTotal.toFixed()); + expect(Object.keys(nominatorRequests.requests[alith.address].whenExecutable).length).equal(1); + }); + + it('should successfully kick out lowest bottom nomination - dorothy kicked out by ethan', async function () { + const defaultStake = new BigNumber(DEFAULT_STAKING_AMOUNT); + const stake = defaultStake.multipliedBy(2); // make sure it goes to top + + const accountBefore = await context.polkadotApi.query.system.account(dorothy.address); + expect(accountBefore['data'].reserved.toString()).equal(new BigNumber(900).multipliedBy(10 ** 18).toFixed()); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + // ethan joins top + // baltathar moves to bottom + // dorothy is kicked out + + // the reserved stake will be immediately returned - including any other pending requests + const accountAfter = await context.polkadotApi.query.system.account(dorothy.address); + expect(accountAfter['data'].reserved.toString()).equal(new BigNumber(0).toFixed()); + + // nominator state should be deleted + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(dorothy.address); + const nominatorState = rawNominatorState.toJSON(); + expect(nominatorState).is.null; + + // top nominations should be updated - ethan joined, dorothy moved to bottom + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap().toJSON(); + expect(topNominations.nominations[0].owner.toLowerCase()).equal(ethan.address.toLowerCase()); + expect(topNominations.nominations[1].owner.toLowerCase()).equal(charleth.address.toLowerCase()); + expect(topNominations.nominations.length).equal(2); + expect(new BigNumber(topNominations.total.toString()).toFixed()).equal(stake.plus(defaultStake).toFixed()); + + // bottom nominations should be updated - baltathar moved to bottom, dorothy kicked out + const rawBottomNominations: any = await context.polkadotApi.query.bfcStaking.bottomNominations(alith.address); + const bottomNominations = rawBottomNominations.unwrap().toJSON(); + expect(bottomNominations.nominations[0].owner.toLowerCase()).equal(baltathar.address.toLowerCase()); + expect(bottomNominations.nominations.length).equal(1); + expect(new BigNumber(bottomNominations.total.toString()).toFixed()).equal(new BigNumber(950).multipliedBy(10 ** 18).toFixed()); + }); + + it('should successfully receive round rewards while decreasing stake', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT).multipliedBy(2); // 2000 BFC + const less = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(10); // 100 BFC + + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(alith.address, less.toFixed()) + .signAndSend(ethan); + + const accountBefore = await context.polkadotApi.query.system.account(ethan.address); + expect(accountBefore['data'].reserved.toString()).equal(stake.toFixed()); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + // we jump two rounds to ensure the nomination is included in the next payout + await jumpToRound(context, currentRound + 2); + + const accountAfter = await context.polkadotApi.query.system.account(ethan.address); + // the reserved stake should have increased due to the round rewards + expect(new BigNumber(accountAfter['data'].reserved.toString()).gt(new BigNumber(accountBefore['data'].reserved.toString()))).is.true; + }); + + it('should successfully cancel nomination request - decrease (ethan)', async function () { + const less = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(10); // 100 BFC + + const rawNominatorStateBefore: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorStateBefore = rawNominatorStateBefore.unwrap().toJSON(); + const nominatorRequestsBefore = rawNominatorStateBefore.unwrap().requests.toJSON(); + const stakeBefore = new BigNumber(nominatorStateBefore.nominations[alith.address].toString()); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = rawTotalBefore.toJSON(); + + const rawTopNominationsBefore: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominationsBefore = rawTopNominationsBefore.unwrap().toJSON(); + + const when = parseInt(Object.keys(nominatorRequestsBefore.requests[alith.address].whenExecutable)[0]); + + await context.polkadotApi.tx.bfcStaking + .cancelNominationRequest(alith.address, when) + .signAndSend(ethan); + + await context.createBlock(); + + // nominator state should be rollbacked + const rawNominatorStateAfter: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorStateAfter = rawNominatorStateAfter.unwrap().toJSON(); + const nominatorRequestsAfter = rawNominatorStateAfter.unwrap().requests.toJSON(); + expect(nominatorRequestsAfter.requests).is.empty; + expect(new BigNumber(nominatorStateAfter.nominations[alith.address].toString()).toFixed()).equal(stakeBefore.plus(less).toFixed()); + expect(new BigNumber(nominatorStateAfter.total.toString()).toFixed()).equal(stakeBefore.plus(less).toFixed()); + + // total should be rollbacked + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = rawTotalAfter.toJSON(); + expect(new BigNumber(totalAfter.toString()).toFixed()).equal(new BigNumber(totalBefore.toString()).plus(less).toFixed()); + + // top nominations should be rollbacked + const rawTopNominationsAfter: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominationsAfter = rawTopNominationsAfter.unwrap().toJSON(); + expect(new BigNumber(topNominationsAfter.total.toString()).toFixed()).equal(new BigNumber(topNominationsBefore.total.toString()).plus(less).toFixed()); + expect(new BigNumber(topNominationsAfter.nominations[0].amount.toString()).toFixed()).equal(new BigNumber(topNominationsBefore.nominations[0].amount.toString()).plus(less).toFixed()); + }); + + it('should successfully schedule a revoke request', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + const rawNominatorStateBefore: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorStateBefore = rawNominatorStateBefore.unwrap(); + const revoke = new BigNumber(nominatorStateBefore.total.toString()); + + // we first add a new candidate to ensure the nominator can request a revoke + await context.polkadotApi.tx.bfcStaking + .joinCandidates(faith.address, null, stake.toFixed(), 1) + .signAndSend(faithStash); + + await context.createBlock(); + + // nominate faith to ensure ethan has a nomination + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = rawTotalBefore.toJSON(); + + // now we can schedule a revoke request + await context.polkadotApi.tx.bfcStaking + .scheduleRevokeNomination(alith.address) + .signAndSend(ethan); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + const nominatorRequests = rawNominatorState.unwrap().requests.toJSON(); + // nomination should be set to zero + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(new BigNumber(0).toFixed()); + // total nomination should be decreased + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(stake.toFixed()); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + // a revoke request should be scheduled + expect(new BigNumber(nominatorRequests.lessTotal.toString()).toFixed()).equal(revoke.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].amount.toString()).toFixed()).equal(revoke.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(revoke.toFixed()); + expect(Object.keys(nominatorRequests.requests[alith.address].whenExecutable).length).equal(1); + expect(nominatorRequests.requests[alith.address].action).equal('Revoke'); + + // ethan removed + // baltathar moved to top (with charleth) + // empty bottom + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap().toJSON(); + expect(topNominations.nominations.length).equal(2); + expect(topNominations.nominations[0].owner.toString().toLowerCase()).equal(charleth.address.toLowerCase()); + expect(topNominations.nominations[1].owner.toString().toLowerCase()).equal(baltathar.address.toLowerCase()); + + const rawBottomNominations: any = await context.polkadotApi.query.bfcStaking.bottomNominations(alith.address); + const bottomNominations = rawBottomNominations.unwrap().toJSON(); + expect(bottomNominations.nominations.length).equal(0); + + // it should be moved to unstaking nominations + const rawUnstakingNominations: any = await context.polkadotApi.query.bfcStaking.unstakingNominations(alith.address); + const unstakingNominations = rawUnstakingNominations.unwrap().toJSON(); + expect(unstakingNominations.nominations.length).equal(2); + expect(unstakingNominations.nominations[0].owner.toString().toLowerCase()).equal(ethan.address.toLowerCase()); + expect(new BigNumber(unstakingNominations.nominations[0].amount.toString()).toFixed()).equal(revoke.toFixed()); + + // total should be decreased + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = rawTotalAfter.toJSON(); + expect(new BigNumber(totalAfter.toString()).toFixed()).equal(new BigNumber(totalBefore.toString()).minus(revoke).toFixed()); + }); + + it('should successfully cancel a revoke request', async function () { + const rawNominatorStateBefore: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorStateBefore = rawNominatorStateBefore.unwrap().toJSON(); + const nominatorRequestsBefore = rawNominatorStateBefore.unwrap().requests.toJSON(); + const stakeBefore = new BigNumber(nominatorStateBefore.nominations[alith.address].toString()); + const totalNominationBefore = new BigNumber(nominatorStateBefore.total.toString()); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = new BigNumber(rawTotalBefore.toJSON().toString()); + + const when = parseInt(Object.keys(nominatorRequestsBefore.requests[alith.address].whenExecutable)[0]); + + await context.polkadotApi.tx.bfcStaking + .cancelNominationRequest(alith.address, when) + .signAndSend(ethan); + + await context.createBlock(); + + // ethan added to top (with charleth) + // baltathar moved to bottom + + // top nominations should be rollbacked + const rawTopNominationsAfter: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominationsAfter = rawTopNominationsAfter.unwrap().toJSON(); + expect(topNominationsAfter.nominations.length).equal(2); + expect(topNominationsAfter.nominations[0].owner.toString().toLowerCase()).equal(ethan.address.toLowerCase()); + expect(topNominationsAfter.nominations[1].owner.toString().toLowerCase()).equal(charleth.address.toLowerCase()); + const cancelled = new BigNumber(topNominationsAfter.nominations[0].amount.toString()); + const lowestTopNomination = new BigNumber(topNominationsAfter.nominations[1].amount.toString()); + + // bottom nominations should be rollbacked + const rawBottomNominationsAfter: any = await context.polkadotApi.query.bfcStaking.bottomNominations(alith.address); + const bottomNominationsAfter = rawBottomNominationsAfter.unwrap().toJSON(); + expect(bottomNominationsAfter.nominations.length).equal(1); + expect(bottomNominationsAfter.nominations[0].owner.toString().toLowerCase()).equal(baltathar.address.toLowerCase()); + expect(new BigNumber(bottomNominationsAfter.total.toString()).toFixed()).equal(new BigNumber(bottomNominationsAfter.nominations[0].amount.toString()).toFixed()); + const highestBottomNomination = new BigNumber(bottomNominationsAfter.nominations[0].amount.toString()); + + // nominator state should be rollbacked + const rawNominatorStateAfter: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorStateAfter = rawNominatorStateAfter.unwrap().toJSON(); + const nominatorRequestsAfter = rawNominatorStateAfter.unwrap().requests.toJSON(); + expect(nominatorRequestsAfter.requests).is.empty; + expect(new BigNumber(nominatorStateAfter.nominations[alith.address].toString()).toFixed()).equal(stakeBefore.plus(cancelled).toFixed()); + expect(new BigNumber(nominatorStateAfter.total.toString()).toFixed()).equal(totalNominationBefore.plus(cancelled).toFixed()); + + // total should be rollbacked + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = rawTotalAfter.toJSON(); + expect(new BigNumber(totalAfter.toString()).toFixed()).equal(totalBefore.plus(cancelled).toFixed()); + + // candidate state should be rollbacked + const rawCandidateStateAfter: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); + const candidateStateAfter = rawCandidateStateAfter.unwrap(); + const selfBondAfter = new BigNumber(candidateStateAfter.bond.toString()); + const votingPowerAfter = new BigNumber(candidateStateAfter.votingPower.toString()); + // voting power is the sum of self bond and top nominations + expect(votingPowerAfter.toFixed()).equal(cancelled.plus(lowestTopNomination).plus(selfBondAfter).toFixed()); + expect(new BigNumber(candidateStateAfter.lowestTopNominationAmount.toString()).toFixed()).equal(lowestTopNomination.toFixed()); + expect(new BigNumber(candidateStateAfter.highestBottomNominationAmount.toString()).toFixed()).equal(highestBottomNomination.toFixed()); + }); + + it('should successfully execute a revoke request', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .scheduleRevokeNomination(alith.address) + .signAndSend(ethan); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorRequests = rawNominatorState.unwrap().requests.toJSON(); + const when = parseInt(Object.keys(nominatorRequests.requests[alith.address].whenExecutable)[0]); + const revoked = new BigNumber(nominatorRequests.requests[alith.address].amount.toString()); + + const accountBefore = await context.polkadotApi.query.system.account(ethan.address); + expect(accountBefore['data'].reserved.toString()).equal(revoked.plus(stake).toFixed()); + + await jumpToRound(context, when); + + await context.polkadotApi.tx.bfcStaking + .executeNominationRequest(alith.address, when) + .signAndSend(ethan); + + await context.createBlock(); + + // the reserved stake should be returned + const accountAfter = await context.polkadotApi.query.system.account(ethan.address); + expect(accountAfter['data'].reserved.toString()).equal(new BigNumber(stake).toFixed()); + + // the nomination should be revoked + const rawNominatorStateAfter: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorStateAfter = rawNominatorStateAfter.unwrap().toJSON(); + expect(nominatorStateAfter.nominations[alith.address]).is.undefined; + }); + + it('should successfully schedule a leave request', async function () { + const rawNominatorStateBefore: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorStateBefore = rawNominatorStateBefore.unwrap(); + const stake = new BigNumber(nominatorStateBefore.total.toString()); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = rawTotalBefore.toJSON(); + + const rawCandidateStateBefore: any = await context.polkadotApi.query.bfcStaking.candidateInfo(faith.address); + const candidateStateBefore = rawCandidateStateBefore.unwrap(); + const votingPowerBefore = new BigNumber(candidateStateBefore.votingPower.toString()); + + await context.polkadotApi.tx.bfcStaking + .scheduleLeaveNominators() + .signAndSend(ethan); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + const nominatorRequests = rawNominatorState.unwrap().requests.toJSON(); + // nomination should be set to zero + expect(new BigNumber(nominatorState.nominations[faith.address].toString()).toFixed()).equal(new BigNumber(0).toFixed()); + // total nomination should be set to zero + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(new BigNumber(0).toFixed()); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + // a leave request should be scheduled + expect(new BigNumber(nominatorRequests.lessTotal.toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorRequests.requests[faith.address].amount.toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorRequests.requests[faith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(stake.toFixed()); + expect(Object.keys(nominatorRequests.requests[faith.address].whenExecutable).length).equal(1); + expect(nominatorRequests.requests[faith.address].action).equal('Leave'); + + // ethan removed + // empty top + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(faith.address); + const topNominations = rawTopNominations.unwrap().toJSON(); + expect(topNominations.nominations.length).equal(0); + + // it should be moved to unstaking nominations + const rawUnstakingNominations: any = await context.polkadotApi.query.bfcStaking.unstakingNominations(faith.address); + const unstakingNominations = rawUnstakingNominations.unwrap().toJSON(); + expect(unstakingNominations.nominations.length).equal(1); + expect(unstakingNominations.nominations[0].owner.toString().toLowerCase()).equal(ethan.address.toLowerCase()); + expect(new BigNumber(unstakingNominations.nominations[0].amount.toString()).toFixed()).equal(stake.toFixed()); + + // total should be decreased + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = rawTotalAfter.toJSON(); + expect(new BigNumber(totalAfter.toString()).toFixed()).equal(new BigNumber(totalBefore.toString()).minus(stake).toFixed()); + + // candidate state should be updated + const rawCandidateStateAfter: any = await context.polkadotApi.query.bfcStaking.candidateInfo(faith.address); + const candidateStateAfter = rawCandidateStateAfter.unwrap(); + let votingPowerAfter = new BigNumber(candidateStateAfter.votingPower.toString()); + expect(votingPowerAfter.toFixed()).equal(votingPowerBefore.minus(stake).toFixed()); + expect(new BigNumber(candidateStateAfter.lowestTopNominationAmount.toString()).toFixed()).equal(new BigNumber(0).toFixed()); + + // candidate pool should be updated + const rawCandidatePool: any = await context.polkadotApi.query.bfcStaking.candidatePool(); + const candidatePool = rawCandidatePool.toJSON(); + expect(new BigNumber(candidatePool[faith.address].toString()).toFixed()).equal(votingPowerAfter.toFixed()); + }); + + it('should successfully bond more when leaving', async function () { + const rawNominatorStateBefore: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorRequestsBefore = rawNominatorStateBefore.unwrap().requests.toJSON(); + const stakeBefore = new BigNumber(nominatorRequestsBefore.lessTotal.toString()); + + const more = new BigNumber(DEFAULT_STAKING_AMOUNT); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = new BigNumber(rawTotalBefore.toJSON().toString()); + + await context.polkadotApi.tx.bfcStaking + .nominatorBondMore(faith.address, more.toFixed()) + .signAndSend(ethan); + + await context.createBlock(); + + const rawNominatorStateAfter: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorStateAfter = rawNominatorStateAfter.unwrap().toJSON(); + const nominatorRequestsAfter = rawNominatorStateAfter.unwrap().requests.toJSON(); + const stakeAfter = new BigNumber(nominatorStateAfter.nominations[faith.address].toString()); + expect(stakeAfter.toFixed()).equal(stakeBefore.plus(more).toFixed()); // the nomination should be increased (with the more amount) + expect(nominatorRequestsAfter.requests).is.empty; // the request should be cancelled + + // total should be increased (with cancelled amount) + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = new BigNumber(rawTotalAfter.toJSON().toString()); + expect(totalAfter.toFixed()).equal(totalBefore.plus(more).plus(stakeBefore).toFixed()); + }); + + it('should successfully execute a leave request', async function () { + const rawNominatorStateBefore: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorStateBefore = rawNominatorStateBefore.unwrap().toJSON(); + const reservedBefore = new BigNumber(nominatorStateBefore.total.toString()); + + const accountBefore = await context.polkadotApi.query.system.account(ethan.address); + expect(accountBefore['data'].reserved.toString()).equal(reservedBefore.toFixed()); + + await context.polkadotApi.tx.bfcStaking + .scheduleLeaveNominators() + .signAndSend(ethan); + + await context.createBlock(); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + await jumpToRound(context, currentRound + 1); + + await context.polkadotApi.tx.bfcStaking + .executeLeaveNominators(10) + .signAndSend(ethan); + + await context.createBlock(); + + // ethan removed + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorState = rawNominatorState.toJSON(); + expect(nominatorState).is.null; + + // the reserved stake should be returned + const accountAfter = await context.polkadotApi.query.system.account(ethan.address); + expect(accountAfter['data'].reserved.toString()).equal(new BigNumber(0).toFixed()); + }); +}); + +describeDevNode('pallet_bfc_staking - candidate leave (while decreasing. last nomination)', (context) => { + const keyring = new Keyring({ type: 'ethereum' }); + const ethan = keyring.addFromUri(TEST_CONTROLLERS[4].private); + + const faith = keyring.addFromUri(TEST_CONTROLLERS[5].private); + const faithStash = keyring.addFromUri(TEST_STASHES[5].private); + + before('should successfully join candidate pool', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + const less = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(10); // 100 BFC + + await context.polkadotApi.tx.bfcStaking + .joinCandidates(faith.address, null, stake.toFixed(), 1) + .signAndSend(faithStash); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(faith.address, less.toFixed()) + .signAndSend(ethan); + + await context.createBlock(); + + const account = await context.polkadotApi.query.system.account(ethan.address); + expect(account['data'].reserved.toString()).equal(stake.toFixed()); + }); + + it('should successfully schedule a candidate leave request', async function () { + await context.polkadotApi.tx.bfcStaking + .scheduleLeaveCandidates(10) + .signAndSend(faith); + + await context.createBlock(); + + const rawCandidateState: any = await context.polkadotApi.query.bfcStaking.candidateInfo(faith.address); + const candidateState = rawCandidateState.unwrap().toJSON(); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + expect(candidateState.status.leaving).equal(currentRound + 1); + }); + + it('should successfully execute a candidate leave request', async function () { + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + await jumpToRound(context, currentRound + 1); + + const rawNominatorStateBefore: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorStateBefore = rawNominatorStateBefore.unwrap().toJSON(); + const stakeBefore = new BigNumber(nominatorStateBefore.nominations[faith.address].toString()); + + const accountBefore = await context.polkadotApi.query.system.account(ethan.address); + expect(accountBefore['data'].reserved.toString()).equal(new BigNumber(DEFAULT_STAKING_AMOUNT).toFixed()); + + const rawCandidateStateBefore: any = await context.polkadotApi.query.bfcStaking.candidateInfo(faith.address); + const candidateStateBefore = rawCandidateStateBefore.unwrap(); + const selfBondBefore = new BigNumber(candidateStateBefore.bond.toString()); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = new BigNumber(rawTotalBefore.toJSON().toString()); + + await context.polkadotApi.tx.bfcStaking + .executeLeaveCandidates(10) + .signAndSend(faithStash); + + await context.createBlock(); + + const rawCandidateStateAfter: any = await context.polkadotApi.query.bfcStaking.candidateInfo(faith.address); + const candidateStateAfter = rawCandidateStateAfter.toJSON(); + expect(candidateStateAfter).is.null; + + // the reserved stake should be returned + const accountAfter = await context.polkadotApi.query.system.account(ethan.address); + expect(accountAfter['data'].reserved.toString()).equal(new BigNumber(0).toFixed()); + + // nominator state should be removed (last nomination) + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorState = rawNominatorState.toJSON(); + expect(nominatorState).is.null; + + // total should be decreased + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = new BigNumber(rawTotalAfter.toJSON().toString()); + expect(totalAfter.toFixed()).equal(totalBefore.minus(stakeBefore).minus(selfBondBefore).toFixed()); + }); +}); + +describeDevNode('pallet_bfc_staking - candidate leave (while decreasing. not last nomination)', (context) => { + const keyring = new Keyring({ type: 'ethereum' }); + const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); + const ethan = keyring.addFromUri(TEST_CONTROLLERS[4].private); + + const faith = keyring.addFromUri(TEST_CONTROLLERS[5].private); + const faithStash = keyring.addFromUri(TEST_STASHES[5].private); + + before('should successfully join candidate pool', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + const less = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(10); // 100 BFC + + await context.polkadotApi.tx.bfcStaking + .joinCandidates(faith.address, null, stake.toFixed(), 1) + .signAndSend(faithStash); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(faith.address, less.toFixed()) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleLeaveCandidates(10) + .signAndSend(faith); + + await context.createBlock(); + }); + + it('should successfully execute a candidate leave request', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + const less = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(10); // 100 BFC + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + await jumpToRound(context, currentRound + 1); + + const accountBefore = await context.polkadotApi.query.system.account(ethan.address); + expect(accountBefore['data'].reserved.toString()).equal(stake.multipliedBy(2).toFixed()); // faith + alith + + const rawCandidateStateBefore: any = await context.polkadotApi.query.bfcStaking.candidateInfo(faith.address); + const candidateStateBefore = rawCandidateStateBefore.unwrap(); + const selfBondBefore = new BigNumber(candidateStateBefore.bond.toString()); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = new BigNumber(rawTotalBefore.toJSON().toString()); + + await context.polkadotApi.tx.bfcStaking + .executeLeaveCandidates(10) + .signAndSend(faithStash); + + await context.createBlock(); + + // the reserved stake should be returned + const accountAfter = await context.polkadotApi.query.system.account(ethan.address); + expect(accountAfter['data'].reserved.toString()).equal(stake.toFixed()); + + // nominator state should be updated + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + expect(Object.keys(nominatorState.nominations).length).equal(1); + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(stake.toFixed()); + + // total should be decreased + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = new BigNumber(rawTotalAfter.toJSON().toString()); + expect(totalAfter.toFixed()).equal(totalBefore.minus(selfBondBefore).minus(stake.minus(less)).toFixed()); + }); +}); + +describeDevNode('pallet_bfc_staking - candidate leave (while revoking)', (context) => { + const keyring = new Keyring({ type: 'ethereum' }); + const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); + const dorothy = keyring.addFromUri(TEST_CONTROLLERS[3].private); + const ethan = keyring.addFromUri(TEST_CONTROLLERS[4].private); + + const faith = keyring.addFromUri(TEST_CONTROLLERS[5].private); + const faithStash = keyring.addFromUri(TEST_STASHES[5].private); + + before('should successfully join candidate pool', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .joinCandidates(faith.address, null, stake.toFixed(), 1) + .signAndSend(faithStash); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.multipliedBy(2).toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(dorothy); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleRevokeNomination(faith.address) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleLeaveCandidates(10) + .signAndSend(faith); + + await context.createBlock(); + }); + + it('should successfully execute a candidate leave request', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + await jumpToRound(context, currentRound + 1); + + const accountBefore = await context.polkadotApi.query.system.account(ethan.address); + expect(accountBefore['data'].reserved.toString()).equal(stake.multipliedBy(2).plus(stake).toFixed()); // faith + alith + + const rawCandidateStateBefore: any = await context.polkadotApi.query.bfcStaking.candidateInfo(faith.address); + const candidateStateBefore = rawCandidateStateBefore.unwrap(); + const selfBondBefore = new BigNumber(candidateStateBefore.bond.toString()); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = new BigNumber(rawTotalBefore.toJSON().toString()); + + await context.polkadotApi.tx.bfcStaking + .executeLeaveCandidates(10) + .signAndSend(faithStash); + + await context.createBlock(); + + // the reserved stake should be returned + const accountAfter = await context.polkadotApi.query.system.account(ethan.address); + expect(accountAfter['data'].reserved.toString()).equal(stake.toFixed()); // alith + + // nominator state should be updated + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + expect(Object.keys(nominatorState.nominations).length).equal(1); + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(stake.toFixed()); + + // total should be decreased + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = new BigNumber(rawTotalAfter.toJSON().toString()); + expect(totalAfter.toFixed()).equal(totalBefore.minus(selfBondBefore).minus(stake).toFixed()); // decrease dorothy stake + }); +}); + +describeDevNode('pallet_bfc_staking - candidate leave (while leaving)', (context) => { + const keyring = new Keyring({ type: 'ethereum' }); + const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); + const dorothy = keyring.addFromUri(TEST_CONTROLLERS[3].private); + const ethan = keyring.addFromUri(TEST_CONTROLLERS[4].private); + + const faith = keyring.addFromUri(TEST_CONTROLLERS[5].private); + const faithStash = keyring.addFromUri(TEST_STASHES[5].private); + + before('should successfully join candidate pool', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + // default Total = 1000 BFC + + await context.polkadotApi.tx.bfcStaking + .joinCandidates(faith.address, null, stake.toFixed(), 1) // Total += 1000 BFC + .signAndSend(faithStash); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.multipliedBy(2).toFixed(), 10, 10) // Total += 2000 BFC + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) // Total += 1000 BFC + .signAndSend(dorothy); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) // Total += 1000 BFC + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleLeaveNominators() // Total -= (2000 + 1000) BFC + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleLeaveCandidates(10) + .signAndSend(faith); + + await context.createBlock(); + }); + + it('should successfully execute a candidate leave request', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + await jumpToRound(context, currentRound + 1); + + const accountBefore = await context.polkadotApi.query.system.account(ethan.address); + expect(accountBefore['data'].reserved.toString()).equal(stake.multipliedBy(2).plus(stake).toFixed()); // faith + alith + + const rawCandidateStateBefore: any = await context.polkadotApi.query.bfcStaking.candidateInfo(faith.address); + const candidateStateBefore = rawCandidateStateBefore.unwrap(); + const selfBondBefore = new BigNumber(candidateStateBefore.bond.toString()); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = new BigNumber(rawTotalBefore.toJSON().toString()); + + await context.polkadotApi.tx.bfcStaking + .executeLeaveCandidates(10) + .signAndSend(faithStash); + + await context.createBlock(); + + // the reserved stake should be returned + const accountAfter = await context.polkadotApi.query.system.account(ethan.address); + expect(accountAfter['data'].reserved.toString()).equal(stake.toFixed()); // alith + + // nominator state should be updated + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + const nominatorRequests = rawNominatorState.unwrap().requests.toJSON(); + expect(Object.keys(nominatorState.nominations).length).equal(1); // leave request for alith should be remained + expect(Object.keys(nominatorRequests.requests).length).equal(1); + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(new BigNumber(0).toFixed()); + expect(new BigNumber(nominatorRequests.lessTotal.toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].amount.toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(stake.toFixed()); + expect(Object.keys(nominatorRequests.requests[alith.address].whenExecutable).length).equal(1); + expect(nominatorRequests.requests[alith.address].action).equal('Leave'); + + // total should be decreased + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = new BigNumber(rawTotalAfter.toJSON().toString()); + expect(totalAfter.toFixed()).equal(totalBefore.minus(selfBondBefore).minus(stake).toFixed()); // decrease faith self-bond + dorothy stake + }); +}); + +describeDevNode('pallet_bfc_staking - cancel failures (revoke)', (context) => { + const keyring = new Keyring({ type: 'ethereum' }); + const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); + const baltathar = keyring.addFromUri(TEST_CONTROLLERS[1].private); + const charleth = keyring.addFromUri(TEST_CONTROLLERS[2].private); + const dorothy = keyring.addFromUri(TEST_CONTROLLERS[3].private); + const ethan = keyring.addFromUri(TEST_CONTROLLERS[4].private); + + const faith = keyring.addFromUri(TEST_CONTROLLERS[5].private); + const faithStash = keyring.addFromUri(TEST_STASHES[5].private); + + before('should successfully join candidate pool', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + const lowStake = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(10); // 100 BFC + + // default Total = 1000 BFC + + await context.polkadotApi.tx.bfcStaking + .joinCandidates(faith.address, null, stake.toFixed(), 1) + .signAndSend(faithStash); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, lowStake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(dorothy); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(charleth); + + await context.createBlock(); + }); + + it('should fail to cancel revoke request due to below lowest bottom nomination', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .scheduleRevokeNomination(faith.address) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(baltathar); + + await context.createBlock(); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + await context.polkadotApi.tx.bfcStaking + .cancelNominationRequest(faith.address, currentRound + 1) + .signAndSend(ethan); + + await context.createBlock(); + + const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'cancelNominationRequest'); + expect(extrinsicResult).equal('CannotNominateLessThanLowestBottomWhenBottomIsFull'); + }); +}); + +describeDevNode('pallet_bfc_staking - cancel failures (leave)', (context) => { + const keyring = new Keyring({ type: 'ethereum' }); + const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); + const baltathar = keyring.addFromUri(TEST_CONTROLLERS[1].private); + const charleth = keyring.addFromUri(TEST_CONTROLLERS[2].private); + const dorothy = keyring.addFromUri(TEST_CONTROLLERS[3].private); + const ethan = keyring.addFromUri(TEST_CONTROLLERS[4].private); + + const faith = keyring.addFromUri(TEST_CONTROLLERS[5].private); + const faithStash = keyring.addFromUri(TEST_STASHES[5].private); + + before('should successfully join candidate pool', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + const lowStake = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(10); // 100 BFC + + // default Total = 1000 BFC + + await context.polkadotApi.tx.bfcStaking + .joinCandidates(faith.address, null, stake.toFixed(), 1) + .signAndSend(faithStash); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, lowStake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(dorothy); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(charleth); + + await context.createBlock(); + }); + + it('should fail to cancel leave request due to below lowest bottom nomination', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .scheduleLeaveNominators() + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(baltathar); + + await context.createBlock(); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + await context.polkadotApi.tx.bfcStaking + .cancelNominationRequest(faith.address, currentRound + 1) + .signAndSend(ethan); + + await context.createBlock(); + + const extrinsicResult = await getExtrinsicResult(context, 'bfcStaking', 'cancelNominationRequest'); + expect(extrinsicResult).equal('CannotNominateLessThanLowestBottomWhenBottomIsFull'); + }); +}); + +describeDevNode('pallet_bfc_staking - bond more with existing pending requests', (context) => { + const keyring = new Keyring({ type: 'ethereum' }); + const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); + // const baltathar = keyring.addFromUri(TEST_CONTROLLERS[1].private); + const charleth = keyring.addFromUri(TEST_CONTROLLERS[2].private); + const dorothy = keyring.addFromUri(TEST_CONTROLLERS[3].private); + const ethan = keyring.addFromUri(TEST_CONTROLLERS[4].private); + + const faith = keyring.addFromUri(TEST_CONTROLLERS[5].private); + const faithStash = keyring.addFromUri(TEST_STASHES[5].private); + + before('should successfully join candidate pool', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + }); + + it('should successfully bond more with existing decrease request', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + const less = stake.dividedBy(10); // 100 BFC + const more = less; + + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(alith.address, less.toFixed()) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominatorBondMore(alith.address, more.toFixed()) + .signAndSend(ethan); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + const nominatorRequests = rawNominatorState.unwrap().requests.toJSON(); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + // the decrease request should be remained + expect(Object.keys(nominatorRequests.requests).length).equal(1); + expect(nominatorRequests.requests[alith.address].action).equal('Decrease'); + expect(new BigNumber(nominatorRequests.requests[alith.address].amount.toString()).toFixed()).equal(less.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(less.toFixed()); + expect(Object.keys(nominatorRequests.requests[alith.address].whenExecutable).length).equal(1); + + // nominator state should be updated + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(stake.toFixed()); + + // candidate state should be updated + const rawCandidateState: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); + const candidateState = rawCandidateState.unwrap(); + expect(candidateState.nominationCount.toString()).equal('1'); + expect(candidateState.lowestTopNominationAmount.toString()).equal(stake.toFixed()); + + const selfBond = new BigNumber(candidateState.bond.toString()); + const expectedStake = selfBond.plus(stake); + expect(candidateState.votingPower.toString()).equal(expectedStake.toFixed()); + + // top nominations should be updated + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap(); + expect(topNominations.nominations.length).equal(1); + expect(topNominations.nominations[0].owner.toString().toLowerCase()).equal(ethan.address.toLowerCase()); + expect(topNominations.nominations[0].amount.toString()).equal(stake.toFixed()); + }); + + it('should successfully bond more with existing revoke request', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .joinCandidates(faith.address, null, stake.toFixed(), 1) + .signAndSend(faithStash); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(dorothy); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(dorothy); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleRevokeNomination(alith.address) + .signAndSend(dorothy); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominatorBondMore(alith.address, stake.toFixed()) + .signAndSend(dorothy); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(dorothy.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + const nominatorRequests = rawNominatorState.unwrap().requests.toJSON(); + + // the revoke request should be cancelled + expect(Object.keys(nominatorRequests.requests).length).equal(0); + + // nominator state should be updated (bond more + cancelled amount) + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(stake.plus(stake).toFixed()); + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(stake.plus(stake.multipliedBy(2)).toFixed()); + + // candidate state should be updated + const rawCandidateState: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); + const candidateState = rawCandidateState.unwrap(); + expect(candidateState.nominationCount.toString()).equal('2'); + expect(candidateState.lowestTopNominationAmount.toString()).equal(stake.toFixed()); + + const selfBond = new BigNumber(candidateState.bond.toString()); + const expectedStake = selfBond.plus(stake.plus(stake.multipliedBy(2))); + expect(candidateState.votingPower.toString()).equal(expectedStake.toFixed()); + + // top nominations should be updated + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap(); + expect(topNominations.nominations.length).equal(2); + expect(topNominations.nominations[0].owner.toString().toLowerCase()).equal(dorothy.address.toLowerCase()); + expect(topNominations.nominations[0].amount.toString()).equal(stake.plus(stake).toFixed()); + }); + + it('should successfully bond more with existing leave request', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(charleth); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(charleth); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleLeaveNominators() + .signAndSend(charleth); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominatorBondMore(alith.address, stake.toFixed()) + .signAndSend(charleth); + + await context.createBlock(); + + // the leave request should be cancelled + // the remaining request should be switched to Revoke + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(charleth.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + const nominatorRequests = rawNominatorState.unwrap().requests.toJSON(); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + expect(Object.keys(nominatorRequests.requests).length).equal(1); + expect(nominatorRequests.requests[faith.address].action).equal('Revoke'); + expect(new BigNumber(nominatorRequests.requests[faith.address].amount.toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorRequests.requests[faith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(stake.toFixed()); + expect(Object.keys(nominatorRequests.requests[faith.address].whenExecutable).length).equal(1); + + // nominator state should be updated + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(stake.plus(stake).toFixed()); + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(stake.plus(stake).toFixed()); + + // candidate state should be updated + const rawCandidateState: any = await context.polkadotApi.query.bfcStaking.candidateInfo(alith.address); + const candidateState = rawCandidateState.unwrap(); + expect(candidateState.nominationCount.toString()).equal('3'); + expect(candidateState.lowestTopNominationAmount.toString()).equal(stake.multipliedBy(2).toFixed()); + + const selfBond = new BigNumber(candidateState.bond.toString()); + const expectedStake = selfBond.plus(stake.plus(stake.multipliedBy(3))); + expect(candidateState.votingPower.toString()).equal(expectedStake.toFixed()); + }); +}); + +describeDevNode('pallet_bfc_staking - revoke last nomination', (context) => { + const keyring = new Keyring({ type: 'ethereum' }); + const alith = keyring.addFromUri(TEST_CONTROLLERS[0].private); + const ethan = keyring.addFromUri(TEST_CONTROLLERS[4].private); + + const faith = keyring.addFromUri(TEST_CONTROLLERS[5].private); + const faithStash = keyring.addFromUri(TEST_STASHES[5].private); + + before('should successfully join candidate pool', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + await context.polkadotApi.tx.bfcStaking + .joinCandidates(faith.address, null, stake.toFixed(), 1) + .signAndSend(faithStash); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + }); + + it('should successfully revoke last nomination', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = rawTotalBefore.toJSON(); + + await context.polkadotApi.tx.bfcStaking + .scheduleRevokeNomination(alith.address) + .signAndSend(ethan); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + const nominatorRequests = rawNominatorState.unwrap().requests.toJSON(); + // nomination should be set to zero + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(new BigNumber(0).toFixed()); + // total nomination should be decreased + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(new BigNumber(0).toFixed()); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + // a revoke request should be scheduled + expect(new BigNumber(nominatorRequests.lessTotal.toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].amount.toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(stake.toFixed()); + expect(Object.keys(nominatorRequests.requests[alith.address].whenExecutable).length).equal(1); + expect(nominatorRequests.requests[alith.address].action).equal('Revoke'); + + // empty top and bottom nominations + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap().toJSON(); + expect(topNominations.nominations.length).equal(0); + const rawBottomNominations: any = await context.polkadotApi.query.bfcStaking.bottomNominations(alith.address); + const bottomNominations = rawBottomNominations.unwrap().toJSON(); + expect(bottomNominations.nominations.length).equal(0); + + // it should be moved to unstaking nominations + const rawUnstakingNominations: any = await context.polkadotApi.query.bfcStaking.unstakingNominations(alith.address); + const unstakingNominations = rawUnstakingNominations.unwrap().toJSON(); + expect(unstakingNominations.nominations.length).equal(1); + expect(unstakingNominations.nominations[0].owner.toString().toLowerCase()).equal(ethan.address.toLowerCase()); + expect(new BigNumber(unstakingNominations.nominations[0].amount.toString()).toFixed()).equal(stake.toFixed()); + + // total should be decreased + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = rawTotalAfter.toJSON(); + expect(new BigNumber(totalAfter.toString()).toFixed()).equal(new BigNumber(totalBefore.toString()).minus(stake).toFixed()); + }); + + it('should successfully revoke last nomination with existing revoke request', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + const totalRevoke = stake.multipliedBy(2); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(ethan); + + await context.createBlock(); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + + await context.polkadotApi.tx.bfcStaking + .cancelNominationRequest(alith.address, currentRound + 1) + .signAndSend(ethan); + + await context.createBlock(); + + const rawTotalBefore: any = await context.polkadotApi.query.bfcStaking.total(); + const totalBefore = rawTotalBefore.toJSON(); + + await context.polkadotApi.tx.bfcStaking + .scheduleRevokeNomination(alith.address) + .signAndSend(ethan); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleRevokeNomination(faith.address) + .signAndSend(ethan); + + await context.createBlock(); + + const rawNominatorState: any = await context.polkadotApi.query.bfcStaking.nominatorState(ethan.address); + const nominatorState = rawNominatorState.unwrap().toJSON(); + const nominatorRequests = rawNominatorState.unwrap().requests.toJSON(); + // nomination should be set to zero + expect(new BigNumber(nominatorState.nominations[alith.address].toString()).toFixed()).equal(new BigNumber(0).toFixed()); + expect(new BigNumber(nominatorState.nominations[faith.address].toString()).toFixed()).equal(new BigNumber(0).toFixed()); + // total nomination should be decreased + expect(new BigNumber(nominatorState.total.toString()).toFixed()).equal(new BigNumber(0).toFixed()); + + // a revoke request should be scheduled + expect(new BigNumber(nominatorRequests.lessTotal.toString()).toFixed()).equal(totalRevoke.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].amount.toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorRequests.requests[alith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(stake.toFixed()); + expect(Object.keys(nominatorRequests.requests[alith.address].whenExecutable).length).equal(1); + expect(nominatorRequests.requests[alith.address].action).equal('Revoke'); + expect(new BigNumber(nominatorRequests.requests[faith.address].amount.toString()).toFixed()).equal(stake.toFixed()); + expect(new BigNumber(nominatorRequests.requests[faith.address].whenExecutable[currentRound + 1].toString()).toFixed()).equal(stake.toFixed()); + expect(Object.keys(nominatorRequests.requests[faith.address].whenExecutable).length).equal(1); + expect(nominatorRequests.requests[faith.address].action).equal('Revoke'); + + // empty top and bottom nominations + const rawTopNominations: any = await context.polkadotApi.query.bfcStaking.topNominations(alith.address); + const topNominations = rawTopNominations.unwrap().toJSON(); + expect(topNominations.nominations.length).equal(0); + const rawBottomNominations: any = await context.polkadotApi.query.bfcStaking.bottomNominations(alith.address); + const bottomNominations = rawBottomNominations.unwrap().toJSON(); + expect(bottomNominations.nominations.length).equal(0); + + // it should be moved to unstaking nominations + const rawUnstakingNominations: any = await context.polkadotApi.query.bfcStaking.unstakingNominations(alith.address); + const unstakingNominations = rawUnstakingNominations.unwrap().toJSON(); + expect(unstakingNominations.nominations.length).equal(1); + expect(unstakingNominations.nominations[0].owner.toString().toLowerCase()).equal(ethan.address.toLowerCase()); + expect(new BigNumber(unstakingNominations.nominations[0].amount.toString()).toFixed()).equal(stake.toFixed()); + + // total should be decreased + const rawTotalAfter: any = await context.polkadotApi.query.bfcStaking.total(); + const totalAfter = rawTotalAfter.toJSON(); + expect(new BigNumber(totalAfter.toString()).toFixed()).equal(new BigNumber(totalBefore.toString()).minus(totalRevoke).toFixed()); + }); +}); diff --git a/tests/tests/precompiles/test_bfc_staking.ts b/tests/tests/precompiles/test_bfc_staking.ts index 91924275..002afb95 100644 --- a/tests/tests/precompiles/test_bfc_staking.ts +++ b/tests/tests/precompiles/test_bfc_staking.ts @@ -2,7 +2,11 @@ import BigNumber from 'bignumber.js'; import { expect } from 'chai'; import { numberToHex } from 'web3-utils'; -import { MIN_FULL_CANDIDATE_STAKING_AMOUNT } from '../../constants/currency'; +import { Keyring } from '@polkadot/api'; + +import { + DEFAULT_STAKING_AMOUNT, MIN_FULL_CANDIDATE_STAKING_AMOUNT +} from '../../constants/currency'; import { TEST_CONTROLLERS, TEST_RELAYERS, TEST_STASHES } from '../../constants/keys'; @@ -81,8 +85,59 @@ const SELECTORS = { const PRECOMPILE_ADDRESS = '0x0000000000000000000000000000000000000400'; describeDevNode('precompile_bfc_staking - precompile view functions', (context) => { + const keyring = new Keyring({ type: 'ethereum' }); + const alith: { public: string, private: string } = TEST_CONTROLLERS[0]; + const baltathar = keyring.addFromUri(TEST_CONTROLLERS[1].private); + const faith = keyring.addFromUri(TEST_CONTROLLERS[5].private); + const faithStash = keyring.addFromUri(TEST_STASHES[5].private); + + before('it should setup state for nominator_requests', async function () { + const stake = new BigNumber(DEFAULT_STAKING_AMOUNT); + const less = stake.dividedBy(10); + + await context.polkadotApi.tx.bfcStaking + .joinCandidates(faith.address, null, stake.toFixed(), 1) + .signAndSend(faithStash); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(alith.public, stake.toFixed(), 10, 10) + .signAndSend(baltathar); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .nominate(faith.address, stake.toFixed(), 10, 10) + .signAndSend(baltathar); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(alith.public, less.toFixed()) + .signAndSend(baltathar); + + await context.createBlock(); + + const rawCurrentRound: any = await context.polkadotApi.query.bfcStaking.round(); + const currentRound = rawCurrentRound.currentRoundIndex.toNumber(); + await jumpToRound(context, currentRound + 1); + + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(alith.public, less.toFixed()) + .signAndSend(baltathar); + + await context.createBlock(); + + await context.polkadotApi.tx.bfcStaking + .scheduleNominatorBondLess(faith.address, less.toFixed()) + .signAndSend(baltathar); + + await context.createBlock(); + }); + it('should successfully verify validator/candidate roles', async function () { const is_candidate = await callPrecompile( context, @@ -140,7 +195,7 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) ['tuple(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)'], round_info, )[0]; - expect(Number(decoded_round_info[0])).equal(1); + expect(Number(decoded_round_info[0])).equal(2); const latest_round = await callPrecompile( context, @@ -154,7 +209,7 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) ['uint256'], latest_round, )[0]; - expect(Number(decoded_latest_round)).equal(1); + expect(Number(decoded_latest_round)).equal(2); const majority = await callPrecompile( context, @@ -191,7 +246,7 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) PRECOMPILE_ADDRESS, SELECTORS, 'points', - ['0x1'], + ['0x2'], ); const decoded_points = context.web3.eth.abi.decodeParameters( ['uint256'], @@ -205,7 +260,7 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) PRECOMPILE_ADDRESS, SELECTORS, 'validator_points', - ['0x1', alith.public], + ['0x2', alith.public], ); const decoded_validator_points = context.web3.eth.abi.decodeParameters( ['uint256'], @@ -316,8 +371,8 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) max_nominations_per_candidate, ); expect(decoded_max_nominations_per_candidate.__length__).equal(2); - expect(Number(decoded_max_nominations_per_candidate[0])).equal(10); - expect(Number(decoded_max_nominations_per_candidate[1])).equal(2); + expect(Number(decoded_max_nominations_per_candidate[0])).equal(2); + expect(Number(decoded_max_nominations_per_candidate[1])).equal(1); const candidate_bond_less_delay = await callPrecompile( context, @@ -366,7 +421,7 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) ['uint256'], candidate_count, ); - expect(Number(decoded_candidate_count[0])).equal(1); + expect(Number(decoded_candidate_count[0])).equal(2); const selected_candidates = await callPrecompile( context, @@ -410,9 +465,9 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) ['address[]', 'uint256[]'], candidate_pool, ); - expect(decoded_candidate_pool[0].length).equal(1); - expect(decoded_candidate_pool[1].length).equal(1); - expect(decoded_candidate_pool[0][0]).equal(alith.public); + expect(decoded_candidate_pool[0].length).equal(2); + expect(decoded_candidate_pool[1].length).equal(2); + expect(decoded_candidate_pool[0][1]).equal(alith.public); const candidate_state = await callPrecompile( context, @@ -463,7 +518,7 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) ], candidate_states, ); - expect(decoded_candidate_states[0][0]).equal(alith.public); + expect(decoded_candidate_states[0][1]).equal(alith.public); const candidate_states_by_selection = await callPrecompile( context, @@ -568,17 +623,17 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) ], candidate_nomination_count, )[0]; - expect(Number(decoded_candidate_nomination_count)).equal(0); + expect(Number(decoded_candidate_nomination_count)).equal(1); }); it('should successfully verify nominator storage existance', async function () { const nominator_state = await callPrecompile( context, - alith.public, + baltathar.address, PRECOMPILE_ADDRESS, SELECTORS, 'nominator_state', - [alith.public], + [baltathar.address], ); const decoded_nominator_state = context.web3.eth.abi.decodeParameters( [ @@ -586,7 +641,6 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) 'uint256', 'uint256', 'uint256', - 'uint256', 'address[]', 'uint256[]', 'uint256[]', @@ -596,29 +650,36 @@ describeDevNode('precompile_bfc_staking - precompile view functions', (context) ], nominator_state, ); - expect(decoded_nominator_state[0]).equal(alith.public); + expect(decoded_nominator_state[0]).equal(baltathar.address); const nominator_requests = await callPrecompile( context, - alith.public, + baltathar.address, PRECOMPILE_ADDRESS, SELECTORS, 'nominator_requests', - [alith.public], + [baltathar.address], ); const decoded_nominator_requests = context.web3.eth.abi.decodeParameters( [ 'address', 'uint256', - 'uint256', 'address[]', - 'uint256[]', - 'uint256[]', + 'uint256[][]', + 'uint256[][]', 'uint256[]', ], nominator_requests, ); - expect(decoded_nominator_requests[0]).equal(alith.public); + const less = new BigNumber(DEFAULT_STAKING_AMOUNT).dividedBy(10); + expect(decoded_nominator_requests[0]).equal(baltathar.address); + expect(new BigNumber(decoded_nominator_requests[1] as any).toFixed()).equal(less.multipliedBy(3).toFixed()); + expect((decoded_nominator_requests as any)[2].length).equal(2); + expect((decoded_nominator_requests as any)[3][0].length).equal(1); + expect((decoded_nominator_requests as any)[3][1].length).equal(2); + expect((decoded_nominator_requests as any)[4][0].length).equal(1); + expect((decoded_nominator_requests as any)[4][1].length).equal(2); + expect((decoded_nominator_requests as any)[5].length).equal(2); const nominator_nomination_count = await callPrecompile( context,