diff --git a/.gitignore b/.gitignore index 23409363..de63cccc 100755 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,7 @@ target # Ignore backup files creates by cargo fmt. **/*.rs.bk -# Remove Cargo.lock when creating an executable, leave it for libraries -# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock -Cargo.lock - local .idea ink-doc-gen +.DS_Store diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..9224d38f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,766 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "array-init" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ddc_bucket" +version = "1.0.0" +dependencies = [ + "ink_allocator", + "ink_env", + "ink_lang", + "ink_lang_codegen", + "ink_lang_ir", + "ink_lang_macro", + "ink_metadata", + "ink_prelude", + "ink_primitives", + "ink_storage", + "ink_storage_derive", + "more-asserts", + "parity-scale-codec", + "parity-scale-codec-derive", + "scale-info", + "scale-info-derive", + "serde", + "serde_json", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "ink_allocator" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c9588a59a0e8997c0b2153cd11b5aaa77c06a0537a6b18f3811d1f1aa098b12" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ink_engine" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487c3b390b7feb0620496b0cd38683433c7d7e6946b1caabda51e1f23eb24b30" +dependencies = [ + "blake2", + "derive_more", + "parity-scale-codec", + "rand", + "secp256k1", + "sha2", + "sha3", +] + +[[package]] +name = "ink_env" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a891d34301a3dbb1c7b7424c49ae184282b163491c54f9acd17fcbe14a80447b" +dependencies = [ + "arrayref", + "blake2", + "cfg-if", + "derive_more", + "ink_allocator", + "ink_engine", + "ink_metadata", + "ink_prelude", + "ink_primitives", + "num-traits", + "parity-scale-codec", + "paste", + "rand", + "rlibc", + "scale-info", + "secp256k1", + "sha2", + "sha3", + "static_assertions", +] + +[[package]] +name = "ink_lang" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca26e374e0f89c82cf5dabb4309ef3c76a01659ad95186f4e84455c5f4621a0" +dependencies = [ + "derive_more", + "ink_env", + "ink_lang_macro", + "ink_prelude", + "ink_primitives", + "ink_storage", + "parity-scale-codec", +] + +[[package]] +name = "ink_lang_codegen" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fe57826726d89c84fe0b1fafe0dee328f58c8e927be40f0290f04602aacc45c" +dependencies = [ + "blake2", + "derive_more", + "either", + "heck", + "impl-serde", + "ink_lang_ir", + "itertools", + "parity-scale-codec", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ink_lang_ir" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47d16b2a5340df90f11b2ec2242b37907f5c8396dbbc72c52ec9f2b1a8c90c8" +dependencies = [ + "blake2", + "either", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ink_lang_macro" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b858be42ac6cde2c15ce6d7fa75cef59b64a3baf37f7105f39208f2b84dadb" +dependencies = [ + "ink_lang_codegen", + "ink_lang_ir", + "ink_primitives", + "parity-scale-codec", + "proc-macro2", + "syn", +] + +[[package]] +name = "ink_metadata" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74913aaed5751f5615af4631b7559328b8ed56c9cb821b89e14af0706176e849" +dependencies = [ + "derive_more", + "impl-serde", + "ink_prelude", + "ink_primitives", + "scale-info", + "serde", +] + +[[package]] +name = "ink_prelude" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f031e6b8495594a7288b089bf4122e76c26b994959d1b2b693bdfe846b14c0e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ink_primitives" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12cf42dce81d060401c7cec95a392ad6d3c2f18661fa3083f619ce135133c33" +dependencies = [ + "cfg-if", + "ink_prelude", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "ink_storage" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c0a98b6acbd79eedf44720412437d713e7195d1407822604de5885b0ee6c7e1" +dependencies = [ + "array-init", + "cfg-if", + "derive_more", + "ink_env", + "ink_metadata", + "ink_prelude", + "ink_primitives", + "ink_storage_derive", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "ink_storage_derive" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "babf1d8903dc9219ad8e8aa181eddb919d9794aad1da23ccdce770925b7de2ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.146" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "more-asserts" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5474f8732dc7e0635ae9df6595bcd39cd30e3cfe8479850d4fa3e69306c19712" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parity-scale-codec" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b44461635bbb1a0300f100a841e571e7d919c81c73075ef5d152ffdb521066" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c45ed1f39709f5a89338fab50e59816b2e8815f5bb58276e7ddf9afd495f73f8" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rlibc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "scale-info" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0563970d79bcbf3c537ce3ad36d859b30d36fc5b190efd227f1f7a84d7cf0d42" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", +] + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "toml_datetime" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" + +[[package]] +name = "toml_edit" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winnow" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/Cargo.toml b/Cargo.toml index 941c7156..da6d1c5c 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ resolver = "2" members = [ "bucket", - "ddc_nft_registry", ] # Needed until https://github.com/paritytech/ink/issues/364 is resolved. diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index de614daf..00000000 --- a/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -ARG VCS_REF=master -ARG BUILD_DATE="" -ARG REGISTRY_PATH=docker.io/paritytech - -FROM ${REGISTRY_PATH}/base-ci-linux:latest - -ARG RUST_NIGHTLY="2023-03-21" - -WORKDIR /builds - -RUN apt-get update && apt-get upgrade - -RUN apt-get install -y binaryen - -RUN rustup target add wasm32-unknown-unknown --toolchain stable && \ - rustup component add rust-src --toolchain stable && \ - rustup default stable && \ - - # We also use the nightly toolchain for linting. We perform checks using RustFmt, and - # Cargo Clippy. - # - # Note that we pin the nightly toolchain since it often creates breaking changes during - # the RustFmt and Clippy stages of the CI. - rustup toolchain install nightly-${RUST_NIGHTLY} --target wasm32-unknown-unknown \ - --profile minimal --component rustfmt clippy rust-src && \ - - # Alias pinned toolchain as nightly, otherwise it appears as though we - # don't have a nightly toolchain (i.e rustc +nightly --version is empty) - ln -s "/usr/local/rustup/toolchains/nightly-${RUST_NIGHTLY}-x86_64-unknown-linux-gnu" \ - /usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu && \ - - # `cargo-dylint` and `dylint-link` are dependencies needed to run `cargo-contract`. - cargo install cargo-dylint dylint-link && \ - - # Install the latest `cargo-contract` - cargo install cargo-contract@1.5.0 && \ - - # apt clean up - apt-get autoremove -y && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* diff --git a/README.md b/README.md index e136a802..39fdae49 100644 --- a/README.md +++ b/README.md @@ -25,19 +25,14 @@ cargo install cargo-contract --version 1.5.0 --force --locked brew install binaryen # For Debian\Ubuntu: # apt-get install binaryen - -# Install the documentation generator -git clone https://github.com/Cerebellum-Network/ink-doc-gen.git -cd ink-doc-gen && git checkout v0.1.0 && yarn ``` ## Building ```bash -# Build DDC Bucket and DDC NFT Registry contracts +# Build DDC Bucket contract cargo +nightly-2023-02-07 test && -cargo +nightly-2023-02-07 contract build --release --manifest-path bucket/Cargo.toml && -cargo +nightly-2023-02-07 contract build --release --manifest-path ddc_nft_registry/Cargo.toml +cargo +nightly-2023-02-07 contract build --release --manifest-path bucket/Cargo.toml ``` Note: if you are encountering errors during build process, they may be related to your specific processor's architecture. If this is the case, try out the *Instalation using Docker image* option, [described in the official docs](https://github.com/paritytech/cargo-contract#installation-using-docker-image) diff --git a/bucket/Cargo.toml b/bucket/Cargo.toml index d61440b4..ea5a2b9a 100755 --- a/bucket/Cargo.toml +++ b/bucket/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ddc_bucket" -version = "0.5.2" +version = "1.0.0" authors = ["Aurélien Nicolas "] edition = "2021" description = "DDC v2 Smart Contracts -- Orchestrate the network around clusters and buckets" @@ -8,7 +8,9 @@ license = "Apache-2.0" [dependencies] scale = { package = "parity-scale-codec", version = "=3.1.2", default-features = false, features = ["derive"] } +scale-derive = { package = "parity-scale-codec-derive", version = "=3.1.2", default-features = false } scale-info = { version = "=2.0.1", default-features = false, features = ["derive"], optional = true } +scale-info-derive = { version = "=2.5.0", default-features = false, optional = true } ink_primitives = { version = "3.4.0", default-features = false } ink_metadata = { version = "3.4.0", default-features = false, features = ["derive"], optional = true } diff --git a/bucket/README.md b/bucket/README.md index d29ce01a..6b954961 100644 --- a/bucket/README.md +++ b/bucket/README.md @@ -67,10 +67,9 @@ A cluster may be managed by a trusted party using an external account, or by a s - Providers registers Nodes. node_create - Providers signal trust to a Manager. - node_trust_manager + grant_trusted_manager_permission - Manager creates a Cluster and reserve some capacity from Nodes. cluster_create - cluster_reserve_resource - Owner creates a Bucket in a Cluster. bucket_create - Owner deposits some funds. diff --git a/bucket/ddc_bucket/account/entity.rs b/bucket/ddc_bucket/account/entity.rs index 424a1688..32f29d99 100644 --- a/bucket/ddc_bucket/account/entity.rs +++ b/bucket/ddc_bucket/account/entity.rs @@ -3,12 +3,14 @@ use ink_storage::traits::{PackedLayout, SpreadLayout}; use scale::{Decode, Encode}; +use crate::ddc_bucket::currency::{CurrencyConverter, USD}; use crate::ddc_bucket::{ - Balance, cash::{Cash, Payable}, - Error::*, Result, + cash::{Cash, Payable}, schedule::Schedule, + Balance, + Error::*, + Result, }; -use crate::ddc_bucket::currency::{USD, CurrencyConverter}; #[derive(Clone, PartialEq, Encode, Decode, SpreadLayout, PackedLayout)] #[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] @@ -37,16 +39,21 @@ impl Account { self.deposit.increase(cash); } - pub fn bond(&mut self, time_ms: u64, conv: &CurrencyConverter, bond_amount: Balance) -> Result<()> { + pub fn bond( + &mut self, + time_ms: u64, + conv: &CurrencyConverter, + bond_amount: Balance, + ) -> Result<()> { let payable = Payable(bond_amount); if self.get_withdrawable(time_ms, conv) >= payable.peek() { let parsed_payable: u128; - if self.negative.peek() > 0 && payable.peek() >= self.negative.peek() { + if self.negative.peek() > 0 && payable.peek() >= self.negative.peek() { parsed_payable = payable.peek() - self.negative.peek(); self.deposit.pay_unchecked(payable); self.bonded.increase(Cash(parsed_payable)); Ok(()) - } else if self.negative.peek() > 0 && payable.peek() < self.negative.peek(){ + } else if self.negative.peek() > 0 && payable.peek() < self.negative.peek() { Err(InsufficientBalance) } else { let bonded_amount = Cash(payable.peek()); @@ -71,7 +78,12 @@ impl Account { } } - pub fn withdraw(&mut self, time_ms: u64, conv: &CurrencyConverter, payable: Payable) -> Result<()> { + pub fn withdraw( + &mut self, + time_ms: u64, + conv: &CurrencyConverter, + payable: Payable, + ) -> Result<()> { if self.get_withdrawable(time_ms, conv) >= payable.peek() { self.deposit.pay_unchecked(payable); Ok(()) @@ -80,7 +92,7 @@ impl Account { } } - // Add logics when balance is below requested + // Add logics when balance is below requested pub fn withdraw_bonded(&mut self, payable: Payable) -> Result<()> { let remaining_bonded = self.bonded.peek() - self.unbonded_amount.peek(); if remaining_bonded >= payable.peek() { @@ -143,4 +155,4 @@ impl Account { } } -pub const MS_PER_WEEK: u64 = 7 * 24 * 3600 * 1000; \ No newline at end of file +pub const MS_PER_WEEK: u64 = 7 * 24 * 3600 * 1000; diff --git a/bucket/ddc_bucket/account/messages.rs b/bucket/ddc_bucket/account/messages.rs index d3f5739a..28dac13d 100644 --- a/bucket/ddc_bucket/account/messages.rs +++ b/bucket/ddc_bucket/account/messages.rs @@ -1,17 +1,16 @@ //! The public interface of Accounts and deposits. -use ink_prelude::vec::Vec; use ink_lang::codegen::{EmitEvent, StaticEnv}; +use ink_prelude::vec::Vec; -use crate::ddc_bucket::{AccountId, Balance, Cash, DdcBucket, Deposit, Payable, Result, TOKEN}; -use crate::ddc_bucket::Error::InsufficientBalance; use crate::ddc_bucket::perm::entity::Permission; +use crate::ddc_bucket::Error::InsufficientBalance; +use crate::ddc_bucket::{AccountId, Balance, Cash, DdcBucket, Deposit, Payable, Result, TOKEN}; impl DdcBucket { - // todo: remove this method as we can not support iterable data structures of arbitrary data size pub fn message_get_accounts(&self) -> Vec { - self.accounts.2.iter().cloned().collect() + self.accounts.accounts_keys.iter().cloned().collect() } pub fn message_account_deposit(&mut self) -> Result<()> { @@ -20,9 +19,12 @@ impl DdcBucket { let account_id = Self::env().caller(); // Create the account, if necessary. - self.accounts.create_if_not_exist(account_id); + self.accounts.create_if_not_exist(account_id)?; - Self::env().emit_event(Deposit { account_id, value: cash.peek() }); + Self::env().emit_event(Deposit { + account_id, + value: cash.peek(), + }); let mut account = self.accounts.get(&account_id)?; account.deposit(cash); @@ -36,7 +38,7 @@ impl DdcBucket { let account_id = Self::env().caller(); if let Ok(mut account) = self.accounts.get(&account_id) { - let conv = &self.accounts.1; + let conv = &self.protocol.curr_converter; account.bond(time_ms, conv, bond_amount)?; self.accounts.save(&account_id, &account); Ok(()) @@ -68,12 +70,13 @@ impl DdcBucket { } pub fn message_account_get_usd_per_cere(&self) -> Balance { - self.accounts.1.to_usd(1 * TOKEN) + self.protocol.curr_converter.to_usd(1 * TOKEN) } - pub fn message_account_set_usd_per_cere(&mut self, usd_per_cere: Balance) { - self.only_with_permission(Permission::SetExchangeRate).unwrap(); - self.accounts.1.set_usd_per_cere(usd_per_cere) + pub fn message_account_set_usd_per_cere(&mut self, usd_per_cere: Balance) -> Result<()> { + self.only_with_permission(Permission::SetExchangeRate)?; + self.protocol.curr_converter.set_usd_per_cere(usd_per_cere); + Ok(()) } pub fn receive_cash() -> Cash { @@ -81,7 +84,9 @@ impl DdcBucket { } pub fn send_cash(destination: AccountId, cash: Cash) -> Result<()> { - if cash.peek() == 0 { return Ok(()); } + if cash.peek() == 0 { + return Ok(()); + } match Self::env().transfer(destination, cash.consume()) { Err(_e) => panic!("Transfer failed"), // Err(Error::TransferFailed), Ok(_v) => Ok(()), @@ -91,7 +96,7 @@ impl DdcBucket { fn _account_withdraw(&mut self, from: AccountId, payable: Payable) -> Result<()> { if let Ok(mut account) = self.accounts.get(&from) { let time_ms = Self::env().block_timestamp(); - let conv = &self.accounts.1; + let conv = &self.protocol.curr_converter; account.withdraw(time_ms, conv, payable)?; self.accounts.save(&from, &account); Ok(()) @@ -111,11 +116,11 @@ impl DdcBucket { } fn _account_get_net(&self, from: AccountId) -> Balance { - match self.accounts.0.get(&from) { + match self.accounts.accounts.get(&from) { None => 0, Some(account) => { let time_ms = Self::env().block_timestamp(); - let conv = &self.accounts.1; + let conv = &self.protocol.curr_converter; account.get_withdrawable(time_ms, conv) } } @@ -126,7 +131,7 @@ impl DdcBucket { self._account_withdraw(from, payable)?; // Create the account, if necessary. - self.accounts.create_if_not_exist(to); + self.accounts.create_if_not_exist(to)?; let mut account = self.accounts.get(&to)?; account.deposit(cash); diff --git a/bucket/ddc_bucket/account/mod.rs b/bucket/ddc_bucket/account/mod.rs index e7bb8d95..300b6723 100644 --- a/bucket/ddc_bucket/account/mod.rs +++ b/bucket/ddc_bucket/account/mod.rs @@ -1,5 +1,5 @@ //! Account and deposit management. pub mod entity; +pub mod messages; pub mod store; -pub mod messages; \ No newline at end of file diff --git a/bucket/ddc_bucket/account/store.rs b/bucket/ddc_bucket/account/store.rs index 18a69245..86f7f650 100644 --- a/bucket/ddc_bucket/account/store.rs +++ b/bucket/ddc_bucket/account/store.rs @@ -1,56 +1,66 @@ //! The store to create and access Accounts. -use ink_storage::Mapping; - +use super::entity::Account; +use crate::ddc_bucket::flow::Flow; use crate::ddc_bucket::{ - AccountId, Balance, cash::Cash, Error::*, + cash::Cash, currency::CurrencyConverter, schedule::Schedule, AccountId, Balance, Error::*, Result, - schedule::Schedule, }; -use crate::ddc_bucket::currency::CurrencyConverter; -use crate::ddc_bucket::flow::Flow; -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; use ink_prelude::vec::Vec; -use super::entity::Account; +use ink_storage::traits::{SpreadAllocate, SpreadLayout}; +use ink_storage::Mapping; + +// https://use.ink/datastructures/storage-layout#packed-vs-non-packed-layout +// There is a buffer with only limited capacity (around 16KB in the default configuration) available. +pub const MAX_ACCOUNTS_LEN_IN_VEC: usize = 400; #[derive(Default, SpreadLayout, SpreadAllocate)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] -pub struct AccountStore( - pub Mapping, - pub CurrencyConverter, +#[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout, Debug))] +pub struct AccountStore { + pub accounts: Mapping, // todo: remove this vector as it can store an arbitrary number of elements and easily exceed 16KB limit - pub Vec, -); + pub accounts_keys: Vec, +} impl AccountStore { /// Create a record for the given account if it does not exist yet. /// Does not return extra contract storage used, due to blockchain changes. - pub fn create_if_not_exist(&mut self, account_id: AccountId) { - if !self.0.contains(account_id) { + pub fn create_if_not_exist(&mut self, account_id: AccountId) -> Result<()> { + if !self.accounts.contains(account_id) { + if self.accounts_keys.len() + 1 > MAX_ACCOUNTS_LEN_IN_VEC { + return Err(AccountsSizeExceedsLimit); + } let acc = Account::new(); - self.0.insert(account_id, &acc); - self.2.push(account_id); + self.accounts.insert(account_id, &acc); + self.accounts_keys.push(account_id); }; + + Ok(()) } pub fn balance(&self, account_id: &AccountId) -> Balance { - match self.0.get(account_id) { + match self.accounts.get(account_id) { None => 0, Some(account) => account.deposit.peek(), } } pub fn get(&self, account_id: &AccountId) -> Result { - self.0.get(account_id).ok_or(AccountDoesNotExist) + self.accounts.get(account_id).ok_or(AccountDoesNotExist) } pub fn save(&mut self, account_id: &AccountId, account: &Account) { - self.0.insert(account_id, account) + self.accounts.insert(account_id, account) } /// Increase the rate of the given flow starting from the given time. /// Lock the payment flow from the deposit of the payer account. - pub fn increase_flow(&mut self, start_ms: u64, extra_rate: Balance, flow: &mut Flow) -> Result<()> { + pub fn increase_flow( + &mut self, + start_ms: u64, + extra_rate: Balance, + flow: &mut Flow, + ) -> Result<()> { let extra_schedule = Schedule::new(start_ms, extra_rate); flow.schedule.add_schedule(extra_schedule.clone()); @@ -61,9 +71,14 @@ impl AccountStore { Ok(()) } - pub fn settle_flow(&mut self, now_ms: u64, flow: &mut Flow) -> Result { + pub fn settle_flow( + &mut self, + now_ms: u64, + flow: &mut Flow, + curr_converter: &CurrencyConverter, + ) -> Result { let flowed_usd = flow.schedule.take_value_at_time(now_ms); - let flowed_cere = self.1.to_cere(flowed_usd); + let flowed_cere = curr_converter.to_cere(flowed_usd); let (payable, cash) = Cash::borrow_payable_cash(flowed_cere); let mut account = self.get(&flow.from)?; @@ -73,10 +88,14 @@ impl AccountStore { Ok(cash) } - pub fn flow_covered_until(&self, flow: &Flow) -> Result { + pub fn flow_covered_until( + &self, + flow: &Flow, + curr_converter: &CurrencyConverter, + ) -> Result { let account = self.get(&flow.from)?; let deposit_cere = account.deposit.peek(); - let deposit_usd = self.1.to_usd(deposit_cere); + let deposit_usd = curr_converter.to_usd(deposit_cere); Ok(account.schedule_covered_until(deposit_usd)) } } diff --git a/bucket/ddc_bucket/admin.rs b/bucket/ddc_bucket/admin.rs index c5666b76..9d765f66 100644 --- a/bucket/ddc_bucket/admin.rs +++ b/bucket/ddc_bucket/admin.rs @@ -1,21 +1,127 @@ //! The privileged interface for admin tasks. - use crate::ddc_bucket::perm::entity::Permission; -use crate::ddc_bucket::{AccountId, Balance, Cash, DdcBucket, Result}; +use crate::ddc_bucket::{ + AccountId, Balance, BasisPoints, Cash, CdnNodeOwnershipTransferred, DdcBucket, Error::*, + NetworkFeeConfig, NodeKey, NodeOwnershipTransferred, Payable, PermissionGranted, + PermissionRevoked, Result, +}; +use ink_lang::codegen::{EmitEvent, StaticEnv}; impl DdcBucket { pub fn message_admin_grant_permission( &mut self, grantee: AccountId, permission: Permission, - is_granted: bool, ) -> Result<()> { - self.only_with_permission(Permission::SuperAdmin)?; - self.impl_grant_permission(grantee, permission, is_granted) + self.only_with_permission(Permission::SuperAdmin) + .map_err(|_| OnlySuperAdmin)?; + + self.grant_permission(grantee, permission)?; + + Self::env().emit_event(PermissionGranted { + account_id: grantee, + permission, + }); + + Ok(()) + } + + pub fn message_admin_revoke_permission( + &mut self, + grantee: AccountId, + permission: Permission, + ) -> Result<()> { + self.only_with_permission(Permission::SuperAdmin) + .map_err(|_| OnlySuperAdmin)?; + + self.revoke_permission(grantee, permission)?; + + Self::env().emit_event(PermissionRevoked { + account_id: grantee, + permission, + }); + + Ok(()) + } + + pub fn message_admin_transfer_node_ownership( + &mut self, + node_key: NodeKey, + new_owner: AccountId, + ) -> Result<()> { + let admin = self + .only_with_permission(Permission::SuperAdmin) + .map_err(|_| OnlySuperAdmin)?; + + let mut node = self.nodes.get(node_key)?; + // allow node ownership transfer only if the current owner is the admin + node.only_provider(admin) + .map_err(|_| NodeProviderIsNotSuperAdmin)?; + + node.provider_id = new_owner; + self.nodes.update(node_key, &node)?; + + Self::env().emit_event(NodeOwnershipTransferred { + account_id: new_owner, + node_key, + }); + + Ok(()) + } + + pub fn message_admin_transfer_cdn_node_ownership( + &mut self, + cdn_node_key: NodeKey, + new_owner: AccountId, + ) -> Result<()> { + let admin = self + .only_with_permission(Permission::SuperAdmin) + .map_err(|_| OnlySuperAdmin)?; + + let mut cdn_node = self.cdn_nodes.get(cdn_node_key)?; + // allow node ownership transfer only if the current owner is the admin + cdn_node + .only_provider(admin) + .map_err(|_| CdnNodeOwnerIsNotSuperAdmin)?; + + cdn_node.provider_id = new_owner; + self.cdn_nodes.update(cdn_node_key, &cdn_node)?; + + Self::env().emit_event(CdnNodeOwnershipTransferred { + account_id: new_owner, + cdn_node_key, + }); + + Ok(()) } pub fn message_admin_withdraw(&mut self, amount: Balance) -> Result<()> { - let admin = self.only_with_permission(Permission::SuperAdmin)?; + let admin = self + .only_with_permission(Permission::SuperAdmin) + .map_err(|_| OnlySuperAdmin)?; + Self::send_cash(admin, Cash(amount)) } + + pub fn message_admin_set_protocol_fee_bp( + &mut self, + protocol_fee_bp: BasisPoints, + ) -> Result<()> { + self.only_with_permission(Permission::SuperAdmin)?; + self.protocol.set_protocol_fee_bp(protocol_fee_bp); + Ok(()) + } + + pub fn message_admin_set_network_fee_config(&mut self, config: NetworkFeeConfig) -> Result<()> { + self.only_with_permission(Permission::SuperAdmin)?; + self.protocol.set_network_fee_config(config); + Ok(()) + } + + pub fn message_admin_withdraw_revenues(&mut self, amount: u128) -> Result<()> { + self.only_with_permission(Permission::SuperAdmin)?; + self.protocol.withdraw_revenues(Payable(amount))?; + Self::send_cash(self.protocol.get_protocol_fee_dest(), Cash(amount))?; + Ok(()) + } } diff --git a/bucket/ddc_bucket/bucket/entity.rs b/bucket/ddc_bucket/bucket/entity.rs index 00335ac9..379123b9 100644 --- a/bucket/ddc_bucket/bucket/entity.rs +++ b/bucket/ddc_bucket/bucket/entity.rs @@ -1,19 +1,16 @@ //! The data structure of Buckets. -use ink_prelude::vec::Vec; -use ink_storage::traits::{SpreadAllocate, PackedAllocate, PackedLayout, SpreadLayout}; -use scale::{Decode, Encode}; -use ink_primitives::Key; -use crate::ddc_bucket::{ - AccountId, ClusterId, - Error::*, Result, -}; use crate::ddc_bucket::flow::Flow; use crate::ddc_bucket::node::entity::Resource; -use crate::ddc_bucket::params::store::Params; +use crate::ddc_bucket::{AccountId, ClusterId, Error::*, Result}; +use ink_prelude::string::String; +use ink_prelude::vec::Vec; +use ink_primitives::Key; +use ink_storage::traits::{PackedAllocate, PackedLayout, SpreadAllocate, SpreadLayout}; +use scale::{Decode, Encode}; pub type BucketId = u32; -pub type BucketParams = Params; +pub type BucketParams = String; #[derive(Clone, PartialEq, Encode, Decode, SpreadAllocate, SpreadLayout, PackedLayout)] #[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] @@ -23,7 +20,8 @@ pub struct Bucket { pub flow: Flow, pub resource_reserved: Resource, pub public_availability: bool, - pub resource_consumption_cap: Resource, + pub resource_consumption_cap: Resource, + pub bucket_params: BucketParams, } // https://use.ink/3.x/ink-vs-solidity#nested-mappings--custom--advanced-structures @@ -44,7 +42,7 @@ pub struct BucketInStatus { // TODO: find a fix, then return the entire Bucket structure. pub resource_reserved: Resource, pub public_availability: bool, - pub resource_consumption_cap: Resource, + pub resource_consumption_cap: Resource, } #[derive(Clone, PartialEq, Encode, Decode)] @@ -58,9 +56,15 @@ pub struct BucketStatus { pub rent_covered_until_ms: u64, } +pub const BUCKET_PARAMS_MAX_LEN: usize = 100_000; + impl Bucket { pub fn only_owner(&self, caller: AccountId) -> Result<()> { - if self.owner_id == caller { Ok(()) } else { Err(UnauthorizedOwner) } + if self.owner_id == caller { + Ok(()) + } else { + Err(OnlyOwner) + } } pub fn put_resource(&mut self, amount: Resource) { @@ -78,6 +82,14 @@ impl Bucket { pub fn change_owner(&mut self, owner_id: AccountId) { self.owner_id = owner_id; } + + pub fn set_params(&mut self, bucket_params: BucketParams) -> Result<()> { + if bucket_params.len() > BUCKET_PARAMS_MAX_LEN { + return Err(ParamsSizeExceedsLimit); + } + self.bucket_params = bucket_params; + Ok(()) + } } impl From for BucketInStatus { diff --git a/bucket/ddc_bucket/bucket/messages.rs b/bucket/ddc_bucket/bucket/messages.rs index 1a256b29..28108bd1 100644 --- a/bucket/ddc_bucket/bucket/messages.rs +++ b/bucket/ddc_bucket/bucket/messages.rs @@ -3,79 +3,133 @@ use ink_lang::codegen::{EmitEvent, StaticEnv}; use ink_prelude::vec::Vec; -use crate::ddc_bucket::{AccountId, BucketAllocated, BucketCreated, BucketSettlePayment, BucketAvailabilityUpdated, DdcBucket, Result}; use crate::ddc_bucket::cluster::entity::{Cluster, ClusterId}; use crate::ddc_bucket::node::entity::Resource; +use crate::ddc_bucket::{ + AccountId, Balance, BucketAllocated, BucketAvailabilityUpdated, BucketCreated, BucketParamsSet, + BucketSettlePayment, DdcBucket, Error::*, Result, +}; use super::entity::{Bucket, BucketId, BucketParams, BucketStatus}; impl DdcBucket { - pub fn message_bucket_create(&mut self, bucket_params: BucketParams, cluster_id: ClusterId, owner_id: Option) -> Result { + pub fn message_bucket_create( + &mut self, + bucket_params: BucketParams, + cluster_id: ClusterId, + owner_id: Option, + ) -> Result { let owner_id = owner_id.unwrap_or(Self::env().caller()); - self.accounts.create_if_not_exist(owner_id); - let bucket_id = self.buckets.create(owner_id, cluster_id); - let params_id = self.bucket_params.create(bucket_params)?; - assert_eq!(bucket_id, params_id); - Self::env().emit_event(BucketCreated { bucket_id, owner_id }); + self.accounts.create_if_not_exist(owner_id)?; + let bucket_id = self.buckets.create(owner_id, cluster_id, bucket_params); + Self::env().emit_event(BucketCreated { + bucket_id, + owner_id, + }); Ok(bucket_id) } - pub fn message_bucket_change_owner(&mut self, bucket_id: BucketId, owner_id: AccountId) -> Result<()> { + pub fn message_bucket_change_owner( + &mut self, + bucket_id: BucketId, + owner_id: AccountId, + ) -> Result<()> { let caller = Self::env().caller(); - let bucket = self.buckets.get_mut(bucket_id)?; + let mut bucket = self.buckets.get(bucket_id)?; bucket.only_owner(caller)?; bucket.change_owner(owner_id); + self.buckets.update(bucket_id, &bucket)?; Ok(()) } - pub fn message_bucket_alloc_into_cluster(&mut self, bucket_id: BucketId, resource: Resource) -> Result<()> { - let bucket = self.buckets.get_mut(bucket_id)?; - let cluster = self.clusters.get_mut(bucket.cluster_id)?; - Self::only_owner_or_cluster_manager(bucket, cluster)?; + pub fn message_bucket_alloc_into_cluster( + &mut self, + bucket_id: BucketId, + resource: Resource, + ) -> Result<()> { + let mut bucket = self.buckets.get(bucket_id)?; + let mut cluster = self.clusters.get(bucket.cluster_id)?; + Self::only_owner_or_cluster_manager(&bucket, &cluster)?; + + let cluster_v_nodes = self.get_v_nodes_by_cluster(bucket.cluster_id); + let cluster_v_nodes_len: u32 = cluster_v_nodes.len().try_into().unwrap(); + let max_cluster_resource = cluster_v_nodes_len * cluster.resource_per_v_node; + + if cluster.resource_used + resource > max_cluster_resource { + return Err(InsufficientClusterResources); + } - cluster.take_resource(resource)?; + cluster.take_resource(resource); + self.clusters.update(bucket.cluster_id, &cluster)?; bucket.put_resource(resource); // Start the payment flow to the cluster. - let rent = cluster.get_rent(resource); + let extra_rate = cluster.total_rent * resource as Balance; let now_ms = Self::env().block_timestamp(); - self.accounts.increase_flow(now_ms, rent, &mut bucket.flow)?; + self.accounts + .increase_flow(now_ms, extra_rate, &mut bucket.flow)?; + self.buckets.update(bucket_id, &bucket)?; - Self::env().emit_event(BucketAllocated { bucket_id, cluster_id: bucket.cluster_id, resource }); + Self::env().emit_event(BucketAllocated { + bucket_id, + cluster_id: bucket.cluster_id, + resource, + }); Ok(()) } pub fn message_bucket_settle_payment(&mut self, bucket_id: BucketId) -> Result<()> { - let bucket = self.buckets.get_mut(bucket_id)?; + let mut bucket = self.buckets.get(bucket_id)?; let now_ms = Self::env().block_timestamp(); - let cash = self.accounts.settle_flow(now_ms, &mut bucket.flow)?; + let cash = + self.accounts + .settle_flow(now_ms, &mut bucket.flow, &self.protocol.curr_converter)?; - let cluster = self.clusters.get_mut(bucket.cluster_id)?; + let mut cluster = self.clusters.get(bucket.cluster_id)?; cluster.revenues.increase(cash); + self.clusters.update(bucket.cluster_id, &cluster)?; + self.buckets.update(bucket_id, &bucket)?; - Self::env().emit_event(BucketSettlePayment { bucket_id, cluster_id: bucket.cluster_id }); + Self::env().emit_event(BucketSettlePayment { + bucket_id, + cluster_id: bucket.cluster_id, + }); Ok(()) } - pub fn message_bucket_change_params(&mut self, bucket_id: BucketId, params: BucketParams) -> Result<()> { + pub fn message_bucket_change_params( + &mut self, + bucket_id: BucketId, + bucket_params: BucketParams, + ) -> Result<()> { let caller = Self::env().caller(); - let bucket = self.buckets.get(bucket_id)?; + let mut bucket = self.buckets.get(bucket_id)?; bucket.only_owner(caller)?; - - Self::impl_change_params(&mut self.bucket_params, bucket_id, params) + bucket.set_params(bucket_params.clone())?; + self.buckets.update(bucket_id, &bucket)?; + Self::env().emit_event(BucketParamsSet { + bucket_id, + bucket_params, + }); + Ok(()) } pub fn message_bucket_get(&self, bucket_id: BucketId) -> Result { - let bucket = self.buckets.get(bucket_id)?.clone(); + let bucket = self.buckets.get(bucket_id)?; self.bucket_calculate_status(bucket_id, bucket) } - pub fn message_bucket_list(&self, offset: u32, limit: u32, filter_owner_id: Option) -> (Vec, u32) { + pub fn message_bucket_list( + &self, + offset: u32, + limit: u32, + filter_owner_id: Option, + ) -> (Vec, u32) { let mut bucket_statuses = Vec::with_capacity(limit as usize); for bucket_id in offset..offset + limit { - let bucket = match self.buckets.0.get(bucket_id as usize) { + let bucket = match self.buckets.buckets.get(bucket_id) { None => break, // No more buckets, stop. Some(bucket) => bucket, }; @@ -88,99 +142,160 @@ impl DdcBucket { // Collect all the details of the bucket. match self.bucket_calculate_status(bucket_id, bucket.clone()) { Err(_) => continue, // Skip on unexpected error. - Ok(status) => - bucket_statuses.push(status), + Ok(status) => bucket_statuses.push(status), }; } - (bucket_statuses, self.buckets.0.len().try_into().unwrap()) + (bucket_statuses, self.buckets.next_bucket_id) } pub fn message_bucket_list_for_account(&self, owner_id: AccountId) -> Vec { - self.buckets.0 - .iter() - .filter(|bucket| bucket.owner_id == owner_id) - .cloned() - .collect() + let mut result: Vec = Vec::new(); + + for bucket_id in 0..self.buckets.next_bucket_id { + let bucket = self.buckets.get(bucket_id).unwrap(); + + if bucket.owner_id == owner_id { + result.push(bucket); + }; + } + + result } - pub fn bucket_calculate_status(&self, bucket_id: BucketId, bucket: Bucket) -> Result { - let mut writer_ids = self.buckets_perms.get_bucket_readers(bucket_id); + pub fn bucket_calculate_status( + &self, + bucket_id: BucketId, + bucket: Bucket, + ) -> Result { + let mut writer_ids = self.buckets.get_bucket_readers(bucket_id); writer_ids.push(bucket.owner_id); - let rent_covered_until_ms = self.accounts.flow_covered_until(&bucket.flow)?; - let params = self.bucket_params.get(bucket_id)?.clone(); - let reader_ids = self.buckets_perms.get_bucket_readers(bucket_id); - Ok(BucketStatus { bucket_id, bucket: bucket.into(), params, writer_ids, reader_ids, rent_covered_until_ms }) + let rent_covered_until_ms = self + .accounts + .flow_covered_until(&bucket.flow, &self.protocol.curr_converter)?; + let reader_ids = self.buckets.get_bucket_readers(bucket_id); + let bucket_params = bucket.bucket_params.clone(); + + Ok(BucketStatus { + bucket_id, + params: bucket_params, + bucket: bucket.into(), + writer_ids, + reader_ids, + rent_covered_until_ms, + }) } - pub fn message_bucket_set_resource_cap(&mut self, bucket_id: BucketId, new_resource_cap: Resource) -> Result<()> { - let bucket = self.buckets.get_mut(bucket_id)?; - let cluster = self.clusters.get_mut(bucket.cluster_id)?; + pub fn message_bucket_set_resource_cap( + &mut self, + bucket_id: BucketId, + new_resource_cap: Resource, + ) -> Result<()> { + let mut bucket = self.buckets.get(bucket_id)?; + let cluster = self.clusters.get(bucket.cluster_id)?; - Self::only_owner_or_cluster_manager(bucket, cluster)?; + Self::only_owner_or_cluster_manager(&bucket, &cluster)?; bucket.set_cap(new_resource_cap); + self.buckets.update(bucket_id, &bucket)?; + Ok(()) } - pub fn message_bucket_set_availability(&mut self, bucket_id: BucketId, public_availability: bool) -> Result<()> { - let bucket = self.buckets.get_mut(bucket_id)?; - let cluster = self.clusters.get_mut(bucket.cluster_id)?; + pub fn message_bucket_set_availability( + &mut self, + bucket_id: BucketId, + public_availability: bool, + ) -> Result<()> { + let mut bucket = self.buckets.get(bucket_id)?; + let cluster = self.clusters.get(bucket.cluster_id)?; - Self::only_owner_or_cluster_manager(bucket, cluster)?; + Self::only_owner_or_cluster_manager(&bucket, &cluster)?; bucket.set_availability(public_availability); - Self::env().emit_event(BucketAvailabilityUpdated { bucket_id, public_availability }); + + self.buckets.update(bucket_id, &bucket)?; + + Self::env().emit_event(BucketAvailabilityUpdated { + bucket_id, + public_availability, + }); Ok(()) } - pub fn message_get_bucket_writers(&mut self, bucket_id: BucketId) -> Result> { - let writers = self.buckets_perms.get_bucket_writers(bucket_id); - Ok(writers) + pub fn message_get_bucket_writers(&mut self, bucket_id: BucketId) -> Vec { + self.buckets.get_bucket_writers(bucket_id) } - pub fn message_grant_writer_permission(&mut self, bucket_id: BucketId, writer: AccountId) -> Result<()> { - let bucket = self.buckets.get_mut(bucket_id)?; - let cluster = self.clusters.get_mut(bucket.cluster_id)?; + pub fn message_grant_writer_permission( + &mut self, + bucket_id: BucketId, + writer: AccountId, + ) -> Result<()> { + let bucket = self.buckets.get(bucket_id)?; + let cluster = self.clusters.get(bucket.cluster_id)?; + + Self::only_owner_or_cluster_manager(&bucket, &cluster)?; + self.buckets + .grant_writer_permission(bucket_id, writer) + .unwrap(); - Self::only_owner_or_cluster_manager(bucket, cluster)?; - self.buckets_perms.grant_writer_permission(bucket_id, writer).unwrap(); Ok(()) } - pub fn message_revoke_writer_permission(&mut self, bucket_id: BucketId, writer: AccountId) -> Result<()> { - let bucket = self.buckets.get_mut(bucket_id)?; - let cluster = self.clusters.get_mut(bucket.cluster_id)?; + pub fn message_revoke_writer_permission( + &mut self, + bucket_id: BucketId, + writer: AccountId, + ) -> Result<()> { + let bucket = self.buckets.get(bucket_id)?; + let cluster = self.clusters.get(bucket.cluster_id)?; + + Self::only_owner_or_cluster_manager(&bucket, &cluster)?; + self.buckets + .revoke_writer_permission(bucket_id, writer) + .unwrap(); - Self::only_owner_or_cluster_manager(bucket, cluster)?; - self.buckets_perms.revoke_writer_permission(bucket_id, writer).unwrap(); Ok(()) } - pub fn message_get_bucket_readers(&mut self, bucket_id: BucketId) -> Result> { - let readers = self.buckets_perms.get_bucket_readers(bucket_id); - Ok(readers) + pub fn message_get_bucket_readers(&mut self, bucket_id: BucketId) -> Vec { + self.buckets.get_bucket_readers(bucket_id) } - pub fn message_grant_reader_permission(&mut self, bucket_id: BucketId, reader: AccountId) -> Result<()> { - let bucket = self.buckets.get_mut(bucket_id)?; - let cluster = self.clusters.get_mut(bucket.cluster_id)?; + pub fn message_grant_reader_permission( + &mut self, + bucket_id: BucketId, + reader: AccountId, + ) -> Result<()> { + let bucket = self.buckets.get(bucket_id)?; + let cluster = self.clusters.get(bucket.cluster_id)?; + + Self::only_owner_or_cluster_manager(&bucket, &cluster)?; + self.buckets + .grant_reader_permission(bucket_id, reader) + .unwrap(); - Self::only_owner_or_cluster_manager(bucket, cluster)?; - self.buckets_perms.grant_reader_permission(bucket_id, reader).unwrap(); Ok(()) } - pub fn message_revoke_reader_permission(&mut self, bucket_id: BucketId, reader: AccountId) -> Result<()> { - let bucket = self.buckets.get_mut(bucket_id)?; - let cluster = self.clusters.get_mut(bucket.cluster_id)?; + pub fn message_revoke_reader_permission( + &mut self, + bucket_id: BucketId, + reader: AccountId, + ) -> Result<()> { + let bucket = self.buckets.get(bucket_id)?; + let cluster = self.clusters.get(bucket.cluster_id)?; + + Self::only_owner_or_cluster_manager(&bucket, &cluster)?; + self.buckets + .revoke_reader_permission(bucket_id, reader) + .unwrap(); - Self::only_owner_or_cluster_manager(bucket, cluster)?; - self.buckets_perms.revoke_reader_permission(bucket_id, reader).unwrap(); Ok(()) } fn only_owner_or_cluster_manager(bucket: &Bucket, cluster: &Cluster) -> Result<()> { let caller = Self::env().caller(); - cluster.only_manager(caller) - .or_else(|_| - bucket.only_owner(caller)) + cluster + .only_manager(caller) + .or_else(|_| bucket.only_owner(caller)) } -} \ No newline at end of file +} diff --git a/bucket/ddc_bucket/bucket/mod.rs b/bucket/ddc_bucket/bucket/mod.rs index bf0ae353..cd2face0 100644 --- a/bucket/ddc_bucket/bucket/mod.rs +++ b/bucket/ddc_bucket/bucket/mod.rs @@ -1,5 +1,5 @@ //! Bucket management. pub mod entity; -pub mod store; pub mod messages; +pub mod store; diff --git a/bucket/ddc_bucket/bucket/store.rs b/bucket/ddc_bucket/bucket/store.rs index 311e6293..c79fd2eb 100644 --- a/bucket/ddc_bucket/bucket/store.rs +++ b/bucket/ddc_bucket/bucket/store.rs @@ -1,40 +1,132 @@ -//! The store to create and access Buckets. - -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; -use ink_prelude::vec::Vec; - -use crate::ddc_bucket::{AccountId, Error::*, Result}; +use super::entity::{Bucket, BucketId, BucketParams}; use crate::ddc_bucket::cluster::entity::ClusterId; use crate::ddc_bucket::flow::Flow; use crate::ddc_bucket::schedule::Schedule; - -use super::entity::{Bucket, BucketId}; +use crate::ddc_bucket::{AccountId, Error::*, Result}; +use ink_prelude::vec::Vec; +use ink_storage::traits::{SpreadAllocate, SpreadLayout}; +use ink_storage::Mapping; #[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] -pub struct BucketStore(pub Vec); +#[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout, Debug))] +pub struct BucketStore { + pub next_bucket_id: u32, + pub buckets: Mapping, + pub writers: Mapping>, + pub readers: Mapping>, +} impl BucketStore { #[must_use] - pub fn create(&mut self, owner_id: AccountId, cluster_id: ClusterId) -> BucketId { + pub fn create( + &mut self, + owner_id: AccountId, + cluster_id: ClusterId, + bucket_params: BucketParams, + ) -> BucketId { + let bucket_id = self.next_bucket_id; + self.next_bucket_id = self.next_bucket_id + 1; + let bucket = Bucket { owner_id, cluster_id, - flow: Flow { from: owner_id, schedule: Schedule::empty() }, + flow: Flow { + from: owner_id, + schedule: Schedule::empty(), + }, resource_reserved: 0, resource_consumption_cap: 0, public_availability: false, + bucket_params, }; - let bucket_id: BucketId = self.0.len().try_into().unwrap(); - self.0.push(bucket); + + self.buckets.insert(&bucket_id, &bucket); bucket_id } - pub fn get(&self, bucket_id: BucketId) -> Result<&Bucket> { - self.0.get(bucket_id as usize).ok_or(BucketDoesNotExist) + pub fn get(&self, bucket_id: BucketId) -> Result { + self.buckets.get(bucket_id).ok_or(BucketDoesNotExist) + } + + pub fn update(&mut self, bucket_id: BucketId, bucket: &Bucket) -> Result<()> { + if !self.buckets.contains(&bucket_id) { + Err(BucketDoesNotExist) + } else { + self.buckets.insert(bucket_id, bucket); + Ok(()) + } } - pub fn get_mut(&mut self, bucket_id: BucketId) -> Result<&mut Bucket> { - self.0.get_mut(bucket_id as usize).ok_or(BucketDoesNotExist) + // get accounts with permission for bucket writing + pub fn get_bucket_writers(&self, key: BucketId) -> Vec { + let writers: Vec = self + .writers + .get(&key) + .unwrap_or(Vec::new()) + .iter() + .cloned() + .collect(); + return writers; + } + + // grant permission for bucket writing for some account + pub fn grant_writer_permission(&mut self, key: BucketId, writer: AccountId) -> Result<()> { + if !self.writers.contains(&key) { + let empty_vec: Vec = Vec::new(); + self.writers.insert(key, &empty_vec); + } + + let mut writers = self.writers.get(&key).unwrap(); + writers.push(writer); + self.writers.insert(key, &writers); + + Ok(()) + } + + // revoke permission for bucket writing for some account + pub fn revoke_writer_permission(&mut self, key: BucketId, writer: AccountId) -> Result<()> { + let mut writers = self.writers.get(&key).unwrap(); + if let Some(pos) = writers.iter().position(|x| *x == writer) { + writers.remove(pos); + self.writers.insert(key, &writers); + } + + Ok(()) + } + + // get accounts with permission for bucket reading + pub fn get_bucket_readers(&self, key: BucketId) -> Vec { + self.readers + .get(&key) + .unwrap_or(Vec::new()) + .iter() + .cloned() + .collect() + } + + // grant permission for bucket reading for some account + pub fn grant_reader_permission(&mut self, key: BucketId, reader: AccountId) -> Result<()> { + if !self.readers.contains(&key) { + let empty_vec: Vec = Vec::new(); + self.readers.insert(key, &empty_vec); + } + + let mut readers = self.readers.get(&key).unwrap(); + readers.push(reader); + self.readers.insert(key, &readers); + + Ok(()) + } + + // revoke permission for bucket writing for some account + pub fn revoke_reader_permission(&mut self, key: BucketId, reader: AccountId) -> Result<()> { + let mut readers = self.readers.get(&key).unwrap(); + + if let Some(pos) = readers.iter().position(|x| *x == reader) { + readers.remove(pos); + self.readers.insert(key, &readers); + } + + Ok(()) } } diff --git a/bucket/ddc_bucket/buckets_perms/mod.rs b/bucket/ddc_bucket/buckets_perms/mod.rs deleted file mode 100644 index c8b20147..00000000 --- a/bucket/ddc_bucket/buckets_perms/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Bucket perms management. - -pub mod store; diff --git a/bucket/ddc_bucket/buckets_perms/store.rs b/bucket/ddc_bucket/buckets_perms/store.rs deleted file mode 100644 index 147baf90..00000000 --- a/bucket/ddc_bucket/buckets_perms/store.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! The store to adjust write/read permissions for DDC Buckets - -use crate::ddc_bucket::{AccountId, BucketId, Result}; - -use ink_prelude::vec::Vec; -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; -use ink_storage::Mapping; - -#[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] -pub struct BucketsPermsStore { - writers: Mapping>, - readers: Mapping> -} - -impl BucketsPermsStore { - - // get accounts with permission for bucket writing - pub fn get_bucket_writers(&self, key: BucketId) -> Vec { - let writers : Vec = self.writers.get(&key) - .unwrap_or(Vec::new()) - .iter() - .cloned() - .collect(); - return writers; - } - - // grant permission for bucket writing for some account - pub fn grant_writer_permission(&mut self, key: BucketId, writer: AccountId) -> Result<()> { - if !self.writers.contains(&key) { - let empty_vec: Vec = Vec::new(); - self.writers.insert(key, &empty_vec); - } - - let mut writers = self.writers.get(&key).unwrap(); - writers.push(writer); - self.writers.insert(key, &writers); - - Ok(()) - } - - // revoke permission for bucket writing for some account - pub fn revoke_writer_permission(&mut self, key: BucketId, writer: AccountId) -> Result<()> { - let mut writers = self.writers.get(&key).unwrap(); - if let Some(pos) = writers.iter().position(|x| *x == writer) { - writers.remove(pos); - self.writers.insert(key, &writers); - } - - Ok(()) - } - - // get accounts with permission for bucket reading - pub fn get_bucket_readers(&self, key: BucketId) -> Vec { - let readers = self.readers.get(&key) - .unwrap_or(Vec::new()) - .iter() - .cloned() - .collect(); - - return readers; - } - - // grant permission for bucket reading for some account - pub fn grant_reader_permission(&mut self, key: BucketId, reader: AccountId) -> Result<()> { - if !self.readers.contains(&key) { - let empty_vec: Vec = Vec::new(); - self.readers.insert(key, &empty_vec); - } - - let mut readers = self.readers.get(&key).unwrap(); - readers.push(reader); - self.readers.insert(key, &readers); - - Ok(()) - } - - // revoke permission for bucket writing for some account - pub fn revoke_reader_permission(&mut self, key: BucketId, reader: AccountId) -> Result<()> { - let mut readers = self.readers.get(&key).unwrap(); - - if let Some(pos) = readers.iter().position(|x| *x == reader) { - readers.remove(pos); - self.readers.insert(key, &readers); - } - - Ok(()) - } - -} diff --git a/bucket/ddc_bucket/cash.rs b/bucket/ddc_bucket/cash.rs index 9c76b8d9..191b6d16 100644 --- a/bucket/ddc_bucket/cash.rs +++ b/bucket/ddc_bucket/cash.rs @@ -2,32 +2,21 @@ //! //! These data structures facilitate the correctness of money-related calculations using the Rust type system. -use ink_storage::traits::{SpreadAllocate, SpreadLayout, PackedLayout, StorageLayout}; -use ink_storage::traits::KeyPtr; +use ink_storage::traits::{PackedLayout, SpreadAllocate, SpreadLayout}; use scale::{Decode, Encode}; -use crate::ddc_bucket::{Balance, Result}; use crate::ddc_bucket::Error::InsufficientBalance; +use crate::ddc_bucket::{Balance, Result}; // TODO: remove Clone. /// Cash represents some value that was taken from someone, and that must be credited to someone. #[must_use] -#[derive(Clone, Copy, PartialEq, Encode, Decode, SpreadLayout, PackedLayout)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] +#[derive( + Default, Clone, Copy, PartialEq, Encode, Decode, SpreadAllocate, SpreadLayout, PackedLayout, +)] +#[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout, Debug))] pub struct Cash(pub Balance); -impl SpreadAllocate for Cash { - fn allocate_spread(_: &mut KeyPtr) -> Self { - Self(0) - } -} - -impl Default for Cash { - fn default() -> Self { - Self(0) - } -} - /// Payable represents some value that was credited to someone, and that must be paid by someone. /// Payable must be covered by Cash at all times to guarantee the balance of the contract. #[must_use] @@ -41,9 +30,13 @@ impl Cash { } #[must_use] - pub fn consume(self) -> Balance { self.0 } + pub fn consume(self) -> Balance { + self.0 + } - pub fn peek(&self) -> Balance { self.0 } + pub fn peek(&self) -> Balance { + self.0 + } pub fn increase(&mut self, cash: Cash) { self.0 += cash.consume(); @@ -69,9 +62,13 @@ impl Payable { } #[must_use] - pub fn consume(self) -> Balance { self.0 } + pub fn consume(self) -> Balance { + self.0 + } - pub fn peek(&self) -> Balance { self.0 } + pub fn peek(&self) -> Balance { + self.0 + } } // Implement TypeInfo with a field "value" to work with polkadot.js. @@ -87,7 +84,7 @@ impl ::scale_info::TypeInfo for Cash { .type_params([]) .composite( ::scale_info::build::Fields::named() - .field(|f| f.ty::().name("value").type_name("Balance")) + .field(|f| f.ty::().name("value").type_name("Balance")), ) } } diff --git a/bucket/ddc_bucket/cdn_cluster/entity.rs b/bucket/ddc_bucket/cdn_cluster/entity.rs deleted file mode 100644 index 8b12f810..00000000 --- a/bucket/ddc_bucket/cdn_cluster/entity.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! The data structure of Clusters. - -use ink_prelude::vec::Vec; -use ink_storage::traits::{SpreadAllocate, SpreadLayout, PackedLayout, PackedAllocate}; -use scale::{Decode, Encode}; -use ink_primitives::Key; -use crate::ddc_bucket::{AccountId, Balance, NodeId, Result}; -use crate::ddc_bucket::cash::{Cash, Payable}; -use crate::ddc_bucket::Error::{UnauthorizedClusterManager, InsufficientBalance}; -use crate::ddc_bucket::cdn_node::entity::Resource; -use crate::ddc_bucket::params::store::Params; - -pub type ClusterId = u32; -pub type ClusterParams = Params; -pub type VNodeIndex = u32; -pub type VNodeId = (ClusterId, VNodeIndex); - -#[derive(Clone, PartialEq, Encode, Decode, SpreadAllocate, SpreadLayout, PackedLayout)] -#[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] -pub struct CdnCluster { - pub manager_id: AccountId, - pub cdn_nodes: Vec, - pub resources_used: Resource, - pub revenues: Cash, - pub usd_per_gb: Balance, -} - -// https://use.ink/3.x/ink-vs-solidity#nested-mappings--custom--advanced-structures -#[allow(unconditional_recursion)] -impl ink_storage::traits::PackedAllocate for CdnCluster { - fn allocate_packed(&mut self, at: &Key) { - PackedAllocate::allocate_packed(&mut *self, at) - } -} - -#[derive(Clone, PartialEq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] -pub struct CdnClusterStatus { - pub cluster_id: ClusterId, - pub cluster: CdnCluster, -} - -impl CdnCluster { - pub fn new( - manager_id: AccountId, - cdn_nodes: Vec, - ) -> Self { - CdnCluster { - manager_id, - cdn_nodes, - // usd_per_gb: 100_000_000, // setting initially to 1 cent per GB - usd_per_gb: 104_857_600, // setting initially to 1 cent per GB - resources_used: 0, - revenues: Cash(0), - } - } - - pub fn get_revenue_cere(&self) -> Cash { - self.revenues - } - - pub fn set_rate(&mut self, usd_per_gb: Balance) -> Result<()> { - self.usd_per_gb = usd_per_gb; - Ok(()) - } - - pub fn get_rate(&self) -> Balance { - self.usd_per_gb - } - - pub fn put_revenues(&mut self, amount: Cash) { - self.revenues.increase(amount); - } - - pub fn take_revenues(&mut self, amount: Payable) -> Result<()> { - if amount.peek() > self.revenues.peek() { - return Err(InsufficientBalance); - } - self.revenues.pay_unchecked(amount); - Ok(()) - } - - pub fn only_manager(&self, caller: AccountId) -> Result<()> { - if self.manager_id == caller { Ok(()) } else { Err(UnauthorizedClusterManager) } - } -} diff --git a/bucket/ddc_bucket/cdn_cluster/messages.rs b/bucket/ddc_bucket/cdn_cluster/messages.rs deleted file mode 100644 index 3abfbf5e..00000000 --- a/bucket/ddc_bucket/cdn_cluster/messages.rs +++ /dev/null @@ -1,200 +0,0 @@ -//! The public interface to manage Clusters. - -use ink_lang::codegen::{EmitEvent, StaticEnv}; -use ink_prelude::vec::Vec; - -use crate::ddc_bucket::{AccountId, Balance, BucketId, CdnClusterCreated, ClusterDistributeRevenues, ClusterId, DdcBucket, Result}; -use crate::ddc_bucket::cash::{Cash, Payable}; -use crate::ddc_bucket::cdn_cluster::entity::{CdnCluster, CdnClusterStatus}; -use crate::ddc_bucket::Error::{ClusterManagerIsNotTrusted, UnauthorizedClusterManager, InsufficientBalance}; -use crate::ddc_bucket::cdn_node::entity::{CdnNode, NodeId, Resource}; -use crate::ddc_bucket::perm::entity::Permission; -use crate::ddc_bucket::perm::store::PermStore; - -const KB_PER_GB: Balance = 1_000_000; - -impl DdcBucket { - pub fn message_cdn_cluster_create( - &mut self, - cdn_nodes_ids: Vec, - ) -> Result { - let manager = Self::env().caller(); - - let cdn_nodes = cdn_nodes_ids.clone(); - - let mut nodes = Vec::<(NodeId, &CdnNode)>::new(); - for node_id in cdn_nodes_ids { - let node = self.cdn_nodes.get(node_id)?; - nodes.push((node_id, node)); - - // Verify that the node provider trusts the cluster manager. - Self::only_cdn_trusted_manager(&self.perms, manager, node.provider_id)?; - } - - let cluster_id = self.cdn_clusters.create(manager, cdn_nodes)?; - - Self::env().emit_event(CdnClusterCreated { cluster_id, manager }); - Ok(cluster_id) - } - - // Set the price usd per gb - pub fn message_cdn_set_rate(&mut self, cluster_id: ClusterId, usd_per_gb: u128) -> Result<()> { - let cluster = self.cdn_clusters.get_mut(cluster_id)?; - Self::only_cdn_cluster_manager(cluster)?; - - cluster.set_rate(usd_per_gb)?; - - Ok(()) - } - - // Get the price usd per gb - pub fn message_cdn_get_rate(&self, cluster_id: ClusterId) -> Result { - let cluster = self.cdn_clusters.get(cluster_id)?; - let rate = cluster.get_rate(); - Ok(rate) - } - - // First payment is for aggregate consumption for account, second is the aggregate payment for the node (u32 for ids) - pub fn message_cdn_cluster_put_revenue(&mut self, cluster_id: ClusterId, aggregates_accounts: Vec<(AccountId, u128)>, aggregates_nodes: Vec<(u32, u128)>, aggregates_buckets: Vec<(BucketId, Resource)>, era: u64) -> Result<()> { - let cluster = self.cdn_clusters.get_mut(cluster_id)?; - // Self::only_cdn_cluster_manager(cluster)?; - - let mut cluster_payment = 0; - let mut _undistributed_payment_accounts = 0; - - let aggregate_payments_accounts; - { - let conv = &self.accounts.1; - aggregate_payments_accounts = aggregates_accounts.iter().map(|(client_id, resources_used)| { - let account_id = *client_id; - let cere_payment: Balance = conv.to_cere(*resources_used as Balance * cluster.usd_per_gb / KB_PER_GB ); - (account_id, cere_payment) - }).collect::>(); - } - - for &(client_id, payment) in aggregate_payments_accounts.iter() { - if let Ok(mut account) = self.accounts.get(&client_id) { - account.withdraw_bonded(Payable(payment))?; - _undistributed_payment_accounts += payment; - self.accounts.save(&client_id, &account); - } else { - return Err(InsufficientBalance) - } - }; - - - let conv = &self.accounts.1; - let committer = &mut self.committer_store; - - for &(node_id, resources_used) in aggregates_nodes.iter() { - let node = self.cdn_nodes.get_mut(node_id)?; - let protocol_fee = self.protocol_store.get_fee_bp(); - let protocol = &mut self.protocol_store; - - let payment = conv.to_cere (resources_used as Balance * cluster.usd_per_gb / KB_PER_GB ); - - // let protocol_payment = payment * protocol_fee as u128/ 10_000; - let node_payment = payment * (10_000 - protocol_fee) as u128 / 10_000; - let protocol_payment = payment - node_payment; - - node.put_payment(node_payment); - protocol.put_revenues(Cash(protocol_payment)); - - committer.set_validated_commit(node_id, era).unwrap(); - cluster_payment += node_payment; - } - // Add check that two payments should equal? - - // Go through buckets and deduct used resources - for &(bucket_id, resources_used) in aggregates_buckets.iter() { - let bucket = self.buckets.get_mut(bucket_id)?; - - if bucket.resource_consumption_cap <= resources_used { - bucket.resource_consumption_cap -= resources_used; - } - } - - // Add revenues to cluster - cluster.put_revenues(Cash(cluster_payment)); - - Ok(()) - } - - pub fn message_cdn_cluster_distribute_revenues(&mut self, cluster_id: ClusterId) -> Result<()> { - let cluster = self.cdn_clusters.get_mut(cluster_id)?; - - // Charge the network fee from the cluster. - Self::capture_network_fee(&self.network_fee, &mut cluster.revenues)?; - - // Charge the cluster management fee. - Self::capture_fee( - self.network_fee.cluster_management_fee_bp(), - cluster.manager_id, - &mut cluster.revenues)?; - - // First accumulated revenues to distribute. - let mut distributed_revenue = 0; - - for node_id in &cluster.cdn_nodes { - let node = self.cdn_nodes.get(*node_id)?; - distributed_revenue += node.undistributed_payment; - } - - // Charge the provider payments from the cluster. - cluster.revenues.pay(Payable(distributed_revenue))?; - - // Distribute revenues to nodes - for node_id in &cluster.cdn_nodes { - let node = self.cdn_nodes.get_mut(*node_id)?; - - Self::send_cash(node.provider_id, Cash(node.undistributed_payment))?; - node.take_payment(node.undistributed_payment)?; - Self::env().emit_event(ClusterDistributeRevenues { cluster_id, provider_id: node.provider_id }); - } - - Ok(()) - } - - pub fn message_cdn_cluster_get(&self, cluster_id: ClusterId) -> Result { - let cluster = self.cdn_clusters.get(cluster_id)?.clone(); - Ok(CdnClusterStatus { cluster_id, cluster }) - } - - pub fn message_cdn_cluster_list(&self, offset: u32, limit: u32, filter_manager_id: Option) -> (Vec, u32) { - let mut clusters = Vec::with_capacity(limit as usize); - for cluster_id in offset..offset + limit { - let cluster = match self.cdn_clusters.0.get(cluster_id as usize) { - None => break, // No more items, stop. - Some(cluster) => cluster, - }; - // Apply the filter if given. - if let Some(manager_id) = filter_manager_id { - if manager_id != cluster.manager_id { - continue; // Skip non-matches. - } - } - // Include the complete status of matched items. - let status = CdnClusterStatus { - cluster_id, - cluster: cluster.clone(), - }; - clusters.push(status); - } - (clusters, self.clusters.0.len().try_into().unwrap()) - } - - fn only_cdn_cluster_manager(cluster: &CdnCluster) -> Result { - let caller = Self::env().caller(); - if caller == cluster.manager_id { - Ok(caller) - } else { - Err(UnauthorizedClusterManager) - } - } - - fn only_cdn_trusted_manager(perm_store: &PermStore, manager: AccountId, trusted_by: AccountId) -> Result<()> { - let perm = Permission::ManagerTrustedBy(trusted_by); - let trusts = perm_store.has_permission(manager, perm); - if trusts { Ok(()) } else { Err(ClusterManagerIsNotTrusted) } - } -} diff --git a/bucket/ddc_bucket/cdn_cluster/mod.rs b/bucket/ddc_bucket/cdn_cluster/mod.rs deleted file mode 100644 index e896ccbb..00000000 --- a/bucket/ddc_bucket/cdn_cluster/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! CDN Cluster management. - -pub mod entity; -pub mod store; -pub mod messages; \ No newline at end of file diff --git a/bucket/ddc_bucket/cdn_cluster/store.rs b/bucket/ddc_bucket/cdn_cluster/store.rs deleted file mode 100644 index c685af1a..00000000 --- a/bucket/ddc_bucket/cdn_cluster/store.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! The store where to create and access Clusters by ID. - -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; -use ink_prelude::vec::Vec; - -use crate::ddc_bucket::{AccountId, Error::*, NodeId, Result}; - -use super::entity::{CdnCluster, ClusterId}; - -pub const MAX_VNODES: u32 = 300; - -#[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] -pub struct CdnClusterStore(pub Vec); - -impl CdnClusterStore { - pub fn create( - &mut self, - manager_id: AccountId, - cdn_nodes: Vec, - ) -> Result { - let cluster = CdnCluster::new(manager_id, cdn_nodes); - - let cluster_id: ClusterId = self.0.len().try_into().unwrap(); - self.0.push(cluster); - - Ok(cluster_id) - } - - pub fn get(&self, cluster_id: ClusterId) -> Result<&CdnCluster> { - self.0.get(cluster_id as usize).ok_or(ClusterDoesNotExist) - } - - pub fn get_mut(&mut self, cluster_id: ClusterId) -> Result<&mut CdnCluster> { - self.0.get_mut(cluster_id as usize).ok_or(ClusterDoesNotExist) - } -} diff --git a/bucket/ddc_bucket/cdn_node/entity.rs b/bucket/ddc_bucket/cdn_node/entity.rs index 9f337506..e4b48c10 100644 --- a/bucket/ddc_bucket/cdn_node/entity.rs +++ b/bucket/ddc_bucket/cdn_node/entity.rs @@ -1,22 +1,23 @@ //! The data structure of Nodes. -use ink_storage::traits::{SpreadAllocate, PackedLayout, SpreadLayout, PackedAllocate}; -use scale::{Decode, Encode}; +use crate::ddc_bucket::{AccountId, Balance, ClusterId, Error::*, NodeStatusInCluster, Result}; +use ink_prelude::string::String; use ink_primitives::Key; -use crate::ddc_bucket::{AccountId, Balance, Error::*, Result}; -use crate::ddc_bucket::params::store::Params; +use ink_storage::traits::{PackedAllocate, PackedLayout, SpreadAllocate, SpreadLayout}; +use scale::{Decode, Encode}; pub type ProviderId = AccountId; -pub type NodeId = u32; -pub type CdnNodePublicKey = AccountId; -pub type Resource = u32; +pub type CdnNodeKey = AccountId; +pub type CdnNodeParams = String; #[derive(Clone, PartialEq, Encode, Decode, SpreadAllocate, SpreadLayout, PackedLayout)] #[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] pub struct CdnNode { pub provider_id: ProviderId, pub undistributed_payment: Balance, - pub node_pub_key: CdnNodePublicKey, + pub cdn_node_params: CdnNodeParams, + pub cluster_id: Option, + pub status_in_cluster: Option, } // https://use.ink/3.x/ink-vs-solidity#nested-mappings--custom--advanced-structures @@ -29,19 +30,70 @@ impl ink_storage::traits::PackedAllocate for CdnNode { #[derive(Clone, PartialEq, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] -pub struct CdnNodeStatus { - pub node_id: NodeId, - pub node: CdnNode, - pub params: Params, +pub struct CdnNodeInfo { + pub cdn_node_key: CdnNodeKey, + pub cdn_node: CdnNode, } +pub const CDN_NODE_PARAMS_MAX_LEN: usize = 100_000; + impl CdnNode { - pub fn cdn_account_id(&self) -> AccountId { - self.provider_id + pub fn new( + provider_id: AccountId, + cdn_node_params: CdnNodeParams, + undistributed_payment: Balance, + ) -> Result { + let mut cdn_node = CdnNode { + provider_id, + cdn_node_params: CdnNodeParams::default(), + undistributed_payment, + cluster_id: None, + status_in_cluster: None, + }; + + cdn_node.set_params(cdn_node_params)?; + Ok(cdn_node) + } + + pub fn only_provider(&self, caller: AccountId) -> Result<()> { + (self.provider_id == caller) + .then(|| ()) + .ok_or(OnlyCdnNodeProvider) + } + + pub fn only_without_cluster(&self) -> Result<()> { + self.cluster_id.map_or(Ok(()), |cluster_id| { + Err(CdnNodeIsAddedToCluster(cluster_id)) + }) + } + + pub fn only_with_cluster(&self, cluster_id: ClusterId) -> Result<()> { + self.cluster_id + .is_some() + .then(|| ()) + .ok_or(CdnNodeIsNotAddedToCluster(cluster_id)) } - pub fn only_owner(&self, provider_id: AccountId) -> Result<()> { - if self.provider_id == provider_id { Ok(()) } else { Err(UnauthorizedProvider) } + pub fn set_params(&mut self, cdn_node_params: CdnNodeParams) -> Result<()> { + if cdn_node_params.len() > CDN_NODE_PARAMS_MAX_LEN { + return Err(ParamsSizeExceedsLimit); + } + self.cdn_node_params = cdn_node_params; + Ok(()) + } + + pub fn set_cluster(&mut self, cluster_id: ClusterId, status: NodeStatusInCluster) { + self.cluster_id = Some(cluster_id); + self.status_in_cluster = Some(status); + } + + pub fn unset_cluster(&mut self) { + self.cluster_id = None; + self.status_in_cluster = None; + } + + pub fn change_status_in_cluster(&mut self, status: NodeStatusInCluster) { + self.status_in_cluster = Some(status); } pub fn put_payment(&mut self, amount: Balance) { @@ -56,4 +108,4 @@ impl CdnNode { Err(InsufficientBalance) } } -} \ No newline at end of file +} diff --git a/bucket/ddc_bucket/cdn_node/messages.rs b/bucket/ddc_bucket/cdn_node/messages.rs index 28843d9f..4112c452 100644 --- a/bucket/ddc_bucket/cdn_node/messages.rs +++ b/bucket/ddc_bucket/cdn_node/messages.rs @@ -1,87 +1,107 @@ //! The public interface to manage Nodes. -use ink_lang::codegen::StaticEnv; +use crate::ddc_bucket::{ + AccountId, Balance, CdnNodeCreated, CdnNodeParamsSet, CdnNodeRemoved, DdcBucket, Result, +}; +use ink_lang::codegen::{EmitEvent, StaticEnv}; use ink_prelude::vec::Vec; -use crate::ddc_bucket::{AccountId, DdcBucket, Result}; -use crate::ddc_bucket::params::store::Params; -use crate::ddc_bucket::perm::entity::Permission; - -use super::entity::{NodeId, CdnNodeStatus}; +use super::entity::{CdnNodeInfo, CdnNodeKey, CdnNodeParams}; impl DdcBucket { - pub fn message_cdn_node_trust_manager(&mut self, manager: AccountId, is_trusted: bool) -> Result<()> { - let trust_giver = Self::env().caller(); - let permission = Permission::ManagerTrustedBy(trust_giver); - self.impl_grant_permission(manager, permission, is_trusted) + pub fn message_cdn_node_create( + &mut self, + cdn_node_key: CdnNodeKey, + cdn_node_params: CdnNodeParams, + ) -> Result { + let caller = Self::env().caller(); + let undistributed_payment: Balance = 0; + self.cdn_nodes.create( + cdn_node_key, + caller, + cdn_node_params.clone(), + undistributed_payment, + )?; + + Self::env().emit_event(CdnNodeCreated { + cdn_node_key, + provider_id: caller, + cdn_node_params, + undistributed_payment, + }); + + Ok(cdn_node_key) } - pub fn message_cdn_node_create(&mut self, node_params: Params, pubkey: AccountId) -> Result { - let provider_id = Self::env().caller(); + pub fn message_remove_cdn_node(&mut self, cdn_node_key: CdnNodeKey) -> Result<()> { + let caller = Self::env().caller(); + let cdn_node = self.cdn_nodes.get(cdn_node_key)?; + cdn_node.only_provider(caller)?; + cdn_node.only_without_cluster()?; + self.cdn_nodes.remove(cdn_node_key); - let node_id = self.cdn_nodes.create(provider_id, 0, pubkey).unwrap(); - let params_id = self.cdn_node_params.create(node_params.clone())?; - assert_eq!(node_id, params_id); + Self::env().emit_event(CdnNodeRemoved { cdn_node_key }); - Ok(node_id) + Ok(()) } - pub fn message_cdn_node_change_params(&mut self, node_id: NodeId, params: Params) -> Result<()> { + pub fn message_cdn_node_set_params( + &mut self, + cdn_node_key: CdnNodeKey, + cdn_node_params: CdnNodeParams, + ) -> Result<()> { let caller = Self::env().caller(); - let node = self.cdn_nodes.get(node_id)?; - node.only_owner(caller)?; + let mut cdn_node = self.cdn_nodes.get(cdn_node_key)?; + cdn_node.only_provider(caller)?; + cdn_node.set_params(cdn_node_params.clone())?; + self.cdn_nodes.update(cdn_node_key, &cdn_node)?; + + Self::env().emit_event(CdnNodeParamsSet { + cdn_node_key, + cdn_node_params, + }); - Self::impl_change_params(&mut self.cdn_node_params, node_id, params) + Ok(()) } - pub fn message_cdn_node_get(&self, node_id: NodeId) -> Result { - let node = self.cdn_nodes.get(node_id)?.clone(); - let params = self.cdn_node_params.get(node_id)?.clone(); - Ok(CdnNodeStatus { node_id, node, params }) + pub fn message_cdn_node_get(&self, cdn_node_key: CdnNodeKey) -> Result { + let cdn_node = self.cdn_nodes.get(cdn_node_key)?; + Ok(CdnNodeInfo { + cdn_node_key, + cdn_node, + }) } - pub fn message_cdn_node_list(&self, offset: u32, limit: u32, filter_provider_id: Option) -> (Vec, u32) { + pub fn message_cdn_node_list( + &self, + offset: u32, + limit: u32, + filter_provider_id: Option, + ) -> (Vec, u32) { let mut cdn_nodes = Vec::with_capacity(limit as usize); - for node_id in offset..offset + limit { - let node = match self.cdn_nodes.cdn_nodes.get(node_id as usize) { + for idx in offset..offset + limit { + let cdn_node_key = match self.cdn_nodes.keys.get(idx as usize) { None => break, // No more items, stop. - Some(node) => node, + Some(cdn_node_key) => cdn_node_key.clone(), }; + + let cdn_node = self.cdn_nodes.cdn_nodes.get(cdn_node_key).unwrap(); // Apply the filter if given. if let Some(provider_id) = filter_provider_id { - if provider_id != node.provider_id { + if provider_id != cdn_node.provider_id { continue; // Skip non-matches. } } // Include the complete status of matched items. - let status = CdnNodeStatus { - node_id, - node: node.clone(), - params: self.cdn_node_params.get(node_id).unwrap().clone(), + let status = CdnNodeInfo { + cdn_node_key, + cdn_node, }; - + cdn_nodes.push(status); } - (cdn_nodes, self.cdn_nodes.cdn_nodes.len().try_into().unwrap()) - } - - pub fn message_cdn_node_get_by_pub_key(&self, pubkey: AccountId) -> Result { - let node_id = self.cdn_nodes.get_by_pub_key(pubkey).unwrap(); - let node = self.cdn_nodes.get(node_id)?.clone(); - let params = self.cdn_node_params.get(node_id)?.clone(); - Ok(CdnNodeStatus { - node_id, - node, - params, - }) - } - - pub fn message_remove_cdn_node(&mut self, node_id: NodeId) -> Result<()> { - let caller = Self::env().caller(); - let node = self.cdn_nodes.get_mut(node_id)?; - node.only_owner(caller)?; - self.cdn_nodes.remove_node(node_id) + (cdn_nodes, self.cdn_nodes.keys.len().try_into().unwrap()) } } diff --git a/bucket/ddc_bucket/cdn_node/mod.rs b/bucket/ddc_bucket/cdn_node/mod.rs index 41a81e69..6c50e8f6 100644 --- a/bucket/ddc_bucket/cdn_node/mod.rs +++ b/bucket/ddc_bucket/cdn_node/mod.rs @@ -1,5 +1,5 @@ //! CDN Node management. pub mod entity; +pub mod messages; pub mod store; -pub mod messages; \ No newline at end of file diff --git a/bucket/ddc_bucket/cdn_node/store.rs b/bucket/ddc_bucket/cdn_node/store.rs index 8a01a0f1..0fdde083 100644 --- a/bucket/ddc_bucket/cdn_node/store.rs +++ b/bucket/ddc_bucket/cdn_node/store.rs @@ -1,59 +1,62 @@ //! The store where to create and access Nodes. -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; -use ink_prelude::vec::Vec as InkVec; -use ink_storage::Mapping; - +use super::entity::{CdnNode, CdnNodeKey, CdnNodeParams}; use crate::ddc_bucket::{AccountId, Balance, Error::*, Result}; +use ink_prelude::vec::Vec; +use ink_storage::traits::{SpreadAllocate, SpreadLayout}; +use ink_storage::Mapping; -use super::entity::{CdnNode, NodeId}; - -pub type NodePublicKey = AccountId; +// https://use.ink/datastructures/storage-layout#packed-vs-non-packed-layout +// There is a buffer with only limited capacity (around 16KB in the default configuration) available. +pub const MAX_CDN_NODES_LEN_IN_VEC: usize = 400; #[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] +#[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout, Debug))] pub struct CdnNodeStore { - pub pub_key_to_node: Mapping, - pub cdn_nodes: InkVec + pub cdn_nodes: Mapping, + // todo: remove this vector as it can store an arbitrary number of elements and easily exceed 16KB limit + pub keys: Vec, } impl CdnNodeStore { - pub fn create( - &mut self, - provider_id: AccountId, - undistributed_payment: Balance, - pubkey: AccountId, - ) -> Result { - let node_id: NodeId = self.cdn_nodes.len().try_into().unwrap(); - let node = CdnNode { provider_id, undistributed_payment, node_pub_key: pubkey }; - - let exists = self.pub_key_to_node.contains(&pubkey); - if exists { - return Err(NodeAlreadyExists); - } - - self.cdn_nodes.push(node); - self.pub_key_to_node.insert(&pubkey, &node_id); - Ok(node_id) - } - - pub fn get_by_pub_key(&self, pubkey: AccountId) -> Result { - self.pub_key_to_node.get(&pubkey).ok_or(NodeDoesNotExist) -} - - pub fn get(&self, node_id: NodeId) -> Result<&CdnNode> { - self.cdn_nodes.get(node_id as usize).ok_or(NodeDoesNotExist) - } - - pub fn get_mut(&mut self, node_id: NodeId) -> Result<&mut CdnNode> { - self.cdn_nodes.get_mut(node_id as usize).ok_or(NodeDoesNotExist) - } - - pub fn remove_node(&mut self, node_id: NodeId) -> Result<()> { - let total_nodes = self.cdn_nodes.len(); - let last_node = self.cdn_nodes.get(total_nodes - 1).ok_or(NodeDoesNotExist).unwrap(); - self.pub_key_to_node.insert(&last_node.node_pub_key, &node_id); - self.cdn_nodes.swap_remove(node_id.try_into().unwrap()); - Ok(()) -} + pub fn create( + &mut self, + cdn_node_key: CdnNodeKey, + provider_id: AccountId, + cdn_node_params: CdnNodeParams, + undistributed_payment: Balance, + ) -> Result { + if self.cdn_nodes.contains(&cdn_node_key) { + return Err(CdnNodeAlreadyExists); + } + + if self.keys.len() + 1 > MAX_CDN_NODES_LEN_IN_VEC { + return Err(CdnNodesSizeExceedsLimit); + } + + let cdn_node = CdnNode::new(provider_id, cdn_node_params, undistributed_payment)?; + self.cdn_nodes.insert(&cdn_node_key, &cdn_node); + self.keys.push(cdn_node_key); + Ok(cdn_node_key) + } + + pub fn get(&self, cdn_node_key: CdnNodeKey) -> Result { + self.cdn_nodes.get(cdn_node_key).ok_or(CdnNodeDoesNotExist) + } + + pub fn update(&mut self, cdn_node_key: CdnNodeKey, cdn_node: &CdnNode) -> Result<()> { + if !self.cdn_nodes.contains(&cdn_node_key) { + Err(CdnNodeDoesNotExist) + } else { + self.cdn_nodes.insert(cdn_node_key, cdn_node); + Ok(()) + } + } + + pub fn remove(&mut self, cdn_node_key: CdnNodeKey) { + self.cdn_nodes.remove(cdn_node_key); + if let Some(pos) = self.keys.iter().position(|x| *x == cdn_node_key) { + self.keys.remove(pos); + }; + } } diff --git a/bucket/ddc_bucket/cluster/entity.rs b/bucket/ddc_bucket/cluster/entity.rs index 35e435d9..896dba5f 100644 --- a/bucket/ddc_bucket/cluster/entity.rs +++ b/bucket/ddc_bucket/cluster/entity.rs @@ -1,32 +1,40 @@ //! The data structure of Clusters. -// use ink_storage::Mapping; -// use ink_prelude::vec::Vec; +use crate::ddc_bucket::cash::{Cash, Payable}; +use crate::ddc_bucket::cdn_node::entity::CdnNodeKey; +use crate::ddc_bucket::node::entity::{NodeKey, Resource}; +use crate::ddc_bucket::Error::{InsufficientBalance, OnlyClusterManager}; +use crate::ddc_bucket::{AccountId, Balance, Error::*, Result, VNodeToken}; +use ink_prelude::string::String; use ink_prelude::vec::Vec; -use ink_storage::traits::{SpreadAllocate, SpreadLayout, PackedLayout, PackedAllocate}; -use scale::{Decode, Encode}; use ink_primitives::Key; -use crate::ddc_bucket::cash::Cash; -use crate::ddc_bucket::node::entity::NodeId; -use crate::ddc_bucket::node::entity::Resource; -use crate::ddc_bucket::params::store::Params; -use crate::ddc_bucket::Error::UnauthorizedClusterManager; -use crate::ddc_bucket::{AccountId, Balance, Error::InsufficientResources, Result}; +use ink_storage::traits::{PackedAllocate, PackedLayout, SpreadAllocate, SpreadLayout}; +use scale::{Decode, Encode}; pub type ClusterId = u32; -pub type ClusterParams = Params; -pub type VNodeIndex = u32; -pub type VNodeId = (ClusterId, VNodeIndex); +pub type ClusterParams = String; + +// https://use.ink/datastructures/storage-layout#packed-vs-non-packed-layout +// There is a buffer with only limited capacity (around 16KB in the default configuration) available. +pub const MAX_CLUSTER_NODES_LEN_IN_VEC: usize = 200; +pub const MAX_CLUSTER_CDN_NODES_LEN_IN_VEC: usize = 200; #[derive(Clone, PartialEq, Encode, Decode, SpreadAllocate, PackedLayout, SpreadLayout)] #[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] pub struct Cluster { pub manager_id: AccountId, - pub resource_per_vnode: Resource, + pub cluster_params: ClusterParams, + + // storage nodes + pub nodes_keys: Vec, + pub resource_per_v_node: Resource, pub resource_used: Resource, pub revenues: Cash, - pub node_ids: Vec, - pub v_nodes: Vec>, pub total_rent: Balance, + + // cdn nodes + pub cdn_nodes_keys: Vec, + pub cdn_revenues: Cash, + pub cdn_usd_per_gb: Balance, } // https://use.ink/3.x/ink-vs-solidity#nested-mappings--custom--advanced-structures @@ -39,103 +47,126 @@ impl ink_storage::traits::PackedAllocate for Cluster { #[derive(Clone, PartialEq, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] -pub struct ClusterStatus { +pub struct ClusterInfo { pub cluster_id: ClusterId, pub cluster: Cluster, - pub params: Params, + pub cluster_v_nodes: Vec, } +pub const CLUSTER_PARAMS_MAX_LEN: usize = 100_000; +pub const CDN_USD_PER_GB: Balance = 104_857_600; +pub const KB_PER_GB: Balance = 1_000_000; + impl Cluster { - pub fn new(manager_id: AccountId, v_nodes_arr: &Vec>, node_ids: &Vec) -> Self { - Cluster { + pub fn new( + manager_id: AccountId, + cluster_params: ClusterParams, + resource_per_v_node: Resource, + ) -> Result { + let mut cluster = Cluster { manager_id, - resource_per_vnode: 0, - resource_used: 0, + cluster_params: ClusterParams::default(), + nodes_keys: Vec::new(), + resource_per_v_node, + resource_used: Resource::from(0u32), revenues: Cash(0), - v_nodes: v_nodes_arr.clone(), - node_ids: node_ids.clone(), - total_rent: 0, - } + total_rent: Balance::from(0u128), + cdn_nodes_keys: Vec::new(), + cdn_usd_per_gb: CDN_USD_PER_GB, // setting initially to 1 cent per GB + cdn_revenues: Cash(0), + }; + + cluster.set_params(cluster_params)?; + Ok(cluster) } - pub fn get_rent(&self, resource: Resource) -> Balance { - return self.total_rent * resource as Balance; + pub fn only_manager(&self, caller: AccountId) -> Result<()> { + (self.manager_id == caller) + .then(|| ()) + .ok_or(OnlyClusterManager) } - pub fn put_resource(&mut self, amount: Resource) { - self.resource_per_vnode += amount; + pub fn only_without_nodes(&self) -> Result<()> { + if self.nodes_keys.is_empty() && self.cdn_nodes_keys.is_empty() { + Ok(()) + } else { + Err(ClusterIsNotEmpty) + } } - pub fn take_resource(&mut self, amount: Resource) -> Result<()> { - let used = self.resource_used + amount; - if used > self.resource_per_vnode { - return Err(InsufficientResources); + pub fn add_node(&mut self, node_key: NodeKey) -> Result<()> { + if self.nodes_keys.len() + 1 > MAX_CLUSTER_NODES_LEN_IN_VEC { + return Err(NodesSizeExceedsLimit); } - self.resource_used = used; + self.nodes_keys.push(node_key); Ok(()) } - // v_nodes should be sorted - pub fn replace_v_node(&mut self, v_nodes: Vec, node_id: NodeId) { - let old_v_nodes = &self.v_nodes; - let old_node_ids = &self.node_ids; - - let mut new_v_nodes = Vec::>::new(); - let mut new_node_ids = Vec::::new(); - - let mut new_v_nodes_idx = 0; - let mut v_nodes_for_new_node = Vec::::new(); - - for wrapper_idx in 0..old_v_nodes.len() { - let mut v_nodes_wrapper = Vec::::new(); - for idx in 0..old_v_nodes.get(wrapper_idx).unwrap().len() { - let new_v_node = match v_nodes.get(new_v_nodes_idx) { - Some(v) => *v, - None => 0, - }; - - if old_v_nodes - .get(wrapper_idx) - .unwrap() - .get(idx) - .unwrap() - .clone() - == new_v_node - { - v_nodes_for_new_node.push(new_v_node); - new_v_nodes_idx += 1; - } else { - v_nodes_wrapper.push( - old_v_nodes - .get(wrapper_idx) - .unwrap() - .get(idx) - .unwrap() - .clone(), - ); - } - } - - new_v_nodes.push(v_nodes_wrapper); - new_node_ids.push(*old_node_ids.get(wrapper_idx).unwrap()); + pub fn remove_node(&mut self, node_key: NodeKey) { + if let Some(pos) = self.nodes_keys.iter().position(|x| *x == node_key) { + self.nodes_keys.remove(pos); } + } - new_v_nodes.push(v_nodes_for_new_node); - new_node_ids.push(node_id); + pub fn add_cdn_node(&mut self, cdn_node_key: CdnNodeKey) -> Result<()> { + if self.cdn_nodes_keys.len() + 1 > MAX_CLUSTER_CDN_NODES_LEN_IN_VEC { + return Err(CdnNodesSizeExceedsLimit); + } + self.cdn_nodes_keys.push(cdn_node_key); + Ok(()) + } - self.v_nodes = new_v_nodes; - self.node_ids = new_node_ids; + pub fn remove_cdn_node(&mut self, cdn_node_key: CdnNodeKey) { + if let Some(pos) = self.cdn_nodes_keys.iter().position(|x| *x == cdn_node_key) { + self.cdn_nodes_keys.remove(pos); + } } - pub fn only_manager(&self, caller: AccountId) -> Result<()> { - if self.manager_id == caller { - Ok(()) - } else { - Err(UnauthorizedClusterManager) + pub fn set_params(&mut self, cluster_params: ClusterParams) -> Result<()> { + if cluster_params.len() > CLUSTER_PARAMS_MAX_LEN { + return Err(ParamsSizeExceedsLimit); } + self.cluster_params = cluster_params; + Ok(()) + } + + pub fn increase_rent(&mut self, amount: Balance) { + self.total_rent += amount; + } + + pub fn decrease_rent(&mut self, amount: Balance) { + self.total_rent -= amount; } - pub fn change_rent(&mut self, rent: Balance) { - self.total_rent = rent; + pub fn set_resource_per_v_node(&mut self, resource_per_v_node: Resource) { + self.resource_per_v_node = resource_per_v_node; + } + + pub fn take_resource(&mut self, amount: Resource) { + self.resource_used = self.resource_used + amount; + } + + pub fn cdn_get_revenue_cere(&self) -> Cash { + self.cdn_revenues + } + + pub fn cdn_set_rate(&mut self, cdn_usd_per_gb: Balance) { + self.cdn_usd_per_gb = cdn_usd_per_gb; + } + + pub fn cdn_get_rate(&self) -> Balance { + self.cdn_usd_per_gb + } + + pub fn cdn_put_revenues(&mut self, amount: Cash) { + self.cdn_revenues.increase(amount); + } + + pub fn cdn_take_revenues(&mut self, amount: Payable) -> Result<()> { + if amount.peek() > self.cdn_revenues.peek() { + return Err(InsufficientBalance); + } + self.cdn_revenues.pay_unchecked(amount); + Ok(()) } } diff --git a/bucket/ddc_bucket/cluster/messages.rs b/bucket/ddc_bucket/cluster/messages.rs index dc60b33d..11b6043d 100644 --- a/bucket/ddc_bucket/cluster/messages.rs +++ b/bucket/ddc_bucket/cluster/messages.rs @@ -2,16 +2,20 @@ use ink_lang::codegen::{EmitEvent, StaticEnv}; use ink_prelude::vec::Vec; +use crate::ddc_bucket::bucket::entity::BucketId; use crate::ddc_bucket::cash::{Cash, Payable}; -use crate::ddc_bucket::cluster::entity::{Cluster, ClusterStatus}; -use crate::ddc_bucket::node::entity::{Node, NodeId, Resource}; +use crate::ddc_bucket::cdn_node::entity::{CdnNode, CdnNodeKey}; +use crate::ddc_bucket::cluster::entity::{ClusterInfo, KB_PER_GB}; +use crate::ddc_bucket::node::entity::{Node, NodeKey, Resource}; use crate::ddc_bucket::perm::entity::Permission; -use crate::ddc_bucket::perm::store::PermStore; +use crate::ddc_bucket::topology::store::VNodeToken; use crate::ddc_bucket::ClusterNodeReplaced; -use crate::ddc_bucket::Error::{ClusterManagerIsNotTrusted, UnauthorizedClusterManager}; use crate::ddc_bucket::{ - AccountId, Balance, ClusterCreated, ClusterDistributeRevenues, ClusterReserveResource, - DdcBucket, Result, + AccountId, Balance, ClusterCdnNodeAdded, ClusterCdnNodeRemoved, ClusterCdnNodeStatusSet, + ClusterCreated, ClusterDistributeCdnRevenues, ClusterDistributeRevenues, ClusterNodeAdded, + ClusterNodeRemoved, ClusterNodeReset, ClusterNodeStatusSet, ClusterParamsSet, ClusterRemoved, + ClusterReserveResource, DdcBucket, Error::*, NodeStatusInCluster, PermissionGranted, + PermissionRevoked, Result, BASIS_POINTS, }; use super::entity::{ClusterId, ClusterParams}; @@ -19,196 +23,401 @@ use super::entity::{ClusterId, ClusterParams}; impl DdcBucket { pub fn message_cluster_create( &mut self, - v_nodes: Vec>, - node_ids: Vec, cluster_params: ClusterParams, + resource_per_v_node: Resource, ) -> Result { - let manager = Self::env().caller(); - - let mut nodes = Vec::<(NodeId, &Node)>::new(); - for node_id in &node_ids { - let node = self.nodes.get(*node_id)?; - nodes.push((*node_id, node)); - - // Verify that the node provider trusts the cluster manager. - Self::only_trusted_manager(&self.perms, manager, node.provider_id)?; - } - - let cluster_id = self.clusters.create(manager, &v_nodes, &node_ids)?; - let rent = self - .topology_store - .create_topology(cluster_id, v_nodes, nodes.iter().map(|(id, node)| (*id, *node)).collect())?; + let caller = Self::env().caller(); - self.clusters.get_mut(cluster_id).unwrap().change_rent(rent); + let cluster_id = + self.clusters + .create(caller, cluster_params.clone(), resource_per_v_node)?; - let params_id = self.cluster_params.create(cluster_params.clone())?; - assert_eq!(cluster_id, params_id); + self.topology.create_topology(cluster_id)?; Self::env().emit_event(ClusterCreated { cluster_id, - manager, + manager_id: caller, cluster_params, }); + Ok(cluster_id) } pub fn message_cluster_add_node( &mut self, cluster_id: ClusterId, - node_ids: Vec, - v_nodes: Vec>, + node_key: NodeKey, + v_nodes: Vec, + ) -> Result<()> { + let caller = Self::env().caller(); + + let mut node: Node = self.nodes.get(node_key)?; + node.only_without_cluster()?; + self.only_trusted_cluster_manager(node.provider_id)?; + + let mut cluster = self.clusters.get(cluster_id)?; + cluster.only_manager(caller)?; + + node.set_cluster(cluster_id, NodeStatusInCluster::ADDING); + cluster.add_node(node_key)?; + for _v_node in &v_nodes { + node.reserve_resource(cluster.resource_per_v_node)?; + cluster.increase_rent(node.rent_v_node_per_month); + } + + self.nodes.update(node_key, &node)?; + self.clusters.update(cluster_id, &cluster)?; + + self.topology + .add_node(cluster_id, node_key, v_nodes.clone())?; + + Self::env().emit_event(ClusterNodeAdded { + cluster_id, + node_key, + v_nodes, + }); + + Ok(()) + } + + pub fn message_cluster_remove_node( + &mut self, + cluster_id: ClusterId, + node_key: NodeKey, ) -> Result<()> { - let manager = Self::env().caller(); - let mut nodes = Vec::<(NodeId, &Node)>::new(); + let caller = Self::env().caller(); - for node_id in &node_ids { - let node = self.nodes.get(*node_id)?; - nodes.push((*node_id, node)); + let mut node = self.nodes.get(node_key)?; + let mut cluster = self.clusters.get(cluster_id)?; - // Verify that the node provider trusts the cluster manager. - Self::only_trusted_manager(&self.perms, manager, node.provider_id)?; + if !cluster.only_manager(caller).is_ok() && !node.only_provider(caller).is_ok() { + return Err(OnlyClusterManagerOrNodeProvider); + } + node.only_with_cluster(cluster_id)?; + + node.unset_cluster(); + cluster.remove_node(node_key); + let v_nodes = self.topology.get_v_nodes_by_node(node_key); + for _v_node in &v_nodes { + node.release_resource(cluster.resource_per_v_node); + cluster.decrease_rent(node.rent_v_node_per_month); } - // add node and redistribute v_nodes + self.nodes.update(node_key, &node)?; + self.clusters.update(cluster_id, &cluster)?; + + self.topology.remove_node(cluster_id, node_key)?; + + Self::env().emit_event(ClusterNodeRemoved { + cluster_id, + node_key, + }); + + Ok(()) + } + + pub fn message_cluster_replace_node( + &mut self, + cluster_id: ClusterId, + v_nodes: Vec, + new_node_key: NodeKey, + ) -> Result<()> { + let caller = Self::env().caller(); + let cluster = self.clusters.get(cluster_id)?; + let mut new_node = self.nodes.get(new_node_key)?; + new_node.only_with_cluster(cluster_id)?; + cluster.only_manager(caller)?; - let mut old_v_nodes = Vec::::new(); - for v_node_wrapper in &cluster.v_nodes { - for v_node in v_node_wrapper { - old_v_nodes.push(*v_node); - } + // Give back resources to the old node for all its v_nodes + for v_node in &v_nodes { + let old_node_key = self.topology.get_node_by_v_node(cluster_id, *v_node)?; + + // Give back resources to the old node + let mut old_node = self.nodes.get(old_node_key)?; + old_node.release_resource(cluster.resource_per_v_node); + self.nodes.update(old_node_key, &old_node)?; + + // Reserve resources on the new node. + new_node.reserve_resource(cluster.resource_per_v_node)?; + self.nodes.update(new_node_key, &new_node)?; } - // TODO: change v_nodes inside cluster entity - let total_rent = self - .topology_store - .add_node(cluster_id, &old_v_nodes, &v_nodes, nodes.iter().map(|(id, node)| (*id, *node)).collect())?; + self.topology + .replace_node(cluster_id, new_node_key, v_nodes.clone())?; - let cluster = self.clusters.get_mut(cluster_id)?; - cluster.total_rent = total_rent as Balance; - cluster.v_nodes = v_nodes; - cluster.node_ids = node_ids; + Self::env().emit_event(ClusterNodeReplaced { + cluster_id, + node_key: new_node_key, + v_nodes, + }); Ok(()) } - pub fn message_cluster_reserve_resource( + pub fn message_cluster_reset_node( &mut self, cluster_id: ClusterId, - resource: Resource, + node_key: NodeKey, + new_v_nodes: Vec, ) -> Result<()> { - let cluster = self.clusters.get_mut(cluster_id)?; - Self::only_cluster_manager(cluster)?; - cluster.put_resource(resource); - - for v_nodes_wrapper in &cluster.v_nodes { - for &v_node in v_nodes_wrapper { - let node_id = self.topology_store.get(cluster_id, v_node)?; - let node = self.nodes.get_mut(node_id)?; - node.take_resource(resource)?; + let caller = Self::env().caller(); + + let mut cluster = self.clusters.get(cluster_id)?; + + let mut node = self.nodes.get(node_key)?; + node.only_with_cluster(cluster_id)?; + cluster.only_manager(caller)?; + + let old_v_nodes = self.topology.get_v_nodes_by_node(node_key); + + if new_v_nodes.len() != old_v_nodes.len() { + if new_v_nodes.len() > old_v_nodes.len() { + for _i in 0..new_v_nodes.len() - old_v_nodes.len() { + node.reserve_resource(cluster.resource_per_v_node)?; + cluster.increase_rent(node.rent_v_node_per_month); + } + } else if new_v_nodes.len() < old_v_nodes.len() { + for _i in 0..old_v_nodes.len() - new_v_nodes.len() { + node.release_resource(cluster.resource_per_v_node); + cluster.decrease_rent(node.rent_v_node_per_month); + } } + + self.nodes.update(node_key, &node)?; + self.clusters.update(cluster_id, &cluster)?; } - Self::env().emit_event(ClusterReserveResource { + self.topology + .reset_node(cluster_id, node_key, new_v_nodes.clone())?; + + Self::env().emit_event(ClusterNodeReset { cluster_id, - resource, + node_key: node_key, + v_nodes: new_v_nodes, }); + Ok(()) } - // v_nodes length should be equal to v_nodes which were assigned to a physical node before - pub fn message_cluster_replace_node( + pub fn message_cluster_add_cdn_node( &mut self, cluster_id: ClusterId, - v_nodes: Vec, - new_node_id: NodeId, + cdn_node_key: CdnNodeKey, ) -> Result<()> { - let cluster = self.clusters.get_mut(cluster_id)?; - let manager = Self::only_cluster_manager(cluster)?; + let caller = Self::env().caller(); - // Give back resources to the old node for all its v_nodes - for v_node in v_nodes.clone() { - let old_node_id = self.topology_store.get(cluster_id, v_node)?; + let mut cdn_node: CdnNode = self.cdn_nodes.get(cdn_node_key)?; + cdn_node.only_without_cluster()?; + self.only_trusted_cluster_manager(cdn_node.provider_id)?; - // Give back resources to the old node - let old_node = self.nodes.get_mut(old_node_id)?; - old_node.put_resource(cluster.resource_per_vnode); + let mut cluster = self.clusters.get(cluster_id)?; + cluster.only_manager(caller)?; - let new_node = self.nodes.get_mut(new_node_id)?; + cdn_node.set_cluster(cluster_id, NodeStatusInCluster::ADDING); + self.cdn_nodes.update(cdn_node_key, &cdn_node)?; - // Verify that the provider of the new node trusts the cluster manager. - Self::only_trusted_manager(&self.perms, manager, new_node.provider_id)?; + cluster.add_cdn_node(cdn_node_key)?; + self.clusters.update(cluster_id, &cluster)?; - // Reserve resources on the new node. - new_node.take_resource(cluster.resource_per_vnode)?; + Self::env().emit_event(ClusterCdnNodeAdded { + cluster_id, + cdn_node_key, + }); + + Ok(()) + } + + pub fn message_cluster_remove_cdn_node( + &mut self, + cluster_id: ClusterId, + cdn_node_key: CdnNodeKey, + ) -> Result<()> { + let caller = Self::env().caller(); - self.topology_store.save(cluster_id, v_node, new_node_id); + let mut cdn_node: CdnNode = self.cdn_nodes.get(cdn_node_key)?; + let mut cluster = self.clusters.get(cluster_id)?; + + if !cluster.only_manager(caller).is_ok() && !cdn_node.only_provider(caller).is_ok() { + return Err(OnlyClusterManagerOrCdnNodeProvider); } - self.topology_store - .replace_node(cluster_id, v_nodes.clone(), new_node_id)?; + cdn_node.only_with_cluster(cluster_id)?; - cluster.replace_v_node(v_nodes, new_node_id); + cdn_node.unset_cluster(); + self.cdn_nodes.update(cdn_node_key, &cdn_node)?; - Self::env().emit_event(ClusterNodeReplaced { + cluster.remove_cdn_node(cdn_node_key); + self.clusters.update(cluster_id, &cluster)?; + + Self::env().emit_event(ClusterCdnNodeRemoved { cluster_id, - node_id: new_node_id, + cdn_node_key, }); + Ok(()) } - pub fn message_cluster_distribute_revenues(&mut self, cluster_id: ClusterId) -> Result<()> { - let cluster = self.clusters.get_mut(cluster_id)?; + pub fn message_cluster_remove(&mut self, cluster_id: ClusterId) -> Result<()> { + let caller = Self::env().caller(); - // Charge the network fee from the cluster. - Self::capture_network_fee(&self.network_fee, &mut cluster.revenues)?; + let cluster = self.clusters.get(cluster_id)?; + cluster.only_manager(caller)?; + cluster.only_without_nodes()?; - // Charge the cluster management fee. - Self::capture_fee( - self.network_fee.cluster_management_fee_bp(), - cluster.manager_id, - &mut cluster.revenues, - )?; + self.clusters.remove(cluster_id); + self.topology.remove_topology(cluster_id)?; - // Charge the provider payments from the cluster. - let num_shares = cluster.v_nodes.len() as Balance; - let per_share = cluster.revenues.peek() / num_shares; - cluster.revenues.pay(Payable(per_share * num_shares))?; + Self::env().emit_event(ClusterRemoved { cluster_id }); - for node_id in &cluster.node_ids { - let node = self.nodes.get(*node_id)?; - Self::send_cash(node.provider_id, Cash(per_share))?; + Ok(()) + } - Self::env().emit_event(ClusterDistributeRevenues { - cluster_id, - provider_id: node.provider_id, - }); - } - // TODO: set a maximum node count, or support paging. - // TODO: aggregate the payments per node_id or per provider_id. + pub fn message_cluster_set_node_status( + &mut self, + cluster_id: ClusterId, + node_key: NodeKey, + status_in_cluster: NodeStatusInCluster, + ) -> Result<()> { + let caller = Self::env().caller(); + + let mut node = self.nodes.get(node_key)?; + let cluster = self.clusters.get(cluster_id)?; + cluster.only_manager(caller)?; + + node.change_status_in_cluster(status_in_cluster.clone()); + self.nodes.update(node_key, &node)?; + + Self::env().emit_event(ClusterNodeStatusSet { + node_key, + cluster_id, + status: status_in_cluster, + }); Ok(()) } - pub fn message_cluster_change_params( + pub fn message_cluster_set_cdn_node_status( &mut self, cluster_id: ClusterId, - params: ClusterParams, + cdn_node_key: CdnNodeKey, + status_in_cluster: NodeStatusInCluster, ) -> Result<()> { let caller = Self::env().caller(); + + let mut cdn_node = self.cdn_nodes.get(cdn_node_key)?; let cluster = self.clusters.get(cluster_id)?; cluster.only_manager(caller)?; - Self::impl_change_params(&mut self.cluster_params, cluster_id, params) + cdn_node.change_status_in_cluster(status_in_cluster.clone()); + self.cdn_nodes.update(cdn_node_key, &cdn_node)?; + + Self::env().emit_event(ClusterCdnNodeStatusSet { + cdn_node_key, + cluster_id, + status: status_in_cluster, + }); + + Ok(()) + } + + pub fn message_grant_trusted_manager_permission( + &mut self, + manager_id: AccountId, + ) -> Result<()> { + let grantor = Self::env().caller(); + let permission = Permission::ClusterManagerTrustedBy(grantor); + self.grant_permission(manager_id, permission)?; + + Self::env().emit_event(PermissionGranted { + account_id: manager_id, + permission, + }); + + Ok(()) + } + + pub fn message_revoke_trusted_manager_permission( + &mut self, + manager_id: AccountId, + ) -> Result<()> { + let grantor = Self::env().caller(); + let permission = Permission::ClusterManagerTrustedBy(grantor); + self.revoke_permission(manager_id, permission)?; + + Self::env().emit_event(PermissionRevoked { + account_id: manager_id, + permission, + }); + + Ok(()) + } + + pub fn message_cluster_set_params( + &mut self, + cluster_id: ClusterId, + cluster_params: ClusterParams, + ) -> Result<()> { + let caller = Self::env().caller(); + let mut cluster = self.clusters.get(cluster_id)?; + cluster.only_manager(caller)?; + cluster.set_params(cluster_params.clone())?; + self.clusters.update(cluster_id, &cluster)?; + + Self::env().emit_event(ClusterParamsSet { + cluster_id, + cluster_params, + }); + + Ok(()) + } + + pub fn message_cluster_set_resource_per_v_node( + &mut self, + cluster_id: ClusterId, + new_resource_per_v_node: Resource, + ) -> Result<()> { + let caller = Self::env().caller(); + let mut cluster = self.clusters.get(cluster_id)?; + cluster.only_manager(caller)?; + + let old_resource_per_v_node = cluster.resource_per_v_node; + + cluster.set_resource_per_v_node(new_resource_per_v_node); + let cluster_v_nodes = self.topology.get_v_nodes_by_cluster(cluster_id); + let cluster_v_nodes_len: u32 = cluster_v_nodes.len().try_into().unwrap(); + + let new_max_cluster_resource = cluster_v_nodes_len * new_resource_per_v_node; + if cluster.resource_used > new_max_cluster_resource { + return Err(InsufficientClusterResources); + } + + for v_node in cluster_v_nodes { + let node_key = self.topology.get_node_by_v_node(cluster_id, v_node)?; + let mut node = self.nodes.get(node_key)?; + node.release_resource(old_resource_per_v_node); + node.reserve_resource(new_resource_per_v_node)?; + self.nodes.update(node_key, &node)?; + } + + self.clusters.update(cluster_id, &cluster)?; + + Self::env().emit_event(ClusterReserveResource { + cluster_id, + resource: new_resource_per_v_node, + }); + + Ok(()) } - pub fn message_cluster_get(&self, cluster_id: ClusterId) -> Result { - let cluster = self.clusters.get(cluster_id)?.clone(); - let params = self.cluster_params.get(cluster_id)?.clone(); - Ok(ClusterStatus { + pub fn message_cluster_get(&self, cluster_id: ClusterId) -> Result { + let cluster = self.clusters.get(cluster_id)?; + let cluster_v_nodes = self.topology.get_v_nodes_by_cluster(cluster_id); + + Ok(ClusterInfo { cluster_id, cluster, - params, + cluster_v_nodes, }) } @@ -217,10 +426,10 @@ impl DdcBucket { offset: u32, limit: u32, filter_manager_id: Option, - ) -> (Vec, u32) { + ) -> (Vec, u32) { let mut clusters = Vec::with_capacity(limit as usize); for cluster_id in offset..offset + limit { - let cluster = match self.clusters.0.get(cluster_id as usize) { + let cluster = match self.clusters.clusters.get(cluster_id) { None => break, // No more items, stop. Some(cluster) => cluster, }; @@ -230,37 +439,200 @@ impl DdcBucket { continue; // Skip non-matches. } } + + let cluster_v_nodes = self.topology.get_v_nodes_by_cluster(cluster_id); + // Include the complete status of matched items. - let status = ClusterStatus { + let cluster_info = ClusterInfo { cluster_id, - cluster: cluster.clone(), - params: self.cluster_params.get(cluster_id).unwrap().clone(), + cluster, + cluster_v_nodes, }; - clusters.push(status); + + clusters.push(cluster_info); } - (clusters, self.clusters.0.len().try_into().unwrap()) + (clusters, self.clusters.next_cluster_id) } - fn only_cluster_manager(cluster: &Cluster) -> Result { - let caller = Self::env().caller(); - if caller == cluster.manager_id { - Ok(caller) - } else { - Err(UnauthorizedClusterManager) + pub fn message_cluster_distribute_revenues(&mut self, cluster_id: ClusterId) -> Result<()> { + let mut cluster = self.clusters.get(cluster_id)?; + + // Charge the network fee from the cluster. + self.capture_network_fee(&mut cluster.revenues)?; + + // Charge the cluster management fee. + self.capture_fee( + self.protocol.get_cluster_management_fee_bp(), + cluster.manager_id, + &mut cluster.revenues, + )?; + + // Charge the provider payments from the cluster. + let num_shares = cluster.nodes_keys.len() as Balance; + let per_share = cluster.revenues.peek() / num_shares; + cluster.revenues.pay(Payable(per_share * num_shares))?; + + for node_key in &cluster.nodes_keys { + let node = self.nodes.get(*node_key)?; + Self::send_cash(node.provider_id, Cash(per_share))?; + + Self::env().emit_event(ClusterDistributeRevenues { + cluster_id, + provider_id: node.provider_id, + }); } + + self.clusters.update(cluster_id, &cluster)?; + + // TODO: set a maximum node count, or support paging. + // TODO: aggregate the payments per node_id or per provider_id. + + Ok(()) + } + + // Set the price usd per gb + pub fn message_cdn_set_rate( + &mut self, + cluster_id: ClusterId, + cdn_usd_per_gb: Balance, + ) -> Result<()> { + let caller = Self::env().caller(); + + let mut cluster = self.clusters.get(cluster_id)?; + cluster.only_manager(caller)?; + cluster.cdn_set_rate(cdn_usd_per_gb); + self.clusters.update(cluster_id, &cluster)?; + + Ok(()) } - fn only_trusted_manager( - perm_store: &PermStore, - manager: AccountId, - trusted_by: AccountId, + // Get the price usd per gb + pub fn message_cdn_get_rate(&self, cluster_id: ClusterId) -> Result { + let cluster = self.clusters.get(cluster_id)?; + let rate = cluster.cdn_get_rate(); + Ok(rate) + } + + // First payment is for aggregate consumption for account, second is the aggregate payment for the node (u32 for ids) + pub fn message_cluster_put_cdn_revenue( + &mut self, + cluster_id: ClusterId, + aggregates_accounts: Vec<(AccountId, u128)>, + aggregates_nodes: Vec<(CdnNodeKey, u128)>, + aggregates_buckets: Vec<(BucketId, Resource)>, + era: u64, ) -> Result<()> { - let perm = Permission::ManagerTrustedBy(trusted_by); - let trusts = perm_store.has_permission(manager, perm); - if trusts { - Ok(()) - } else { - Err(ClusterManagerIsNotTrusted) + self.only_validator()?; + + let mut cluster = self.clusters.get(cluster_id)?; + let mut cluster_payment = 0; + let mut _undistributed_payment_accounts = 0; + + let aggregate_payments_accounts; + { + let conv = &self.protocol.curr_converter; + aggregate_payments_accounts = aggregates_accounts + .iter() + .map(|(client_id, resources_used)| { + let account_id = *client_id; + let cere_payment: Balance = conv + .to_cere(*resources_used as Balance * cluster.cdn_usd_per_gb / KB_PER_GB); + (account_id, cere_payment) + }) + .collect::>(); + } + + for &(client_id, payment) in aggregate_payments_accounts.iter() { + if let Ok(mut account) = self.accounts.get(&client_id) { + account.withdraw_bonded(Payable(payment))?; + _undistributed_payment_accounts += payment; + self.accounts.save(&client_id, &account); + } else { + return Err(InsufficientBalance); + } + } + + let conv = self.protocol.curr_converter.clone(); + let committer = &mut self.committer; + + for &(cdn_node_key, resources_used) in aggregates_nodes.iter() { + let mut cdn_node = self.cdn_nodes.get(cdn_node_key)?; + let protocol_fee = self.protocol.get_protocol_fee_bp(); + let protocol = &mut self.protocol; + + let payment = + conv.to_cere(resources_used as Balance * cluster.cdn_usd_per_gb / KB_PER_GB); + + // let protocol_payment = payment * protocol_fee as u128/ BASIS_POINTS; + let node_payment = payment * (BASIS_POINTS - protocol_fee) as u128 / BASIS_POINTS; + let protocol_payment = payment - node_payment; + + cdn_node.put_payment(node_payment); + + protocol.put_revenues(Cash(protocol_payment)); + self.cdn_nodes.update(cdn_node_key, &cdn_node)?; + + committer.set_validated_commit(cdn_node_key, era).unwrap(); + cluster_payment += node_payment; + } + // Add check that two payments should equal? + + // Go through buckets and deduct used resources + for &(bucket_id, resources_used) in aggregates_buckets.iter() { + let mut bucket = self.buckets.get(bucket_id)?; + + if bucket.resource_consumption_cap <= resources_used { + bucket.resource_consumption_cap -= resources_used; + self.buckets.update(bucket_id, &bucket)?; + } + } + + // Add revenues to cluster + cluster.cdn_put_revenues(Cash(cluster_payment)); + self.clusters.update(cluster_id, &cluster)?; + + Ok(()) + } + + pub fn message_cluster_distribute_cdn_revenue(&mut self, cluster_id: ClusterId) -> Result<()> { + let mut cluster = self.clusters.get(cluster_id)?; + + // Charge the network fee from the cluster. + self.capture_network_fee(&mut cluster.cdn_revenues)?; + + // Charge the cluster management fee. + self.capture_fee( + self.protocol.get_cluster_management_fee_bp(), + cluster.manager_id, + &mut cluster.cdn_revenues, + )?; + + // First accumulated revenues to distribute. + let mut distributed_revenue = 0; + + for cdn_node_key in &cluster.cdn_nodes_keys { + let cdn_node = self.cdn_nodes.get(*cdn_node_key)?; + distributed_revenue += cdn_node.undistributed_payment; + } + + // Charge the provider payments from the cluster. + cluster.cdn_revenues.pay(Payable(distributed_revenue))?; + + // Distribute revenues to nodes + for cdn_node_key in &cluster.cdn_nodes_keys { + let mut cdn_node = self.cdn_nodes.get(*cdn_node_key)?; + + Self::send_cash(cdn_node.provider_id, Cash(cdn_node.undistributed_payment))?; + cdn_node.take_payment(cdn_node.undistributed_payment)?; + self.cdn_nodes.update(*cdn_node_key, &cdn_node)?; + + Self::env().emit_event(ClusterDistributeCdnRevenues { + cluster_id, + provider_id: cdn_node.provider_id, + }); } + self.clusters.update(cluster_id, &cluster)?; + + Ok(()) } } diff --git a/bucket/ddc_bucket/cluster/mod.rs b/bucket/ddc_bucket/cluster/mod.rs index 76c0f62f..26f0c149 100644 --- a/bucket/ddc_bucket/cluster/mod.rs +++ b/bucket/ddc_bucket/cluster/mod.rs @@ -1,5 +1,5 @@ //! Cluster management. pub mod entity; +pub mod messages; pub mod store; -pub mod messages; \ No newline at end of file diff --git a/bucket/ddc_bucket/cluster/store.rs b/bucket/ddc_bucket/cluster/store.rs index 718ffa70..a79f5b54 100644 --- a/bucket/ddc_bucket/cluster/store.rs +++ b/bucket/ddc_bucket/cluster/store.rs @@ -1,39 +1,47 @@ //! The store where to create and access Clusters by ID. -use ink_prelude::vec::Vec; -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; - -use crate::ddc_bucket::node::entity::NodeId; -use crate::ddc_bucket::{AccountId, Error::*, Result}; - -use super::entity::{Cluster, ClusterId}; +use super::entity::{Cluster, ClusterId, ClusterParams}; +use crate::ddc_bucket::{AccountId, Error::*, Resource, Result}; +use ink_storage::traits::{SpreadAllocate, SpreadLayout}; +use ink_storage::Mapping; #[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] -pub struct ClusterStore(pub Vec); +#[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout, Debug))] +pub struct ClusterStore { + pub next_cluster_id: u32, + pub clusters: Mapping, +} impl ClusterStore { pub fn create( &mut self, manager_id: AccountId, - v_nodes: &Vec>, - node_ids: &Vec, + cluster_params: ClusterParams, + resource_per_v_node: Resource, ) -> Result { - let cluster = Cluster::new(manager_id, v_nodes, node_ids); + let cluster_id = self.next_cluster_id; + self.next_cluster_id = self.next_cluster_id + 1; - let cluster_id: ClusterId = self.0.len().try_into().unwrap(); - self.0.push(cluster); + let cluster = Cluster::new(manager_id, cluster_params, resource_per_v_node)?; + self.clusters.insert(&cluster_id, &cluster); Ok(cluster_id) } - pub fn get(&self, cluster_id: ClusterId) -> Result<&Cluster> { - self.0.get(cluster_id as usize).ok_or(ClusterDoesNotExist) + pub fn get(&self, cluster_id: ClusterId) -> Result { + self.clusters.get(cluster_id).ok_or(ClusterDoesNotExist) + } + + pub fn update(&mut self, cluster_id: ClusterId, cluster: &Cluster) -> Result<()> { + if !self.clusters.contains(&cluster_id) { + Err(ClusterDoesNotExist) + } else { + self.clusters.insert(cluster_id, cluster); + Ok(()) + } } - pub fn get_mut(&mut self, cluster_id: ClusterId) -> Result<&mut Cluster> { - self.0 - .get_mut(cluster_id as usize) - .ok_or(ClusterDoesNotExist) + pub fn remove(&mut self, cluster_id: ClusterId) { + self.clusters.remove(cluster_id); } } diff --git a/bucket/ddc_bucket/committer/messages.rs b/bucket/ddc_bucket/committer/messages.rs index 349d8e17..8ffdd1c7 100644 --- a/bucket/ddc_bucket/committer/messages.rs +++ b/bucket/ddc_bucket/committer/messages.rs @@ -1,40 +1,43 @@ use ink_lang::codegen::StaticEnv; -use ink_env::Error; use ink_prelude::vec::Vec; -use crate::ddc_bucket::{AccountId, DdcBucket, NodeId}; -use super::store::{Commit, EraConfig, EraStatus, EraAndTimestamp}; - -pub type Result = core::result::Result; +use super::store::{Commit, EraAndTimestamp, EraConfig, EraStatus}; +use crate::ddc_bucket::{AccountId, CdnNodeKey, DdcBucket, Error::*, Result}; impl DdcBucket { - pub fn message_set_commit(&mut self, cdn_owner: AccountId, node_id: NodeId, commit: Commit) { - self.committer_store.set_commit(cdn_owner, node_id, commit); + pub fn message_set_commit( + &mut self, + cdn_owner: AccountId, + cdn_node_key: CdnNodeKey, + commit: Commit, + ) -> Result<()> { + self.committer.set_commit(cdn_owner, cdn_node_key, commit); + Ok(()) } - pub fn message_get_commit(&self, cdn_owner: AccountId) -> Vec<(NodeId, Commit)> { - self.committer_store.get_commit(cdn_owner) + pub fn message_get_commit(&self, cdn_owner: AccountId) -> Vec<(CdnNodeKey, Commit)> { + self.committer.get_commit(cdn_owner) } - pub fn message_get_validated_commit(&self, node: NodeId) -> EraAndTimestamp { - self.committer_store.get_validate_commit(node) + pub fn message_get_validated_commit(&self, cdn_node_key: CdnNodeKey) -> EraAndTimestamp { + self.committer.get_validate_commit(cdn_node_key) } pub fn message_set_era(&mut self, era_config: EraConfig) -> Result<()> { let caller = Self::env().caller(); - - match self.committer_store.set_era(caller, era_config) { - Err(_e) => panic!("Setting era failed"), + + match self.committer.set_era(caller, era_config) { + Err(_e) => Err(EraSettingFailed), Ok(_v) => Ok(()), } - } - + } + pub fn message_get_era(&self) -> EraStatus { let timestamp = Self::env().block_timestamp(); - self.committer_store.get_era(timestamp) + self.committer.get_era(timestamp) } pub fn message_get_era_settings(&self) -> EraConfig { - self.committer_store.get_era_settings() + self.committer.get_era_settings() } -} \ No newline at end of file +} diff --git a/bucket/ddc_bucket/committer/mod.rs b/bucket/ddc_bucket/committer/mod.rs index 48079abd..0b51b05a 100644 --- a/bucket/ddc_bucket/committer/mod.rs +++ b/bucket/ddc_bucket/committer/mod.rs @@ -1,4 +1,4 @@ -// Committer Management +// Committer Management +pub mod messages; pub mod store; -pub mod messages; \ No newline at end of file diff --git a/bucket/ddc_bucket/committer/store.rs b/bucket/ddc_bucket/committer/store.rs index 06d5346d..f2a67672 100644 --- a/bucket/ddc_bucket/committer/store.rs +++ b/bucket/ddc_bucket/committer/store.rs @@ -1,14 +1,14 @@ -use crate::ddc_bucket::{AccountId, Hash, NodeId}; +use crate::ddc_bucket::{AccountId, CdnNodeKey, Hash}; -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout, PackedLayout}; use ink_prelude::vec::Vec; +use ink_storage::traits::{PackedLayout, SpreadAllocate, SpreadLayout}; use ink_storage::Mapping; #[derive(Debug, PartialEq, scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum Error { /// The caller is not the authorised operator of the smart contract - UnauthorizedOperator + UnauthorizedOperator, } /// Within the concept of era we would like to return specific phase to interested agents @@ -17,12 +17,12 @@ pub enum Error { pub enum Phase { Commit, Valiadation, - Payout + Payout, } #[derive(Debug, PartialEq, scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub struct EraStatus { +pub struct EraStatus { current_era: u64, current_phase: Phase, previous_era: u64, @@ -30,31 +30,38 @@ pub struct EraStatus { prev_era_to_timestamp: u64, } -#[derive(Copy, Clone, SpreadAllocate, PackedLayout, SpreadLayout, scale::Encode, scale::Decode, Debug)] +#[derive( + Copy, Clone, SpreadAllocate, PackedLayout, SpreadLayout, scale::Encode, scale::Decode, Debug, +)] #[cfg_attr(feature = "std", derive(::scale_info::TypeInfo))] pub struct Commit { hash: Hash, total_logs: u128, from_timestamp: u64, to_timestamp: u64, -} +} -#[derive(Default, Copy, Clone, SpreadAllocate, SpreadLayout, scale::Encode, scale::Decode, Debug)] -#[cfg_attr(feature = "std", derive(::scale_info::TypeInfo, StorageLayout))] +#[derive( + Default, Copy, Clone, SpreadAllocate, SpreadLayout, scale::Encode, scale::Decode, Debug, +)] +#[cfg_attr( + feature = "std", + derive(::scale_info::TypeInfo, ink_storage::traits::StorageLayout) +)] pub struct EraConfig { start: u64, interval: u64, commit_duration: u64, - validation_duration:u64 -} + validation_duration: u64, +} #[derive(Default, SpreadAllocate, SpreadLayout, Debug)] -#[cfg_attr(feature = "std", derive(StorageLayout))] +#[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout))] pub struct CommitterStore { operator_id: AccountId, - commits: Mapping>, - validated_commits: Mapping, - era_settings: EraConfig + commits: Mapping>, + validated_commits: Mapping, + era_settings: EraConfig, } pub type Result = core::result::Result; @@ -68,40 +75,54 @@ impl CommitterStore { /// The node can set the latest commit with this function /// check the sender !!!! - pub fn set_commit(&mut self, cdn_owner: AccountId, node_id: NodeId, commit: Commit) { + pub fn set_commit(&mut self, cdn_owner: AccountId, cdn_node_key: CdnNodeKey, commit: Commit) { if !self.commits.contains(&cdn_owner) { - let empty_vec = Vec::<(u32, Commit)>::new(); + let empty_vec = Vec::<(CdnNodeKey, Commit)>::new(); self.commits.insert(cdn_owner, &empty_vec); } let mut account_commits = self.commits.get(&cdn_owner).unwrap(); - let index = account_commits.iter().position(|(node, _)| *node == node_id).unwrap_or(usize::MAX); + let index = account_commits + .iter() + .position(|(node, _)| *node == cdn_node_key) + .unwrap_or(usize::MAX); if index != usize::MAX { account_commits.remove(index); } - account_commits.push((node_id, commit)); + account_commits.push((cdn_node_key, commit)); self.commits.insert(&cdn_owner, &account_commits); } - pub fn get_commit(&self, cdn_owner: AccountId) -> Vec<(NodeId, Commit)> { - self.commits.get(&cdn_owner).unwrap_or(Vec::<(u32, Commit)>::new()).iter().cloned().collect() + pub fn get_commit(&self, cdn_owner: AccountId) -> Vec<(CdnNodeKey, Commit)> { + self.commits + .get(&cdn_owner) + .unwrap_or(Vec::<(CdnNodeKey, Commit)>::new()) + .iter() + .cloned() + .collect() } // Set the last validated commit per CDN node - pub fn set_validated_commit(&mut self, node: NodeId, era: u64) -> Result<()> { - let prev_era_to_timestamp = self.era_settings.start + self.era_settings.interval * (era + 1); - self.validated_commits.insert(&node, &(era, prev_era_to_timestamp)); + pub fn set_validated_commit(&mut self, cdn_node_key: CdnNodeKey, era: u64) -> Result<()> { + let prev_era_to_timestamp = + self.era_settings.start + self.era_settings.interval * (era + 1); + self.validated_commits + .insert(&cdn_node_key, &(era, prev_era_to_timestamp)); Ok(()) } // Get the last era & timestamp validated per CDN node - pub fn get_validate_commit(&self, node: NodeId) -> EraAndTimestamp { - self.validated_commits.get(&node).unwrap_or((0,0)) + pub fn get_validate_commit(&self, cdn_node_key: CdnNodeKey) -> EraAndTimestamp { + self.validated_commits.get(&cdn_node_key).unwrap_or((0, 0)) } // Akin to modifier pub fn only_owner(&self, operator_id: AccountId) -> Result<()> { - if self.operator_id == operator_id { Ok(()) } else { Err(Error::UnauthorizedOperator) } + if self.operator_id == operator_id { + Ok(()) + } else { + Err(Error::UnauthorizedOperator) + } } // Set the new value for the era config @@ -116,10 +137,12 @@ impl CommitterStore { let era_start = self.era_settings.start; let interval = self.era_settings.interval; let elapsed_time_within_interval = (timestamp - era_start) % interval; - + let current_phase = if elapsed_time_within_interval < self.era_settings.commit_duration { Phase::Commit - } else if elapsed_time_within_interval < self.era_settings.validation_duration + self.era_settings.commit_duration { + } else if elapsed_time_within_interval + < self.era_settings.validation_duration + self.era_settings.commit_duration + { Phase::Valiadation } else { Phase::Payout @@ -130,12 +153,12 @@ impl CommitterStore { let prev_era_from_timestamp = era_start + interval * previous_era; let prev_era_to_timestamp = era_start + interval * current_era; - EraStatus { - current_era, - current_phase, - previous_era, - prev_era_from_timestamp, - prev_era_to_timestamp + EraStatus { + current_era, + current_phase, + previous_era, + prev_era_from_timestamp, + prev_era_to_timestamp, } } diff --git a/bucket/ddc_bucket/currency.rs b/bucket/ddc_bucket/currency.rs index 880e937c..6a3bcb66 100644 --- a/bucket/ddc_bucket/currency.rs +++ b/bucket/ddc_bucket/currency.rs @@ -1,42 +1,38 @@ //! The privileged interface for admin tasks. -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; -use ink_storage::traits::KeyPtr; - use crate::ddc_bucket::{Balance, TOKEN}; +use ink_storage::traits::{PackedLayout, SpreadAllocate, SpreadLayout}; +use scale::{Decode, Encode}; + +pub const PRECISION: Balance = 10_000_000; pub type CERE = Balance; pub type USD = Balance; - -#[derive(SpreadLayout)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] -pub struct CurrencyConverter(Balance /* how many USD for PRECISION CERE */); - -const PRECISION: Balance = 10_000_000; - -impl SpreadAllocate for CurrencyConverter { - fn allocate_spread(_: &mut KeyPtr) -> Self { - Self(PRECISION) - } +#[derive(Default, Clone, PartialEq, Encode, Decode, SpreadAllocate, PackedLayout, SpreadLayout)] +#[cfg_attr( + feature = "std", + derive(ink_storage::traits::StorageLayout, Debug, scale_info::TypeInfo) +)] +pub struct CurrencyConverter { + /* how many USD for PRECISION CERE */ + rate: Balance, } -impl Default for CurrencyConverter { - fn default() -> Self { - Self(PRECISION) +impl CurrencyConverter { + pub fn new() -> Self { + Self { rate: PRECISION } } -} -impl CurrencyConverter { // 10_000_000 pub fn set_usd_per_cere(&mut self, usd_per_cere: USD) { - self.0 = usd_per_cere * PRECISION / TOKEN; + self.rate = usd_per_cere * PRECISION / TOKEN; } pub fn to_cere(&self, usd: USD) -> CERE { - usd * PRECISION / self.0 + usd * PRECISION / self.rate } pub fn to_usd(&self, cere: CERE) -> USD { - self.0 * cere / PRECISION + self.rate * cere / PRECISION } } diff --git a/bucket/ddc_bucket/flow.rs b/bucket/ddc_bucket/flow.rs index a847ed72..ba97501a 100644 --- a/bucket/ddc_bucket/flow.rs +++ b/bucket/ddc_bucket/flow.rs @@ -1,12 +1,9 @@ //! The Flow data structure represents an outgoing stream of payments from an account. -use ink_storage::traits::{SpreadAllocate, PackedLayout, SpreadLayout}; +use ink_storage::traits::{PackedLayout, SpreadAllocate, SpreadLayout}; use scale::{Decode, Encode}; -use crate::ddc_bucket::{ - AccountId, - schedule::Schedule, -}; +use crate::ddc_bucket::{schedule::Schedule, AccountId}; // TODO: remove Clone. #[derive(Clone, PartialEq, Encode, Decode, SpreadAllocate, SpreadLayout, PackedLayout)] diff --git a/bucket/ddc_bucket/network_fee.rs b/bucket/ddc_bucket/network_fee.rs deleted file mode 100644 index 25e6b4cf..00000000 --- a/bucket/ddc_bucket/network_fee.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! This module captures fees on behalf of the entire Cere network. - -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; -use scale::{Decode, Encode}; - -use crate::ddc_bucket::{AccountId, Balance, DdcBucket, Result}; -use crate::ddc_bucket::cash::Cash; -use crate::ddc_bucket::perm::entity::Permission; - -pub type BasisPoints = Balance; - -const BP: BasisPoints = 10_000; // 100% - -#[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] -pub struct NetworkFeeStore( - pub FeeConfig, -); - -impl NetworkFeeStore { - pub fn cluster_management_fee_bp(&self) -> BasisPoints { - self.0.cluster_management_fee_bp - } -} - -/// The configuration of fees. -#[derive(SpreadAllocate, SpreadLayout, Default, Decode, Encode)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug, scale_info::TypeInfo))] -pub struct FeeConfig { - /// The fee rate from cluster revenues to the overall network. In basis points (1% of 1%). - pub network_fee_bp: BasisPoints, - /// The destination account of network fees. Use the 0 account to burn the fees. - pub network_fee_destination: AccountId, - - /// The fee rate from cluster revenues to the cluster manager. In basis points (1% of 1%). - pub cluster_management_fee_bp: BasisPoints, -} - - -impl DdcBucket { - /// Take a network fee from the given revenues (in place). - pub fn capture_network_fee(store: &NetworkFeeStore, revenues: &mut Cash) -> Result<()> { - let config = &store.0; - Self::capture_fee(config.network_fee_bp, config.network_fee_destination, revenues) - } - - /// Take a fee from the given revenues (in place) and send it to the destination. - pub fn capture_fee(rate_bp: Balance, destination: AccountId, revenues: &mut Cash) -> Result<()> { - let fee = revenues.peek() * rate_bp / BP; - let (payable, cash) = Cash::borrow_payable_cash(fee); - revenues.pay(payable)?; - Self::send_cash(destination, cash) - } - - pub fn message_admin_set_fee_config(&mut self, config: FeeConfig) -> Result<()> { - self.only_with_permission(Permission::SuperAdmin)?; - self.network_fee.0 = config; - Ok(()) - } -} diff --git a/bucket/ddc_bucket/node/entity.rs b/bucket/ddc_bucket/node/entity.rs index d58750d8..975e8a0d 100644 --- a/bucket/ddc_bucket/node/entity.rs +++ b/bucket/ddc_bucket/node/entity.rs @@ -1,27 +1,30 @@ //! The data structure of Nodes. -use ink_storage::traits::{SpreadAllocate, PackedLayout, SpreadLayout, PackedAllocate}; +use ink_prelude::string::String; +use ink_prelude::vec::Vec; +use ink_storage::traits::{PackedAllocate, PackedLayout, SpreadAllocate, SpreadLayout}; use scale::{Decode, Encode}; -use crate::ddc_bucket::params::store::Params; -use crate::ddc_bucket::{AccountId, Balance, Error::*, Result}; -use ink_storage::traits::KeyPtr; +use crate::ddc_bucket::cluster::entity::ClusterId; +use crate::ddc_bucket::{AccountId, Balance, Error::*, Result, VNodeToken}; + use ink_primitives::Key; +use ink_storage::traits::KeyPtr; pub type ProviderId = AccountId; -pub type NodeId = u32; -pub type NodePublicKey = AccountId; -pub type NodeParams = Params; +pub type NodeKey = AccountId; +pub type NodeParams = String; pub type Resource = u32; #[derive(Clone, PartialEq, Encode, Decode, SpreadAllocate, SpreadLayout, PackedLayout)] #[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] pub struct Node { pub provider_id: ProviderId, - pub rent_per_month: Balance, + pub rent_v_node_per_month: Balance, pub free_resource: Resource, - pub node_tag: NodeTag, - pub node_pub_key: NodePublicKey, + pub node_params: NodeParams, + pub cluster_id: Option, + pub status_in_cluster: Option, } // https://use.ink/3.x/ink-vs-solidity#nested-mappings--custom--advanced-structures @@ -34,61 +37,105 @@ impl ink_storage::traits::PackedAllocate for Node { #[derive(Clone, PartialEq, Encode, Decode, SpreadLayout, PackedLayout)] #[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] -pub enum NodeTag { - UNKNOWN, - ACTIVE, +pub enum NodeStatusInCluster { ADDING, + ACTIVE, DELETING, OFFLINE, } -impl SpreadAllocate for NodeTag { - fn allocate_spread(_: &mut KeyPtr) -> Self { - NodeTag::UNKNOWN +impl SpreadAllocate for NodeStatusInCluster { + fn allocate_spread(_: &mut KeyPtr) -> Self { + NodeStatusInCluster::ADDING } } -impl Default for NodeTag { +impl Default for NodeStatusInCluster { fn default() -> Self { - NodeTag::UNKNOWN + NodeStatusInCluster::ADDING } } #[derive(Clone, PartialEq, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] -pub struct NodeStatus { - pub node_id: NodeId, +pub struct NodeInfo { + pub node_key: NodeKey, pub node: Node, - pub params: Params, + pub v_nodes: Vec, } +pub const NODE_PARAMS_MAX_LEN: usize = 100_000; + impl Node { - pub fn revenue_account_id(&self) -> AccountId { - self.provider_id + pub fn new( + provider_id: AccountId, + node_params: NodeParams, + capacity: Resource, + rent_v_node_per_month: Balance, + ) -> Result { + let mut node = Node { + provider_id, + node_params: NodeParams::default(), + free_resource: capacity, + rent_v_node_per_month, + cluster_id: None, + status_in_cluster: None, + }; + + node.set_params(node_params)?; + Ok(node) } - pub fn only_owner(&self, provider_id: AccountId) -> Result<()> { - if self.provider_id == provider_id { - Ok(()) - } else { - Err(UnauthorizedProvider) + pub fn only_provider(&self, caller: AccountId) -> Result<()> { + (self.provider_id == caller) + .then(|| ()) + .ok_or(OnlyNodeProvider) + } + + pub fn only_without_cluster(&self) -> Result<()> { + self.cluster_id + .map_or(Ok(()), |cluster_id| Err(NodeIsAddedToCluster(cluster_id))) + } + + pub fn only_with_cluster(&self, cluster_id: ClusterId) -> Result<()> { + self.cluster_id + .is_some() + .then(|| ()) + .ok_or(NodeIsNotAddedToCluster(cluster_id)) + } + + pub fn set_params(&mut self, node_params: NodeParams) -> Result<()> { + if node_params.len() > NODE_PARAMS_MAX_LEN { + return Err(ParamsSizeExceedsLimit); } + self.node_params = node_params; + Ok(()) + } + + pub fn set_cluster(&mut self, cluster_id: ClusterId, status: NodeStatusInCluster) { + self.cluster_id = Some(cluster_id); + self.status_in_cluster = Some(status); + } + + pub fn unset_cluster(&mut self) { + self.cluster_id = None; + self.status_in_cluster = None; } - pub fn change_tag(&mut self, new_tag: NodeTag) { - self.node_tag = new_tag; + pub fn change_status_in_cluster(&mut self, status: NodeStatusInCluster) { + self.status_in_cluster = Some(status); } - pub fn put_resource(&mut self, amount: Resource) { + pub fn release_resource(&mut self, amount: Resource) { self.free_resource += amount; } - pub fn take_resource(&mut self, amount: Resource) -> Result<()> { + pub fn reserve_resource(&mut self, amount: Resource) -> Result<()> { if self.free_resource >= amount { self.free_resource -= amount; Ok(()) } else { - Err(InsufficientResources) + Err(InsufficientNodeResources) } } } diff --git a/bucket/ddc_bucket/node/messages.rs b/bucket/ddc_bucket/node/messages.rs index b7238884..9194733d 100644 --- a/bucket/ddc_bucket/node/messages.rs +++ b/bucket/ddc_bucket/node/messages.rs @@ -1,80 +1,79 @@ //! The public interface to manage Nodes. -use crate::ddc_bucket::node::entity::{NodeStatus, Resource}; -use crate::ddc_bucket::perm::entity::Permission; -use crate::ddc_bucket::{AccountId, Balance, DdcBucket, NodeCreated, Result}; +use crate::ddc_bucket::node::entity::{NodeInfo, NodeKey, NodeParams, Resource}; +use crate::ddc_bucket::{ + AccountId, Balance, DdcBucket, NodeCreated, NodeParamsSet, NodeRemoved, Result, +}; use ink_lang::codegen::{EmitEvent, StaticEnv}; use ink_prelude::vec::Vec; -use super::entity::{NodeId, NodeParams, NodeTag}; - impl DdcBucket { - pub fn message_node_trust_manager( - &mut self, - manager: AccountId, - is_trusted: bool, - ) -> Result<()> { - let trust_giver = Self::env().caller(); - let permission = Permission::ManagerTrustedBy(trust_giver); - self.impl_grant_permission(manager, permission, is_trusted) - } - pub fn message_node_create( &mut self, - rent_per_month: Balance, + node_key: NodeKey, node_params: NodeParams, capacity: Resource, - node_tag: NodeTag, - pubkey: AccountId, - ) -> Result { - let provider_id = Self::env().caller(); - - let node_id = self - .nodes - .create(provider_id, rent_per_month, capacity, node_tag, pubkey) - .unwrap(); - - let params_id = self.node_params.create(node_params.clone())?; - assert_eq!(node_id, params_id); + rent_v_node_per_month: Balance, + ) -> Result { + let caller = Self::env().caller(); + self.nodes.create( + node_key, + caller, + node_params.clone(), + capacity, + rent_v_node_per_month, + )?; Self::env().emit_event(NodeCreated { - node_id, - provider_id, - rent_per_month, + node_key, + provider_id: caller, + rent_v_node_per_month, node_params, }); - Ok(node_id) + + Ok(node_key) } - pub fn message_node_change_tag(&mut self, node_id: NodeId, new_tag: NodeTag) -> Result<()> { + pub fn message_node_remove(&mut self, node_key: NodeKey) -> Result<()> { let caller = Self::env().caller(); - let node = self.nodes.get_mut(node_id)?; - node.only_owner(caller)?; + let node = self.nodes.get(node_key)?; + node.only_provider(caller)?; + node.only_without_cluster()?; + self.nodes.remove(node_key); - node.change_tag(new_tag); - Ok(()) - } + Self::env().emit_event(NodeRemoved { node_key }); - pub fn message_node_get(&self, node_id: NodeId) -> Result { - let node = self.nodes.get(node_id)?.clone(); - let params = self.node_params.get(node_id)?.clone(); - Ok(NodeStatus { - node_id, - node, - params, - }) + Ok(()) } - pub fn message_node_change_params( + pub fn message_node_set_params( &mut self, - node_id: NodeId, - params: NodeParams, + node_key: NodeKey, + node_params: NodeParams, ) -> Result<()> { let caller = Self::env().caller(); - let node = self.nodes.get(node_id)?; - node.only_owner(caller)?; + let mut node = self.nodes.get(node_key)?; + node.only_provider(caller)?; + node.set_params(node_params.clone())?; + self.nodes.update(node_key, &node)?; + + Self::env().emit_event(NodeParamsSet { + node_key, + node_params, + }); - Self::impl_change_params(&mut self.node_params, node_id, params) + Ok(()) + } + + pub fn message_node_get(&self, node_key: NodeKey) -> Result { + let node = self.nodes.get(node_key)?; + let v_nodes = self.topology.get_v_nodes_by_node(node_key); + + Ok(NodeInfo { + node_key, + node, + v_nodes, + }) } pub fn message_node_list( @@ -82,47 +81,34 @@ impl DdcBucket { offset: u32, limit: u32, filter_provider_id: Option, - ) -> (Vec, u32) { + ) -> (Vec, u32) { let mut nodes = Vec::with_capacity(limit as usize); - for node_id in offset..offset + limit { - let node = match self.nodes.nodes.get(node_id as usize) { + for idx in offset..offset + limit { + let node_key = match self.nodes.keys.get(idx as usize) { None => break, // No more items, stop. - Some(node) => node, + Some(node_key) => node_key.clone(), }; + + let node = self.nodes.nodes.get(node_key).unwrap(); // Apply the filter if given. if let Some(provider_id) = filter_provider_id { if provider_id != node.provider_id { continue; // Skip non-matches. } } - // Include the complete status of matched items. - let status = NodeStatus { - node_id, - node: node.clone(), - params: self.node_params.get(node_id).unwrap().clone(), - }; - nodes.push(status); - } - (nodes, self.nodes.nodes.len().try_into().unwrap()) - } + let v_nodes = self.topology.get_v_nodes_by_node(node_key); - pub fn message_node_get_by_pub_key(&self, pubkey: AccountId) -> Result { - let node_id = self.nodes.get_by_pub_key(pubkey).unwrap(); - let node = self.nodes.get(node_id)?.clone(); - let params = self.node_params.get(node_id)?.clone(); - Ok(NodeStatus { - node_id, - node, - params, - }) - } + // Include the complete status of matched items. + let node_info = NodeInfo { + node_key, + node, + v_nodes, + }; - pub fn message_remove_node(&mut self, node_id: NodeId) -> Result<()> { - let caller = Self::env().caller(); - let node = self.nodes.get_mut(node_id)?; - node.only_owner(caller)?; + nodes.push(node_info); + } - self.nodes.remove_node(node_id) + (nodes, self.nodes.keys.len().try_into().unwrap()) } -} \ No newline at end of file +} diff --git a/bucket/ddc_bucket/node/mod.rs b/bucket/ddc_bucket/node/mod.rs index f9a8cdd9..07c7abbf 100644 --- a/bucket/ddc_bucket/node/mod.rs +++ b/bucket/ddc_bucket/node/mod.rs @@ -1,5 +1,5 @@ //! Node management. pub mod entity; +pub mod messages; pub mod store; -pub mod messages; \ No newline at end of file diff --git a/bucket/ddc_bucket/node/store.rs b/bucket/ddc_bucket/node/store.rs index 607bced9..7d7b0bee 100644 --- a/bucket/ddc_bucket/node/store.rs +++ b/bucket/ddc_bucket/node/store.rs @@ -1,67 +1,64 @@ //! The store where to create and access Nodes. -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; +use super::entity::{Node, NodeKey, NodeParams, Resource}; +use crate::ddc_bucket::{AccountId, Balance, Error::*, Result}; use ink_prelude::vec::Vec; +use ink_storage::traits::{SpreadAllocate, SpreadLayout}; use ink_storage::Mapping; -use crate::ddc_bucket::node::entity::Resource; -use crate::ddc_bucket::{AccountId, Balance, Error::*, Result}; - -use super::entity::{Node, NodeId, NodeTag}; +// https://use.ink/datastructures/storage-layout#packed-vs-non-packed-layout +// There is a buffer with only limited capacity (around 16KB in the default configuration) available. +pub const MAX_NODES_LEN_IN_VEC: usize = 400; #[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] +#[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout, Debug))] pub struct NodeStore { - pub account_node: Mapping, - pub nodes: Vec, + pub nodes: Mapping, + // todo: remove this vector as it can store an arbitrary number of elements and easily exceed 16KB limit + pub keys: Vec, } impl NodeStore { pub fn create( &mut self, + node_key: AccountId, provider_id: AccountId, - rent_per_month: Balance, + node_params: NodeParams, capacity: Resource, - node_tag: NodeTag, - pubkey: AccountId, - ) -> Result { - let node_id: NodeId = self.nodes.len().try_into().unwrap(); - let node = Node { - provider_id, - rent_per_month, - free_resource: capacity, - node_tag, - node_pub_key: pubkey, - }; - - let exists = self.account_node.contains(&pubkey); - if exists { + rent_v_node_per_month: Balance, + ) -> Result { + if self.nodes.contains(&node_key) { return Err(NodeAlreadyExists); } - self.nodes.push(node); - self.account_node.insert(&pubkey, &node_id); + if self.keys.len() + 1 > MAX_NODES_LEN_IN_VEC { + return Err(NodesSizeExceedsLimit); + } - Ok(node_id) - } + let node = Node::new(provider_id, node_params, capacity, rent_v_node_per_month)?; - pub fn get_by_pub_key(&self, pubkey: AccountId) -> Result { - self.account_node.get(&pubkey).ok_or(NodeDoesNotExist) + self.nodes.insert(node_key, &node); + self.keys.push(node_key); + Ok(node_key) } - pub fn get(&self, node_id: NodeId) -> Result<&Node> { - self.nodes.get(node_id as usize).ok_or(NodeDoesNotExist) + pub fn get(&self, node_key: NodeKey) -> Result { + self.nodes.get(node_key).ok_or(NodeDoesNotExist) } - pub fn get_mut(&mut self, node_id: NodeId) -> Result<&mut Node> { - self.nodes.get_mut(node_id as usize).ok_or(NodeDoesNotExist) + pub fn update(&mut self, node_key: NodeKey, node: &Node) -> Result<()> { + if !self.nodes.contains(&node_key) { + Err(NodeDoesNotExist) + } else { + self.nodes.insert(node_key, node); + Ok(()) + } } - pub fn remove_node(&mut self, node_id: NodeId) -> Result<()> { - let total_nodes = self.nodes.len(); - let last_node = self.nodes.get(total_nodes - 1).ok_or(NodeDoesNotExist).unwrap(); - self.account_node.insert(&last_node.node_pub_key, &node_id); - self.nodes.swap_remove(node_id.try_into().unwrap()); - Ok(()) + pub fn remove(&mut self, node_key: NodeKey) { + self.nodes.remove(node_key); + if let Some(pos) = self.keys.iter().position(|x| *x == node_key) { + self.keys.remove(pos); + }; } } diff --git a/bucket/ddc_bucket/params/messages.rs b/bucket/ddc_bucket/params/messages.rs deleted file mode 100644 index 92b7d5ab..00000000 --- a/bucket/ddc_bucket/params/messages.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! The public interface to change Params. - -use crate::ddc_bucket::{DdcBucket, Result}; - -use super::store::{Params, ParamsId, ParamsStore}; - -impl DdcBucket { - pub fn impl_change_params(store: &mut ParamsStore, params_id: ParamsId, params: Params) -> Result<()> { - store.change(params_id, params)?; - Ok(()) - } -} \ No newline at end of file diff --git a/bucket/ddc_bucket/params/mod.rs b/bucket/ddc_bucket/params/mod.rs deleted file mode 100644 index 94be958f..00000000 --- a/bucket/ddc_bucket/params/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! Params management. - -pub mod store; -pub mod messages; diff --git a/bucket/ddc_bucket/params/store.rs b/bucket/ddc_bucket/params/store.rs deleted file mode 100644 index 13933bc6..00000000 --- a/bucket/ddc_bucket/params/store.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! The store where to create and access Nodes. - -use ink_prelude::string::String; -use ink_prelude::vec::Vec; -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; - -use crate::ddc_bucket::{Error::ParamsDoesNotExist, Result}; -use crate::ddc_bucket::Error::ParamsTooBig; - -pub type ParamsId = u32; -pub type Params = String; - -pub const PARAMS_MAX_LEN: usize = 100_000; - -#[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] -pub struct ParamsStore(pub Vec); - -impl ParamsStore { - pub fn create(&mut self, params: Params) -> Result { - if params.len() > PARAMS_MAX_LEN { - return Err(ParamsTooBig); - } - let params_id: ParamsId = self.0.len().try_into().unwrap(); - self.0.push(params); - Ok(params_id) - } - - pub fn change(&mut self, params_id: ParamsId, params: Params) -> Result { - let current = self.0.get_mut(params_id as usize).ok_or(ParamsDoesNotExist)?; - - if params.len() > PARAMS_MAX_LEN { - return Err(ParamsTooBig); - } - let record_size = if params.len() > current.len() { - params.len() - current.len() - } else { 0 }; - - *current = params; - Ok(record_size) - } - - pub fn get(&self, params_id: ParamsId) -> Result<&Params> { - self.0.get(params_id as usize).ok_or(ParamsDoesNotExist) - } -} diff --git a/bucket/ddc_bucket/perm/entity.rs b/bucket/ddc_bucket/perm/entity.rs index 316470ba..3643e9ec 100644 --- a/bucket/ddc_bucket/perm/entity.rs +++ b/bucket/ddc_bucket/perm/entity.rs @@ -7,7 +7,8 @@ use crate::ddc_bucket::AccountId; #[derive(Copy, Clone, Encode, Decode)] #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] pub enum Permission { - ManagerTrustedBy(AccountId), + ClusterManagerTrustedBy(AccountId), SetExchangeRate, SuperAdmin, + Validator, } diff --git a/bucket/ddc_bucket/perm/messages.rs b/bucket/ddc_bucket/perm/messages.rs index 5d8e7324..6ecb68a7 100644 --- a/bucket/ddc_bucket/perm/messages.rs +++ b/bucket/ddc_bucket/perm/messages.rs @@ -1,30 +1,52 @@ //! The public interface for permission management. -use ink_lang::codegen::{EmitEvent, StaticEnv}; +use ink_lang::codegen::StaticEnv; -use crate::ddc_bucket::{AccountId, DdcBucket, GrantPermission, Result, RevokePermission}; -use crate::ddc_bucket::Error::Unauthorized; use crate::ddc_bucket::perm::entity::Permission; +use crate::ddc_bucket::{AccountId, DdcBucket, Error::*, Result}; impl DdcBucket { - pub fn impl_grant_permission(&mut self, account_id: AccountId, permission: Permission, is_granted: bool) -> Result<()> { - if is_granted { - self.perms.grant_permission(account_id, &permission); - Self::env().emit_event(GrantPermission { account_id, permission }); - Ok(()) - } else { - self.perms.revoke_permission(account_id, &permission); - Self::env().emit_event(RevokePermission { account_id, permission }); - Ok(()) - } + pub fn grant_permission( + &mut self, + account_id: AccountId, + permission: Permission, + ) -> Result<()> { + self.perms.grant_permission(account_id, permission); + Ok(()) + } + + pub fn revoke_permission( + &mut self, + account_id: AccountId, + permission: Permission, + ) -> Result<()> { + self.perms.revoke_permission(account_id, permission); + Ok(()) } pub fn only_with_permission(&self, permission: Permission) -> Result { let caller = Self::env().caller(); - if self.perms.has_permission(caller, permission) { - Ok(caller) - } else { - Err(Unauthorized) - } + self.perms + .has_permission(caller, permission) + .then(|| caller) + .ok_or(Unauthorized) + } + + pub fn only_trusted_cluster_manager(&self, provider_id: AccountId) -> Result { + let caller = Self::env().caller(); + let perm = Permission::ClusterManagerTrustedBy(provider_id); + self.perms + .has_permission(caller, perm) + .then(|| caller) + .ok_or(OnlyTrustedClusterManager) + } + + pub fn only_validator(&self) -> Result { + let caller = Self::env().caller(); + let perm = Permission::Validator; + self.perms + .has_permission(caller, perm) + .then(|| caller) + .ok_or(OnlyValidator) } } diff --git a/bucket/ddc_bucket/perm/mod.rs b/bucket/ddc_bucket/perm/mod.rs index 88205564..169bb94d 100644 --- a/bucket/ddc_bucket/perm/mod.rs +++ b/bucket/ddc_bucket/perm/mod.rs @@ -1,5 +1,5 @@ //! Permission management. -pub mod store; +pub mod entity; pub mod messages; -pub mod entity; \ No newline at end of file +pub mod store; diff --git a/bucket/ddc_bucket/perm/store.rs b/bucket/ddc_bucket/perm/store.rs index 69a2edc4..6c667a84 100644 --- a/bucket/ddc_bucket/perm/store.rs +++ b/bucket/ddc_bucket/perm/store.rs @@ -1,8 +1,8 @@ //! The store to create and access Accounts. use ink_prelude::vec::Vec; +use ink_storage::traits::{SpreadAllocate, SpreadLayout}; use ink_storage::Mapping; -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; use scale::Encode; use crate::ddc_bucket::AccountId; @@ -11,33 +11,32 @@ use super::entity::Permission; pub type TrustedBy = AccountId; - type PermKey = Vec; #[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] -pub struct PermStore(pub Mapping); -// TODO: Switch to Mapping (must upgrade ink first). - +#[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout, Debug))] +pub struct PermStore { + pub perms: Mapping, +} impl PermStore { - pub fn grant_permission(&mut self, account_id: AccountId, permission: &Permission) { + pub fn grant_permission(&mut self, account_id: AccountId, permission: Permission) { let key = (account_id, permission).encode(); - self.0.insert(key, &true); + self.perms.insert(key, &true); } - pub fn revoke_permission(&mut self, account_id: AccountId, permission: &Permission) { + pub fn revoke_permission(&mut self, account_id: AccountId, permission: Permission) { let key = (account_id, permission).encode(); - self.0.remove(&key); + self.perms.remove(&key); } pub fn has_permission(&self, account_id: AccountId, permission: Permission) -> bool { let key = (account_id, permission).encode(); - if self.0.contains(&key) { + if self.perms.contains(&key) { return true; } let admin_key = (account_id, Permission::SuperAdmin).encode(); - self.0.contains(&admin_key) + self.perms.contains(&admin_key) } } diff --git a/bucket/ddc_bucket/protocol/messages.rs b/bucket/ddc_bucket/protocol/messages.rs index 88007687..f0586164 100644 --- a/bucket/ddc_bucket/protocol/messages.rs +++ b/bucket/ddc_bucket/protocol/messages.rs @@ -1,35 +1,18 @@ //! The public interface to manage Protocol (fees included). -use crate::ddc_bucket::{DdcBucket, Result}; -use crate::ddc_bucket::cash::{Cash, Payable}; +use crate::ddc_bucket::cash::Cash; +use crate::ddc_bucket::{BasisPoints, DdcBucket, NetworkFeeConfig}; impl DdcBucket { - pub fn message_get_fee_bp(&self) -> u32 { - self.protocol_store.get_fee_bp() + pub fn message_get_protocol_fee_bp(&self) -> BasisPoints { + self.protocol.get_protocol_fee_bp() } - pub fn message_set_fee_bp(&mut self, fee_bp: u32) -> Result<()> { - match self.protocol_store.set_fee_bp(fee_bp) { - Err(_e) => panic!("Setting fee failed"), - Ok(_v) => Ok(()), - } + pub fn message_get_network_fee_config(&self) -> NetworkFeeConfig { + self.protocol.get_network_fee_config() } - pub fn message_get_fee_revenues(&self) -> Cash { - self.protocol_store.get_fee_revenues() + pub fn message_get_protocol_revenues(&self) -> Cash { + self.protocol.get_revenues() } - - pub fn message_put_fee_revenues(&mut self, amount: Cash) -> Result<()> { - self.protocol_store.put_revenues(amount); - - Ok(()) - } - - pub fn message_withdraw_revenues(&mut self, amount: u128) -> Result<()> { - self.protocol_store.withdraw_revenues(Payable(amount))?; - - Self::send_cash(self.protocol_store.admin, Cash(amount))?; - - Ok(()) - } -} \ No newline at end of file +} diff --git a/bucket/ddc_bucket/protocol/mod.rs b/bucket/ddc_bucket/protocol/mod.rs index 15cd69c4..04516ccb 100644 --- a/bucket/ddc_bucket/protocol/mod.rs +++ b/bucket/ddc_bucket/protocol/mod.rs @@ -1,4 +1,4 @@ // Protocol Management (Fees, ...) +pub mod messages; pub mod store; -pub mod messages; \ No newline at end of file diff --git a/bucket/ddc_bucket/protocol/store.rs b/bucket/ddc_bucket/protocol/store.rs index 9a5ee390..2f72aa42 100644 --- a/bucket/ddc_bucket/protocol/store.rs +++ b/bucket/ddc_bucket/protocol/store.rs @@ -1,47 +1,138 @@ -use ink_storage::traits::{SpreadAllocate, SpreadLayout, PackedLayout, StorageLayout}; - +use crate::ddc_bucket::cash::{Cash, Payable}; +use crate::ddc_bucket::currency::CurrencyConverter; +use crate::ddc_bucket::{ + AccountId, Balance, BasisPoints, DdcBucket, Error::*, Result, BASIS_POINTS, +}; +use ink_storage::traits::{PackedLayout, SpreadAllocate, SpreadLayout}; use scale::{Decode, Encode}; -use crate::ddc_bucket::{AccountId, Error::*, Result}; -use crate::ddc_bucket::cash::{Cash, Payable}; +/// The configuration of fees. +#[derive(Default, Clone, PartialEq, Encode, Decode, SpreadAllocate, SpreadLayout, PackedLayout)] +#[cfg_attr( + feature = "std", + derive(ink_storage::traits::StorageLayout, Debug, scale_info::TypeInfo) +)] +pub struct NetworkFeeConfig { + /// The fee rate from cluster revenues to the overall network. In basis points (1% of 1%). + pub network_fee_bp: BasisPoints, + /// The destination account of network fees. Use the 0 account to burn the fees. + pub network_fee_destination: AccountId, + /// The fee rate from cluster revenues to the cluster manager. In basis points (1% of 1%). + pub cluster_management_fee_bp: BasisPoints, +} + +impl NetworkFeeConfig { + pub fn new( + network_fee_bp: BasisPoints, + network_fee_destination: AccountId, + cluster_management_fee_bp: BasisPoints, + ) -> Self { + Self { + network_fee_bp, + network_fee_destination, + cluster_management_fee_bp, + } + } +} #[derive(Default, Clone, PartialEq, Encode, Decode, SpreadAllocate, SpreadLayout, PackedLayout)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug, scale_info::TypeInfo))] -pub struct ProtocolStore { - pub admin: AccountId, - pub fee_bp: u32, - pub revenues: Cash, +#[cfg_attr( + feature = "std", + derive(ink_storage::traits::StorageLayout, Debug, scale_info::TypeInfo) +)] +pub struct ProtocolStore { + pub protocol_fee_bp: BasisPoints, + pub protocol_fee_destination: AccountId, + pub revenues: Cash, + pub curr_converter: CurrencyConverter, + pub network_fee_config: NetworkFeeConfig, } impl ProtocolStore { + pub fn init( + &mut self, + protocol_fee_bp: BasisPoints, + protocol_fee_dest: AccountId, + network_fee_bp: BasisPoints, + network_fee_dest: AccountId, + cluster_fee_bp: BasisPoints, + ) { + self.protocol_fee_bp = protocol_fee_bp; + self.protocol_fee_destination = protocol_fee_dest; + self.curr_converter = CurrencyConverter::new(); + self.network_fee_config = + NetworkFeeConfig::new(network_fee_bp, network_fee_dest, cluster_fee_bp); + } + + pub fn get_protocol_fee_bp(&self) -> BasisPoints { + self.protocol_fee_bp + } + + pub fn set_protocol_fee_bp(&mut self, protocol_fee_bp: BasisPoints) { + self.protocol_fee_bp = protocol_fee_bp; + } + + pub fn get_protocol_fee_dest(&self) -> AccountId { + self.protocol_fee_destination + } + + pub fn get_revenues(&self) -> Cash { + self.revenues + } - pub fn init(&mut self, admin: AccountId, fee_bp: u32) { - self.admin = admin; - self.fee_bp = fee_bp - } - - pub fn get_fee_bp(&self) -> u32 { - self.fee_bp - } - - pub fn set_fee_bp(&mut self, fee_bp: u32) -> Result<()> { - self.fee_bp = fee_bp; - Ok(()) - } - - pub fn get_fee_revenues(&self) -> Cash { - self.revenues - } - - pub fn put_revenues(&mut self, amount: Cash) { - self.revenues.increase(amount); - } - - pub fn withdraw_revenues(&mut self, amount: Payable) -> Result<()> { - if amount.peek() > self.revenues.peek() { - return Err(InsufficientBalance); - } - self.revenues.pay_unchecked(amount); - Ok(()) - } -} \ No newline at end of file + pub fn put_revenues(&mut self, amount: Cash) { + self.revenues.increase(amount); + } + + pub fn withdraw_revenues(&mut self, amount: Payable) -> Result<()> { + if amount.peek() > self.revenues.peek() { + return Err(InsufficientBalance); + } + self.revenues.pay_unchecked(amount); + Ok(()) + } + + pub fn get_network_fee_config(&self) -> NetworkFeeConfig { + self.network_fee_config.clone() + } + + pub fn set_network_fee_config(&mut self, config: NetworkFeeConfig) { + self.network_fee_config = config; + } + + pub fn get_network_fee_bp(&self) -> BasisPoints { + self.network_fee_config.network_fee_bp + } + + pub fn get_network_fee_dest(&self) -> AccountId { + self.network_fee_config.network_fee_destination + } + + pub fn get_cluster_management_fee_bp(&self) -> BasisPoints { + self.network_fee_config.cluster_management_fee_bp + } +} + +impl DdcBucket { + /// Take a network fee from the given revenues (in place). + pub fn capture_network_fee(&mut self, revenues: &mut Cash) -> Result<()> { + self.capture_fee( + self.protocol.get_network_fee_bp(), + self.protocol.get_network_fee_dest(), + revenues, + ) + } + + /// Take a fee from the given revenues (in place) and send it to the destination. + pub fn capture_fee( + &self, + rate_bp: Balance, + destination: AccountId, + revenues: &mut Cash, + ) -> Result<()> { + let fee = revenues.peek() * rate_bp / BASIS_POINTS; + let (payable, cash) = Cash::borrow_payable_cash(fee); + revenues.pay(payable)?; + Self::send_cash(destination, cash) + } +} diff --git a/bucket/ddc_bucket/schedule.rs b/bucket/ddc_bucket/schedule.rs index d8e8c2b1..2c348983 100644 --- a/bucket/ddc_bucket/schedule.rs +++ b/bucket/ddc_bucket/schedule.rs @@ -1,6 +1,6 @@ //! The Schedule data structure implements a value that increases over time. -use ink_storage::traits::{SpreadAllocate, PackedLayout, SpreadLayout}; +use ink_storage::traits::{PackedLayout, SpreadAllocate, SpreadLayout}; use scale::{Decode, Encode}; use crate::ddc_bucket::Balance; @@ -19,7 +19,9 @@ impl Schedule { Schedule { rate, offset } } - pub fn empty() -> Schedule { Schedule::new(0, 0) } + pub fn empty() -> Schedule { + Schedule::new(0, 0) + } pub fn value_at_time(&self, time_ms: u64) -> Balance { let absolute = self.rate * time_ms as Balance / MS_PER_MONTH; @@ -28,7 +30,9 @@ impl Schedule { } pub fn time_of_value(&self, value: Balance) -> u64 { - if self.rate == 0 { return u64::MAX; } + if self.rate == 0 { + return u64::MAX; + } let absolute = self.offset + value; let time = absolute * MS_PER_MONTH / self.rate; @@ -52,4 +56,4 @@ impl Schedule { } } -pub const MS_PER_MONTH: u128 = 31 * 24 * 3600 * 1000; \ No newline at end of file +pub const MS_PER_MONTH: u128 = 31 * 24 * 3600 * 1000; diff --git a/bucket/ddc_bucket/tests/env_utils.rs b/bucket/ddc_bucket/tests/env_utils.rs index e84bca93..b99a832b 100644 --- a/bucket/ddc_bucket/tests/env_utils.rs +++ b/bucket/ddc_bucket/tests/env_utils.rs @@ -1,18 +1,16 @@ #![allow(unused_variables, dead_code)] pub use ink_env::{ - call, test, block_timestamp, + block_timestamp, call, test, test::{advance_block, DefaultAccounts}, DefaultEnvironment, }; -use scale::Decode; use crate::ddc_bucket::*; - +use scale::Decode; pub type Event = ::Type; - /// Recommended contract fee for all operations with reasonable data amounts. pub const CONTRACT_FEE_LIMIT: Balance = 10 * TOKEN; @@ -55,19 +53,12 @@ pub fn get_events() -> Vec { raw_events.iter().map(decode_event).collect() } -pub fn admin_id() -> AccountId { - get_accounts().alice -} - -pub fn contract_id() -> AccountId { - AccountId::from([0x09; 32]) -} - pub fn print_events(events: &[Event]) { for ev in events.iter() { match ev { Event::ClusterCreated(ev) => println!("EVENT {:?}", ev), Event::ClusterNodeReplaced(ev) => println!("EVENT {:?}", ev), + Event::ClusterNodeReset(ev) => println!("EVENT {:?}", ev), Event::ClusterReserveResource(ev) => println!("EVENT {:?}", ev), Event::ClusterDistributeRevenues(ev) => println!("EVENT {:?}", ev), Event::NodeCreated(ev) => println!("EVENT {:?}", ev), @@ -76,11 +67,25 @@ pub fn print_events(events: &[Event]) { Event::BucketSettlePayment(ev) => println!("EVENT {:?}", ev), Event::BucketAvailabilityUpdated(ev) => println!("EVENT {:?}", ev), Event::Deposit(ev) => println!("EVENT {:?}", ev), - Event::GrantPermission(ev) => println!("EVENT {:?}", ev), - Event::RevokePermission(ev) => println!("EVENT {:?}", ev), - Event::CdnClusterCreated(ev) => println!("EVENT {:?}", ev), - Event::CdnClusterDistributeRevenues(ev) => println!("EVENT {:?}", ev), + Event::PermissionGranted(ev) => println!("EVENT {:?}", ev), + Event::PermissionRevoked(ev) => println!("EVENT {:?}", ev), + Event::ClusterDistributeCdnRevenues(ev) => println!("EVENT {:?}", ev), Event::CdnNodeCreated(ev) => println!("EVENT {:?}", ev), + Event::ClusterNodeAdded(ev) => println!("EVENT {:?}", ev), + Event::ClusterCdnNodeAdded(ev) => println!("{:?}", ev), + Event::ClusterNodeRemoved(ev) => println!("EVENT {:?}", ev), + Event::ClusterCdnNodeRemoved(ev) => println!("EVENT {:?}", ev), + Event::ClusterParamsSet(ev) => println!("EVENT {:?}", ev), + Event::ClusterRemoved(ev) => println!("EVENT {:?}", ev), + Event::ClusterNodeStatusSet(ev) => println!("EVENT {:?}", ev), + Event::ClusterCdnNodeStatusSet(ev) => println!("EVENT {:?}", ev), + Event::CdnNodeRemoved(ev) => println!("EVENT {:?}", ev), + Event::CdnNodeParamsSet(ev) => println!("EVENT {:?}", ev), + Event::NodeRemoved(ev) => println!("EVENT {:?}", ev), + Event::NodeParamsSet(ev) => println!("EVENT {:?}", ev), + Event::NodeOwnershipTransferred(ev) => println!("EVENT {:?}", ev), + Event::CdnNodeOwnershipTransferred(ev) => println!("EVENT {:?}", ev), + Event::BucketParamsSet(ev) => println!("EVENT {:?}", ev), } } } diff --git a/bucket/ddc_bucket/tests/mod.rs b/bucket/ddc_bucket/tests/mod.rs index c82a3596..6d068f21 100644 --- a/bucket/ddc_bucket/tests/mod.rs +++ b/bucket/ddc_bucket/tests/mod.rs @@ -1,6 +1,10 @@ mod env_utils; +mod setup_utils; +mod test_account; mod test_admin; -mod test_contract; +mod test_bucket; +mod test_cdn_node; +mod test_cluster; mod test_currency; -// mod test_committer; +mod test_node; diff --git a/bucket/ddc_bucket/tests/setup_utils.rs b/bucket/ddc_bucket/tests/setup_utils.rs new file mode 100644 index 00000000..42efea88 --- /dev/null +++ b/bucket/ddc_bucket/tests/setup_utils.rs @@ -0,0 +1,304 @@ +use super::env_utils::*; +use crate::ddc_bucket::*; + +pub fn admin_id() -> AccountId { + get_accounts().alice +} + +pub fn contract_id() -> AccountId { + AccountId::from([0x09; 32]) +} + +pub fn setup_contract() -> DdcBucket { + set_caller(admin_id()); + set_callee(contract_id()); + let contract = DdcBucket::new(); + set_balance(contract_id(), 10); + contract +} + +pub struct TestCluster { + pub contract: DdcBucket, + + pub provider_id0: AccountId, + pub provider_id1: AccountId, + pub provider_id2: AccountId, + + pub node_key0: NodeKey, + pub node_key1: NodeKey, + pub node_key2: NodeKey, + pub node_params0: NodeParams, + pub node_params1: NodeParams, + pub node_params2: NodeParams, + pub v_nodes0: Vec, + pub v_nodes1: Vec, + pub v_nodes2: Vec, + pub node_capacity0: u32, + pub node_capacity1: u32, + pub node_capacity2: u32, + pub rent_v_node_per_month0: Balance, + pub rent_v_node_per_month1: Balance, + pub rent_v_node_per_month2: Balance, + + pub cdn_node_key0: CdnNodeKey, + pub cdn_node_key1: CdnNodeKey, + pub cdn_node_key2: CdnNodeKey, + pub cdn_node_params0: CdnNodeParams, + pub cdn_node_params1: CdnNodeParams, + pub cdn_node_params2: CdnNodeParams, + + pub manager_id: AccountId, + pub cluster_id: ClusterId, + pub cluster_params: ClusterParams, + pub cluster_v_nodes: Vec, + pub nodes_keys: Vec, + pub cdn_nodes_keys: Vec, + pub resource_per_v_node: Resource, + // pub rent_v_node_per_month: Balance, + // pub reserved_resource: u32, +} + +pub fn setup_cluster() -> TestCluster { + let mut contract: DdcBucket = setup_contract(); + + let provider_id0 = AccountId::from([ + 0xae, 0x7d, 0xe8, 0x17, 0xa4, 0xa5, 0x12, 0x57, 0xd2, 0x49, 0x64, 0x28, 0x3b, 0x25, 0x69, + 0x09, 0xdf, 0x0c, 0x99, 0x97, 0xc0, 0x3e, 0x2b, 0x88, 0x02, 0x02, 0xee, 0x10, 0xf4, 0x4d, + 0x72, 0x48, + ]); + let provider_id1 = AccountId::from([ + 0xc4, 0xba, 0xfd, 0x6a, 0xa1, 0x5a, 0x14, 0xd6, 0xee, 0xf2, 0xea, 0x92, 0xb7, 0xc6, 0x84, + 0x51, 0x68, 0x39, 0xbe, 0x96, 0xd6, 0xbf, 0xca, 0xa3, 0x68, 0xd2, 0x4f, 0xff, 0x09, 0x85, + 0xa7, 0x1e, + ]); + let provider_id2 = AccountId::from([ + 0xfa, 0x01, 0x28, 0xf8, 0xe1, 0x32, 0xc6, 0x81, 0x21, 0x06, 0xa5, 0xce, 0xae, 0x6d, 0xcf, + 0xf3, 0xd2, 0xc0, 0x1b, 0xb0, 0x13, 0xf2, 0xd7, 0x75, 0x6f, 0x20, 0xf9, 0x50, 0x00, 0xd6, + 0xc7, 0x2b, + ]); + let manager_id = AccountId::from([ + 0xd2, 0xc5, 0xea, 0xa2, 0x0c, 0xd0, 0x4e, 0xfb, 0x3f, 0x10, 0xb8, 0xad, 0xa9, 0xa4, 0x4f, + 0xe0, 0x85, 0x41, 0x1f, 0x59, 0xf2, 0x34, 0x1a, 0x92, 0xa3, 0x48, 0x4f, 0x04, 0x51, 0x87, + 0x68, 0x54, + ]); + + set_balance(provider_id0, 1000 * TOKEN); + set_balance(provider_id1, 1000 * TOKEN); + set_balance(provider_id2, 1000 * TOKEN); + set_balance(manager_id, 1000 * TOKEN); + + // let rent_v_node_per_month: Balance = 10 * TOKEN; + // let reserved_resource = 10; + + // Create the 1st storage node + let node_key0 = AccountId::from([0x0a; 32]); + let node_params0 = NodeParams::from("{\"url\":\"https://ddc.cere.network/storage/0\"}"); + let node_capacity0 = 100; + let rent_v_node_per_month0 = 10 * TOKEN; + set_caller_value(provider_id0, CONTRACT_FEE_LIMIT); + contract + .node_create( + node_key0, + node_params0.clone(), + node_capacity0, + rent_v_node_per_month0, + ) + .unwrap(); + + // Create the 2nd storage node + let node_key1 = AccountId::from([0x0b; 32]); + let node_params1 = NodeParams::from("{\"url\":\"https://ddc-1.cere.network/storage/1\"}"); + let node_capacity1 = 100; + let rent_v_node_per_month1 = 10 * TOKEN; + set_caller_value(provider_id1, CONTRACT_FEE_LIMIT); + contract + .node_create( + node_key1, + node_params1.clone(), + node_capacity1, + rent_v_node_per_month1, + ) + .unwrap(); + + // Create the 3rd storage node + let node_key2 = AccountId::from([0x0c; 32]); + let node_params2 = NodeParams::from("{\"url\":\"https://ddc-2.cere.network/storage/2\"}"); + let node_capacity2 = 100; + let rent_v_node_per_month2 = 10 * TOKEN; + set_caller_value(provider_id2, CONTRACT_FEE_LIMIT); + let node_key2 = contract + .node_create( + node_key2, + node_params2.clone(), + node_capacity2, + rent_v_node_per_month2, + ) + .unwrap(); + + // Create the 1st cdn node + let cdn_node_key0 = AccountId::from([0x0d; 32]); + let cdn_node_params0 = CdnNodeParams::from("{\"url\":\"https://ddc.cere.network/cdn/0\"}"); + set_caller_value(provider_id0, CONTRACT_FEE_LIMIT); + contract + .cdn_node_create(cdn_node_key0, cdn_node_params0.clone()) + .unwrap(); + + // Create the 2nd cdn node + let cdn_node_key1 = AccountId::from([0x0e; 32]); + let cdn_node_params1 = CdnNodeParams::from("{\"url\":\"https://ddc.cere.network/cdn/1\"}"); + set_caller_value(provider_id1, CONTRACT_FEE_LIMIT); + contract + .cdn_node_create(cdn_node_key1, cdn_node_params1.clone()) + .unwrap(); + + // Create the 3rd cdn node + let cdn_node_key2 = AccountId::from([0x0f; 32]); + let cdn_node_params2 = CdnNodeParams::from("{\"url\":\"https://ddc.cere.network/cdn/2\"}"); + set_caller_value(provider_id2, CONTRACT_FEE_LIMIT); + contract + .cdn_node_create(cdn_node_key2, cdn_node_params2.clone()) + .unwrap(); + + // Create a Cluster + let cluster_params = ClusterParams::from("{}"); + let resource_per_v_node = 10; + set_caller_value(manager_id, CONTRACT_FEE_LIMIT); + let cluster_id = contract + .cluster_create(cluster_params.clone(), resource_per_v_node) + .unwrap(); + + // Grant trusted manager_id permission from node providers to the cluster manager_id + for provider_id in [provider_id0, provider_id1, provider_id2] { + set_caller_value(provider_id, CONTRACT_FEE_LIMIT); + contract + .grant_trusted_manager_permission(manager_id) + .unwrap(); + let expected_perm = Permission::ClusterManagerTrustedBy(provider_id); + assert!(contract.has_permission(manager_id, expected_perm)); + } + + // Add the 1st storage node to the Cluster + let v_nodes0 = vec![1, 2, 3]; + set_caller_value(manager_id, CONTRACT_FEE_LIMIT); + contract + .cluster_add_node(cluster_id, node_key0, v_nodes0.clone()) + .unwrap(); + + // Add the 2nd storage node to the Cluster + let v_nodes1 = vec![4, 5, 6]; + set_caller_value(manager_id, CONTRACT_FEE_LIMIT); + contract + .cluster_add_node(cluster_id, node_key1, v_nodes1.clone()) + .unwrap(); + + // Add the 3rd storage node to the Cluster + let v_nodes2 = vec![7, 8, 9]; + set_caller_value(manager_id, CONTRACT_FEE_LIMIT); + contract + .cluster_add_node(cluster_id, node_key2, v_nodes2.clone()) + .unwrap(); + + // Add the 1st cdn node to the Cluster + set_caller_value(manager_id, CONTRACT_FEE_LIMIT); + contract + .cluster_add_cdn_node(cluster_id, cdn_node_key0) + .unwrap(); + + // Add the 2nd cdn node to the Cluster + set_caller_value(manager_id, CONTRACT_FEE_LIMIT); + contract + .cluster_add_cdn_node(cluster_id, cdn_node_key1) + .unwrap(); + + // Add the 3rd cdn node to the Cluster + set_caller_value(manager_id, CONTRACT_FEE_LIMIT); + contract + .cluster_add_cdn_node(cluster_id, cdn_node_key2) + .unwrap(); + + let nodes_keys = vec![node_key0, node_key1, node_key2]; + + let cdn_nodes_keys = vec![cdn_node_key0, cdn_node_key1, cdn_node_key2]; + + let mut cluster_v_nodes = Vec::::new(); + cluster_v_nodes.extend(v_nodes0.clone()); + cluster_v_nodes.extend(v_nodes1.clone()); + cluster_v_nodes.extend(v_nodes2.clone()); + + TestCluster { + contract, + + provider_id0, + provider_id1, + provider_id2, + + node_key0, + node_key1, + node_key2, + node_params0, + node_params1, + node_params2, + v_nodes0, + v_nodes1, + v_nodes2, + rent_v_node_per_month0, + rent_v_node_per_month1, + rent_v_node_per_month2, + + cdn_node_key0, + cdn_node_key1, + cdn_node_key2, + cdn_node_params0, + cdn_node_params1, + cdn_node_params2, + node_capacity0, + node_capacity1, + node_capacity2, + + manager_id, + cluster_id, + cluster_params, + nodes_keys, + cdn_nodes_keys, + cluster_v_nodes, + resource_per_v_node, + } +} + +pub struct TestBucket { + pub bucket_id: BucketId, + pub owner_id: AccountId, + pub resource: u32, +} + +pub fn setup_bucket(ctx: &mut TestCluster) -> TestBucket { + let owner_id = AccountId::from([ + 0xd4, 0x8f, 0x63, 0x67, 0xe2, 0x15, 0x51, 0xdf, 0x11, 0x1c, 0x92, 0x69, 0x0d, 0x04, 0x3f, + 0x75, 0xcb, 0x39, 0xf8, 0x27, 0xbb, 0xc7, 0x46, 0x4b, 0x8d, 0x5d, 0x70, 0xd1, 0x02, 0xaa, + 0x71, 0x0a, + ]); + set_balance(owner_id, 1000 * TOKEN); + set_caller_value(owner_id, CONTRACT_FEE_LIMIT); + + let bucket_id = ctx + .contract + .bucket_create("{}".to_string(), ctx.cluster_id, None) + .unwrap(); + + // Reserve some resources for the bucket from the cluster. + set_caller_value(owner_id, CONTRACT_FEE_LIMIT); + let resource = 1; + ctx.contract + .bucket_alloc_into_cluster(bucket_id, resource) + .unwrap(); + + // Deposit some value to pay for buckets. + set_caller_value(owner_id, 10 * TOKEN); + ctx.contract.account_deposit().unwrap(); + + TestBucket { + bucket_id, + owner_id, + resource, + } +} diff --git a/bucket/ddc_bucket/tests/test_account.rs b/bucket/ddc_bucket/tests/test_account.rs new file mode 100644 index 00000000..40cef68d --- /dev/null +++ b/bucket/ddc_bucket/tests/test_account.rs @@ -0,0 +1,78 @@ +use ink_lang as ink; + +use super::env_utils::*; +use super::setup_utils::*; +use crate::ddc_bucket::schedule::Schedule; +use crate::ddc_bucket::Error::*; +use crate::ddc_bucket::*; + +#[ink::test] +fn account_deposit_ok() { + let account_id = AccountId::from([ + 0x76, 0x95, 0x7c, 0xa6, 0xbe, 0xf5, 0xa3, 0x6d, 0x67, 0x0d, 0x3a, 0x84, 0xc6, 0x0a, 0xe2, + 0xbb, 0xc9, 0x5e, 0xee, 0xde, 0x3a, 0x5f, 0x27, 0x0e, 0x26, 0xe3, 0x43, 0x4c, 0x46, 0xe2, + 0x98, 0x10, + ]); + set_balance(account_id, 1000 * TOKEN); + + let mut contract = setup_contract(); + + assert_eq!( + contract.account_get(account_id), + Err(AccountDoesNotExist), + "must not get a non-existent account" + ); + + let deposit = 10 * TOKEN; + let deposit_after_fee = deposit; + + // Deposit some value. + set_caller_value(account_id, deposit); + contract.account_deposit()?; + + let account = contract.account_get(account_id)?; + assert_eq!( + account, + Account { + deposit: Cash(deposit_after_fee), + payable_schedule: Schedule::empty(), + bonded: Cash(0), + unbonded_amount: Cash(0), + negative: Cash(0), + unbonded_timestamp: 0, + }, + "must take deposit minus creation fee" + ); + + // Deposit more value. + set_caller_value(account_id, deposit); + contract.account_deposit()?; + + let account = contract.account_get(account_id)?; + assert_eq!( + account, + Account { + deposit: Cash(deposit_after_fee + deposit), + payable_schedule: Schedule::empty(), + bonded: Cash(0), + unbonded_amount: Cash(0), + negative: Cash(0), + unbonded_timestamp: 0, + }, + "must take more deposits without creation fee" + ); + + // Check events. + let mut events = get_events(); + events.reverse(); // Work with pop(). + + // First deposit event. + assert!(matches!(events.pop().unwrap(), Event::Deposit(ev) if ev == + Deposit { account_id, value: deposit_after_fee })); + + // Second deposit event. No deposit_contract_fee because the account already exists. + assert!(matches!(events.pop().unwrap(), Event::Deposit(ev) if ev == + Deposit { account_id, value: deposit })); + + assert_eq!(events.len(), 0, "all events must be checked"); +} diff --git a/bucket/ddc_bucket/tests/test_admin.rs b/bucket/ddc_bucket/tests/test_admin.rs index 8c4c22c0..d378caa9 100644 --- a/bucket/ddc_bucket/tests/test_admin.rs +++ b/bucket/ddc_bucket/tests/test_admin.rs @@ -1,12 +1,17 @@ use ink_lang as ink; +use super::env_utils::*; +use super::setup_utils::*; +use crate::ddc_bucket::Error::*; use crate::ddc_bucket::*; -use super::env_utils::*; +fn not_admin_id() -> AccountId { + get_accounts().bob +} #[ink::test] -fn admin_init_works() { - let contract = setup(); +fn admin_init_ok() { + let contract = setup_contract(); // The deployer is SuperAdmin. assert!(contract.has_permission(admin_id(), Permission::SuperAdmin)); @@ -18,101 +23,341 @@ fn admin_init_works() { } #[ink::test] -fn admin_withdraw_works() { - let mut contract = setup(); +fn admin_withdraw_ok() { + let mut contract = setup_contract(); assert_eq!(balance_of(contract_id()), 10); - + set_caller(admin_id()); - contract.admin_withdraw(9); + contract.admin_withdraw(9).unwrap(); assert_eq!(balance_of(contract_id()), 1); } #[ink::test] #[should_panic] -fn admin_withdraw_only_admin() { - let mut contract = setup(); +fn admin_withdraw_err_if_not_admin() { + let mut contract = setup_contract(); set_caller(not_admin_id()); - - contract.admin_withdraw(9); // panic. + + contract.admin_withdraw(9).unwrap(); // panic. } #[ink::test] -fn admin_grant_works() { - let mut contract = setup(); +fn admin_grant_ok() { + let mut contract = setup_contract(); let permission = Permission::SuperAdmin; set_caller_value(admin_id(), CONTRACT_FEE_LIMIT); let new_admin_id = not_admin_id(); - contract.admin_grant_permission(new_admin_id, permission); + contract.admin_grant_permission(new_admin_id, permission)?; // Check the last event. let ev = get_events().pop().unwrap(); - assert!(matches!(ev, Event::GrantPermission(ev) if ev == - GrantPermission { account_id: not_admin_id(), permission })); + assert!(matches!(ev, Event::PermissionGranted(ev) if ev == + PermissionGranted { account_id: not_admin_id(), permission })); assert!(contract.has_permission(new_admin_id, permission)); set_caller(new_admin_id); - contract.admin_withdraw(9); + contract.admin_withdraw(9).unwrap(); } #[ink::test] -#[should_panic] -fn admin_grant_only_admin() { - let mut contract = setup(); +fn admin_grant_err_if_not_admin() { + let mut contract = setup_contract(); set_caller_value(not_admin_id(), CONTRACT_FEE_LIMIT); - contract.admin_grant_permission(get_accounts().charlie, Permission::SuperAdmin); // panic. + assert_eq!( + contract.admin_grant_permission(get_accounts().charlie, Permission::SuperAdmin), + Err(OnlySuperAdmin) + ); } #[ink::test] #[should_panic] -fn admin_revoke_works() { - let mut contract = setup(); +fn admin_revoke_ok() { + let mut contract = setup_contract(); let permission = Permission::SuperAdmin; set_caller(admin_id()); - contract.admin_revoke_permission(admin_id(), permission); + contract.admin_revoke_permission(admin_id(), permission)?; // Check the last event. let ev = get_events().pop().unwrap(); - assert!(matches!(ev, Event::RevokePermission(ev) if ev == - RevokePermission { account_id: not_admin_id(), permission })); + assert!(matches!(ev, Event::PermissionRevoked(ev) if ev == + PermissionRevoked { account_id: not_admin_id(), permission })); assert!(!contract.has_permission(admin_id(), permission)); // Cannot withdraw because no more permission. set_caller(admin_id()); - contract.admin_withdraw(9); // panic. + contract.admin_withdraw(9)?; } #[ink::test] -#[should_panic] -fn admin_revoke_only_admin() { - let mut contract = setup(); +fn admin_revoke_err_if_not_admin() { + let mut contract = setup_contract(); set_caller_value(not_admin_id(), CONTRACT_FEE_LIMIT); - contract.admin_revoke_permission(admin_id(), Permission::SuperAdmin); // panic. + assert_eq!( + contract.admin_revoke_permission(admin_id(), Permission::SuperAdmin), + Err(OnlySuperAdmin) + ); +} + +#[ink::test] +fn admin_transfer_node_ownership_err_if_not_admin() { + let mut contract = setup_contract(); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + let not_admin_id = AccountId::from([ + 0x00, 0xc9, 0x91, 0xf1, 0x63, 0x0f, 0xb4, 0x51, 0xf6, 0x6c, 0x9e, 0xa5, 0xc6, 0xdd, 0xf3, + 0x33, 0xd8, 0x48, 0x75, 0xc6, 0x22, 0xf5, 0xd3, 0xde, 0x4a, 0x39, 0xe7, 0x71, 0x6f, 0x74, + 0xf0, 0x49, + ]); + set_balance(not_admin_id, 1000 * TOKEN); + + set_caller_value(not_admin_id, CONTRACT_FEE_LIMIT); + contract.node_create( + new_node_key, + NodeParams::from("{\"url\":\"https://ddc-1.cere.network/storage/new\"}"), + 100, + 10 * TOKEN, + )?; + + let node_info = contract.node_get(new_node_key)?; + assert_eq!(node_info.node.provider_id, not_admin_id); + + let new_owner_id = AccountId::from([ + 0xf8, 0x9e, 0xfb, 0x5c, 0x80, 0x72, 0x8e, 0x2a, 0x69, 0x54, 0x73, 0x32, 0x52, 0x8b, 0x03, + 0xb7, 0x9d, 0x2c, 0xd5, 0x06, 0xed, 0x38, 0x72, 0x95, 0x19, 0x9c, 0x6b, 0x8f, 0x7e, 0xa3, + 0x47, 0x16, + ]); + set_balance(new_owner_id, 1000 * TOKEN); + + set_caller(not_admin_id); + + assert_eq!( + contract.admin_transfer_node_ownership(new_node_key, new_owner_id), + Err(OnlySuperAdmin) + ); } -fn setup() -> DdcBucket { +#[ink::test] +fn admin_transfer_node_ownership_err_if_provider_is_not_admin() { + let mut contract = setup_contract(); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + let not_admin_id = AccountId::from([ + 0x00, 0xc9, 0x91, 0xf1, 0x63, 0x0f, 0xb4, 0x51, 0xf6, 0x6c, 0x9e, 0xa5, 0xc6, 0xdd, 0xf3, + 0x33, 0xd8, 0x48, 0x75, 0xc6, 0x22, 0xf5, 0xd3, 0xde, 0x4a, 0x39, 0xe7, 0x71, 0x6f, 0x74, + 0xf0, 0x49, + ]); + set_balance(not_admin_id, 1000 * TOKEN); + + set_caller_value(not_admin_id, CONTRACT_FEE_LIMIT); + contract.node_create( + new_node_key, + NodeParams::from("{\"url\":\"https://ddc-1.cere.network/storage/new\"}"), + 100, + 10 * TOKEN, + )?; + + let node_info = contract.node_get(new_node_key)?; + assert_eq!(node_info.node.provider_id, not_admin_id); + + let new_owner_id = AccountId::from([ + 0xf8, 0x9e, 0xfb, 0x5c, 0x80, 0x72, 0x8e, 0x2a, 0x69, 0x54, 0x73, 0x32, 0x52, 0x8b, 0x03, + 0xb7, 0x9d, 0x2c, 0xd5, 0x06, 0xed, 0x38, 0x72, 0x95, 0x19, 0x9c, 0x6b, 0x8f, 0x7e, 0xa3, + 0x47, 0x16, + ]); + set_balance(new_owner_id, 1000 * TOKEN); + set_caller(admin_id()); - set_callee(contract_id()); - let contract = DdcBucket::new(); - set_balance(contract_id(), 10); - contract + + assert_eq!( + contract.admin_transfer_node_ownership(new_node_key, new_owner_id), + Err(NodeProviderIsNotSuperAdmin) + ); } -fn not_admin_id() -> AccountId { - get_accounts().bob -} \ No newline at end of file +#[ink::test] +fn admin_transfer_node_ownership_ok() { + let mut contract = setup_contract(); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + set_caller_value(admin_id(), CONTRACT_FEE_LIMIT); + contract.node_create( + new_node_key, + NodeParams::from("{\"url\":\"https://ddc-1.cere.network/storage/new\"}"), + 100, + 10 * TOKEN, + )?; + + let node_info1 = contract.node_get(new_node_key)?; + assert_eq!(node_info1.node.provider_id, admin_id()); + + let new_owner_id = AccountId::from([ + 0xf8, 0x9e, 0xfb, 0x5c, 0x80, 0x72, 0x8e, 0x2a, 0x69, 0x54, 0x73, 0x32, 0x52, 0x8b, 0x03, + 0xb7, 0x9d, 0x2c, 0xd5, 0x06, 0xed, 0x38, 0x72, 0x95, 0x19, 0x9c, 0x6b, 0x8f, 0x7e, 0xa3, + 0x47, 0x16, + ]); + set_balance(new_owner_id, 1000 * TOKEN); + + set_caller(admin_id()); + contract.admin_transfer_node_ownership(new_node_key, new_owner_id)?; + + let node_info2 = contract.node_get(new_node_key)?; + assert_eq!(node_info2.node.provider_id, new_owner_id); + + assert!( + matches!(get_events().pop().unwrap(), Event::NodeOwnershipTransferred(ev) if ev == + NodeOwnershipTransferred { + account_id: new_owner_id, + node_key: new_node_key + } + ) + ); +} + +#[ink::test] +fn admin_transfer_cdn_node_ownership_err_if_not_admin() { + let mut contract = setup_contract(); + + let new_cdn_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + let not_admin_id = AccountId::from([ + 0x00, 0xc9, 0x91, 0xf1, 0x63, 0x0f, 0xb4, 0x51, 0xf6, 0x6c, 0x9e, 0xa5, 0xc6, 0xdd, 0xf3, + 0x33, 0xd8, 0x48, 0x75, 0xc6, 0x22, 0xf5, 0xd3, 0xde, 0x4a, 0x39, 0xe7, 0x71, 0x6f, 0x74, + 0xf0, 0x49, + ]); + set_balance(not_admin_id, 1000 * TOKEN); + + set_caller_value(not_admin_id, CONTRACT_FEE_LIMIT); + contract.cdn_node_create( + new_cdn_node_key, + CdnNodeParams::from("{\"url\":\"https://ddc-1.cere.network/cdn/new\"}"), + )?; + + let cdn_node_info = contract.cdn_node_get(new_cdn_node_key)?; + assert_eq!(cdn_node_info.cdn_node.provider_id, not_admin_id); + + let new_owner_id = AccountId::from([ + 0xf8, 0x9e, 0xfb, 0x5c, 0x80, 0x72, 0x8e, 0x2a, 0x69, 0x54, 0x73, 0x32, 0x52, 0x8b, 0x03, + 0xb7, 0x9d, 0x2c, 0xd5, 0x06, 0xed, 0x38, 0x72, 0x95, 0x19, 0x9c, 0x6b, 0x8f, 0x7e, 0xa3, + 0x47, 0x16, + ]); + set_balance(new_owner_id, 1000 * TOKEN); + + set_caller(not_admin_id); + + assert_eq!( + contract.admin_transfer_cdn_node_ownership(new_cdn_node_key, new_owner_id), + Err(OnlySuperAdmin) + ); +} + +#[ink::test] +fn admin_transfer_cdn_node_ownership_err_if_provider_is_not_admin() { + let mut contract = setup_contract(); + + let new_cdn_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + let not_admin_id = AccountId::from([ + 0x00, 0xc9, 0x91, 0xf1, 0x63, 0x0f, 0xb4, 0x51, 0xf6, 0x6c, 0x9e, 0xa5, 0xc6, 0xdd, 0xf3, + 0x33, 0xd8, 0x48, 0x75, 0xc6, 0x22, 0xf5, 0xd3, 0xde, 0x4a, 0x39, 0xe7, 0x71, 0x6f, 0x74, + 0xf0, 0x49, + ]); + set_balance(not_admin_id, 1000 * TOKEN); + + set_caller_value(not_admin_id, CONTRACT_FEE_LIMIT); + contract.cdn_node_create( + new_cdn_node_key, + CdnNodeParams::from("{\"url\":\"https://ddc-1.cere.network/cdn/new\"}"), + )?; + + let cdn_node_info = contract.cdn_node_get(new_cdn_node_key)?; + assert_eq!(cdn_node_info.cdn_node.provider_id, not_admin_id); + + let new_owner_id = AccountId::from([ + 0xf8, 0x9e, 0xfb, 0x5c, 0x80, 0x72, 0x8e, 0x2a, 0x69, 0x54, 0x73, 0x32, 0x52, 0x8b, 0x03, + 0xb7, 0x9d, 0x2c, 0xd5, 0x06, 0xed, 0x38, 0x72, 0x95, 0x19, 0x9c, 0x6b, 0x8f, 0x7e, 0xa3, + 0x47, 0x16, + ]); + set_balance(new_owner_id, 1000 * TOKEN); + + set_caller(admin_id()); + + assert_eq!( + contract.admin_transfer_cdn_node_ownership(new_cdn_node_key, new_owner_id), + Err(CdnNodeOwnerIsNotSuperAdmin) + ); +} + +#[ink::test] +fn admin_transfer_cdn_node_ownership_ok() { + let mut contract = setup_contract(); + + let new_cdn_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + set_caller_value(admin_id(), CONTRACT_FEE_LIMIT); + contract.cdn_node_create( + new_cdn_node_key, + CdnNodeParams::from("{\"url\":\"https://ddc-1.cere.network/cdn/new\"}"), + )?; + + let cdn_node_info1 = contract.cdn_node_get(new_cdn_node_key)?; + assert_eq!(cdn_node_info1.cdn_node.provider_id, admin_id()); + + let new_owner_id = AccountId::from([ + 0xf8, 0x9e, 0xfb, 0x5c, 0x80, 0x72, 0x8e, 0x2a, 0x69, 0x54, 0x73, 0x32, 0x52, 0x8b, 0x03, + 0xb7, 0x9d, 0x2c, 0xd5, 0x06, 0xed, 0x38, 0x72, 0x95, 0x19, 0x9c, 0x6b, 0x8f, 0x7e, 0xa3, + 0x47, 0x16, + ]); + set_balance(new_owner_id, 1000 * TOKEN); + + set_caller(admin_id()); + contract.admin_transfer_cdn_node_ownership(new_cdn_node_key, new_owner_id)?; + + let cdn_node_info2 = contract.cdn_node_get(new_cdn_node_key)?; + assert_eq!(cdn_node_info2.cdn_node.provider_id, new_owner_id); + + assert!( + matches!(get_events().pop().unwrap(), Event::CdnNodeOwnershipTransferred(ev) if ev == + CdnNodeOwnershipTransferred { + account_id: new_owner_id, + cdn_node_key: new_cdn_node_key + } + ) + ); +} diff --git a/bucket/ddc_bucket/tests/test_bucket.rs b/bucket/ddc_bucket/tests/test_bucket.rs new file mode 100644 index 00000000..8524bbce --- /dev/null +++ b/bucket/ddc_bucket/tests/test_bucket.rs @@ -0,0 +1,272 @@ +use ink_lang as ink; + +use super::env_utils::*; +use super::setup_utils::*; +use crate::ddc_bucket::flow::Flow; +use crate::ddc_bucket::schedule::{Schedule, MS_PER_MONTH}; +use crate::ddc_bucket::*; + +fn do_bucket_pays_cluster( + ctx: &mut TestCluster, + test_bucket: &TestBucket, + usd_per_cere: Balance, +) -> Result<()> { + let expected_rent = ctx.rent_v_node_per_month0 * ctx.v_nodes0.len() as Balance + + ctx.rent_v_node_per_month1 * ctx.v_nodes1.len() as Balance + + ctx.rent_v_node_per_month2 * ctx.v_nodes2.len() as Balance; + + // Check the state before payment. + let before = ctx + .contract + .account_get(test_bucket.owner_id)? + .deposit + .peek(); + let bucket = ctx.contract.bucket_get(test_bucket.bucket_id)?.bucket; + assert_eq!(bucket.owner_id, test_bucket.owner_id); + /* TODO: Not testable at the moment, see struct BucketInStatus. + assert_eq!(bucket.flow, + Flow { + from: test_bucket.owner_id, + schedule: Schedule::new(0, expected_rent), + }); + */ + let timestamp_before = block_timestamp::(); + // Go to the future when some revenues are due. + advance_block::(); + // Pay the due thus far. + set_caller_value(ctx.manager_id, CONTRACT_FEE_LIMIT); + ctx.contract.bucket_settle_payment(test_bucket.bucket_id)?; + let timestamp_after = block_timestamp::(); + + // Check the last event. + let ev = get_events().pop().unwrap(); + assert!(matches!(ev, Event::BucketSettlePayment(ev) if ev == + BucketSettlePayment { + bucket_id: test_bucket.bucket_id, + cluster_id: ctx.cluster_id + } + )); + + // Check the state after payment. + let after = ctx + .contract + .account_get(test_bucket.owner_id)? + .deposit + .peek(); + let spent = before - after; + /* TODO: Not testable at the moment, see struct BucketInStatus. + let bucket = ctx.contract.bucket_get(test_bucket.bucket_id)?.bucket; + assert_eq!(bucket.flow, + Flow { + from: test_bucket.owner_id, + schedule: Schedule::new(BLOCK_TIME, expected_rent), + }); + */ + let timespan = timestamp_after - timestamp_before; + let expect_revenues_usd = expected_rent * timespan as u128 / MS_PER_MONTH as u128; + let expect_revenues = expect_revenues_usd / usd_per_cere; + assert!(expect_revenues > 0); + assert_eq!( + expect_revenues, spent, + "revenues must come from the bucket owner" + ); + + let cluster = ctx.contract.cluster_get(ctx.cluster_id)?.cluster; + assert_eq!( + cluster.revenues.peek(), + expect_revenues, + "must get revenues into the cluster" + ); + + Ok(()) +} + +#[ink::test] +fn bucket_pays_cluster_ok() { + let ctx = &mut setup_cluster(); + let test_bucket = &setup_bucket(ctx); + do_bucket_pays_cluster(ctx, test_bucket, 1).unwrap(); +} + +#[ink::test] +fn bucket_pays_cluster_at_new_rate_ok() { + let ctx = &mut setup_cluster(); + + let test_bucket = &setup_bucket(ctx); + // Set up an exchange rate manager_id. + set_caller(admin_id()); + ctx.contract + .admin_grant_permission(admin_id(), Permission::SetExchangeRate) + .unwrap(); + + // Change the currency exchange rate. + let usd_per_cere = 2; + set_caller(admin_id()); + ctx.contract + .account_set_usd_per_cere(usd_per_cere * TOKEN)?; + + do_bucket_pays_cluster(ctx, test_bucket, usd_per_cere).unwrap(); +} + +#[ink::test] +fn bucket_create_ok() { + let ctx = &mut setup_cluster(); + let test_bucket = &setup_bucket(ctx); + + // Check the structure of the bucket including the payment flow. + let total_rent = ctx.rent_v_node_per_month0 * ctx.v_nodes0.len() as Balance + + ctx.rent_v_node_per_month1 * ctx.v_nodes1.len() as Balance + + ctx.rent_v_node_per_month2 * ctx.v_nodes2.len() as Balance; + + let bucket_params = BucketParams::from(""); + let expect_bucket = Bucket { + owner_id: test_bucket.owner_id, + cluster_id: ctx.cluster_id, + flow: Flow { + from: test_bucket.owner_id, + schedule: Schedule::new(0, total_rent), + }, + resource_reserved: test_bucket.resource, + public_availability: false, + resource_consumption_cap: 0, + bucket_params: bucket_params, + }; + + // Check the status of the bucket. + let bucket_status = ctx.contract.bucket_get(test_bucket.bucket_id)?; + assert_eq!( + bucket_status, + BucketStatus { + bucket_id: test_bucket.bucket_id, + bucket: expect_bucket.into(), + params: "{}".to_string(), + writer_ids: vec![test_bucket.owner_id], + reader_ids: vec![], + rent_covered_until_ms: 297600000, // TODO: check this value. + } + ); + + let mut events = get_events(); + events.reverse(); // Work with pop(). + events.truncate(8 - 3 - 2); // Skip 3 NodeCreated and 2 cluster setup_contract events. + + // Create bucket. + assert!( + matches!(events.pop().unwrap(), Event::BucketCreated(ev) if ev == + BucketCreated { bucket_id: test_bucket.bucket_id, owner_id: test_bucket.owner_id }) + ); + + assert!( + matches!(events.pop().unwrap(), Event::BucketAllocated(ev) if ev == + BucketAllocated { bucket_id: test_bucket.bucket_id, cluster_id: ctx.cluster_id, resource: test_bucket.resource }) + ); + + // Deposit more. + let net_deposit = 10 * TOKEN; + assert!(matches!(events.pop().unwrap(), Event::Deposit(ev) if ev == + Deposit { account_id: test_bucket.owner_id, value: net_deposit })); + + assert_eq!(events.len(), 0, "all events must be checked"); +} + +#[ink::test] +fn bucket_change_params_ok() { + let ctx = &mut setup_cluster(); + let test_bucket = &setup_bucket(ctx); + + // Change params. + set_caller_value(test_bucket.owner_id, CONTRACT_FEE_LIMIT); + ctx.contract + .bucket_change_params(test_bucket.bucket_id, "new params".to_string())?; + + // Check the changed params. + let status = ctx.contract.bucket_get(test_bucket.bucket_id)?; + assert_eq!(status.params, "new params"); +} + +#[ink::test] +#[should_panic] +fn bucket_change_params_only_owner() { + let ctx = &mut setup_cluster(); + let test_bucket = &setup_bucket(ctx); + + // Change params. + set_caller_value(get_accounts().bob, CONTRACT_FEE_LIMIT); + ctx.contract + .bucket_change_params(test_bucket.bucket_id, "new params".to_string())?; + // Panic. +} + +#[ink::test] +fn bucket_list_ok() { + let mut ddc_bucket = setup_contract(); + + let owner_id1 = AccountId::from([ + 0xd8, 0x69, 0x19, 0x54, 0xea, 0xdc, 0x9a, 0xc0, 0x3d, 0x37, 0x56, 0x9f, 0x2a, 0xe8, 0xdf, + 0x59, 0x34, 0x3f, 0x32, 0x65, 0xba, 0xd4, 0x16, 0xac, 0x07, 0xdf, 0x06, 0xeb, 0x4d, 0xbc, + 0x6a, 0x66, + ]); + set_balance(owner_id1, 1000 * TOKEN); + let owner_id2 = AccountId::from([ + 0x2a, 0x5f, 0xbc, 0xcf, 0x71, 0x0b, 0x65, 0x04, 0x88, 0x91, 0x12, 0x7e, 0x5e, 0xe3, 0x78, + 0xdb, 0x48, 0x63, 0x09, 0x44, 0xcc, 0xc5, 0x75, 0xbd, 0xa5, 0xaa, 0xa5, 0x0e, 0x77, 0xab, + 0x7b, 0x4e, + ]); + set_balance(owner_id2, 1000 * TOKEN); + let owner_id3 = AccountId::from([ + 0x64, 0xef, 0xd7, 0xb4, 0x41, 0xb2, 0x58, 0xb5, 0x56, 0x6b, 0xfc, 0x4b, 0x19, 0xb8, 0xe5, + 0x09, 0x5d, 0x17, 0xb3, 0xc3, 0x44, 0x38, 0x58, 0xa9, 0x7d, 0x20, 0x49, 0x39, 0xbd, 0xbd, + 0xb6, 0x48, + ]); + set_balance(owner_id3, 1000 * TOKEN); + + let cluster_id = 0; + + set_caller_value(owner_id1, CONTRACT_FEE_LIMIT); + let bucket_id1 = ddc_bucket.bucket_create("".to_string(), cluster_id, None)?; + let bucket_status1 = ddc_bucket.bucket_get(bucket_id1)?; + + set_caller_value(owner_id2, CONTRACT_FEE_LIMIT); + let bucket_id2 = ddc_bucket.bucket_create("".to_string(), cluster_id, None)?; + let bucket_status2 = ddc_bucket.bucket_get(bucket_id2)?; + + assert_ne!(bucket_id1, bucket_id2); + let count = 2; + + assert_eq!( + ddc_bucket.bucket_list(0, 100, None), + (vec![bucket_status1.clone(), bucket_status2.clone()], count) + ); + + assert_eq!( + ddc_bucket.bucket_list(0, 2, None), + (vec![bucket_status1.clone(), bucket_status2.clone()], count) + ); + + assert_eq!( + ddc_bucket.bucket_list(0, 1, None), + (vec![bucket_status1.clone()], count) + ); + assert_eq!( + ddc_bucket.bucket_list(1, 1, None), + (vec![bucket_status2.clone()], count) + ); + + assert_eq!(ddc_bucket.bucket_list(count, 20, None), (vec![], count)); + + // Filter by owner. + assert_eq!( + ddc_bucket.bucket_list(0, 100, Some(owner_id1)), + (vec![bucket_status1.clone()], count) + ); + + assert_eq!( + ddc_bucket.bucket_list(0, 100, Some(owner_id2)), + (vec![bucket_status2.clone()], count) + ); + + assert_eq!( + ddc_bucket.bucket_list(0, 100, Some(owner_id3)), + (vec![], count) + ); +} diff --git a/bucket/ddc_bucket/tests/test_cdn_node.rs b/bucket/ddc_bucket/tests/test_cdn_node.rs new file mode 100644 index 00000000..57c51b30 --- /dev/null +++ b/bucket/ddc_bucket/tests/test_cdn_node.rs @@ -0,0 +1,272 @@ +use ink_lang as ink; + +use crate::ddc_bucket::Error::*; +use crate::ddc_bucket::*; +use cdn_node::entity::*; + +use super::env_utils::*; +use super::setup_utils::*; + +#[ink::test] +fn cdn_node_create_err_if_node_exists() { + let mut ctx = setup_cluster(); + assert_eq!( + ctx.contract + .cdn_node_create(ctx.cdn_node_key1, ctx.cdn_node_params1,), + Err(CdnNodeAlreadyExists) + ); +} + +#[ink::test] +fn cdn_node_create_ok() { + let mut ctx = setup_cluster(); + + let new_provider_id = AccountId::from([ + 0x76, 0x30, 0xc6, 0x96, 0x6f, 0xd3, 0x26, 0xba, 0x1a, 0xa0, 0x6f, 0xd8, 0x7f, 0x7b, 0xf2, + 0xef, 0x14, 0x11, 0xf0, 0x0d, 0x00, 0xa9, 0xe7, 0x11, 0xdf, 0xd1, 0x65, 0x14, 0x5d, 0x01, + 0xdb, 0x59, + ]); + set_balance(new_provider_id, 1000 * TOKEN); + + let new_cdn_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + let new_cdn_node_params = + CdnNodeParams::from("{\"url\":\"https://ddc-1.cere.network/cdn/new\"}"); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .cdn_node_create(new_cdn_node_key, new_cdn_node_params.clone())?; + + assert!( + matches!(get_events().pop().unwrap(), Event::CdnNodeCreated(ev) if ev == + CdnNodeCreated { + cdn_node_key: new_cdn_node_key, + provider_id: new_provider_id, + undistributed_payment: 0, + cdn_node_params: new_cdn_node_params.clone() + }) + ); + + let cdn_node_info = ctx.contract.cdn_node_get(new_cdn_node_key)?; + let _expected_cdn_node = CdnNode { + provider_id: new_provider_id, + undistributed_payment: 0, + cdn_node_params: new_cdn_node_params, + cluster_id: None, + status_in_cluster: None, + }; + assert!(matches!(cdn_node_info.cdn_node, _expected_cdn_node)); +} + +#[ink::test] +fn cdn_node_remove_err_if_not_provider() { + let mut ctx = setup_cluster(); + + let not_provider_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_provider_id, 1000 * TOKEN); + + set_caller(not_provider_id); + assert_eq!( + ctx.contract.cdn_node_remove(ctx.cdn_node_key1), + Err(OnlyCdnNodeProvider) + ); +} + +#[ink::test] +fn cdn_node_remove_err_if_node_in_cluster() { + let mut ctx = setup_cluster(); + + set_caller(ctx.provider_id1); + assert_eq!( + ctx.contract.cdn_node_remove(ctx.cdn_node_key1), + Err(CdnNodeIsAddedToCluster(ctx.cluster_id)) + ); +} + +#[ink::test] +fn cdn_node_remove_ok() { + let mut ctx = setup_cluster(); + + set_caller(ctx.provider_id1); + ctx.contract + .cluster_remove_cdn_node(ctx.cluster_id, ctx.cdn_node_key1)?; + ctx.contract.cdn_node_remove(ctx.cdn_node_key1)?; + + assert!( + matches!(get_events().pop().unwrap(), Event::CdnNodeRemoved(ev) if ev == + CdnNodeRemoved { + cdn_node_key: ctx.cdn_node_key1, + } + ) + ); + + assert_eq!( + ctx.contract.cdn_node_get(ctx.cdn_node_key1), + Err(CdnNodeDoesNotExist) + ); +} + +#[ink::test] +fn cdn_node_set_params_err_if_not_provider() { + let mut ctx = setup_cluster(); + + let not_provider = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_provider, 1000 * TOKEN); + // Change params.not_provider + let new_cdn_node_params = CdnNodeParams::from("new cdn node params"); + set_caller_value(not_provider, CONTRACT_FEE_LIMIT); + + assert_eq!( + ctx.contract + .cdn_node_set_params(ctx.cdn_node_key0, new_cdn_node_params), + Err(OnlyCdnNodeProvider) + ); +} + +#[ink::test] +fn node_set_params_ok() { + let mut ctx = setup_cluster(); + + // Change params. + let new_cdn_node_params = NodeParams::from("new cdn node params"); + set_caller_value(ctx.provider_id0, CONTRACT_FEE_LIMIT); + ctx.contract + .cdn_node_set_params(ctx.cdn_node_key0, new_cdn_node_params.clone())?; + + assert!( + matches!(get_events().pop().unwrap(), Event::CdnNodeParamsSet(ev) if ev == + CdnNodeParamsSet { + cdn_node_key: ctx.cdn_node_key0, + cdn_node_params: new_cdn_node_params.clone() + } + ) + ); + + // Check the changed params. + let cdn_node_info = ctx.contract.cdn_node_get(ctx.cdn_node_key0)?; + assert_eq!(cdn_node_info.cdn_node.cdn_node_params, new_cdn_node_params); +} + +#[ink::test] +fn cdn_node_get_err_if_node_does_not_exist() { + let ctx = setup_cluster(); + + let bad_cdn_node_key = AccountId::from([ + 0xf6, 0x8f, 0x06, 0xa8, 0x26, 0xba, 0xaf, 0x7f, 0xbd, 0x9b, 0xff, 0x3d, 0x1e, 0xec, 0xae, + 0xef, 0xc7, 0x7a, 0x01, 0x6d, 0x0b, 0xaf, 0x4c, 0x90, 0x55, 0x6e, 0x7b, 0x15, 0x73, 0x46, + 0x9c, 0x76, + ]); + + assert_eq!( + ctx.contract.cdn_node_get(bad_cdn_node_key), + Err(CdnNodeDoesNotExist) + ); +} + +#[ink::test] +fn node_get_ok() { + let ctx = setup_cluster(); + + assert_eq!( + ctx.contract.cdn_node_get(ctx.cdn_node_key1), + Ok({ + CdnNodeInfo { + cdn_node_key: ctx.cdn_node_key1, + cdn_node: CdnNode { + provider_id: ctx.provider_id1, + undistributed_payment: 0, + cdn_node_params: ctx.cdn_node_params1, + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + }, + } + }) + ); +} + +#[ink::test] +fn node_list_ok() { + let ctx = setup_cluster(); + + let cdn_node_info = ctx.contract.cdn_node_get(ctx.cdn_node_key1)?; + assert_eq!(ctx.provider_id1, cdn_node_info.cdn_node.provider_id.clone()); + + let cdn_node1 = CdnNodeInfo { + cdn_node_key: ctx.cdn_node_key1, + cdn_node: CdnNode { + provider_id: ctx.provider_id1, + undistributed_payment: 0, + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + cdn_node_params: ctx.cdn_node_params1.clone(), + }, + }; + + let cdn_node2 = CdnNodeInfo { + cdn_node_key: ctx.cdn_node_key2, + cdn_node: CdnNode { + provider_id: ctx.provider_id2, + undistributed_payment: 0, + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + cdn_node_params: ctx.cdn_node_params2.clone(), + }, + }; + + let count = 3; + + assert_eq!( + ctx.contract.cdn_node_list(1, 100, None), + (vec![cdn_node1.clone(), cdn_node2.clone()], count) + ); + + assert_eq!( + ctx.contract.cdn_node_list(1, 2, None), + (vec![cdn_node1.clone(), cdn_node2.clone()], count) + ); + + assert_eq!( + ctx.contract.cdn_node_list(1, 1, None), + (vec![cdn_node1.clone()], count) + ); + + assert_eq!( + ctx.contract.cdn_node_list(2, 1, None), + (vec![cdn_node2.clone()], count) + ); + + assert_eq!(ctx.contract.cdn_node_list(21, 20, None), (vec![], count)); + + // Filter by owner. + assert_eq!( + ctx.contract.cdn_node_list(1, 100, Some(ctx.provider_id1)), + (vec![cdn_node1.clone()], count) + ); + + assert_eq!( + ctx.contract.cdn_node_list(1, 100, Some(ctx.provider_id2)), + (vec![cdn_node2.clone()], count) + ); + + let not_provider_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + + assert_eq!( + ctx.contract.cdn_node_list(1, 100, Some(not_provider_id)), + (vec![], count) + ); +} diff --git a/bucket/ddc_bucket/tests/test_cluster.rs b/bucket/ddc_bucket/tests/test_cluster.rs new file mode 100644 index 00000000..5d5ef9da --- /dev/null +++ b/bucket/ddc_bucket/tests/test_cluster.rs @@ -0,0 +1,1943 @@ +use ink_lang as ink; + +use super::env_utils::*; +use super::setup_utils::*; +use crate::ddc_bucket::Error::*; +use crate::ddc_bucket::*; +use cdn_node::entity::*; + +#[ink::test] +fn cluster_create_ok() { + let ctx = setup_cluster(); + let providers_ids = &[ctx.provider_id0, ctx.provider_id1, ctx.provider_id2]; + let node_keys = &[ctx.node_key0, ctx.node_key1, ctx.node_key2]; + let v_nodes = &[ + ctx.v_nodes0.clone(), + ctx.v_nodes1.clone(), + ctx.v_nodes2.clone(), + ]; + + let cdn_node_keys = &[ctx.cdn_node_key0, ctx.cdn_node_key1, ctx.cdn_node_key2]; + let node_params = &[ + ctx.node_params0.clone(), + ctx.node_params1.clone(), + ctx.node_params2.clone(), + ]; + let cdn_node_params = &[ + ctx.cdn_node_params0.clone(), + ctx.cdn_node_params1.clone(), + ctx.cdn_node_params2.clone(), + ]; + let rent_v_node_per_month = &[ + ctx.rent_v_node_per_month0.clone(), + ctx.rent_v_node_per_month1.clone(), + ctx.rent_v_node_per_month2.clone(), + ]; + + assert_eq!(ctx.cluster_id, 0, "cluster_id must start at 0"); + + // Check cluster Storage nodes + + let node0 = ctx.contract.node_get(ctx.node_key0)?; + let v_nodes0 = ctx.contract.get_v_nodes_by_node(ctx.node_key0); + let v_nodes0_len: u32 = v_nodes0.len().try_into().unwrap(); + + assert_eq!( + node0, + NodeInfo { + node_key: ctx.node_key0, + node: Node { + provider_id: ctx.provider_id0, + rent_v_node_per_month: ctx.rent_v_node_per_month0, + free_resource: ctx.node_capacity0 - ctx.resource_per_v_node * v_nodes0_len, + node_params: ctx.node_params0.clone(), + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + }, + v_nodes: v_nodes0 + } + ); + + let node1 = ctx.contract.node_get(ctx.node_key1)?; + let v_nodes1 = ctx.contract.get_v_nodes_by_node(ctx.node_key1); + let v_nodes1_len: u32 = v_nodes1.len().try_into().unwrap(); + + assert_eq!( + node1, + NodeInfo { + node_key: ctx.node_key1, + node: Node { + provider_id: ctx.provider_id1, + rent_v_node_per_month: ctx.rent_v_node_per_month1, + free_resource: ctx.node_capacity1 - ctx.resource_per_v_node * v_nodes1_len, + node_params: ctx.node_params1.clone(), + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + }, + v_nodes: v_nodes1 + } + ); + + let node2 = ctx.contract.node_get(ctx.node_key2)?; + let v_nodes2 = ctx.contract.get_v_nodes_by_node(ctx.node_key2); + let v_nodes2_len: u32 = v_nodes2.len().try_into().unwrap(); + + assert_eq!( + node2, + NodeInfo { + node_key: ctx.node_key2, + node: Node { + provider_id: ctx.provider_id2, + rent_v_node_per_month: ctx.rent_v_node_per_month2, + free_resource: ctx.node_capacity2 - ctx.resource_per_v_node * v_nodes2_len, + node_params: ctx.node_params2.clone(), + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + }, + v_nodes: v_nodes2 + } + ); + + // Check cluster CDN nodes + + let cdn_node0 = ctx.contract.cdn_node_get(ctx.cdn_node_key0)?; + + assert_eq!( + cdn_node0, + CdnNodeInfo { + cdn_node_key: ctx.cdn_node_key0, + cdn_node: CdnNode { + provider_id: ctx.provider_id0, + undistributed_payment: 0, + cdn_node_params: ctx.cdn_node_params0.clone(), + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + }, + } + ); + + let cdn_node1 = ctx.contract.cdn_node_get(ctx.cdn_node_key1)?; + + assert_eq!( + cdn_node1, + CdnNodeInfo { + cdn_node_key: ctx.cdn_node_key1, + cdn_node: CdnNode { + provider_id: ctx.provider_id1, + undistributed_payment: 0, + cdn_node_params: ctx.cdn_node_params1.clone(), + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + }, + } + ); + + let cdn_node2 = ctx.contract.cdn_node_get(ctx.cdn_node_key2)?; + + assert_eq!( + cdn_node2, + CdnNodeInfo { + cdn_node_key: ctx.cdn_node_key2, + cdn_node: CdnNode { + provider_id: ctx.provider_id2, + undistributed_payment: 0, + cdn_node_params: ctx.cdn_node_params2.clone(), + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + }, + } + ); + + // Check the cluster + + let cluster = ctx.contract.cluster_get(ctx.cluster_id)?; + let cluster_v_nodes = ctx.contract.get_v_nodes_by_cluster(ctx.cluster_id); + let total_rent = ctx.rent_v_node_per_month0 * v_nodes0_len as Balance + + ctx.rent_v_node_per_month1 * v_nodes1_len as Balance + + ctx.rent_v_node_per_month2 * v_nodes2_len as Balance; + + assert_eq!( + cluster, + ClusterInfo { + cluster_id: ctx.cluster_id, + cluster: Cluster { + manager_id: ctx.manager_id, + nodes_keys: ctx.nodes_keys, + resource_per_v_node: ctx.resource_per_v_node, + resource_used: 0, + cluster_params: ctx.cluster_params.clone(), + revenues: Cash(0), + total_rent, + cdn_nodes_keys: ctx.cdn_nodes_keys, + cdn_usd_per_gb: CDN_USD_PER_GB, + cdn_revenues: Cash(0), + }, + cluster_v_nodes + } + ); + + // Check emitted events + let mut events = get_events(); + events.reverse(); // Work with pop(). + + // Storage node created event + for i in 0..3 { + assert!( + matches!(events.pop().unwrap(), Event::NodeCreated(ev) if ev == + NodeCreated { + node_key: node_keys[i], + provider_id: providers_ids[i], + rent_v_node_per_month: rent_v_node_per_month[i], + node_params: node_params[i].clone() + }) + ); + } + + // CDN node created event + for i in 0..3 { + assert!( + matches!(events.pop().unwrap(), Event::CdnNodeCreated(ev) if ev == + CdnNodeCreated { + cdn_node_key: cdn_node_keys[i], + provider_id: providers_ids[i], + cdn_node_params: cdn_node_params[i].clone(), + undistributed_payment: 0 + }) + ); + } + + // Cluster created event + assert!( + matches!(events.pop().unwrap(), Event::ClusterCreated(ev) if ev == + ClusterCreated { + cluster_id: ctx.cluster_id, + manager_id: ctx.manager_id, + cluster_params: ctx.cluster_params.clone() + }) + ); + + // Permission granted event + for provider_id in providers_ids { + assert!( + matches!(events.pop().unwrap(), Event::PermissionGranted(ev) if ev == + PermissionGranted { + account_id: ctx.manager_id, + permission: Permission::ClusterManagerTrustedBy(*provider_id) + }) + ); + } + + // Cluster storage node added event + for i in 0..3 { + assert!( + matches!(events.pop().unwrap(), Event::ClusterNodeAdded(ev) if ev == + ClusterNodeAdded { + cluster_id: ctx.cluster_id, + node_key: node_keys[i], + v_nodes: v_nodes[i].clone() + }) + ); + } + + // Cluster cdn node added event + for i in 0..3 { + assert!( + matches!(events.pop().unwrap(), Event::ClusterCdnNodeAdded(ev) if ev == + ClusterCdnNodeAdded { + cluster_id: ctx.cluster_id, + cdn_node_key: cdn_node_keys[i] + }) + ); + } + + assert_eq!(events.len(), 0, "All events must be checked"); +} + +#[ink::test] +fn cluster_add_node_err_if_node_is_in_cluster() { + let mut ctx = setup_cluster(); + + let another_manager_id = AccountId::from([ + 0x54, 0x66, 0x76, 0x6c, 0xf6, 0x17, 0x70, 0xcf, 0x5d, 0x70, 0x6c, 0x55, 0x4d, 0xd4, 0xb7, + 0xf8, 0x83, 0xe6, 0x70, 0x06, 0xea, 0x4c, 0x05, 0x89, 0x16, 0x32, 0x79, 0x79, 0xbb, 0x85, + 0x58, 0x7a, + ]); + set_balance(another_manager_id, 1000 * TOKEN); + + set_caller_value(another_manager_id, CONTRACT_FEE_LIMIT); + let another_cluster_id = ctx.contract.cluster_create(ClusterParams::from("{}"), 10)?; + + assert_eq!( + ctx.contract + .cluster_add_node(another_cluster_id, ctx.node_key1, ctx.v_nodes1,), + Err(NodeIsAddedToCluster(ctx.cluster_id)) + ); +} + +#[ink::test] +fn cluster_add_node_err_if_not_trusted_manager() { + let mut ctx = setup_cluster(); + + let another_manager_id = AccountId::from([ + 0x54, 0x66, 0x76, 0x6c, 0xf6, 0x17, 0x70, 0xcf, 0x5d, 0x70, 0x6c, 0x55, 0x4d, 0xd4, 0xb7, + 0xf8, 0x83, 0xe6, 0x70, 0x06, 0xea, 0x4c, 0x05, 0x89, 0x16, 0x32, 0x79, 0x79, 0xbb, 0x85, + 0x58, 0x7a, + ]); + set_balance(another_manager_id, 1000 * TOKEN); + + set_caller_value(another_manager_id, CONTRACT_FEE_LIMIT); + let another_cluster_id = ctx.contract.cluster_create(ClusterParams::from("{}"), 10)?; + + let new_provider_id = AccountId::from([ + 0x3c, 0x08, 0xea, 0xa6, 0x89, 0xdf, 0x45, 0x2b, 0x77, 0xa1, 0xa5, 0x6b, 0x83, 0x10, 0x1e, + 0x31, 0x06, 0xc9, 0xc7, 0xaf, 0xb3, 0xe9, 0xfd, 0x6f, 0xa6, 0x2b, 0x50, 0x00, 0xf6, 0xeb, + 0xcb, 0x5a, + ]); + set_balance(new_provider_id, 1000 * TOKEN); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + let new_node_key = + ctx.contract + .node_create(new_node_key, NodeParams::from("new_node"), 1000, 100)?; + + let new_v_nodes: Vec = vec![10, 11, 12]; + set_caller_value(another_manager_id, CONTRACT_FEE_LIMIT); + assert_eq!( + ctx.contract + .cluster_add_node(another_cluster_id, new_node_key, new_v_nodes,), + Err(OnlyTrustedClusterManager) + ); +} + +#[ink::test] +fn cluster_add_node_err_if_not_cluster_manager() { + let mut ctx = setup_cluster(); + + let another_manager_id = AccountId::from([ + 0x54, 0x66, 0x76, 0x6c, 0xf6, 0x17, 0x70, 0xcf, 0x5d, 0x70, 0x6c, 0x55, 0x4d, 0xd4, 0xb7, + 0xf8, 0x83, 0xe6, 0x70, 0x06, 0xea, 0x4c, 0x05, 0x89, 0x16, 0x32, 0x79, 0x79, 0xbb, 0x85, + 0x58, 0x7a, + ]); + set_balance(another_manager_id, 1000 * TOKEN); + + set_caller_value(another_manager_id, CONTRACT_FEE_LIMIT); + let another_cluster_id = ctx.contract.cluster_create(ClusterParams::from("{}"), 10)?; + + let new_provider_id = AccountId::from([ + 0x3c, 0x08, 0xea, 0xa6, 0x89, 0xdf, 0x45, 0x2b, 0x77, 0xa1, 0xa5, 0x6b, 0x83, 0x10, 0x1e, + 0x31, 0x06, 0xc9, 0xc7, 0xaf, 0xb3, 0xe9, 0xfd, 0x6f, 0xa6, 0x2b, 0x50, 0x00, 0xf6, 0xeb, + 0xcb, 0x5a, + ]); + set_balance(new_provider_id, 1000 * TOKEN); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .node_create(new_node_key, NodeParams::from("new_node"), 1000, 100)?; + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_manager_id, 1000 * TOKEN); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .grant_trusted_manager_permission(not_manager_id)?; + + let new_v_nodes: Vec = vec![10, 11, 12]; + set_caller_value(not_manager_id, CONTRACT_FEE_LIMIT); + assert_eq!( + ctx.contract + .cluster_add_node(another_cluster_id, new_node_key, new_v_nodes,), + Err(OnlyClusterManager) + ); +} + +#[ink::test] +fn cluster_add_node_err_if_no_v_nodes() { + let mut ctx = setup_cluster(); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + set_caller_value(ctx.provider_id0, 10); + ctx.contract + .node_create(new_node_key, NodeParams::from("new_node"), 1000000000, 1)?; + + set_caller_value(ctx.manager_id, 10); + assert_eq!( + ctx.contract + .cluster_add_node(ctx.cluster_id, new_node_key, vec![],), + Err(AtLeastOneVNodeHasToBeAssigned(ctx.cluster_id, new_node_key)) + ); +} + +#[ink::test] +fn cluster_add_node_err_if_v_nodes_exceeds_limit() { + let mut ctx = setup_cluster(); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + set_caller_value(ctx.provider_id0, 10); + ctx.contract + .node_create(new_node_key, NodeParams::from("new_node"), 1000000000, 1)?; + + set_caller_value(ctx.manager_id, 10); + assert_eq!( + ctx.contract + .cluster_add_node(ctx.cluster_id, new_node_key, vec![100; 1801],), + Err(VNodesSizeExceedsLimit) + ); +} + +#[ink::test] +fn cluster_add_node_ok() { + let mut ctx = setup_cluster(); + + let new_provider_id = AccountId::from([ + 0x3c, 0x08, 0xea, 0xa6, 0x89, 0xdf, 0x45, 0x2b, 0x77, 0xa1, 0xa5, 0x6b, 0x83, 0x10, 0x1e, + 0x31, 0x06, 0xc9, 0xc7, 0xaf, 0xb3, 0xe9, 0xfd, 0x6f, 0xa6, 0x2b, 0x50, 0x00, 0xf6, 0xeb, + 0xcb, 0x5a, + ]); + set_balance(new_provider_id, 1000 * TOKEN); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + let new_node_rent_v_node_per_month = 100; + let new_node_params = NodeParams::from("new_node"); + let new_node_capacity = 1000; + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract.node_create( + new_node_key, + new_node_params.clone(), + new_node_capacity, + new_node_rent_v_node_per_month, + )?; + + assert!( + matches!(get_events().pop().unwrap(), Event::NodeCreated(ev) if ev == + NodeCreated { + node_key: new_node_key, + provider_id: new_provider_id, + rent_v_node_per_month: new_node_rent_v_node_per_month, + node_params: new_node_params.clone(), + }) + ); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .grant_trusted_manager_permission(ctx.manager_id)?; + + assert!(matches!( + get_events().pop().unwrap(), Event::PermissionGranted(ev) if ev == + PermissionGranted { + account_id: ctx.manager_id, + permission: Permission::ClusterManagerTrustedBy(new_provider_id) + } + )); + + let new_v_nodes: Vec = vec![10, 11, 12]; + set_caller_value(ctx.manager_id, CONTRACT_FEE_LIMIT); + ctx.contract + .cluster_add_node(ctx.cluster_id, new_node_key, new_v_nodes.clone())?; + + assert!( + matches!(get_events().pop().unwrap(), Event::ClusterNodeAdded(ev) if ev == + ClusterNodeAdded { + cluster_id: ctx.cluster_id, + node_key: new_node_key, + v_nodes: new_v_nodes.clone() + }) + ); + + let _nodes_keys = vec![ctx.node_key0, ctx.node_key1, ctx.node_key2, new_node_key]; + + let _cluster_v_nodes = vec![ + ctx.v_nodes0, + ctx.v_nodes1, + ctx.v_nodes2, + new_v_nodes.clone(), + ]; + + let mut cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; + cluster_info.cluster_v_nodes.sort(); + assert!(matches!(cluster_info.cluster.nodes_keys, _nodes_keys)); + assert!(matches!(cluster_info.cluster_v_nodes, _cluster_v_nodes)); + + let node_info = ctx.contract.node_get(new_node_key)?; + let _expected_node_info = NodeInfo { + node_key: new_node_key, + node: Node { + provider_id: new_provider_id, + rent_v_node_per_month: new_node_rent_v_node_per_month, + free_resource: new_node_capacity, + node_params: new_node_params, + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + }, + v_nodes: new_v_nodes, + }; + assert!(matches!(node_info, _expected_node_info)); +} + +#[ink::test] +fn cluster_remove_node_err_if_node_is_not_in_cluster() { + let mut ctx = setup_cluster(); + + let new_provider_id = AccountId::from([ + 0x3c, 0x08, 0xea, 0xa6, 0x89, 0xdf, 0x45, 0x2b, 0x77, 0xa1, 0xa5, 0x6b, 0x83, 0x10, 0x1e, + 0x31, 0x06, 0xc9, 0xc7, 0xaf, 0xb3, 0xe9, 0xfd, 0x6f, 0xa6, 0x2b, 0x50, 0x00, 0xf6, 0xeb, + 0xcb, 0x5a, + ]); + set_balance(new_provider_id, 1000 * TOKEN); + + let another_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .node_create(another_node_key, NodeParams::from("new_node"), 1000, 100)?; + + set_caller(ctx.manager_id); + assert_eq!( + ctx.contract + .cluster_remove_node(ctx.cluster_id, another_node_key,), + Err(NodeIsNotAddedToCluster(ctx.cluster_id)) + ); +} + +#[ink::test] +fn cluster_remove_node_err_if_not_manager_and_not_provider() { + let mut ctx = setup_cluster(); + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_manager_id, 1000 * TOKEN); + + set_caller(not_manager_id); + assert_eq!( + ctx.contract + .cluster_remove_node(ctx.cluster_id, ctx.node_key1,), + Err(OnlyClusterManagerOrNodeProvider) + ); +} + +#[ink::test] +fn cluster_remove_node_ok_if_node_provider() { + let mut ctx = setup_cluster(); + + set_caller(ctx.manager_id); + ctx.contract + .cluster_remove_node(ctx.cluster_id, ctx.node_key1)?; + + assert!( + matches!(get_events().pop().unwrap(), Event::ClusterNodeRemoved(ev) if ev == + ClusterNodeRemoved { + cluster_id: ctx.cluster_id, + node_key: ctx.node_key1 + }) + ); + + let _nodes_keys = vec![ctx.node_key0, ctx.node_key2]; + + let _cluster_v_nodes = vec![ctx.v_nodes0.clone(), ctx.v_nodes2]; + + let mut cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; + cluster_info.cluster_v_nodes.sort(); + assert!(matches!(cluster_info.cluster.nodes_keys, _nodes_keys)); + assert!(matches!(cluster_info.cluster_v_nodes, _cluster_v_nodes)); + + let node_info = ctx.contract.node_get(ctx.node_key1)?; + let v_nodes0_len: u32 = ctx.v_nodes0.len().try_into().unwrap(); + let _expected_node_info = NodeInfo { + node_key: ctx.node_key1, + node: Node { + provider_id: ctx.provider_id1, + rent_v_node_per_month: ctx.rent_v_node_per_month1, + free_resource: ctx.node_capacity1 - ctx.resource_per_v_node * v_nodes0_len, + node_params: ctx.node_params1, + cluster_id: None, + status_in_cluster: None, + }, + v_nodes: Vec::new(), + }; + assert!(matches!(node_info, _expected_node_info)); +} + +#[ink::test] +fn cluster_remove_node_ok_if_cluster_manager() { + let mut ctx = setup_cluster(); + + set_caller(ctx.provider_id2); + ctx.contract + .cluster_remove_node(ctx.cluster_id, ctx.node_key2)?; + + assert!( + matches!(get_events().pop().unwrap(), Event::ClusterNodeRemoved(ev) if ev == + ClusterNodeRemoved { + cluster_id: ctx.cluster_id, + node_key: ctx.node_key2 + }) + ); + + let _nodes_keys = vec![ctx.node_key0, ctx.node_key1]; + + let _cluster_v_nodes = vec![ctx.v_nodes0, ctx.v_nodes1]; + + let mut cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; + cluster_info.cluster_v_nodes.sort(); + assert!(matches!(cluster_info.cluster.nodes_keys, _nodes_keys)); + assert!(matches!(cluster_info.cluster_v_nodes, _cluster_v_nodes)); + + let node_info = ctx.contract.node_get(ctx.node_key2)?; + let v_nodes2_len: u32 = ctx.v_nodes2.len().try_into().unwrap(); + + let _expected_node_info = NodeInfo { + node_key: ctx.node_key2, + node: Node { + provider_id: ctx.provider_id2, + rent_v_node_per_month: ctx.rent_v_node_per_month2, + free_resource: ctx.node_capacity2 - ctx.resource_per_v_node * v_nodes2_len, + node_params: ctx.node_params2, + cluster_id: None, + status_in_cluster: None, + }, + v_nodes: Vec::new(), + }; + assert!(matches!(node_info, _expected_node_info)); +} + +#[ink::test] +fn cluster_add_cdn_node_err_if_cdn_node_is_in_cluster() { + let mut ctx = setup_cluster(); + + let another_manager_id = AccountId::from([ + 0x54, 0x66, 0x76, 0x6c, 0xf6, 0x17, 0x70, 0xcf, 0x5d, 0x70, 0x6c, 0x55, 0x4d, 0xd4, 0xb7, + 0xf8, 0x83, 0xe6, 0x70, 0x06, 0xea, 0x4c, 0x05, 0x89, 0x16, 0x32, 0x79, 0x79, 0xbb, 0x85, + 0x58, 0x7a, + ]); + set_balance(another_manager_id, 1000 * TOKEN); + + set_caller_value(another_manager_id, CONTRACT_FEE_LIMIT); + let another_cluster_id = ctx.contract.cluster_create(ClusterParams::from("{}"), 10)?; + + assert_eq!( + ctx.contract + .cluster_add_cdn_node(another_cluster_id, ctx.cdn_node_key1,), + Err(CdnNodeIsAddedToCluster(ctx.cluster_id)) + ); +} + +#[ink::test] +fn cluster_add_cdn_node_err_if_not_trusted_manager() { + let mut ctx = setup_cluster(); + + let another_manager_id = AccountId::from([ + 0x54, 0x66, 0x76, 0x6c, 0xf6, 0x17, 0x70, 0xcf, 0x5d, 0x70, 0x6c, 0x55, 0x4d, 0xd4, 0xb7, + 0xf8, 0x83, 0xe6, 0x70, 0x06, 0xea, 0x4c, 0x05, 0x89, 0x16, 0x32, 0x79, 0x79, 0xbb, 0x85, + 0x58, 0x7a, + ]); + set_balance(another_manager_id, 1000 * TOKEN); + + set_caller_value(another_manager_id, CONTRACT_FEE_LIMIT); + let another_cluster_id = ctx.contract.cluster_create(ClusterParams::from("{}"), 10)?; + + let new_provider_id = AccountId::from([ + 0x3c, 0x08, 0xea, 0xa6, 0x89, 0xdf, 0x45, 0x2b, 0x77, 0xa1, 0xa5, 0x6b, 0x83, 0x10, 0x1e, + 0x31, 0x06, 0xc9, 0xc7, 0xaf, 0xb3, 0xe9, 0xfd, 0x6f, 0xa6, 0x2b, 0x50, 0x00, 0xf6, 0xeb, + 0xcb, 0x5a, + ]); + set_balance(new_provider_id, 1000 * TOKEN); + + let new_cdn_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .cdn_node_create(new_cdn_node_key, CdnNodeParams::from("new_cdn_node"))?; + + set_caller_value(another_manager_id, CONTRACT_FEE_LIMIT); + assert_eq!( + ctx.contract + .cluster_add_cdn_node(another_cluster_id, new_cdn_node_key,), + Err(OnlyTrustedClusterManager) + ); +} + +#[ink::test] +fn cluster_add_cdn_node_err_if_not_cluster_manager() { + let mut ctx = setup_cluster(); + + let another_manager_id = AccountId::from([ + 0x54, 0x66, 0x76, 0x6c, 0xf6, 0x17, 0x70, 0xcf, 0x5d, 0x70, 0x6c, 0x55, 0x4d, 0xd4, 0xb7, + 0xf8, 0x83, 0xe6, 0x70, 0x06, 0xea, 0x4c, 0x05, 0x89, 0x16, 0x32, 0x79, 0x79, 0xbb, 0x85, + 0x58, 0x7a, + ]); + set_balance(another_manager_id, 1000 * TOKEN); + + set_caller_value(another_manager_id, CONTRACT_FEE_LIMIT); + let another_cluster_id = ctx.contract.cluster_create(ClusterParams::from("{}"), 10)?; + + let new_provider_id = AccountId::from([ + 0x3c, 0x08, 0xea, 0xa6, 0x89, 0xdf, 0x45, 0x2b, 0x77, 0xa1, 0xa5, 0x6b, 0x83, 0x10, 0x1e, + 0x31, 0x06, 0xc9, 0xc7, 0xaf, 0xb3, 0xe9, 0xfd, 0x6f, 0xa6, 0x2b, 0x50, 0x00, 0xf6, 0xeb, + 0xcb, 0x5a, + ]); + set_balance(new_provider_id, 1000 * TOKEN); + + let new_cdn_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .cdn_node_create(new_cdn_node_key, CdnNodeParams::from("new_cdn_node"))?; + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_manager_id, 1000 * TOKEN); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .grant_trusted_manager_permission(not_manager_id)?; + + set_caller_value(not_manager_id, CONTRACT_FEE_LIMIT); + assert_eq!( + ctx.contract + .cluster_add_cdn_node(another_cluster_id, new_cdn_node_key,), + Err(OnlyClusterManager) + ); +} + +#[ink::test] +fn cluster_add_cdn_node_ok() { + let mut ctx = setup_cluster(); + + let new_provider_id = AccountId::from([ + 0x3c, 0x08, 0xea, 0xa6, 0x89, 0xdf, 0x45, 0x2b, 0x77, 0xa1, 0xa5, 0x6b, 0x83, 0x10, 0x1e, + 0x31, 0x06, 0xc9, 0xc7, 0xaf, 0xb3, 0xe9, 0xfd, 0x6f, 0xa6, 0x2b, 0x50, 0x00, 0xf6, 0xeb, + 0xcb, 0x5a, + ]); + set_balance(new_provider_id, 1000 * TOKEN); + + let new_cdn_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + let new_cdn_node_params = CdnNodeParams::from("new_cdn_node"); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .cdn_node_create(new_cdn_node_key, new_cdn_node_params.clone())?; + + assert!( + matches!(get_events().pop().unwrap(), Event::CdnNodeCreated(ev) if ev == + CdnNodeCreated { + cdn_node_key: new_cdn_node_key, + provider_id: new_provider_id, + cdn_node_params: new_cdn_node_params.clone(), + undistributed_payment: 0 + }) + ); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .grant_trusted_manager_permission(ctx.manager_id)?; + + assert!(matches!( + get_events().pop().unwrap(), Event::PermissionGranted(ev) if ev == + PermissionGranted { + account_id: ctx.manager_id, + permission: Permission::ClusterManagerTrustedBy(new_provider_id) + } + )); + + set_caller_value(ctx.manager_id, CONTRACT_FEE_LIMIT); + ctx.contract + .cluster_add_cdn_node(ctx.cluster_id, new_cdn_node_key)?; + + assert!( + matches!(get_events().pop().unwrap(), Event::ClusterCdnNodeAdded(ev) if ev == + ClusterCdnNodeAdded { + cluster_id: ctx.cluster_id, + cdn_node_key: new_cdn_node_key + }) + ); + + let _cdn_nodes_keys = vec![ + ctx.cdn_node_key0, + ctx.cdn_node_key1, + ctx.cdn_node_key2, + new_cdn_node_key, + ]; + + let cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; + assert!(matches!( + cluster_info.cluster.cdn_nodes_keys, + _cdn_nodes_keys + )); + + let cdn_node_info = ctx.contract.cdn_node_get(new_cdn_node_key)?; + let _expected_cdn_node_info = CdnNodeInfo { + cdn_node_key: new_cdn_node_key, + cdn_node: CdnNode { + provider_id: new_provider_id, + undistributed_payment: 0, + cdn_node_params: new_cdn_node_params, + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + }, + }; + assert!(matches!(cdn_node_info, _expected_cdn_node_info)); +} + +#[ink::test] +fn cluster_remove_cdn_node_err_if_cdn_node_is_not_in_cluster() { + let mut ctx = setup_cluster(); + + let new_provider_id = AccountId::from([ + 0x3c, 0x08, 0xea, 0xa6, 0x89, 0xdf, 0x45, 0x2b, 0x77, 0xa1, 0xa5, 0x6b, 0x83, 0x10, 0x1e, + 0x31, 0x06, 0xc9, 0xc7, 0xaf, 0xb3, 0xe9, 0xfd, 0x6f, 0xa6, 0x2b, 0x50, 0x00, 0xf6, 0xeb, + 0xcb, 0x5a, + ]); + set_balance(new_provider_id, 1000 * TOKEN); + + let another_cdn_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract + .cdn_node_create(another_cdn_node_key, CdnNodeParams::from("new_cdn_node"))?; + + set_caller(ctx.manager_id); + assert_eq!( + ctx.contract + .cluster_remove_cdn_node(ctx.cluster_id, another_cdn_node_key,), + Err(CdnNodeIsNotAddedToCluster(ctx.cluster_id)) + ); +} + +#[ink::test] +fn cluster_remove_cdn_node_err_if_not_manager_and_not_provider() { + let mut ctx = setup_cluster(); + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_manager_id, 1000 * TOKEN); + + set_caller(not_manager_id); + assert_eq!( + ctx.contract + .cluster_remove_cdn_node(ctx.cluster_id, ctx.cdn_node_key1,), + Err(OnlyClusterManagerOrCdnNodeProvider) + ); +} + +#[ink::test] +fn cluster_remove_cdn_node_ok_if_cdn_node_provider() { + let mut ctx = setup_cluster(); + + set_caller(ctx.manager_id); + ctx.contract + .cluster_remove_cdn_node(ctx.cluster_id, ctx.cdn_node_key1)?; + + assert!( + matches!(get_events().pop().unwrap(), Event::ClusterCdnNodeRemoved(ev) if ev == + ClusterCdnNodeRemoved { + cluster_id: ctx.cluster_id, + cdn_node_key: ctx.cdn_node_key1 + }) + ); + + let _cdn_nodes_keys = vec![ctx.cdn_node_key0, ctx.cdn_node_key2]; + + let cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; + assert!(matches!( + cluster_info.cluster.cdn_nodes_keys, + _cdn_nodes_keys + )); + + let cdn_node_info = ctx.contract.cdn_node_get(ctx.cdn_node_key1)?; + let _expected_cdn_node_info = CdnNodeInfo { + cdn_node_key: ctx.cdn_node_key1, + cdn_node: CdnNode { + provider_id: ctx.provider_id1, + undistributed_payment: 0, + cdn_node_params: ctx.cdn_node_params1, + cluster_id: None, + status_in_cluster: None, + }, + }; + assert!(matches!(cdn_node_info, _expected_cdn_node_info)); +} + +#[ink::test] +fn cluster_remove_cdn_node_ok_if_cluster_manager() { + let mut ctx = setup_cluster(); + + set_caller(ctx.provider_id2); + ctx.contract + .cluster_remove_cdn_node(ctx.cluster_id, ctx.cdn_node_key2)?; + + assert!( + matches!(get_events().pop().unwrap(), Event::ClusterCdnNodeRemoved(ev) if ev == + ClusterCdnNodeRemoved { + cluster_id: ctx.cluster_id, + cdn_node_key: ctx.cdn_node_key2 + }) + ); + + let _cdn_nodes_keys = vec![ctx.cdn_node_key0, ctx.cdn_node_key1]; + + let cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; + assert!(matches!( + cluster_info.cluster.cdn_nodes_keys, + _cdn_nodes_keys + )); + + let cdn_node_info = ctx.contract.cdn_node_get(ctx.cdn_node_key2)?; + let _expected_cdn_node_info = CdnNodeInfo { + cdn_node_key: ctx.cdn_node_key2, + cdn_node: CdnNode { + provider_id: ctx.provider_id2, + undistributed_payment: 0, + cdn_node_params: ctx.cdn_node_params2, + cluster_id: None, + status_in_cluster: None, + }, + }; + assert!(matches!(cdn_node_info, _expected_cdn_node_info)); +} + +#[ink::test] +fn cluster_set_params_err_if_not_cluster_manager() { + let ctx = &mut setup_cluster(); + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_manager_id, 1000 * TOKEN); + // Change params. + let new_cluster_params = NodeParams::from("new cluster params"); + set_caller_value(not_manager_id, CONTRACT_FEE_LIMIT); + + assert_eq!( + ctx.contract + .cluster_set_params(ctx.cluster_id, new_cluster_params), + Err(OnlyClusterManager) + ); +} + +#[ink::test] +fn cluster_set_params_ok() { + let mut ctx = setup_cluster(); + + // Change params. + let new_cluster_params = NodeParams::from("new cluster params"); + set_caller_value(ctx.manager_id, CONTRACT_FEE_LIMIT); + ctx.contract + .cluster_set_params(ctx.cluster_id, new_cluster_params.clone())?; + + // Check the changed params. + let cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; + assert_eq!(cluster_info.cluster.cluster_params, new_cluster_params); +} + +#[ink::test] +fn cluster_replace_node_err_if_not_cluster_manager() { + let mut ctx = setup_cluster(); + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_caller_value(not_manager_id, 0); + + // Reassign a vnode from node1 to node2. + assert_eq!( + ctx.contract + .cluster_replace_node(ctx.cluster_id, vec![1, 2, 3], ctx.node_key2), + Err(OnlyClusterManager) + ); +} + +#[ink::test] +fn cluster_replace_node_err_if_node_does_not_exist() { + let mut ctx = setup_cluster(); + + let bad_node_key = AccountId::from([ + 0xf6, 0x8f, 0x06, 0xa8, 0x26, 0xba, 0xaf, 0x7f, 0xbd, 0x9b, 0xff, 0x3d, 0x1e, 0xec, 0xae, + 0xef, 0xc7, 0x7a, 0x01, 0x6d, 0x0b, 0xaf, 0x4c, 0x90, 0x55, 0x6e, 0x7b, 0x15, 0x73, 0x46, + 0x9c, 0x76, + ]); + set_caller(ctx.manager_id); + assert_eq!( + ctx.contract + .cluster_replace_node(ctx.cluster_id, vec![1, 2, 3], bad_node_key), + Err(NodeDoesNotExist) + ); +} + +#[ink::test] +fn cluster_replace_node_err_if_no_v_nodes() { + let mut ctx = setup_cluster(); + + assert_eq!( + ctx.contract + .cluster_replace_node(ctx.cluster_id, vec![], ctx.node_key2), + Err(AtLeastOneVNodeHasToBeAssigned( + ctx.cluster_id, + ctx.node_key2 + )) + ); +} + +#[ink::test] +fn cluster_replace_node_err_if_node_is_not_in_cluster() { + let mut ctx = setup_cluster(); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + set_caller_value(ctx.provider_id0, 10); + ctx.contract + .node_create(new_node_key, NodeParams::from("new_node"), 1000000000, 1)?; + + set_caller_value(ctx.manager_id, 10); + assert_eq!( + ctx.contract + .cluster_replace_node(ctx.cluster_id, vec![1, 3], new_node_key), + Err(NodeIsNotAddedToCluster(ctx.cluster_id)) + ); +} + +#[ink::test] +fn cluster_replace_node_err_if_v_nodes_exceeds_limit() { + let mut ctx = setup_cluster(); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + set_caller_value(ctx.provider_id0, 10); + ctx.contract + .node_create(new_node_key, NodeParams::from("new_node"), 1000000000, 1)?; + + set_caller_value(ctx.manager_id, 10); + ctx.contract + .cluster_add_node(ctx.cluster_id, new_node_key, vec![100])?; + + let v_nodes: Vec = vec![100; 1801]; + assert_eq!( + ctx.contract + .cluster_replace_node(ctx.cluster_id, v_nodes, new_node_key), + Err(VNodesSizeExceedsLimit) + ); +} + +#[ink::test] +fn cluster_replace_node_err_if_old_node_stays_without_v_nodes() { + let mut ctx = setup_cluster(); + + assert_eq!( + ctx.contract + .cluster_replace_node(ctx.cluster_id, vec![1, 2, 3], ctx.node_key2), + Err(AtLeastOneVNodeHasToBeAssigned( + ctx.cluster_id, + ctx.node_key0 + )) + ); +} + +#[ink::test] +fn cluster_replace_node_ok() { + let mut ctx = setup_cluster(); + + set_caller(ctx.manager_id); + // Reassign a vnode from node0 to node2 + let v_nodes_to_reasign: Vec = vec![1, 3]; + ctx.contract + .cluster_replace_node(ctx.cluster_id, v_nodes_to_reasign.clone(), ctx.node_key2)?; + + // Check the last event + let ev = get_events().pop().unwrap(); + assert!(matches!(ev, Event::ClusterNodeReplaced(ev) if ev == + ClusterNodeReplaced { + cluster_id: ctx.cluster_id, + node_key: ctx.node_key2, + v_nodes: v_nodes_to_reasign.clone(), + } + )); + + let mut cluster_v_nodes = Vec::::new(); + cluster_v_nodes.extend(vec![2]); + cluster_v_nodes.extend(ctx.v_nodes1.clone()); + cluster_v_nodes.extend(ctx.v_nodes2.clone()); + cluster_v_nodes.extend(v_nodes_to_reasign.clone()); + cluster_v_nodes.sort(); + + let mut cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; + cluster_info.cluster_v_nodes.sort(); + assert_eq!( + &cluster_info.cluster_v_nodes, &cluster_v_nodes, + "a v_node must be replaced" + ); + + let mut v_nodes0 = ctx.contract.get_v_nodes_by_node(ctx.node_key0.clone()); + v_nodes0.sort(); + let mut v_nodes1 = ctx.contract.get_v_nodes_by_node(ctx.node_key1.clone()); + v_nodes1.sort(); + let mut v_nodes2 = ctx.contract.get_v_nodes_by_node(ctx.node_key2.clone()); + v_nodes2.sort(); + + assert_eq!( + &v_nodes0, + &vec![2], + "v_nodes must be replaced for the 1st node" + ); + assert_eq!( + &v_nodes1, + &vec![4, 5, 6], + "v_nodes must not be replaced for the 2nd node" + ); + assert_eq!( + &v_nodes2, + &vec![1, 3, 7, 8, 9], + "v_nodes must be assigned to the 3rd node" + ); + + let v_nodes0_len: u32 = v_nodes0.len().try_into().unwrap(); + let v_nodes1_len: u32 = v_nodes1.len().try_into().unwrap(); + let v_nodes2_len: u32 = v_nodes2.len().try_into().unwrap(); + + // Check the changed state of the nodes. + let expected_resources = [ + ( + ctx.node_key0, + ctx.node_capacity0 - ctx.resource_per_v_node * v_nodes0_len, + ), + ( + ctx.node_key1, + ctx.node_capacity1 - ctx.resource_per_v_node * v_nodes1_len, + ), + ( + ctx.node_key2, + ctx.node_capacity2 - ctx.resource_per_v_node * v_nodes2_len, + ), + ]; + + for (node_key, available) in expected_resources { + let node_info = ctx.contract.node_get(node_key).unwrap(); + assert_eq!( + node_info.node.free_resource, available, + "resources must have shifted between nodes" + ); + } +} + +#[ink::test] +fn cluster_reset_node_err_if_not_cluster_manager() { + let mut ctx = setup_cluster(); + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_caller_value(not_manager_id, 0); + + // Reassign a vnode from node1 to node2. + assert_eq!( + ctx.contract + .cluster_reset_node(ctx.cluster_id, ctx.node_key2, vec![10, 11, 12],), + Err(OnlyClusterManager) + ); +} + +#[ink::test] +fn cluster_reset_node_err_if_node_does_not_exist() { + let mut ctx = setup_cluster(); + + let bad_node_key = AccountId::from([ + 0xf6, 0x8f, 0x06, 0xa8, 0x26, 0xba, 0xaf, 0x7f, 0xbd, 0x9b, 0xff, 0x3d, 0x1e, 0xec, 0xae, + 0xef, 0xc7, 0x7a, 0x01, 0x6d, 0x0b, 0xaf, 0x4c, 0x90, 0x55, 0x6e, 0x7b, 0x15, 0x73, 0x46, + 0x9c, 0x76, + ]); + set_caller(ctx.manager_id); + assert_eq!( + ctx.contract + .cluster_reset_node(ctx.cluster_id, bad_node_key, vec![10, 11, 12],), + Err(NodeDoesNotExist) + ); +} + +#[ink::test] +fn cluster_reset_node_err_if_no_v_nodes() { + let mut ctx = setup_cluster(); + + assert_eq!( + ctx.contract + .cluster_reset_node(ctx.cluster_id, ctx.node_key2, vec![],), + Err(AtLeastOneVNodeHasToBeAssigned( + ctx.cluster_id, + ctx.node_key2 + )) + ); +} + +#[ink::test] +fn cluster_reset_node_err_if_v_nodes_exceeds_limit() { + let mut ctx = setup_cluster(); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + set_caller_value(ctx.provider_id0, 10); + ctx.contract + .node_create(new_node_key, NodeParams::from("new_node"), 1000000000, 1)?; + + set_caller_value(ctx.manager_id, 10); + ctx.contract + .cluster_add_node(ctx.cluster_id, new_node_key, vec![100])?; + + let v_nodes: Vec = vec![100; 1801]; + assert_eq!( + ctx.contract + .cluster_reset_node(ctx.cluster_id, new_node_key, v_nodes,), + Err(VNodesSizeExceedsLimit) + ); +} + +#[ink::test] +fn cluster_reset_node_err_if_node_is_not_in_cluster() { + let mut ctx = setup_cluster(); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + set_caller_value(ctx.provider_id0, 10); + ctx.contract + .node_create(new_node_key, NodeParams::from("new_node"), 1000000000, 1)?; + + set_caller_value(ctx.manager_id, 10); + assert_eq!( + ctx.contract + .cluster_reset_node(ctx.cluster_id, new_node_key, vec![10, 11, 12],), + Err(NodeIsNotAddedToCluster(ctx.cluster_id)) + ); +} + +#[ink::test] +fn cluster_reset_node_ok() { + let mut ctx = setup_cluster(); + + set_caller(ctx.manager_id); + // Reset vnode for node1 + let new_v_nodes: Vec = vec![10, 11, 12]; + ctx.contract + .cluster_reset_node(ctx.cluster_id, ctx.node_key1, new_v_nodes.clone())?; + + // Check the last event + let ev = get_events().pop().unwrap(); + assert!(matches!(ev, Event::ClusterNodeReset(ev) if ev == + ClusterNodeReset { + cluster_id: ctx.cluster_id, + node_key: ctx.node_key1, + v_nodes: new_v_nodes + } + )); + + let mut cluster_v_nodes = Vec::::new(); + cluster_v_nodes.extend(ctx.v_nodes0.clone()); + cluster_v_nodes.extend(vec![10, 11, 12]); + cluster_v_nodes.extend(ctx.v_nodes2.clone()); + cluster_v_nodes.sort(); + + let mut cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; + cluster_info.cluster_v_nodes.sort(); + assert_eq!( + &cluster_info.cluster_v_nodes, &cluster_v_nodes, + "a v_node must be replaced" + ); + + let mut v_nodes0 = ctx.contract.get_v_nodes_by_node(ctx.node_key0.clone()); + v_nodes0.sort(); + let mut v_nodes1 = ctx.contract.get_v_nodes_by_node(ctx.node_key1.clone()); + v_nodes1.sort(); + let mut v_nodes2 = ctx.contract.get_v_nodes_by_node(ctx.node_key2.clone()); + v_nodes2.sort(); + + assert_eq!( + &v_nodes0, + &vec![1, 2, 3], + "v_nodes must not be reset for the 1st node" + ); + assert_eq!( + &v_nodes1, + &vec![10, 11, 12], + "v_nodes must not be reset for the 2nd node" + ); + assert_eq!( + &v_nodes2, + &vec![7, 8, 9], + "v_nodes must not be reset to the 3rd node" + ); +} + +#[ink::test] +fn cluster_reserve_resource_ok() { + let mut ctx = setup_cluster(); + set_caller(ctx.manager_id); + + let new_resource_per_v_node = 15; + + // Reserve more resources. + ctx.contract + .cluster_set_resource_per_v_node(ctx.cluster_id, new_resource_per_v_node)?; + + // Check the last event. + let ev = get_events().pop().unwrap(); + assert!(matches!(ev, Event::ClusterReserveResource(ev) if ev == + ClusterReserveResource { + cluster_id: ctx.cluster_id, + resource: new_resource_per_v_node + } + )); + + // Check the changed state of the cluster. + let cluster = ctx.contract.cluster_get(ctx.cluster_id)?.cluster; + assert_eq!(cluster.resource_per_v_node, new_resource_per_v_node); + + let v_nodes0_len: u32 = ctx.v_nodes0.len().try_into().unwrap(); + let v_nodes1_len: u32 = ctx.v_nodes1.len().try_into().unwrap(); + let v_nodes2_len: u32 = ctx.v_nodes2.len().try_into().unwrap(); + + // Check the changed state of the nodes. + let expected_resources = [ + ( + ctx.node_key0, + ctx.node_capacity0 - new_resource_per_v_node * v_nodes0_len, + ), + ( + ctx.node_key1, + ctx.node_capacity1 - new_resource_per_v_node * v_nodes1_len, + ), + ( + ctx.node_key2, + ctx.node_capacity2 - new_resource_per_v_node * v_nodes2_len, + ), + ]; + for (node_id, available) in expected_resources { + assert_eq!( + ctx.contract.node_get(node_id)?.node.free_resource, + available, + "more resources must be reserved from the nodes" + ); + } +} + +#[ink::test] +fn cluster_distribute_revenue_ok() { + let ctx = &mut setup_cluster(); + let test_bucket = &setup_bucket(ctx); + // Go to the future when some revenues are due. + advance_block::(); + // Pay the due thus far. + set_caller_value(ctx.manager_id, CONTRACT_FEE_LIMIT); + ctx.contract.bucket_settle_payment(test_bucket.bucket_id)?; + + // Get state before the distribution. + let to_distribute = ctx + .contract + .cluster_get(ctx.cluster_id)? + .cluster + .revenues + .peek(); + + let before0 = balance_of(ctx.provider_id0); + let before1 = balance_of(ctx.provider_id1); + let before2 = balance_of(ctx.provider_id2); + let before_mgmt = balance_of(ctx.manager_id); + + let skip_events = get_events::().len(); + + // Set a network fee. + let network_fee_bp = 100; // 1% + let cluster_management_fee_bp = 200; // 2% + set_caller_value(admin_id(), CONTRACT_FEE_LIMIT); + ctx.contract + .admin_set_network_fee_config(NetworkFeeConfig { + network_fee_bp, + network_fee_destination: AccountId::default(), + cluster_management_fee_bp, + })?; + + let burned_fee = to_distribute * network_fee_bp / BASIS_POINTS; + let manager_fee = (to_distribute - burned_fee) * cluster_management_fee_bp / BASIS_POINTS; + let provider_fee: u128 = (to_distribute - burned_fee - manager_fee) / 3; + + // Distribute the revenues of the cluster to providers. + ctx.contract.cluster_distribute_revenues(ctx.cluster_id)?; + + // Check the last events. + let mut events = get_events(); + events.reverse(); // Work with pop(). + events.truncate(events.len() - skip_events); + let expected_recipients = vec![ctx.provider_id0, ctx.provider_id1, ctx.provider_id2]; + + for provider_id in expected_recipients { + assert!( + matches!(events.pop().unwrap(), Event::ClusterDistributeRevenues(ev) if ev == + ClusterDistributeRevenues { + cluster_id: ctx.cluster_id, + provider_id + }) + ); + } + + assert_eq!(events.len(), 0, "all events must be checked"); + + // Get state after the distribution. + let rounding_error = ctx + .contract + .cluster_get(ctx.cluster_id)? + .cluster + .revenues + .peek(); + + let earned0 = balance_of(ctx.provider_id0) - before0; + let earned1 = balance_of(ctx.provider_id1) - before1; + let earned2 = balance_of(ctx.provider_id2) - before2; + let earned_mgmt = balance_of(ctx.manager_id) - before_mgmt; + + assert!(provider_fee > 0, "provider must earn something"); + assert_eq!( + earned0, provider_fee, + "providers must earn the correct amount" + ); + assert_eq!( + earned1, provider_fee, + "providers must earn the correct amount" + ); + assert_eq!( + earned2, provider_fee, + "providers must earn the correct amount" + ); + + assert!(burned_fee > 0, "the network must earn something"); + assert!(manager_fee > 0, "the manager_id must earn something"); + assert_eq!( + earned_mgmt, manager_fee, + "the manager_id must earn the correct amount" + ); + + assert!(to_distribute > 0); + assert!( + rounding_error < 10, + "revenues must go out of the cluster (besides rounding)" + ); + assert_eq!( + earned0 + earned1 + earned2 + burned_fee + manager_fee + rounding_error, + to_distribute, + "all revenues must go to providers" + ); +} + +#[ink::test] +fn cluster_remove_err_if_not_cluster_manager() { + let mut ctx = setup_cluster(); + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_manager_id, 1000 * TOKEN); + + set_caller(not_manager_id); + assert_eq!( + ctx.contract.cluster_remove(ctx.cluster_id), + Err(OnlyClusterManager) + ); +} + +#[ink::test] +fn cluster_remove_err_if_cluster_is_not_empty() { + let mut ctx = setup_cluster(); + + set_caller(ctx.manager_id); + assert_eq!( + ctx.contract.cluster_remove(ctx.cluster_id), + Err(ClusterIsNotEmpty) + ); +} + +#[ink::test] +fn cluster_remove_ok() { + let mut ctx = setup_cluster(); + + set_caller(ctx.manager_id); + + ctx.contract + .cluster_remove_node(ctx.cluster_id, ctx.node_key0)?; + + ctx.contract + .cluster_remove_node(ctx.cluster_id, ctx.node_key1)?; + + ctx.contract + .cluster_remove_node(ctx.cluster_id, ctx.node_key2)?; + + ctx.contract + .cluster_remove_cdn_node(ctx.cluster_id, ctx.cdn_node_key0)?; + + ctx.contract + .cluster_remove_cdn_node(ctx.cluster_id, ctx.cdn_node_key1)?; + + ctx.contract + .cluster_remove_cdn_node(ctx.cluster_id, ctx.cdn_node_key2)?; + + ctx.contract.cluster_remove(ctx.cluster_id)?; + + assert!( + matches!(get_events().pop().unwrap(), Event::ClusterRemoved(ev) if ev == + ClusterRemoved { + cluster_id: ctx.cluster_id, + }) + ); + + assert_eq!( + ctx.contract.cluster_get(ctx.cluster_id), + Err(ClusterDoesNotExist) + ); +} + +#[ink::test] +fn cluster_set_node_status_err_if_not_cluster_manager() { + let mut ctx = setup_cluster(); + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_manager_id, 1000 * TOKEN); + + set_caller(not_manager_id); + assert_eq!( + ctx.contract.cluster_set_node_status( + ctx.cluster_id, + ctx.node_key0, + NodeStatusInCluster::ACTIVE + ), + Err(OnlyClusterManager) + ); +} + +#[ink::test] +fn cluster_set_node_status_ok() { + let mut ctx = setup_cluster(); + + set_caller(ctx.manager_id); + ctx.contract.cluster_set_node_status( + ctx.cluster_id, + ctx.node_key0, + NodeStatusInCluster::ACTIVE, + )?; + + let node_info = ctx.contract.node_get(ctx.node_key0)?; + assert_eq!( + node_info.node.status_in_cluster, + Some(NodeStatusInCluster::ACTIVE) + ); +} + +#[ink::test] +fn cluster_set_cdn_node_status_err_if_not_cluster_manager() { + let mut ctx = setup_cluster(); + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_manager_id, 1000 * TOKEN); + + set_caller(not_manager_id); + assert_eq!( + ctx.contract.cluster_set_cdn_node_status( + ctx.cluster_id, + ctx.cdn_node_key0, + NodeStatusInCluster::ACTIVE + ), + Err(OnlyClusterManager) + ); +} + +#[ink::test] +fn cluster_set_cdn_node_status_ok() { + let mut ctx = setup_cluster(); + + set_caller(ctx.manager_id); + ctx.contract.cluster_set_cdn_node_status( + ctx.cluster_id, + ctx.cdn_node_key0, + NodeStatusInCluster::ACTIVE, + )?; + + let cdn_node_info = ctx.contract.cdn_node_get(ctx.cdn_node_key0)?; + assert_eq!( + cdn_node_info.cdn_node.status_in_cluster, + Some(NodeStatusInCluster::ACTIVE) + ); +} + +#[ink::test] +fn cluster_get_err_if_cluster_does_not_exist() { + let ctx = setup_cluster(); + + let bad_cluster_id = 10000; + assert_eq!( + ctx.contract.cluster_get(bad_cluster_id), + Err(ClusterDoesNotExist) + ); +} + +#[ink::test] +fn cluster_get_ok() { + let ctx = setup_cluster(); + + let mut cluster_v_nodes1 = Vec::::new(); + cluster_v_nodes1.extend(ctx.v_nodes0.clone()); + cluster_v_nodes1.extend(ctx.v_nodes1.clone()); + cluster_v_nodes1.extend(ctx.v_nodes2.clone()); + + let total_rent = ctx.rent_v_node_per_month0 * ctx.v_nodes0.len() as Balance + + ctx.rent_v_node_per_month1 * ctx.v_nodes1.len() as Balance + + ctx.rent_v_node_per_month2 * ctx.v_nodes2.len() as Balance; + + assert_eq!( + ctx.contract.cluster_get(ctx.cluster_id), + Ok({ + ClusterInfo { + cluster_id: ctx.cluster_id, + cluster: Cluster { + manager_id: ctx.manager_id, + nodes_keys: ctx.nodes_keys, + resource_per_v_node: ctx.resource_per_v_node, + resource_used: 0, + cluster_params: ctx.cluster_params.clone(), + revenues: Cash(0), + total_rent, + cdn_nodes_keys: ctx.cdn_nodes_keys, + cdn_usd_per_gb: CDN_USD_PER_GB, + cdn_revenues: Cash(0), + }, + cluster_v_nodes: cluster_v_nodes1, + } + }) + ); +} + +#[ink::test] +fn cluster_list_ok() { + let mut ctx = setup_cluster(); + + let mut cluster_v_nodes1 = Vec::::new(); + cluster_v_nodes1.extend(ctx.v_nodes0.clone()); + cluster_v_nodes1.extend(ctx.v_nodes1.clone()); + cluster_v_nodes1.extend(ctx.v_nodes2.clone()); + + let total_rent = ctx.rent_v_node_per_month0 * ctx.v_nodes0.len() as Balance + + ctx.rent_v_node_per_month1 * ctx.v_nodes1.len() as Balance + + ctx.rent_v_node_per_month2 * ctx.v_nodes2.len() as Balance; + + let cluster1 = ClusterInfo { + cluster_id: ctx.cluster_id, + cluster: Cluster { + manager_id: ctx.manager_id, + nodes_keys: ctx.nodes_keys, + resource_per_v_node: ctx.resource_per_v_node, + resource_used: 0, + cluster_params: ctx.cluster_params.clone(), + revenues: Cash(0), + total_rent, + cdn_nodes_keys: ctx.cdn_nodes_keys, + cdn_usd_per_gb: CDN_USD_PER_GB, + cdn_revenues: Cash(0), + }, + cluster_v_nodes: cluster_v_nodes1, + }; + + let cluster_params2 = ClusterParams::from("{}"); + let resource_per_v_node2 = 10; + let manager_id2 = AccountId::from([ + 0x82, 0x61, 0x19, 0xd5, 0xcf, 0x47, 0xdc, 0xb9, 0xc6, 0xff, 0x1a, 0x3e, 0x46, 0x03, 0x6d, + 0xad, 0x1f, 0xea, 0x66, 0x18, 0x96, 0x2e, 0x4a, 0x5e, 0x89, 0xe0, 0x96, 0x74, 0xcf, 0x80, + 0xf1, 0x30, + ]); + + set_balance(manager_id2, 1000 * TOKEN); + + set_caller(manager_id2); + let cluster_id2 = ctx + .contract + .cluster_create(cluster_params2.clone(), resource_per_v_node2)?; + + let cluster2 = ClusterInfo { + cluster_id: cluster_id2, + cluster: Cluster { + manager_id: manager_id2, + nodes_keys: Vec::new(), + resource_per_v_node: resource_per_v_node2, + resource_used: 0, + cluster_params: cluster_params2, + revenues: Cash(0), + total_rent: 0, + cdn_nodes_keys: Vec::new(), + cdn_usd_per_gb: CDN_USD_PER_GB, + cdn_revenues: Cash(0), + }, + cluster_v_nodes: Vec::new(), + }; + + let count = 2; + + assert_eq!( + ctx.contract.cluster_list(0, 100, None), + (vec![cluster1.clone(), cluster2.clone()], count) + ); + + assert_eq!( + ctx.contract.cluster_list(0, 2, None), + (vec![cluster1.clone(), cluster2.clone()], count) + ); + + assert_eq!( + ctx.contract.cluster_list(0, 1, None), + (vec![cluster1.clone()], count) + ); + + assert_eq!( + ctx.contract.cluster_list(1, 1, None), + (vec![cluster2.clone()], count) + ); + + assert_eq!(ctx.contract.cluster_list(21, 20, None), (vec![], count)); + + // Filter by manager. + assert_eq!( + ctx.contract.cluster_list(0, 100, Some(ctx.manager_id)), + (vec![cluster1.clone()], count) + ); + + assert_eq!( + ctx.contract.cluster_list(0, 100, Some(manager_id2)), + (vec![cluster2.clone()], count) + ); + + let not_manager_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + assert_eq!( + ctx.contract.cluster_list(0, 100, Some(not_manager_id)), + (vec![], count) + ); +} + +#[ink::test] +fn grant_and_revoke_trusted_manager_permission_ok() { + let mut contract = setup_contract(); + + let grantor = AccountId::from([ + 0x92, 0xad, 0x47, 0xdf, 0xb9, 0x6b, 0x2b, 0x4a, 0xd5, 0xb0, 0xe3, 0x6d, 0x56, 0x33, 0x27, + 0xfd, 0xcf, 0x9d, 0xee, 0x06, 0xf4, 0x0d, 0x41, 0x48, 0xe1, 0x6a, 0x5c, 0xaa, 0x6c, 0x0d, + 0x17, 0x4b, + ]); + set_balance(grantor, 1000 * TOKEN); + + let grantee = AccountId::from([ + 0x1a, 0xa6, 0x69, 0xb4, 0x23, 0xe4, 0x8b, 0xbd, 0xc4, 0x65, 0xe3, 0xee, 0x17, 0xfd, 0x5b, + 0x6d, 0x6f, 0xae, 0x6f, 0xf1, 0x40, 0x52, 0x03, 0x65, 0x02, 0xe4, 0x50, 0xb5, 0x0b, 0x34, + 0xe2, 0x7a, + ]); + set_balance(grantee, 1000 * TOKEN); + + let permission = Permission::ClusterManagerTrustedBy(grantor); + + assert!(!contract.has_permission(grantee, permission)); + + set_caller(grantor); + contract.grant_trusted_manager_permission(grantee)?; + + assert!(contract.has_permission(grantee, permission)); + + assert!( + matches!(get_events().pop().unwrap(), Event::PermissionGranted(ev) if ev == + PermissionGranted { + account_id: grantee, + permission + }) + ); + + set_caller(grantor); + contract.revoke_trusted_manager_permission(grantee)?; + + assert!(!contract.has_permission(grantee, permission)); + + assert!( + matches!(get_events().pop().unwrap(), Event::PermissionRevoked(ev) if ev == + PermissionRevoked { + account_id: grantee, + permission + }) + ); +} + +#[ink::test] +fn cluster_distribute_cdn_revenue_ok() { + // todo: this test scenario must be revised as it does pure printing without any assertion + println!("Creating new cdn cluster"); + + let mut ctx = setup_cluster(); + + // The provider stops trusting the manager_id. + println!("Cluster id is {}", ctx.cluster_id); + set_caller(ctx.provider_id0); + + let usd_per_cere = TOKEN / 100; + set_caller(admin_id()); + ctx.contract.account_set_usd_per_cere(usd_per_cere)?; + + let usd_amount = ctx.contract.account_get_usd_per_cere(); + println!("Current usd amount is {}", usd_amount); + + let rate = ctx.contract.cdn_get_rate(ctx.cluster_id)?; + println!("The current rate is {}", rate); + + let usd_per_kb = rate / KB_PER_GB; + println!("The current rate per kb {}", usd_per_kb); + + let cere_per_kb = ctx.contract.protocol.curr_converter.to_cere(usd_per_kb); + println!("The current cere rate per kb {}", cere_per_kb); + + set_caller_value(ctx.provider_id0, 10 * TOKEN); + ctx.contract.account_deposit()?; + + set_caller_value(ctx.provider_id0, 10 * TOKEN); + ctx.contract.account_bond(5 * TOKEN)?; + + set_caller(admin_id()); + ctx.contract.admin_set_protocol_fee_bp(1_000)?; + + set_caller(ctx.provider_id0); + let account0_before_putting = ctx.contract.accounts.get(&ctx.provider_id0).unwrap(); + println!("Before putting revenue: {:?}", account0_before_putting); + + let permission = Permission::Validator; + set_caller(admin_id()); + ctx.contract + .admin_grant_permission(admin_id(), permission)?; + + ctx.contract.cluster_put_cdn_revenue( + ctx.cluster_id, + vec![(ctx.provider_id0, 1000), (ctx.provider_id0, 541643)], + vec![(ctx.cdn_node_key0, 1000), (ctx.cdn_node_key1, 541643)], + vec![], + 5, + )?; + let account0_after_putting = ctx.contract.accounts.get(&ctx.provider_id0).unwrap(); + println!("After putting revenue: {:?}", account0_after_putting); + + let cluster_list_1 = ctx.contract.cluster_list(0, 10, None); + println!("Cluster list one {:?}", cluster_list_1); + let cdn_node0 = ctx.contract.cdn_nodes.get(ctx.cdn_node_key0).unwrap(); + let cdn_node1 = ctx.contract.cdn_nodes.get(ctx.cdn_node_key1).unwrap(); + + println!("{:?}", cdn_node0); + println!("{:?}", cdn_node1); + let cluster0 = ctx.contract.clusters.get(ctx.cluster_id); + println!("{:?}", cluster0); + let cluster_list = ctx.contract.cluster_list(0, 10, None); + println!("{:?}", cluster0); + println!("{:?}", cluster_list); + + let validated_commit_node0 = ctx.contract.get_validated_commit(ctx.cdn_node_key0); + println!("Validated commit: {:?}", validated_commit_node0); + + let fee = ctx.contract.get_protocol_fee_bp(); + println!("Protocol fee in basis points: {:?}", fee); + + let protocol_revenues = ctx.contract.get_protocol_revenues(); + println!("Protocol revenues: {:?}", protocol_revenues); + + set_caller(ctx.provider_id0); + + ctx.contract + .cluster_distribute_cdn_revenue(ctx.cluster_id)?; + + let cdn_node0 = ctx.contract.cdn_nodes.get(ctx.cdn_node_key0).unwrap(); + let cdn_node1 = ctx.contract.cdn_nodes.get(ctx.cdn_node_key1).unwrap(); + println!("{:?}", cdn_node0); + println!("{:?}", cdn_node1); + + let cluster_list_1 = ctx.contract.cluster_list(0, 10, None); + println!("{:?}", cluster_list_1); + + let account0_after_distributing = ctx.contract.accounts.get(&ctx.provider_id0).unwrap(); + println!("{:?}", account0_after_distributing); +} diff --git a/bucket/ddc_bucket/tests/test_contract.rs b/bucket/ddc_bucket/tests/test_contract.rs deleted file mode 100644 index 53af3e6f..00000000 --- a/bucket/ddc_bucket/tests/test_contract.rs +++ /dev/null @@ -1,1368 +0,0 @@ -use ink_lang as ink; - -use crate::ddc_bucket::account::entity::Account; -use crate::ddc_bucket::cdn_node::entity::NodeId; -use crate::ddc_bucket::cluster::entity::ClusterStatus; -use crate::ddc_bucket::flow::Flow; -use crate::ddc_bucket::node::entity::NodeTag; -use crate::ddc_bucket::schedule::{Schedule, MS_PER_MONTH}; -use crate::ddc_bucket::Error::*; -use crate::ddc_bucket::*; - -use super::env_utils::*; - -fn setup() -> DdcBucket { - set_caller(admin_id()); - set_callee(contract_id()); - let contract = DdcBucket::new(); - set_balance(contract_id(), 10); - contract -} - -const KB_PER_GB: Balance = 1_048_576; - -struct TestCluster { - contract: DdcBucket, - manager: AccountId, - cluster_id: ClusterId, - provider_id0: AccountId, - provider_id1: AccountId, - provider_id2: AccountId, - node_id0: NodeId, - node_id1: NodeId, - node_id2: NodeId, - rent_per_vnode: Balance, - node_ids: Vec, - vnodes: Vec, - vnodes_wrapper: Vec>, - node_params0: &'static str, - node_params1: &'static str, - node_params2: &'static str, - capacity: u32, - reserved: u32, -} - -fn new_cluster() -> TestCluster { - let accounts = get_accounts(); - set_balance(accounts.charlie, 1000 * TOKEN); - set_balance(accounts.django, 1000 * TOKEN); - let provider_id0 = accounts.alice; - let provider_id1 = accounts.bob; - let provider_id2 = accounts.charlie; - let manager = accounts.django; - - let mut contract = setup(); - - // Provide a Node. - let rent_per_vnode: Balance = 10 * TOKEN; - let node_params0 = "{\"url\":\"https://ddc.cere.network/bucket/{BUCKET_ID}\"}"; - let capacity = 100; - - for provider_id in [provider_id0, provider_id1, provider_id2] { - set_caller_value(provider_id, CONTRACT_FEE_LIMIT); - contract.node_trust_manager(manager); - let expected_perm = Permission::ManagerTrustedBy(provider_id); - assert!(contract.has_permission(manager, expected_perm)); - } - - set_caller_value(provider_id0, CONTRACT_FEE_LIMIT); - let node_id0 = contract.node_create( - rent_per_vnode, - node_params0.to_string(), - capacity, - NodeTag::ADDING, - provider_id0, - ); - - // Provide another Node. - let node_params1 = "{\"url\":\"https://ddc-1.cere.network/bucket/{BUCKET_ID}\"}"; - set_caller_value(provider_id1, CONTRACT_FEE_LIMIT); - - let node_id1 = contract.node_create( - rent_per_vnode, - node_params1.to_string(), - capacity, - NodeTag::ADDING, - provider_id1, - ); - - // Provide another Node. - let node_params2 = "{\"url\":\"https://ddc-2.cere.network/bucket/{BUCKET_ID}\"}"; - set_caller_value(provider_id2, CONTRACT_FEE_LIMIT); - - let node_id2 = contract.node_create( - rent_per_vnode, - node_params2.to_string(), - capacity, - NodeTag::ADDING, - provider_id2, - ); - - // Create a Cluster. - let node_ids = vec![1, 2, 3]; - let cluster_params = "{}"; - let mut vnodes_wrapper = Vec::>::new(); - - let vnodes_for_first_node = vec![1, 2, 3]; - let vnodes_for_second_node = vec![4, 5, 6]; - let vnodes_for_third_node = vec![7, 8, 9]; - - vnodes_wrapper.push(vnodes_for_first_node); - vnodes_wrapper.push(vnodes_for_second_node); - vnodes_wrapper.push(vnodes_for_third_node); - - set_caller_value(manager, CONTRACT_FEE_LIMIT); - - let cluster_id = contract.cluster_create( - manager, - vnodes_wrapper.clone(), - vec![node_id0, node_id1, node_id2], - cluster_params.to_string(), - ); - - let reserved = 10; - contract.cluster_reserve_resource(cluster_id, reserved); - - let mut vnodes = Vec::::new(); - - for v_nodes_vec in vnodes_wrapper.clone() { - for v_node in v_nodes_vec { - vnodes.push(v_node.clone()); - } - } - - TestCluster { - contract, - manager, - cluster_id, - provider_id0, - provider_id1, - provider_id2, - node_id0, - node_id1, - node_id2, - rent_per_vnode, - vnodes_wrapper, - vnodes, - node_ids, - node_params0, - node_params1, - node_params2, - capacity, - reserved, - } -} - -fn new_cluster_cdn() -> TestCluster { - let accounts = get_accounts(); - set_balance(accounts.charlie, 1000 * TOKEN); - set_balance(accounts.django, 1000 * TOKEN); - let provider_id0 = accounts.alice; - let provider_id1 = accounts.bob; - let provider_id2 = accounts.charlie; - let manager = accounts.django; - - let mut contract = setup(); - - // Provide a Node. - let rent_per_vnode: Balance = 10 * TOKEN; - let node_params0 = "{\"url\":\"https://ddc.cere.network/bucket/{BUCKET_ID}\"}"; - let capacity = 100; - - for provider_id in [provider_id0, provider_id1, provider_id2] { - set_caller_value(provider_id, CONTRACT_FEE_LIMIT); - - contract.node_trust_manager(manager); - let expected_perm = Permission::ManagerTrustedBy(provider_id); - assert!(contract.has_permission(manager, expected_perm)); - } - - set_caller_value(provider_id0, CONTRACT_FEE_LIMIT); - - let node_id0 = contract.cdn_node_create(node_params0.to_string(), provider_id0); - - // Provide another Node. - let node_params1 = "{\"url\":\"https://ddc-1.cere.network/bucket/{BUCKET_ID}\"}"; - set_caller_value(provider_id1, CONTRACT_FEE_LIMIT); - - let node_id1 = contract.cdn_node_create(node_params1.to_string(), provider_id1); - - // Provide another Node. - let node_params2 = "{\"url\":\"https://ddc-2.cere.network/bucket/{BUCKET_ID}\"}"; - set_caller_value(provider_id2, CONTRACT_FEE_LIMIT); - - let node_id2 = contract.cdn_node_create(node_params2.to_string(), provider_id2); - - // Create a Cluster. - let _cluster_params = "{}"; - - // TODO: adjust after cdn cluster topology and node addition - - let vnodes = vec![1, 2, 3, 4, 5, 6]; - - let node_ids = vec![1, 2, 3]; - let mut vnodes_wrapper = Vec::>::new(); - vnodes_wrapper.push(vnodes); - - let mut vnodes_wrapper = Vec::>::new(); - - let vnodes_for_first_node = vec![1, 2, 3]; - let vnodes_for_second_node = vec![4, 5, 6]; - let vnodes_for_third_node = vec![7, 8, 9]; - - vnodes_wrapper.push(vnodes_for_first_node); - vnodes_wrapper.push(vnodes_for_second_node); - vnodes_wrapper.push(vnodes_for_third_node); - - set_caller_value(manager, CONTRACT_FEE_LIMIT); - - let cluster_id = contract.cdn_cluster_create(vec![node_id0, node_id1, node_id2]); - - let reserved = 10; - - let mut vnodes = Vec::::new(); - - for v_nodes_vec in vnodes_wrapper.clone() { - for v_node in v_nodes_vec { - vnodes.push(v_node.clone()); - } - } - - TestCluster { - contract, - manager, - cluster_id, - provider_id0, - provider_id1, - provider_id2, - node_id0, - node_id1, - node_id2, - rent_per_vnode, - vnodes_wrapper, - node_params0, - node_params1, - node_params2, - capacity, - reserved, - node_ids, - vnodes, - } -} - -struct TestBucket { - bucket_id: BucketId, - owner_id: AccountId, - resource: u32, -} - -fn new_bucket(ctx: &mut TestCluster) -> TestBucket { - let accounts = get_accounts(); - let owner_id = accounts.django; - set_balance(owner_id, 1000 * TOKEN); - set_caller_value(owner_id, CONTRACT_FEE_LIMIT); - - let bucket_id = ctx - .contract - .bucket_create("{}".to_string(), ctx.cluster_id, None); - - // Reserve some resources for the bucket from the cluster. - set_caller_value(owner_id, CONTRACT_FEE_LIMIT); - let resource = 1; - ctx.contract.bucket_alloc_into_cluster(bucket_id, resource); - - // Deposit some value to pay for buckets. - set_caller_value(owner_id, 10 * TOKEN); - ctx.contract.account_deposit(); - - TestBucket { - bucket_id, - owner_id, - resource, - } -} - -#[ink::test] -fn cluster_create_works() { - let ctx = new_cluster(); - let provider_ids = &[ctx.provider_id0, ctx.provider_id1, ctx.provider_id2]; - let node_ids = &[ctx.node_id0, ctx.node_id1, ctx.node_id2]; - let node_params = &[ctx.node_params0, ctx.node_params1, ctx.node_params2]; - - assert_eq!(ctx.cluster_id, 1, "cluster_id must start at 1"); - assert_eq!(ctx.node_id0, 1, "node_id must start at 1"); - assert_ne!(ctx.node_id0, ctx.node_id1, "nodes must have unique IDs"); - - // Check the nodes. - { - let node0 = ctx.contract.node_get(ctx.node_id0)?; - - assert_eq!( - node0, - NodeStatus { - node_id: ctx.node_id0, - node: Node { - provider_id: ctx.provider_id0, - rent_per_month: ctx.rent_per_vnode, - free_resource: ctx.capacity - ctx.reserved * 3, - node_tag: NodeTag::ADDING, - node_pub_key: ctx.provider_id0, - }, - params: ctx.node_params0.to_string(), - } - ); - - let node1 = ctx.contract.node_get(ctx.node_id1)?; - assert_eq!( - node1, - NodeStatus { - node_id: ctx.node_id1, - node: Node { - provider_id: ctx.provider_id1, - rent_per_month: ctx.rent_per_vnode, - free_resource: ctx.capacity - ctx.reserved * 3, - node_tag: NodeTag::ADDING, - node_pub_key: ctx.provider_id1, - }, - params: ctx.node_params1.to_string(), - } - ); - - let node2 = ctx.contract.node_get(ctx.node_id2)?; - assert_eq!( - node2, - NodeStatus { - node_id: ctx.node_id2, - node: Node { - provider_id: ctx.provider_id2, - rent_per_month: ctx.rent_per_vnode, - free_resource: ctx.capacity - ctx.reserved * 3, - node_tag: NodeTag::ADDING, - node_pub_key: ctx.provider_id2, - }, - params: ctx.node_params2.to_string(), - } - ); - } - - // Check the initial state of the cluster. - { - let cluster = ctx.contract.cluster_get(ctx.cluster_id)?; - assert_eq!( - cluster, - ClusterStatus { - cluster_id: ctx.cluster_id, - cluster: Cluster { - manager_id: ctx.manager, - node_ids: ctx.node_ids, - v_nodes: ctx.vnodes_wrapper, - resource_per_vnode: ctx.reserved, - resource_used: 0, - revenues: Cash(0), - total_rent: ctx.rent_per_vnode * ctx.vnodes.len() as Balance, - }, - params: "{}".to_string(), - } - ); - } - - // Check the events. - let mut evs = get_events(); - evs.reverse(); // Work with pop(). - - // Providers trust Manager. - for provider_id in provider_ids { - assert!( - matches!(evs.pop().unwrap(), Event::GrantPermission(ev) if ev == - GrantPermission { account_id: ctx.manager, permission: Permission::ManagerTrustedBy(*provider_id) }) - ); - } - - // Nodes created. - for i in 0..3 { - assert!(matches!(evs.pop().unwrap(), Event::NodeCreated(ev) if ev == - NodeCreated { - node_id: node_ids[i], - provider_id: provider_ids[i], - rent_per_month: ctx.rent_per_vnode, - node_params: node_params[i].to_string() })); - } - - // Cluster setup. - assert!( - matches!(evs.pop().unwrap(), Event::ClusterCreated(ev) if ev == - ClusterCreated { cluster_id: ctx.cluster_id, manager: ctx.manager, cluster_params: "{}".to_string() }) - ); - - assert!( - matches!(evs.pop().unwrap(), Event::ClusterReserveResource(ev) if ev == - ClusterReserveResource { cluster_id: ctx.cluster_id, resource: ctx.reserved }) - ); - - assert_eq!(evs.len(), 0, "all events must be checked"); -} - -#[ink::test] -fn cluster_replace_node_only_manager() { - let mut ctx = new_cluster(); - let not_manager = get_accounts().alice; - set_caller_value(not_manager, 0); - - // Reassign a vnode from node1 to node2. - assert_eq!( - ctx.contract - .message_cluster_replace_node(ctx.cluster_id, vec![1, 2, 3], ctx.node_id2), - Err(UnauthorizedClusterManager) - ); -} - -#[ink::test] -fn cluster_replace_node_only_trusted_manager() { - let mut ctx = new_cluster(); - - // The provider stops trusting the manager. - set_caller(ctx.provider_id2); - ctx.contract.node_distrust_manager(ctx.manager); - - set_caller_value(ctx.manager, 0); - - // The manager cannot use nodes of the provider. - assert_eq!( - ctx.contract - .message_cluster_replace_node(ctx.cluster_id, vec![1, 2, 3], ctx.node_id2), - Err(ClusterManagerIsNotTrusted) - ); -} - -#[ink::test] -fn cluster_replace_node_works() { - let mut ctx = new_cluster(); - set_caller_value(ctx.manager, 0); - - // Reassign a vnode from node1 to node2. - ctx.contract - .cluster_replace_node(ctx.cluster_id, vec![1, 3], ctx.node_id2); - - // Check the last event. - let ev = get_events().pop().unwrap(); - assert!(matches!(ev, Event::ClusterNodeReplaced(ev) if ev == - ClusterNodeReplaced { cluster_id: ctx.cluster_id, node_id: ctx.node_id2 })); - - let vnodes_for_replaced = vec![2]; - let vnodes_for_second_node = vec![4, 5, 6]; - let vnodes_for_third_node = vec![7, 8, 9]; - let vnodes_for_third_dup = vec![1, 3]; - - let vnodes = vec![ - vnodes_for_replaced, - vnodes_for_second_node, - vnodes_for_third_node, - vnodes_for_third_dup, - ]; - - // Check the changed state of the cluster. - let cluster = ctx.contract.cluster_get(ctx.cluster_id)?.cluster; - println!("cluster.v_nodes: {:?}", cluster.v_nodes.clone()); - println!("cluster.node_ids: {:?}", cluster.node_ids.clone()); - assert_eq!(&cluster.v_nodes, &vnodes, "a vnode must be replaced"); - - // Check the changed state of the nodes. - let expected_resources = [ - (ctx.node_id0, 100 - 10), - (ctx.node_id1, 100 - 10 - 10 - 10), - (ctx.node_id2, 100 - 10 - 10 - 10 - 10 - 10), - ]; - - for (node_id, available) in expected_resources { - let node_status = ctx.contract.node_get(node_id as u32).unwrap(); - assert_eq!( - node_status.node.free_resource, available, - "resources must have shifted between nodes" - ); - } -} - -#[ink::test] -fn cluster_reserve_works() { - let mut ctx = new_cluster(); - set_caller_value(ctx.manager, 0); - - // Reserve more resources. - ctx.contract.cluster_reserve_resource(ctx.cluster_id, 5); - - // Check the last event. - let ev = get_events().pop().unwrap(); - assert!(matches!(ev, Event::ClusterReserveResource(ev) if ev == - ClusterReserveResource { cluster_id: ctx.cluster_id, resource: 5 })); - - // Check the changed state of the cluster. - let cluster = ctx.contract.cluster_get(ctx.cluster_id)?.cluster; - assert_eq!(cluster.resource_per_vnode, 10 + 5); - - // Check the changed state of the nodes. - let expected_resources = [ - (ctx.node_id0, 100 - 40 - 5), - (ctx.node_id1, 100 - 40 - 5), - (ctx.node_id2, 100 - 40 - 5), - ]; - for (node_id, available) in expected_resources { - assert_eq!( - ctx.contract.node_get(node_id)?.node.free_resource, - available, - "more resources must be reserved from the nodes" - ); - } -} - -#[ink::test] -fn cluster_management_validation_works() { - let mut ctx = new_cluster(); - - let not_manager = ctx.provider_id0; - set_caller_value(not_manager, 0); - - assert_eq!( - ctx.contract - .message_cluster_replace_node(ctx.cluster_id, vec![1, 2, 3], 1), - Err(UnauthorizedClusterManager), - "only the manager can modify the cluster" - ); - - set_caller_value(ctx.manager, 0); - - let bad_node_id = ctx.node_id2 + 1; - assert_eq!( - ctx.contract - .message_cluster_replace_node(ctx.cluster_id, vec![1, 2, 3], bad_node_id), - Err(NodeDoesNotExist), - "cluster replacement node must exist" - ); - - assert_eq!( - ctx.contract - .message_cluster_create(vec![vec![1, 2, 3]], vec![bad_node_id], "".to_string()), - Err(NodeDoesNotExist), - "cluster initial nodes must exist" - ); -} - -#[ink::test] -fn cdn_cluster_gas_converter_works() { - println!("Creating new cdn cluster"); - - let mut ctx = new_cluster_cdn(); - - println!("Got cdn cluster back"); - // The provider stops trusting the manager. - println!("Cdn cluster id is {}", ctx.cluster_id); - set_caller(ctx.manager); - ctx.contract.cdn_set_rate(ctx.cluster_id, 3_750_000_000); - set_caller(ctx.provider_id0); - let rate = ctx.contract.cdn_get_rate(ctx.cluster_id); - - let usd_per_cere = TOKEN / 100; - set_caller(ctx.provider_id0); - ctx.contract.account_set_usd_per_cere(usd_per_cere); - - let usd_amount = ctx.contract.account_get_usd_per_cere(); - println!("Current usd amount is {}", usd_amount); - - println!("The current rate is {}", rate); - - let usd_per_kb = rate / KB_PER_GB; - println!("The current rate per kb {}", usd_per_kb); - - let cere_per_kb = ctx.contract.accounts.1.to_cere(usd_per_kb); - println!("The current cere rate per kb {}", cere_per_kb); -} - -#[ink::test] -fn cdn_cluster_payment_works() { - println!("Creating new cdn cluster"); - - let mut ctx = new_cluster_cdn(); - - println!("Got cdn cluster back"); - // The provider stops trusting the manager. - println!("Cdn cluster id is {}", ctx.cluster_id); - set_caller(ctx.provider_id0); - let rate = ctx.contract.cdn_get_rate(ctx.cluster_id); - - let usd_per_cere = TOKEN / 100; - set_caller(ctx.provider_id0); - ctx.contract.account_set_usd_per_cere(usd_per_cere); - - let usd_amount = ctx.contract.account_get_usd_per_cere(); - println!("Current usd amount is {}", usd_amount); - - println!("The current rate is {}", rate); - - let usd_per_kb = rate / KB_PER_GB; - println!("The current rate per kb {}", usd_per_kb); - - let cere_per_kb = ctx.contract.accounts.1.to_cere(usd_per_kb); - println!("The current cere rate per kb {}", cere_per_kb); - - set_caller_value(ctx.provider_id0, 10 * TOKEN); - ctx.contract.account_deposit(); - - set_caller(ctx.provider_id0); - ctx.contract.account_bond(5 * TOKEN); - - set_caller(ctx.provider_id0); - ctx.contract.set_fee_bp(1_000); - - let mut account = ctx.contract.accounts.get(&ctx.provider_id0).unwrap(); - println!("{:?}", account); - - ctx.contract.cdn_cluster_put_revenue( - ctx.cluster_id, - vec![(ctx.provider_id0, 1000), (ctx.provider_id0, 541643)], - vec![(ctx.node_id0, 1000), (ctx.node_id1, 541643)], - vec![], - 5, - ); - account = ctx.contract.accounts.get(&ctx.provider_id0).unwrap(); - println!("{:?}", account); - - let cdn_cluster_list_one = ctx.contract.cdn_cluster_list(0, 10, None); - print!("Cluster list one {:?}", cdn_cluster_list_one); - let node0 = ctx.contract.cdn_nodes.get(ctx.node_id0).unwrap(); - let node1 = ctx.contract.cdn_nodes.get(ctx.node_id1).unwrap(); - - println!("Node 1 {:?}", node0); - println!("Node 2 {:?}", node1); - // let cdn_cluster0 = ctx.contract.cdn_clusters.get(ctx.cluster_id); - // print!("{:?}", cdn_cluster0); - // let cdn_cluster_list = ctx.contract.cdn_cluster_list(0, 10, None); - // print!("{:?}", cdn_cluster0); - // print!("{:?}", cdn_cluster_list); - - let validated_commit_node0 = ctx.contract.get_validated_commit(ctx.node_id0); - print!("Validated commit {:?}", validated_commit_node0); - - let fee = ctx.contract.get_fee_bp(); - print!("Protocol fee in basis points {:?}", fee); - - let protocol_revenues = ctx.contract.get_protocol_revenues(); - print!("Protocol revenues are {:?}", protocol_revenues); - - set_caller(ctx.provider_id0); - ctx.contract.cdn_cluster_distribute_revenues(0); - - let node0 = ctx.contract.cdn_nodes.get(ctx.node_id0).unwrap(); - let node1 = ctx.contract.cdn_nodes.get(ctx.node_id1).unwrap(); - println!("{:?}", node0); - println!("{:?}", node1); - - let cdn_cluster_list = ctx.contract.cdn_cluster_list(0, 10, None); - print!("{:?}", cdn_cluster_list); - - account = ctx.contract.accounts.get(&ctx.provider_id0).unwrap(); - // let node0 = ctx.contract.cdn_nodes.get(ctx.node_id0).unwrap(); - // let node1 = ctx.contract.cdn_nodes.get(ctx.node_id1).unwrap(); - println!("{:?}", account); - // let account1 = ctx.contract.accounts.get(&ctx.provider_id1).unwrap(); - // println!("{:?}", account1); - // let account_balance = ink_env::balance(&ctx.provider_id0); - // println!("{:?}", account_balance); -} - -fn bucket_settle_payment(ctx: &mut TestCluster, test_bucket: &TestBucket) { - // Go to the future when some revenues are due. - advance_block::(); - // Pay the due thus far. - set_caller_value(ctx.manager, CONTRACT_FEE_LIMIT); - ctx.contract.bucket_settle_payment(test_bucket.bucket_id); -} - -#[ink::test] -fn bucket_pays_cluster() { - let ctx = &mut new_cluster(); - let test_bucket = &new_bucket(ctx); - do_bucket_pays_cluster(ctx, test_bucket, 1).unwrap(); -} - -#[ink::test] -fn bucket_pays_cluster_at_new_rate() { - let ctx = &mut new_cluster(); - let test_bucket = &new_bucket(ctx); - - // Set up an exchange rate manager. - set_caller_value(admin_id(), CONTRACT_FEE_LIMIT); - ctx.contract - .admin_grant_permission(admin_id(), Permission::SetExchangeRate); - - // Change the currency exchange rate. - let usd_per_cere = 2; - set_caller(admin_id()); - ctx.contract.account_set_usd_per_cere(usd_per_cere * TOKEN); - - do_bucket_pays_cluster(ctx, test_bucket, usd_per_cere).unwrap(); -} - -fn do_bucket_pays_cluster( - ctx: &mut TestCluster, - test_bucket: &TestBucket, - usd_per_cere: Balance, -) -> Result<()> { - let expected_rent = ctx.rent_per_vnode * ctx.vnodes.len() as Balance; - - // Check the state before payment. - let before = ctx - .contract - .account_get(test_bucket.owner_id)? - .deposit - .peek(); - let bucket = ctx.contract.bucket_get(test_bucket.bucket_id)?.bucket; - assert_eq!(bucket.owner_id, test_bucket.owner_id); - /* TODO: Not testable at the moment, see struct BucketInStatus. - assert_eq!(bucket.flow, - Flow { - from: test_bucket.owner_id, - schedule: Schedule::new(0, expected_rent), - }); - */ - let timestamp_before = block_timestamp::(); - bucket_settle_payment(ctx, &test_bucket); - let timestamp_after = block_timestamp::(); - - // Check the last event. - let ev = get_events().pop().unwrap(); - assert!(matches!(ev, Event::BucketSettlePayment(ev) if ev == - BucketSettlePayment { bucket_id: test_bucket.bucket_id, cluster_id: ctx.cluster_id })); - - // Check the state after payment. - let after = ctx - .contract - .account_get(test_bucket.owner_id)? - .deposit - .peek(); - let spent = before - after; - /* TODO: Not testable at the moment, see struct BucketInStatus. - let bucket = ctx.contract.bucket_get(test_bucket.bucket_id)?.bucket; - assert_eq!(bucket.flow, - Flow { - from: test_bucket.owner_id, - schedule: Schedule::new(BLOCK_TIME, expected_rent), - }); - */ - let timespan = timestamp_after - timestamp_before; - let expect_revenues_usd = expected_rent * timespan as u128 / MS_PER_MONTH as u128; - let expect_revenues = expect_revenues_usd / usd_per_cere; - assert!(expect_revenues > 0); - assert_eq!( - expect_revenues, spent, - "revenues must come from the bucket owner" - ); - - let cluster = ctx.contract.cluster_get(ctx.cluster_id)?.cluster; - assert_eq!( - cluster.revenues.peek(), - expect_revenues, - "must get revenues into the cluster" - ); - - Ok(()) -} - -#[ink::test] -fn cluster_add_node() { - let ctx = &mut new_cluster(); - - let new_provider_id = get_accounts().frank; - set_balance(new_provider_id, 1000 * TOKEN); - - let rent_per_month = 100; - let node_params = "new_node"; - let capacity = 1000; - let node_tag = NodeTag::ACTIVE; - - set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); - let new_node_id = ctx.contract.node_create( - rent_per_month, - node_params.to_string(), - capacity, - node_tag, - ctx.manager, - ); - - set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); - ctx.contract.node_trust_manager(ctx.manager); - assert!( - matches!(get_events().pop().unwrap(), Event::GrantPermission(ev) if ev == - GrantPermission { account_id: ctx.manager, permission: Permission::ManagerTrustedBy(new_provider_id) }) - ); - - let mut node_ids = Vec::::new(); - node_ids.push(ctx.node_id0); - node_ids.push(ctx.node_id1); - node_ids.push(ctx.node_id2); - node_ids.push(new_node_id); - - let mut v_nodes = ctx.vnodes_wrapper.clone(); - v_nodes.push(vec![10, 11, 12]); - - set_caller_value(ctx.manager, CONTRACT_FEE_LIMIT); - ctx.contract - .cluster_add_node(ctx.cluster_id, node_ids.clone(), v_nodes.clone()); - - let cluster_status = ctx.contract.cluster_get(ctx.cluster_id).unwrap(); - assert!(matches!(cluster_status.clone().cluster.node_ids.len(), 4)); - assert!(matches!(cluster_status.clone().cluster.v_nodes.len(), 4)); -} - -#[ink::test] -fn cluster_pays_providers() { - let ctx = &mut new_cluster(); - let test_bucket = &new_bucket(ctx); - bucket_settle_payment(ctx, &test_bucket); - - // Get state before the distribution. - let to_distribute = ctx - .contract - .cluster_get(ctx.cluster_id)? - .cluster - .revenues - .peek(); - let before0 = balance_of(ctx.provider_id0); - let before1 = balance_of(ctx.provider_id1); - let before2 = balance_of(ctx.provider_id2); - let before_mgmt = balance_of(ctx.manager); - - let skip_events = get_events::().len(); - - // Set a network fee. - let network_fee_bp = 100; // 1% - let cluster_management_fee_bp = 200; // 2% - set_caller_value(admin_id(), CONTRACT_FEE_LIMIT); - ctx.contract.admin_set_fee_config(FeeConfig { - network_fee_bp, - network_fee_destination: AccountId::default(), - cluster_management_fee_bp, - }); - - let burned_fee = to_distribute * network_fee_bp / 10_000; - let manager_fee = (to_distribute - burned_fee) * cluster_management_fee_bp / 10_000; - let provider_fee = (to_distribute - burned_fee - manager_fee) / 3; - - // Distribute the revenues of the cluster to providers. - ctx.contract.cluster_distribute_revenues(ctx.cluster_id); - - // Check the last events. - let mut evs = get_events(); - evs.reverse(); // Work with pop(). - evs.truncate(evs.len() - skip_events); - let expected_recipients = vec![ctx.provider_id0, ctx.provider_id1, ctx.provider_id2]; - - for provider_id in expected_recipients { - assert!( - matches!(evs.pop().unwrap(), Event::ClusterDistributeRevenues(ev) if ev == - ClusterDistributeRevenues { cluster_id: ctx.cluster_id, provider_id }) - ); - } - - assert_eq!(evs.len(), 0, "all events must be checked"); - - // Get state after the distribution. - let rounding_error = ctx - .contract - .cluster_get(ctx.cluster_id)? - .cluster - .revenues - .peek(); - let earned0 = balance_of(ctx.provider_id0) - before0; - let earned1 = balance_of(ctx.provider_id1) - before1; - let earned2 = balance_of(ctx.provider_id2) - before2; - let earned_mgmt = balance_of(ctx.manager) - before_mgmt; - - assert!(provider_fee > 0, "provider must earn something"); - assert_eq!( - earned0, provider_fee, - "providers must earn the correct amount" - ); - assert_eq!( - earned1, provider_fee, - "providers must earn the correct amount" - ); - assert_eq!( - earned2, provider_fee, - "providers must earn the correct amount" - ); - - assert!(burned_fee > 0, "the network must earn something"); - assert!(manager_fee > 0, "the manager must earn something"); - assert_eq!( - earned_mgmt, manager_fee, - "the manager must earn the correct amount" - ); - - assert!(to_distribute > 0); - assert!( - rounding_error < 10, - "revenues must go out of the cluster (besides rounding)" - ); - assert_eq!( - earned0 + earned1 + earned2 + burned_fee + manager_fee + rounding_error, - to_distribute, - "all revenues must go to providers" - ); -} - -#[ink::test] -fn bucket_reserve_0_works() { - let contract = setup(); - - assert_eq!( - contract.bucket_list(0, 10, None), - ( - vec![BucketStatus { - bucket_id: 0, - bucket: BucketInStatus { - owner_id: AccountId::default(), - cluster_id: 0, - resource_reserved: 0, - public_availability: false, - resource_consumption_cap: 0, - }, - params: "".to_string(), - writer_ids: vec![AccountId::default()], - reader_ids: vec![], - rent_covered_until_ms: 18446744073709551615, - }], - 1 - ) - ); - - assert_eq!( - contract.cluster_list(0, 10, None), - ( - vec![ClusterStatus { - cluster_id: 0, - cluster: Cluster { - manager_id: AccountId::default(), - v_nodes: vec![], - resource_per_vnode: 0, - resource_used: 0, - revenues: Cash(0), - total_rent: 0, - node_ids: vec![] - }, - params: "".to_string(), - }], - 1 - ) - ); - - assert_eq!( - contract.node_list(0, 10, None), - ( - vec![NodeStatus { - node_id: 0, - node: Node { - provider_id: AccountId::default(), - rent_per_month: 0, - free_resource: 0, - node_tag: NodeTag::ACTIVE, - node_pub_key: AccountId::default(), - }, - params: "".to_string(), - }], - 1 - ) - ); -} - -#[ink::test] -fn bucket_create_works() { - let ctx = &mut new_cluster(); - let test_bucket = &new_bucket(ctx); - - assert_eq!(test_bucket.bucket_id, 1, "bucket_id must start at 1"); - - // Check the structure of the bucket including the payment flow. - let total_rent = ctx.rent_per_vnode * ctx.vnodes.len() as Balance; - let expect_bucket = Bucket { - owner_id: test_bucket.owner_id, - cluster_id: ctx.cluster_id, - flow: Flow { - from: test_bucket.owner_id, - schedule: Schedule::new(0, total_rent), - }, - resource_reserved: test_bucket.resource, - public_availability: false, - resource_consumption_cap: 0, - }; - - // Check the status of the bucket. - let bucket_status = ctx.contract.bucket_get(test_bucket.bucket_id)?; - assert_eq!( - bucket_status, - BucketStatus { - bucket_id: test_bucket.bucket_id, - bucket: expect_bucket.into(), - params: "{}".to_string(), - writer_ids: vec![test_bucket.owner_id], - reader_ids: vec![], - rent_covered_until_ms: 297600000, // TODO: check this value. - } - ); - - let mut evs = get_events(); - evs.reverse(); // Work with pop(). - evs.truncate(8 - 3 - 2); // Skip 3 NodeCreated and 2 cluster setup events. - - // Create bucket. - assert!( - matches!(evs.pop().unwrap(), Event::BucketCreated(ev) if ev == - BucketCreated { bucket_id: test_bucket.bucket_id, owner_id: test_bucket.owner_id }) - ); - - assert!( - matches!(evs.pop().unwrap(), Event::BucketAllocated(ev) if ev == - BucketAllocated { bucket_id: test_bucket.bucket_id, cluster_id: ctx.cluster_id, resource: test_bucket.resource }) - ); - - // Deposit more. - let net_deposit = 10 * TOKEN; - assert!(matches!(evs.pop().unwrap(), Event::Deposit(ev) if ev == - Deposit { account_id: test_bucket.owner_id, value: net_deposit })); - - assert_eq!(evs.len(), 0, "all events must be checked"); -} - -#[ink::test] -fn account_deposit_works() { - let account_id = get_accounts().alice; - let mut contract = setup(); - - assert_eq!( - contract.account_get(account_id), - Err(AccountDoesNotExist), - "must not get a non-existent account" - ); - - let deposit = 10 * TOKEN; - let deposit_after_fee = deposit; - - // Deposit some value. - set_caller_value(account_id, deposit); - contract.account_deposit(); - - let account = contract.account_get(account_id)?; - assert_eq!( - account, - Account { - deposit: Cash(deposit_after_fee), - payable_schedule: Schedule::empty(), - bonded: Cash(0), - unbonded_amount: Cash(0), - negative: Cash(0), - unbonded_timestamp: 0, - }, - "must take deposit minus creation fee" - ); - - // Deposit more value. - set_caller_value(account_id, deposit); - contract.account_deposit(); - - let account = contract.account_get(account_id)?; - assert_eq!( - account, - Account { - deposit: Cash(deposit_after_fee + deposit), - payable_schedule: Schedule::empty(), - bonded: Cash(0), - unbonded_amount: Cash(0), - negative: Cash(0), - unbonded_timestamp: 0, - }, - "must take more deposits without creation fee" - ); - - // Check events. - let mut evs = get_events(); - evs.reverse(); // Work with pop(). - - // First deposit event. - assert!(matches!(evs.pop().unwrap(), Event::Deposit(ev) if ev == - Deposit { account_id, value: deposit_after_fee })); - - // Second deposit event. No deposit_contract_fee because the account already exists. - assert!(matches!(evs.pop().unwrap(), Event::Deposit(ev) if ev == - Deposit { account_id, value: deposit })); - - assert_eq!(evs.len(), 0, "all events must be checked"); -} - -#[ink::test] -fn node_change_params_works() { - let ctx = &mut new_cluster(); - - // Change params. - set_caller_value(ctx.provider_id0, CONTRACT_FEE_LIMIT); - ctx.contract - .node_change_params(ctx.node_id0, "new params".to_string()); - - // Check the changed params. - let status = ctx.contract.node_get(ctx.node_id0)?; - assert_eq!(status.params, "new params"); -} - -#[ink::test] -#[should_panic] -fn node_change_params_only_owner() { - let ctx = &mut new_cluster(); - - // Change params. - set_caller_value(get_accounts().bob, CONTRACT_FEE_LIMIT); - ctx.contract - .node_change_params(ctx.node_id0, "new params".to_string()); - // Panic. -} - -#[ink::test] -fn cluster_change_params_works() { - let ctx = &mut new_cluster(); - - // Change params. - set_caller_value(ctx.manager, CONTRACT_FEE_LIMIT); - ctx.contract - .cluster_change_params(ctx.cluster_id, "new params".to_string()); - - // Check the changed params. - let status = ctx.contract.cluster_get(ctx.cluster_id)?; - assert_eq!(status.params, "new params"); -} - -#[ink::test] -#[should_panic] -fn cluster_change_params_only_owner() { - let ctx = &mut new_cluster(); - - // Change params. - set_caller_value(get_accounts().bob, CONTRACT_FEE_LIMIT); - ctx.contract - .cluster_change_params(ctx.cluster_id, "new params".to_string()); - // Panic. -} - -#[ink::test] -fn bucket_change_params_works() { - let ctx = &mut new_cluster(); - let test_bucket = &new_bucket(ctx); - - // Change params. - set_caller_value(test_bucket.owner_id, CONTRACT_FEE_LIMIT); - ctx.contract - .bucket_change_params(test_bucket.bucket_id, "new params".to_string()); - - // Check the changed params. - let status = ctx.contract.bucket_get(test_bucket.bucket_id)?; - assert_eq!(status.params, "new params"); -} - -#[ink::test] -#[should_panic] -fn bucket_change_params_only_owner() { - let ctx = &mut new_cluster(); - let test_bucket = &new_bucket(ctx); - - // Change params. - set_caller_value(get_accounts().bob, CONTRACT_FEE_LIMIT); - ctx.contract - .bucket_change_params(test_bucket.bucket_id, "new params".to_string()); - // Panic. -} - -#[ink::test] -fn bucket_list_works() { - let mut ddc_bucket = setup(); - let accounts = get_accounts(); - let owner_id1 = accounts.alice; - let owner_id2 = accounts.bob; - let owner_id3 = accounts.charlie; - let cluster_id = 0; - - set_caller_value(owner_id1, CONTRACT_FEE_LIMIT); - let bucket_id1 = ddc_bucket.bucket_create("".to_string(), cluster_id, None); - let bucket_status1 = ddc_bucket.bucket_get(bucket_id1).unwrap(); - - set_caller_value(owner_id2, CONTRACT_FEE_LIMIT); - let bucket_id2 = ddc_bucket.bucket_create("".to_string(), cluster_id, None); - let bucket_status2 = ddc_bucket.bucket_get(bucket_id2)?; - - assert_ne!(bucket_id1, bucket_id2); - let count = 3; - - assert_eq!( - ddc_bucket.bucket_list(1, 100, None), - (vec![bucket_status1.clone(), bucket_status2.clone()], count) - ); - - assert_eq!( - ddc_bucket.bucket_list(1, 2, None), - (vec![bucket_status1.clone(), bucket_status2.clone()], count) - ); - - assert_eq!( - ddc_bucket.bucket_list(1, 1, None), - ( - vec![bucket_status1.clone() /*, bucket_status2.clone()*/], - count - ) - ); - assert_eq!( - ddc_bucket.bucket_list(2, 1, None), - ( - vec![/*bucket_status1.clone(),*/ bucket_status2.clone()], - count - ) - ); - - assert_eq!(ddc_bucket.bucket_list(count, 20, None), (vec![], count)); - - // Filter by owner. - assert_eq!( - ddc_bucket.bucket_list(1, 100, Some(owner_id1)), - ( - vec![bucket_status1.clone() /*, bucket_status2.clone()*/], - count - ) - ); - - assert_eq!( - ddc_bucket.bucket_list(1, 100, Some(owner_id2)), - ( - vec![/*bucket_status1.clone(),*/ bucket_status2.clone()], - count - ) - ); - - assert_eq!( - ddc_bucket.bucket_list(1, 100, Some(owner_id3)), - (vec![], count) - ); -} - -#[ink::test] -fn bucket_set_availability_works() { - let ctx = &mut new_cluster(); - let test_bucket = &new_bucket(ctx); - let status = ctx.contract.bucket_get(test_bucket.bucket_id)?; - assert_eq!(status.bucket.public_availability, false); - - // Set public availability - ctx.contract - .bucket_set_availability(test_bucket.bucket_id, true); - - // Check the last event. - let ev = get_events().pop().unwrap(); - assert!(matches!(ev, Event::BucketAvailabilityUpdated(ev) if ev == - BucketAvailabilityUpdated { bucket_id: test_bucket.bucket_id, public_availability: true })); - - // Check the changed params. - let status = ctx.contract.bucket_get(test_bucket.bucket_id)?; - assert_eq!(status.bucket.public_availability, true); -} - -#[ink::test] -fn node_list_works() { - let mut ddc_bucket = setup(); - let accounts = get_accounts(); - let owner_id1 = accounts.alice; - let owner_id2 = accounts.bob; - let owner_id3 = accounts.charlie; - let rent_per_month: Balance = 10 * TOKEN; - - // Create two Nodes. - let node_params1 = "{\"url\":\"https://ddc-1.cere.network/bucket/{BUCKET_ID}\"}"; - let capacity = 100; - set_caller_value(owner_id1, CONTRACT_FEE_LIMIT); - let node_id1 = ddc_bucket.node_create( - rent_per_month, - node_params1.to_string(), - capacity, - NodeTag::ADDING, - owner_id1, - ); - - let node_params2 = "{\"url\":\"https://ddc-2.cere.network/bucket/{BUCKET_ID}\"}"; - set_caller_value(owner_id2, CONTRACT_FEE_LIMIT); - let node_id2 = ddc_bucket.node_create( - rent_per_month, - node_params2.to_string(), - capacity, - NodeTag::ADDING, - owner_id2, - ); - - let node_status = ddc_bucket.node_get_by_pubkey(owner_id1).unwrap(); - assert_eq!(owner_id1, node_status.node.provider_id.clone()); - - assert_ne!(node_id1, node_id2); - let count = 3; - - let node1 = NodeStatus { - node_id: node_id1, - node: Node { - provider_id: owner_id1, - rent_per_month, - free_resource: capacity, - node_tag: NodeTag::ADDING, - node_pub_key: owner_id1, - }, - params: node_params1.to_string(), - }; - - let node2 = NodeStatus { - node_id: node_id2, - node: Node { - provider_id: owner_id2, - rent_per_month, - free_resource: capacity, - node_tag: NodeTag::ADDING, - node_pub_key: owner_id2, - }, - params: node_params2.to_string(), - }; - - assert_eq!( - ddc_bucket.node_list(1, 100, None), - (vec![node1.clone(), node2.clone()], count) - ); - - assert_eq!( - ddc_bucket.node_list(1, 2, None), - (vec![node1.clone(), node2.clone()], count) - ); - - assert_eq!( - ddc_bucket.node_list(1, 1, None), - (vec![node1.clone() /*, node2.clone()*/], count) - ); - - assert_eq!( - ddc_bucket.node_list(2, 1, None), - (vec![/*node1.clone(),*/ node2.clone()], count) - ); - - assert_eq!(ddc_bucket.node_list(21, 20, None), (vec![], count)); - - // Filter by owner. - assert_eq!( - ddc_bucket.node_list(1, 100, Some(owner_id1)), - (vec![node1.clone() /*, node2.clone()*/], count) - ); - - assert_eq!( - ddc_bucket.node_list(1, 100, Some(owner_id2)), - (vec![/*node1.clone(),*/ node2.clone()], count) - ); - - assert_eq!( - ddc_bucket.node_list(1, 100, Some(owner_id3)), - (vec![], count) - ); -} diff --git a/bucket/ddc_bucket/tests/test_currency.rs b/bucket/ddc_bucket/tests/test_currency.rs index e64c5fef..a541a78c 100644 --- a/bucket/ddc_bucket/tests/test_currency.rs +++ b/bucket/ddc_bucket/tests/test_currency.rs @@ -1,21 +1,12 @@ use ink_lang as ink; -use crate::ddc_bucket::*; - use super::env_utils::*; - -fn setup() -> DdcBucket { - set_caller(admin_id()); - set_callee(contract_id()); - let mut contract = DdcBucket::new(); - set_balance(contract_id(), CONTRACT_FEE_LIMIT); - contract.admin_grant_permission(admin_id(), Permission::SetExchangeRate); - contract -} +use super::setup_utils::*; +use crate::ddc_bucket::*; #[ink::test] -fn currency_conversion_init_works() { - let contract = setup(); +fn currency_conversion_init_ok() { + let contract = setup_contract(); let usd_amount = contract.account_get_usd_per_cere(); println!("{}", usd_amount); assert_eq!( @@ -26,13 +17,13 @@ fn currency_conversion_init_works() { } #[ink::test] -fn currency_conversion_set_rate_works() { - let mut contract = setup(); +fn currency_conversion_set_rate_ok() { + let mut contract = setup_contract(); let usd_per_cere = TOKEN / 10; println!("{}", usd_per_cere); set_caller(admin_id()); - contract.account_set_usd_per_cere(usd_per_cere); + contract.account_set_usd_per_cere(usd_per_cere)?; assert_eq!( contract.account_get_usd_per_cere(), @@ -43,10 +34,40 @@ fn currency_conversion_set_rate_works() { #[ink::test] #[should_panic] -fn currency_conversion_set_rate_only_admin() { - let mut contract = setup(); +fn currency_conversion_set_rate_err_if_not_admin() { + let mut contract = setup_contract(); let not_admin = get_accounts().bob; set_caller(not_admin); - contract.account_set_usd_per_cere(9); + contract.account_set_usd_per_cere(9)?; +} + +#[ink::test] +fn converter_ok() { + // todo: this test scenario must be revised as it does pure printing without any assertion + println!("Creating new cdn cluster"); + + let mut ctx = setup_cluster(); + + // The provider stops trusting the manager_id. + println!("Cdn cluster id is {}", ctx.cluster_id); + set_caller(ctx.manager_id); + ctx.contract.cdn_set_rate(ctx.cluster_id, 3_750_000_000)?; + set_caller(ctx.provider_id0); + let rate = ctx.contract.cdn_get_rate(ctx.cluster_id)?; + + let usd_per_cere = TOKEN / 100; + set_caller(admin_id()); + ctx.contract.account_set_usd_per_cere(usd_per_cere)?; + + let usd_amount = ctx.contract.account_get_usd_per_cere(); + println!("Current usd amount is {}", usd_amount); + + println!("The current rate is {}", rate); + + let usd_per_kb = rate / KB_PER_GB; + println!("The current rate per kb {}", usd_per_kb); + + let cere_per_kb = ctx.contract.protocol.curr_converter.to_cere(usd_per_kb); + println!("The current cere rate per kb {}", cere_per_kb); } diff --git a/bucket/ddc_bucket/tests/test_node.rs b/bucket/ddc_bucket/tests/test_node.rs new file mode 100644 index 00000000..4e7ec42e --- /dev/null +++ b/bucket/ddc_bucket/tests/test_node.rs @@ -0,0 +1,282 @@ +use ink_lang as ink; + +use super::env_utils::*; +use super::setup_utils::*; +use crate::ddc_bucket::Error::*; +use crate::ddc_bucket::*; + +#[ink::test] +fn node_create_err_if_node_exists() { + let mut ctx = setup_cluster(); + assert_eq!( + ctx.contract.node_create( + ctx.node_key1, + ctx.node_params1, + ctx.node_capacity1, + ctx.rent_v_node_per_month1, + ), + Err(NodeAlreadyExists) + ); +} + +#[ink::test] +fn node_create_ok() { + let mut ctx = setup_cluster(); + + let new_provider_id = AccountId::from([ + 0x76, 0x30, 0xc6, 0x96, 0x6f, 0xd3, 0x26, 0xba, 0x1a, 0xa0, 0x6f, 0xd8, 0x7f, 0x7b, 0xf2, + 0xef, 0x14, 0x11, 0xf0, 0x0d, 0x00, 0xa9, 0xe7, 0x11, 0xdf, 0xd1, 0x65, 0x14, 0x5d, 0x01, + 0xdb, 0x59, + ]); + set_balance(new_provider_id, 1000 * TOKEN); + + let new_node_key = AccountId::from([ + 0xc4, 0xcd, 0xaa, 0xfa, 0xf1, 0x30, 0x7d, 0x23, 0xf4, 0x99, 0x84, 0x71, 0xdf, 0x78, 0x59, + 0xce, 0x06, 0x3d, 0xce, 0x78, 0x59, 0xc4, 0x3a, 0xe8, 0xef, 0x12, 0x0a, 0xbc, 0x43, 0xc4, + 0x84, 0x31, + ]); + let new_node_params = NodeParams::from("{\"url\":\"https://ddc-1.cere.network/storage/new\"}"); + let new_node_capacity = 100; + let new_node_rent_v_node_per_month: Balance = 10 * TOKEN; + + set_caller_value(new_provider_id, CONTRACT_FEE_LIMIT); + ctx.contract.node_create( + new_node_key, + new_node_params.clone(), + new_node_capacity, + new_node_rent_v_node_per_month, + )?; + + assert!( + matches!(get_events().pop().unwrap(), Event::NodeCreated(ev) if ev == + NodeCreated { + node_key: new_node_key, + provider_id: new_provider_id, + rent_v_node_per_month: new_node_rent_v_node_per_month, + node_params: new_node_params.clone() + }) + ); + + let node_info = ctx.contract.node_get(new_node_key)?; + let _expected_node_info = Node { + provider_id: new_provider_id, + rent_v_node_per_month: new_node_rent_v_node_per_month, + free_resource: new_node_capacity, + node_params: new_node_params, + cluster_id: None, + status_in_cluster: None, + }; + assert!(matches!(node_info.node, _expected_node_info)); +} + +#[ink::test] +fn node_remove_err_if_not_provider() { + let mut ctx = setup_cluster(); + + let not_provider_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_provider_id, 1000 * TOKEN); + + set_caller(not_provider_id); + assert_eq!( + ctx.contract.node_remove(ctx.node_key1), + Err(OnlyNodeProvider) + ); +} + +#[ink::test] +fn node_remove_err_if_node_in_cluster() { + let mut ctx = setup_cluster(); + + set_caller(ctx.provider_id1); + assert_eq!( + ctx.contract.node_remove(ctx.node_key1), + Err(NodeIsAddedToCluster(ctx.cluster_id)) + ); +} + +#[ink::test] +fn node_remove_ok() { + let mut ctx = setup_cluster(); + + set_caller(ctx.provider_id1); + ctx.contract + .cluster_remove_node(ctx.cluster_id, ctx.node_key1)?; + ctx.contract.node_remove(ctx.node_key1)?; + + assert!( + matches!(get_events().pop().unwrap(), Event::NodeRemoved(ev) if ev == + NodeRemoved { + node_key: ctx.node_key1, + } + ) + ); + + assert_eq!(ctx.contract.node_get(ctx.node_key1), Err(NodeDoesNotExist)); +} + +#[ink::test] +fn node_set_params_err_if_not_provider() { + let mut ctx = setup_cluster(); + + let not_provider = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + set_balance(not_provider, 1000 * TOKEN); + // Change params.not_provider + let new_node_params = NodeParams::from("new node params"); + set_caller_value(not_provider, CONTRACT_FEE_LIMIT); + + assert_eq!( + ctx.contract.node_set_params(ctx.node_key0, new_node_params), + Err(OnlyNodeProvider) + ); +} + +#[ink::test] +fn node_set_params_ok() { + let mut ctx = setup_cluster(); + + // Change params. + let new_node_params = NodeParams::from("new node params"); + set_caller_value(ctx.provider_id0, CONTRACT_FEE_LIMIT); + ctx.contract + .node_set_params(ctx.node_key0, new_node_params.clone())?; + + assert!( + matches!(get_events().pop().unwrap(), Event::NodeParamsSet(ev) if ev == + NodeParamsSet { + node_key: ctx.node_key0, + node_params: new_node_params.clone() + } + ) + ); + + // Check the changed params. + let node_info = ctx.contract.node_get(ctx.node_key0)?; + assert_eq!(node_info.node.node_params, new_node_params); +} + +#[ink::test] +fn node_get_err_if_node_does_not_exist() { + let ctx = setup_cluster(); + + let bad_node_key = AccountId::from([ + 0xf6, 0x8f, 0x06, 0xa8, 0x26, 0xba, 0xaf, 0x7f, 0xbd, 0x9b, 0xff, 0x3d, 0x1e, 0xec, 0xae, + 0xef, 0xc7, 0x7a, 0x01, 0x6d, 0x0b, 0xaf, 0x4c, 0x90, 0x55, 0x6e, 0x7b, 0x15, 0x73, 0x46, + 0x9c, 0x76, + ]); + + assert_eq!(ctx.contract.node_get(bad_node_key), Err(NodeDoesNotExist)); +} + +#[ink::test] +fn node_get_ok() { + let ctx = setup_cluster(); + + let v_nodes1_len: u32 = ctx.v_nodes1.len().try_into().unwrap(); + assert_eq!( + ctx.contract.node_get(ctx.node_key1), + Ok({ + NodeInfo { + node_key: ctx.node_key1, + node: Node { + provider_id: ctx.provider_id1, + rent_v_node_per_month: ctx.rent_v_node_per_month1, + free_resource: ctx.node_capacity1 - ctx.resource_per_v_node * v_nodes1_len, + node_params: ctx.node_params1, + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + }, + v_nodes: ctx.v_nodes1, + } + }) + ); +} + +#[ink::test] +fn node_list_ok() { + let ctx = setup_cluster(); + + let node_info = ctx.contract.node_get(ctx.node_key1)?; + assert_eq!(ctx.provider_id1, node_info.node.provider_id.clone()); + + let v_nodes1_len: u32 = ctx.v_nodes1.len().try_into().unwrap(); + let node1 = NodeInfo { + node_key: ctx.node_key1, + node: Node { + provider_id: ctx.provider_id1, + rent_v_node_per_month: ctx.rent_v_node_per_month1, + free_resource: ctx.node_capacity1 - ctx.resource_per_v_node * v_nodes1_len, + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + node_params: ctx.node_params1, + }, + v_nodes: ctx.v_nodes1.clone(), + }; + + let v_nodes2_len: u32 = ctx.v_nodes2.len().try_into().unwrap(); + let node2 = NodeInfo { + node_key: ctx.node_key2, + node: Node { + provider_id: ctx.provider_id2, + rent_v_node_per_month: ctx.rent_v_node_per_month2, + free_resource: ctx.node_capacity2 - ctx.resource_per_v_node * v_nodes2_len, + cluster_id: Some(ctx.cluster_id), + status_in_cluster: Some(NodeStatusInCluster::ADDING), + node_params: ctx.node_params2, + }, + v_nodes: ctx.v_nodes2.clone(), + }; + + let count = 3; + + assert_eq!( + ctx.contract.node_list(1, 100, None), + (vec![node1.clone(), node2.clone()], count) + ); + + assert_eq!( + ctx.contract.node_list(1, 2, None), + (vec![node1.clone(), node2.clone()], count) + ); + + assert_eq!( + ctx.contract.node_list(1, 1, None), + (vec![node1.clone()], count) + ); + + assert_eq!( + ctx.contract.node_list(2, 1, None), + (vec![node2.clone()], count) + ); + + assert_eq!(ctx.contract.node_list(21, 20, None), (vec![], count)); + + // Filter by owner. + assert_eq!( + ctx.contract.node_list(1, 100, Some(ctx.provider_id1)), + (vec![node1.clone()], count) + ); + + assert_eq!( + ctx.contract.node_list(1, 100, Some(ctx.provider_id2)), + (vec![node2.clone()], count) + ); + + let not_provider_id = AccountId::from([ + 0xee, 0x0a, 0xc9, 0x58, 0xa2, 0x0d, 0xe8, 0xda, 0x73, 0xb2, 0x05, 0xe9, 0xc6, 0x34, 0xa6, + 0xb2, 0x23, 0xcc, 0x54, 0x30, 0x24, 0x5d, 0x89, 0xb6, 0x4d, 0x83, 0x9b, 0x6d, 0xca, 0xc4, + 0xf8, 0x6d, + ]); + + assert_eq!( + ctx.contract.node_list(1, 100, Some(not_provider_id)), + (vec![], count) + ); +} diff --git a/bucket/ddc_bucket/topology/messages.rs b/bucket/ddc_bucket/topology/messages.rs new file mode 100644 index 00000000..40f6f902 --- /dev/null +++ b/bucket/ddc_bucket/topology/messages.rs @@ -0,0 +1,22 @@ +//! The public interface to manage Nodes. + +use crate::ddc_bucket::{ClusterId, DdcBucket, NodeKey, Result, VNodeToken}; +use ink_prelude::vec::Vec; + +impl DdcBucket { + pub fn message_get_v_nodes_by_cluster(&self, cluster_id: ClusterId) -> Vec { + self.topology.get_v_nodes_by_cluster(cluster_id) + } + + pub fn message_get_v_nodes_by_node(&self, node_key: NodeKey) -> Vec { + self.topology.get_v_nodes_by_node(node_key) + } + + pub fn message_get_node_by_v_node( + &self, + cluster_id: ClusterId, + v_node: VNodeToken, + ) -> Result { + self.topology.get_node_by_v_node(cluster_id, v_node) + } +} diff --git a/bucket/ddc_bucket/topology/mod.rs b/bucket/ddc_bucket/topology/mod.rs index 79be79e2..b05b5b88 100644 --- a/bucket/ddc_bucket/topology/mod.rs +++ b/bucket/ddc_bucket/topology/mod.rs @@ -1,3 +1,4 @@ //! cluster topology management. +pub mod messages; pub mod store; diff --git a/bucket/ddc_bucket/topology/store.rs b/bucket/ddc_bucket/topology/store.rs index 8180c1c3..4edc5e7d 100644 --- a/bucket/ddc_bucket/topology/store.rs +++ b/bucket/ddc_bucket/topology/store.rs @@ -1,95 +1,209 @@ //! The store where to create and access Nodes. use ink_prelude::vec::Vec; +use ink_storage::traits::{SpreadAllocate, SpreadLayout}; use ink_storage::Mapping; -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; use crate::ddc_bucket::cluster::entity::ClusterId; -use crate::ddc_bucket::node::entity::Node; -use crate::ddc_bucket::Error::UnknownNode; -use crate::ddc_bucket::{Balance, NodeId, Result}; +use crate::ddc_bucket::node::entity::NodeKey; +use crate::ddc_bucket::{Error::*, Result}; + +pub type VNodeToken = u64; +pub type ClusterVNode = (ClusterId, VNodeToken); + +// https://use.ink/datastructures/storage-layout#packed-vs-non-packed-layout +// There is a buffer with only limited capacity (around 16KB in the default configuration) available. +pub const MAX_V_NODES_LEN_IN_VEC: usize = 1800; #[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug,))] -pub struct TopologyStore(Mapping<(ClusterId, u64), NodeId>); +#[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout, Debug))] +pub struct TopologyStore { + // virtual nodes assigned to a physical node + v_nodes_map: Mapping>, + // physical node assigned to a virtual node + nodes_map: Mapping, + // virtual nodes within a cluster assigned to all its physical nodes + cluster_v_nodes_map: Mapping>, +} impl TopologyStore { + pub fn get_v_nodes_by_cluster(&self, cluster_id: ClusterId) -> Vec { + self.cluster_v_nodes_map + .get(cluster_id) + .unwrap_or(Vec::new()) + } + + pub fn get_v_nodes_by_node(&self, node_key: NodeKey) -> Vec { + self.v_nodes_map.get(node_key).unwrap_or(Vec::new()) + } + + pub fn get_node_by_v_node(&self, cluster_id: ClusterId, v_node: VNodeToken) -> Result { + self.nodes_map + .get((cluster_id, v_node)) + .ok_or(VNodeIsNotAssignedToNode(cluster_id, v_node)) + } + + pub fn v_node_has_node(&self, cluster_id: ClusterId, v_node: VNodeToken) -> bool { + self.nodes_map.contains((cluster_id, v_node)) + } - pub fn create_topology( + pub fn create_topology(&mut self, cluster_id: ClusterId) -> Result<()> { + if self.cluster_v_nodes_map.contains(&cluster_id) { + Err(TopologyAlreadyExists) + } else { + let cluster_v_nodes: Vec = Vec::new(); + self.cluster_v_nodes_map + .insert(cluster_id, &cluster_v_nodes); + Ok(()) + } + } + + pub fn add_node( &mut self, cluster_id: ClusterId, - v_nodes: Vec>, - nodes: Vec<(NodeId, &Node)>, - ) -> Result { - let mut total_rent = 0u128; - let mut vnodes_wrapper_index = 0; - - for node in &nodes { - let v_nodes_for_node = &v_nodes[vnodes_wrapper_index as usize]; - for v_node in v_nodes_for_node.iter() { - self.0.insert((cluster_id, *v_node), &(node.0)); - - total_rent += node.1.rent_per_month as Balance; + node_key: NodeKey, + v_nodes: Vec, + ) -> Result<()> { + if v_nodes.is_empty() { + return Err(AtLeastOneVNodeHasToBeAssigned(cluster_id, node_key)); + } + + if v_nodes.len() > MAX_V_NODES_LEN_IN_VEC { + return Err(VNodesSizeExceedsLimit); + } + + let mut cluster_v_nodes = self.get_v_nodes_by_cluster(cluster_id); + + if cluster_v_nodes.len() + v_nodes.len() > MAX_V_NODES_LEN_IN_VEC { + return Err(VNodesSizeExceedsLimit); + } + + for v_node in &v_nodes { + // vnode that is being added should not exist in the cluster topology + if self.v_node_has_node(cluster_id, *v_node) { + return Err(VNodeIsAlreadyAssignedToNode(node_key)); } - vnodes_wrapper_index += 1; + // vnode that is being added should be assigned to the physical node + self.nodes_map.insert((cluster_id, v_node), &node_key); + + // vnode that is being added should be added to the cluster topology + cluster_v_nodes.push(*v_node); } - Ok(total_rent) + self.cluster_v_nodes_map + .insert(cluster_id, &cluster_v_nodes); + + // vnodes that are being added should be assigned to the physical node + self.v_nodes_map.insert(node_key, &v_nodes); + + Ok(()) } - pub fn replace_node( - &mut self, - cluster_id: u32, - v_nodes: Vec, - new_node_id: NodeId, - ) -> Result<()> { - for v_node in v_nodes { - if self.0.contains(&(cluster_id, v_node)) { - self.0.insert(&(cluster_id, v_node), &new_node_id); - } else { - return Err(UnknownNode) + pub fn remove_node(&mut self, cluster_id: ClusterId, node_key: NodeKey) -> Result<()> { + let mut cluster_v_nodes = self.get_v_nodes_by_cluster(cluster_id); + let v_nodes = self.get_v_nodes_by_node(node_key); + + for v_node in &v_nodes { + // vnode that is being removed should exist in the cluster topology + if !self.v_node_has_node(cluster_id, *v_node) { + return Err(VNodeIsNotAssignedToNode(cluster_id, *v_node)); } + + // vnode that is being removed should be unusigned from the physical node + self.nodes_map.remove((cluster_id, v_node)); + + // vnode that is being removed should be removed from the cluster topology + if let Some(pos) = cluster_v_nodes.iter().position(|x| *x == *v_node) { + cluster_v_nodes.remove(pos); + }; } + self.cluster_v_nodes_map + .insert(cluster_id, &cluster_v_nodes); + + // vnodes that are being removed should be unusigned from the physical node + self.v_nodes_map.remove(node_key); + Ok(()) } - pub fn add_node( + pub fn replace_node( &mut self, - cluster_id: u32, - old_v_nodes: &Vec, - v_nodes: &Vec>, - nodes: Vec<(NodeId, &Node)>, - ) -> Result { - // remove old nodes from topology - for &old_v_node in old_v_nodes { - self.0.insert((cluster_id, old_v_node), &0); + cluster_id: ClusterId, + new_node_key: NodeKey, + v_nodes_to_reasign: Vec, + ) -> Result<()> { + if v_nodes_to_reasign.is_empty() { + return Err(AtLeastOneVNodeHasToBeAssigned(cluster_id, new_node_key)); } - let mut total_rent = 0u32; - let mut index = 0u32; + if v_nodes_to_reasign.len() > MAX_V_NODES_LEN_IN_VEC { + return Err(VNodesSizeExceedsLimit); + } + + let cluster_v_nodes = self.get_v_nodes_by_cluster(cluster_id); + + for v_node in &v_nodes_to_reasign { + // vnode that is being reasigned should be in the cluster topology + if None == cluster_v_nodes.iter().position(|x| *x == *v_node) { + return Err(VNodeDoesNotExistsInCluster(cluster_id)); + }; + + // vnode that is being reasigned should be already assigned to a physical node + let old_node_key = self.get_node_by_v_node(cluster_id, *v_node)?; - // reassign v_nodes to physical ones - for node in nodes { - let v_nodes_for_node = &v_nodes[index as usize]; + // vnode that is being reasigned should be among other vnodes assigned to the old physical node + let mut old_node_v_nodes = self.get_v_nodes_by_node(old_node_key); - for v_node in v_nodes_for_node.iter() { - self.0.insert((cluster_id, *v_node), &(node.0)); + // vnode that is being reasigned should be unasigned from the old physical node + if let Some(pos) = old_node_v_nodes.iter().position(|x| *x == *v_node) { + old_node_v_nodes.remove(pos); + }; - total_rent += node.1.rent_per_month as u32; + if old_node_v_nodes.is_empty() { + return Err(AtLeastOneVNodeHasToBeAssigned(cluster_id, old_node_key)); } - index += 1; + self.v_nodes_map.insert(old_node_key, &old_node_v_nodes); + + // vnode that is being reasigned should be assined to the new physical node + self.nodes_map.insert(&(cluster_id, *v_node), &new_node_key); + } + + // vnodes that are being reasigned should be among other vnodes assigned to the new physical node + let mut new_node_v_nodes = self.get_v_nodes_by_node(new_node_key); + + if new_node_v_nodes.len() + v_nodes_to_reasign.len() > MAX_V_NODES_LEN_IN_VEC { + return Err(VNodesSizeExceedsLimit); } - Ok(total_rent) + new_node_v_nodes.extend(v_nodes_to_reasign); + self.v_nodes_map.insert(new_node_key, &new_node_v_nodes); + + Ok(()) } - pub fn get(&self, cluster_id: ClusterId, v_node: u64) -> Result { - self.0.get((cluster_id, v_node)).ok_or(UnknownNode) + pub fn reset_node( + &mut self, + cluster_id: ClusterId, + node_key: NodeKey, + new_v_nodes: Vec, + ) -> Result<()> { + self.remove_node(cluster_id, node_key)?; + self.add_node(cluster_id, node_key, new_v_nodes) } - pub fn save(&mut self, cluster_id: ClusterId, v_node: u64, node_id: NodeId) { - self.0.insert(&(cluster_id, v_node), &node_id) + pub fn remove_topology(&mut self, cluster_id: ClusterId) -> Result<()> { + let cluster_v_nodes: Vec = self.get_v_nodes_by_cluster(cluster_id); + + for v_node in &cluster_v_nodes { + let node_key = self.get_node_by_v_node(cluster_id, *v_node)?; + self.nodes_map.remove((cluster_id, v_node)); + self.v_nodes_map.remove(node_key); + } + + self.cluster_v_nodes_map.remove(cluster_id); + + Ok(()) } } diff --git a/bucket/lib.rs b/bucket/lib.rs index 7f30adb1..f6463639 100755 --- a/bucket/lib.rs +++ b/bucket/lib.rs @@ -8,7 +8,6 @@ use ink_lang as ink; #[ink::contract] pub mod ddc_bucket { - use ink_prelude::string::ToString; use ink_prelude::vec::Vec; use scale::{Decode, Encode}; @@ -19,36 +18,27 @@ pub mod ddc_bucket { use committer::store::*; use ink_storage::traits::SpreadAllocate; use node::{entity::*, store::*}; - use params::store::*; use perm::store::*; + use topology::store::*; - use crate::ddc_bucket::account::entity::Account; - use crate::ddc_bucket::cdn_cluster::entity::CdnClusterStatus; use crate::ddc_bucket::cdn_node::store::CdnNodeStore; - use crate::ddc_bucket::committer::store::EraConfig; - use crate::ddc_bucket::network_fee::{FeeConfig, NetworkFeeStore}; use crate::ddc_bucket::perm::entity::Permission; - use self::buckets_perms::store::BucketsPermsStore; - use self::cdn_cluster::store::CdnClusterStore; - use self::cdn_node::entity::CdnNodeStatus; - use self::protocol::store::ProtocolStore; + use self::account::entity::Account; + use self::cdn_node::entity::{CdnNodeInfo, CdnNodeKey, CdnNodeParams}; + use self::protocol::store::{NetworkFeeConfig, ProtocolStore}; use self::topology::store::TopologyStore; pub mod account; pub mod admin; pub mod bucket; - pub mod buckets_perms; pub mod cash; - pub mod cdn_cluster; pub mod cdn_node; pub mod cluster; pub mod committer; pub mod currency; pub mod flow; - pub mod network_fee; pub mod node; - pub mod params; pub mod perm; pub mod protocol; pub mod schedule; @@ -61,20 +51,13 @@ pub mod ddc_bucket { pub struct DdcBucket { perms: PermStore, buckets: BucketStore, - buckets_perms: BucketsPermsStore, - bucket_params: ParamsStore, clusters: ClusterStore, - cdn_clusters: CdnClusterStore, - cluster_params: ParamsStore, cdn_nodes: CdnNodeStore, - cdn_node_params: ParamsStore, nodes: NodeStore, - node_params: ParamsStore, + topology: TopologyStore, accounts: AccountStore, - network_fee: NetworkFeeStore, - committer_store: CommitterStore, - protocol_store: ProtocolStore, - topology_store: TopologyStore, + committer: CommitterStore, + protocol: ProtocolStore, } impl DdcBucket { @@ -84,39 +67,18 @@ pub mod ddc_bucket { #[ink(constructor)] pub fn new() -> Self { ink_lang::utils::initialize_contract(|contract: &mut Self| { - let admin_id = Self::env().caller(); - // Make the creator of this contract a super-admin. - contract.perms.grant_permission(admin_id, &Permission::SuperAdmin); - - contract.committer_store.init(admin_id); - contract.protocol_store.init(admin_id, DEFAULT_BASIS_POINTS); - - // Reserve IDs 0. - let _ = contract.accounts.create_if_not_exist(AccountId::default()); - let _ = contract.cdn_nodes.create(AccountId::default(), 0, AccountId::default()); - let _ = contract.cdn_node_params.create("".to_string()).unwrap(); - let _ = contract - .nodes - .create( - AccountId::default(), - 0, - 0, - NodeTag::ACTIVE, - AccountId::default(), - ) - .unwrap(); - let _ = contract.node_params.create("".to_string()).unwrap(); - let _ = contract - .clusters - .create( - AccountId::default(), - &Vec::>::new(), - &Vec::::new(), - ) - .unwrap(); - let _ = contract.cluster_params.create("".to_string()).unwrap(); - let _ = contract.buckets.create(AccountId::default(), 0); - let _ = contract.bucket_params.create("".to_string()).unwrap(); + let admin = Self::env().caller(); + contract + .perms + .grant_permission(admin, Permission::SuperAdmin); + contract.committer.init(admin); + contract.protocol.init( + DEFAULT_PROTOCOL_FEE_BP, + admin, + DEFAULT_NETWORK_FEE_BP, + admin, + DEFAULT_CLUSTER_FEE_BP, + ); }) } } @@ -165,6 +127,14 @@ pub mod ddc_bucket { public_availability: bool, } + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct BucketParamsSet { + #[ink(topic)] + bucket_id: BucketId, + bucket_params: BucketParams, + } + impl DdcBucket { /// Create a new bucket and return its `bucket_id`. /// @@ -179,42 +149,50 @@ pub mod ddc_bucket { bucket_params: BucketParams, cluster_id: ClusterId, owner_id: Option, - ) -> BucketId { + ) -> Result { self.message_bucket_create(bucket_params, cluster_id, owner_id) - .unwrap() } /// Change owner of the bucket /// /// Provide the account of new owner #[ink(message, payable)] - pub fn bucket_change_owner(&mut self, bucket_id: BucketId, owner_id: AccountId) -> () { + pub fn bucket_change_owner( + &mut self, + bucket_id: BucketId, + owner_id: AccountId, + ) -> Result<()> { self.message_bucket_change_owner(bucket_id, owner_id) - .unwrap() } /// Allocate some resources of a cluster to a bucket. /// /// The amount of resources is given per vnode (total resources will be `resource` times the number of vnodes). #[ink(message)] - pub fn bucket_alloc_into_cluster(&mut self, bucket_id: BucketId, resource: Resource) -> () { + pub fn bucket_alloc_into_cluster( + &mut self, + bucket_id: BucketId, + resource: Resource, + ) -> Result<()> { self.message_bucket_alloc_into_cluster(bucket_id, resource) - .unwrap() } /// Settle the due costs of a bucket from its payer account to the cluster account. #[ink(message)] - pub fn bucket_settle_payment(&mut self, bucket_id: BucketId) { - self.message_bucket_settle_payment(bucket_id).unwrap() + pub fn bucket_settle_payment(&mut self, bucket_id: BucketId) -> Result<()> { + self.message_bucket_settle_payment(bucket_id) } /// Change the `bucket_params`, which is configuration used by clients and nodes. /// /// See the [data structure of BucketParams](https://docs.cere.network/ddc/protocols/contract-params-schema) #[ink(message, payable)] - pub fn bucket_change_params(&mut self, bucket_id: BucketId, params: BucketParams) { + pub fn bucket_change_params( + &mut self, + bucket_id: BucketId, + params: BucketParams, + ) -> Result<()> { self.message_bucket_change_params(bucket_id, params) - .unwrap(); } /// Get the current status of a bucket. @@ -253,9 +231,8 @@ pub mod ddc_bucket { &mut self, bucket_id: BucketId, public_availability: bool, - ) -> () { + ) -> Result<()> { self.message_bucket_set_availability(bucket_id, public_availability) - .unwrap() } /// Set max resource cap to be charged by CDN for public bucket @@ -264,66 +241,148 @@ pub mod ddc_bucket { &mut self, bucket_id: BucketId, new_resource_cap: Resource, - ) -> () { + ) -> Result<()> { self.message_bucket_set_resource_cap(bucket_id, new_resource_cap) - .unwrap() } /// Set permission for the reader of the bucket #[ink(message)] pub fn get_bucket_writers(&mut self, bucket_id: BucketId) -> Vec { - self.message_get_bucket_writers(bucket_id).unwrap() + self.message_get_bucket_writers(bucket_id) } /// Set permission for the writer of the bucket #[ink(message)] - pub fn bucket_set_writer_perm(&mut self, bucket_id: BucketId, writer: AccountId) -> () { + pub fn bucket_set_writer_perm( + &mut self, + bucket_id: BucketId, + writer: AccountId, + ) -> Result<()> { self.message_grant_writer_permission(bucket_id, writer) - .unwrap() } /// Revoke permission for the writer of the bucket #[ink(message)] - pub fn bucket_revoke_writer_perm(&mut self, bucket_id: BucketId, writer: AccountId) -> () { + pub fn bucket_revoke_writer_perm( + &mut self, + bucket_id: BucketId, + writer: AccountId, + ) -> Result<()> { self.message_revoke_writer_permission(bucket_id, writer) - .unwrap() } /// Set permission for the reader of the bucket #[ink(message)] pub fn get_bucket_readers(&mut self, bucket_id: BucketId) -> Vec { - self.message_get_bucket_readers(bucket_id).unwrap() + self.message_get_bucket_readers(bucket_id) } /// Set permission for the reader of the bucket #[ink(message)] - pub fn bucket_set_reader_perm(&mut self, bucket_id: BucketId, reader: AccountId) -> () { + pub fn bucket_set_reader_perm( + &mut self, + bucket_id: BucketId, + reader: AccountId, + ) -> Result<()> { self.message_grant_reader_permission(bucket_id, reader) - .unwrap() } /// Revoke permission for the reader of the bucket #[ink(message)] - pub fn bucket_revoke_reader_perm(&mut self, bucket_id: BucketId, writer: AccountId) -> () { + pub fn bucket_revoke_reader_perm( + &mut self, + bucket_id: BucketId, + writer: AccountId, + ) -> Result<()> { self.message_revoke_reader_permission(bucket_id, writer) - .unwrap() } } // ---- End Bucket ---- // ---- Cluster ---- - /// A new cluster was created. #[ink(event)] #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] pub struct ClusterCreated { #[ink(topic)] cluster_id: ClusterId, #[ink(topic)] - manager: AccountId, + manager_id: AccountId, cluster_params: ClusterParams, } + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct ClusterNodeAdded { + #[ink(topic)] + cluster_id: ClusterId, + #[ink(topic)] + node_key: NodeKey, + v_nodes: Vec, + } + + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct ClusterNodeRemoved { + #[ink(topic)] + cluster_id: ClusterId, + #[ink(topic)] + node_key: NodeKey, + } + + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct ClusterCdnNodeAdded { + #[ink(topic)] + cluster_id: ClusterId, + #[ink(topic)] + cdn_node_key: CdnNodeKey, + } + + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct ClusterCdnNodeRemoved { + #[ink(topic)] + cluster_id: ClusterId, + #[ink(topic)] + cdn_node_key: CdnNodeKey, + } + + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct ClusterParamsSet { + #[ink(topic)] + cluster_id: ClusterId, + cluster_params: ClusterParams, + } + + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct ClusterRemoved { + #[ink(topic)] + cluster_id: ClusterId, + } + + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct ClusterNodeStatusSet { + #[ink(topic)] + node_key: NodeKey, + #[ink(topic)] + cluster_id: ClusterId, + status: NodeStatusInCluster, + } + + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct ClusterCdnNodeStatusSet { + #[ink(topic)] + cdn_node_key: CdnNodeKey, + #[ink(topic)] + cluster_id: ClusterId, + status: NodeStatusInCluster, + } + /// A vnode was re-assigned to new node. #[ink(event)] #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] @@ -331,7 +390,19 @@ pub mod ddc_bucket { #[ink(topic)] cluster_id: ClusterId, #[ink(topic)] - node_id: NodeId, + node_key: NodeKey, + v_nodes: Vec, + } + + /// A vnode was re-assigned to new node. + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct ClusterNodeReset { + #[ink(topic)] + cluster_id: ClusterId, + #[ink(topic)] + node_key: NodeKey, + v_nodes: Vec, } /// Some resources were reserved for the cluster from the nodes. @@ -353,250 +424,548 @@ pub mod ddc_bucket { provider_id: AccountId, } + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct ClusterDistributeCdnRevenues { + #[ink(topic)] + cluster_id: ClusterId, + #[ink(topic)] + provider_id: AccountId, + } + impl DdcBucket { - /// Removes a node to an existing cluster + /// Creates a cluster of Storage nodes and CDN nodes. + /// + /// This endpoint creates a cluster of Storage nodes and CDN nodes with specific parameters. + /// The caller will be the cluster manager (cluster owner). In order to add a Storage or CDN node, the manager must be authorized by the node owner using the `trust_manager` endpoint or be the node owner. + /// + /// # Parameters + /// + /// * `cluster_params` - [Cluster parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#cluster-parameters) in protobuf format. + /// * `resource_per_v_node` - Resource value that will be allocated for every virtual node in the cluster. + /// + /// # Output + /// + /// Returns ID of the created cluster. + /// + /// # Events + /// + /// * `ClusterCreated` event on successful cluster creation. /// - /// The caller will be its first manager. + /// # Errors + /// + /// * `InvalidClusterParams` error if there is an invalid cluster parameter. #[ink(message, payable)] - pub fn cluster_remove_node( + pub fn cluster_create( &mut self, - cluster_id: ClusterId, - node_ids: Vec, - v_nodes: Vec>, - ) { - self.message_cluster_add_node(cluster_id, node_ids, v_nodes) - .unwrap() + cluster_params: ClusterParams, + resource_per_v_node: Resource, + ) -> Result { + self.message_cluster_create(cluster_params, resource_per_v_node) } - /// Adds node to an existing cluster + /// Adds a Storage node to the targeting cluster. + /// + /// This endpoint adds a physical Storage node along with its virtual nodes to the targeting cluster. + /// Virtual nodes determines a token (position) on the ring in terms of Consistent Hashing. + /// The Storage node can be added to the cluster by cluster manager only. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// * `node_key` - Public Key associated with the Storage node. + /// * `v_nodes` - List of tokens (positions) related to the Storage node. /// - /// The caller will be its first manager. + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterNodeAdded` event on successful Storage node addition. + /// + /// # Errors + /// + /// * `ClusterDoesNotExist` error if the cluster does not exist. + /// * `OnlyTrustedClusterManager` error if the caller is not a trusted cluster manager. + /// * `NodeDoesNotExist` error if the adding Storage node does not exist. + /// * `NodeIsAddedToCluster(ClusterId)` error if the adding Storage node is already added to this or another cluster. + /// * `AtLeastOneVNodeHasToBeAssigned(ClusterId, NodeKey)` error if there is a Storage node without any virtual nodes in the cluster. + /// * `VNodesSizeExceedsLimit` error if virtual nodes length exceeds storage capacity. + /// * `InsufficientNodeResources` - error if there is not enough resources in a physical node. #[ink(message, payable)] pub fn cluster_add_node( &mut self, cluster_id: ClusterId, - node_ids: Vec, - v_nodes: Vec>, - ) { - self.message_cluster_add_node(cluster_id, node_ids, v_nodes) - .unwrap() + node_key: NodeKey, + v_nodes: Vec, + ) -> Result<()> { + self.message_cluster_add_node(cluster_id, node_key, v_nodes) + } + + /// Removes a Storage node from the targeting cluster. + /// + /// This endpoint removes a physical Storage node along with its virtual nodes from the targeting cluster. + /// The Storage node can be removed from the cluster either by cluster manager or by the node owner. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// * `node_key` - Public Key associated with the Storage node. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterNodeRemoved` event on successful Storage node removal. + /// + /// # Errors + /// + /// * `ClusterDoesNotExist` error if the cluster does not exist. + /// * `OnlyClusterManagerOrNodeProvider` error if the caller is not the cluster manager or node owner. + /// * `NodeDoesNotExist` error if the removing Storage node does not exist. + /// * `NodeIsNotAddedToCluster(ClusterId)` error if the removing Storage node is not in this cluster. + #[ink(message)] + pub fn cluster_remove_node( + &mut self, + cluster_id: ClusterId, + node_key: NodeKey, + ) -> Result<()> { + self.message_cluster_remove_node(cluster_id, node_key) + } + + /// Reasignes existing virtual nodes in the targeting cluster. + /// + /// This endpoint reasignes existing virtual nodes to another physical Storage node within the same cluster. + /// All specifying virtual nodes must pre-exist in the cluster and the new physical Storage node must be added to the cluster preliminary. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// * `v_nodes` - List of tokens (positions) to reasign for the new physical Storage node. + /// * `new_node_key` - Public Key associated with the Storage node that is being reasigned to the specified virtual nodes. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterNodeReplaced` event on successful virtual node reassignment. + /// + /// # Errors + /// + /// * `ClusterDoesNotExist` error if the cluster does not exist. + /// * `OnlyClusterManager` error if the caller is not the cluster manager. + /// * `NodeDoesNotExist` error if the new Storage node does not exist. + /// * `NodeIsNotAddedToCluster(ClusterId)` error if the new Storage node is not added to this cluster. + /// * `NodeIsAddedToCluster(ClusterId)` error if the new Storage node is in another cluster. + /// * `VNodeIsNotAssignedToNode(ClusterId, VNodeToken)` error if the there is some virtual node that is being reasigned, but this virtual node is not assigned to any physical node. + /// * `VNodeIsAlreadyAssignedToNode(NodeKey)` - error if there is some virtual node that is already assigned to other physical node within the same cluster. + /// * `AtLeastOneVNodeHasToBeAssigned(ClusterId, NodeKey)` error if there is a Storage node without any virtual nodes in the cluster. + /// * `VNodesSizeExceedsLimit` error if virtual nodes length exceeds storage capacity. + #[ink(message)] + pub fn cluster_replace_node( + &mut self, + cluster_id: ClusterId, + v_nodes: Vec, + new_node_key: NodeKey, + ) -> Result<()> { + self.message_cluster_replace_node(cluster_id, v_nodes, new_node_key) } - /// Create a new cluster and return its `cluster_id`. + /// Reeset a Storage node in the targeting cluster. /// - /// The caller will be its first manager. + /// This endpoint resets virtual nodes on a physical Storage node in the targeting cluster. + /// Virtual nodes determines a token (position) on the ring in terms of Consistent Hashing. + /// The Storage node can be reset in the cluster by cluster manager only. /// - /// The cluster is split in a number of vnodes. The vnodes are assigned to the given physical nodes in a round-robin. Only nodes of providers that trust the cluster manager can be used (see `node_trust_manager`). The assignment can be changed with the function `cluster_replace_node`. + /// # Parameters /// - /// `cluster_params` is configuration used by clients and nodes. In particular, this describes the semantics of vnodes. See the [data structure of ClusterParams](https://docs.cere.network/ddc/protocols/contract-params-schema) + /// * `cluster_id` - ID of the targeting cluster. + /// * `node_key` - Public Key associated with the Storage node. + /// * `new_v_nodes` - List of tokens (positions) related to the Storage node to reset. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterNodeAdded` event on successful Storage node addition. + /// + /// # Errors + /// + /// * `ClusterDoesNotExist` error if the cluster does not exist. + /// * `OnlyTrustedClusterManager` error if the caller is not a trusted cluster manager. + /// * `NodeDoesNotExist` error if the adding Storage node does not exist. + /// * `NodeIsAddedToCluster(ClusterId)` error if the adding Storage node is already added to this or another cluster. + /// * `AtLeastOneVNodeHasToBeAssigned(ClusterId, NodeKey)` error if there is a Storage node without any virtual nodes in the cluster. + /// * `VNodesSizeExceedsLimit` error if virtual nodes length exceeds storage capacity. + /// * `InsufficientNodeResources` - error if there is not enough resources in a physical node. + #[ink(message)] + pub fn cluster_reset_node( + &mut self, + cluster_id: ClusterId, + node_key: NodeKey, + new_v_nodes: Vec, + ) -> Result<()> { + self.message_cluster_reset_node(cluster_id, node_key, new_v_nodes) + } + + /// Adds a CDN node to the targeting cluster. + /// + /// This endpoint adds a CDN node to the targeting cluster. + /// The CDN node can be added to the cluster by cluster manager only. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// * `cdn_node_key` - Public Key associated with the CDN node. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterCdnNodeAdded` event on successful CDN node addition. + /// + /// # Errors + /// + /// * `ClusterDoesNotExist` error if the cluster does not exist. + /// * `OnlyTrustedClusterManager` error if the caller is not a trusted cluster manager. + /// * `CdnNodeDoesNotExist` error if the adding CDN node does not exist. + /// * `CdnNodeIsAddedToCluster(ClusterId)` error if the adding CDN node is already added to this or another cluster. #[ink(message, payable)] - pub fn cluster_create( + pub fn cluster_add_cdn_node( &mut self, - _unused: AccountId, - v_nodes: Vec>, - node_ids: Vec, - cluster_params: ClusterParams, - ) -> ClusterId { - self.message_cluster_create(v_nodes, node_ids, cluster_params) - .unwrap() + cluster_id: ClusterId, + cdn_node_key: CdnNodeKey, + ) -> Result<()> { + self.message_cluster_add_cdn_node(cluster_id, cdn_node_key) } - /// As manager, reserve more resources for the cluster from the free capacity of nodes. + /// Removes a CDN node from the targeting cluster. /// - /// The amount of resources is given per vnode (total resources will be `resource` times the number of vnodes). + /// This endpoint removes a CDN node the targeting cluster. + /// The CDN node can be removed from the cluster either by cluster manager or by the node owner. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// * `cdn_node_key` - Public Key associated with the CDN node. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterCdnNodeRemoved` event on successful CDN node removal. + /// + /// # Errors + /// + /// * `ClusterDoesNotExist` error if the cluster does not exist. + /// * `OnlyClusterManagerOrCdnNodeProvider` error if the caller is not the cluster manager or node owner. + /// * `CdnNodeDoesNotExist` error if the removing CDN node does not exist. + /// * `CdnNodeIsNotAddedToCluster(ClusterId)` error if the removing CDN node is not in this cluster. #[ink(message)] - pub fn cluster_reserve_resource(&mut self, cluster_id: ClusterId, amount: Resource) -> () { - self.message_cluster_reserve_resource(cluster_id, amount) - .unwrap() + pub fn cluster_remove_cdn_node( + &mut self, + cluster_id: ClusterId, + cdn_node_key: CdnNodeKey, + ) -> Result<()> { + self.message_cluster_remove_cdn_node(cluster_id, cdn_node_key) + } + + /// Sets parameters for the targeting cluster. + /// + /// This endpoint updates [cluster parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#cluster-parameters) in protobuf format. + /// All cluster parameters must be specified as the endpoint works using SET approach. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// * `cluster_params` - [Cluster parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#cluster-parameters) in protobuf format. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterParamsSet` event on successful cluster params setting. + /// + /// # Errors + /// + /// * `OnlyClusterManager` error if the caller is not the cluster manager. + /// * `ClusterDoesNotExist` error if the cluster does not exist. + #[ink(message, payable)] + pub fn cluster_set_params( + &mut self, + cluster_id: ClusterId, + cluster_params: ClusterParams, + ) -> Result<()> { + self.message_cluster_set_params(cluster_id, cluster_params) } - /// As manager, change a node tag + /// Removes a cluster. + /// + /// This endpoint removes the cluster if it does not contain any nodes. + /// Only an empty cluster can be removed. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterRemoved` event on successful cluster removal. + /// + /// # Errors + /// + /// * `OnlyClusterManager` error if the caller is not the cluster manager. + /// * `ClusterDoesNotExist` error if the cluster does not exist. + /// * `ClusterIsNotEmpty` error if the removing cluster contains some Storage or CDN nodes. #[ink(message)] - pub fn cluster_change_node_tag(&mut self, node_id: NodeId, new_tag: NodeTag) -> () { - self.message_node_change_tag(node_id, new_tag).unwrap() + pub fn cluster_remove(&mut self, cluster_id: ClusterId) -> Result<()> { + self.message_cluster_remove(cluster_id) } - /// As manager, re-assign a vnode to another physical node. + /// Changes Storage node status. + /// + /// This endpoint changes Storage node status in a cluster. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// * `node_key` - Public Key associated with the Storage node. + /// * `status` - Status for the targeting Storage node, can be one of the following: ACTIVE, ADDING, DELETING, OFFLINE. + /// + /// # Output /// - /// The cluster manager can only use nodes of providers that trust him (see `node_trust_manager`), or any nodes if he is also SuperAdmin. + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterNodeStatusSet` event on successful Storage status change. + /// + /// # Errors + /// + /// * `OnlyClusterManager` error if the caller is not the cluster manager. + /// * `ClusterDoesNotExist` error if the cluster does not exist. + /// * `NodeIsNotAddedToCluster(ClusterId)` error if the Storage node is not in this cluster. #[ink(message)] - pub fn cluster_replace_node( + pub fn cluster_set_node_status( &mut self, cluster_id: ClusterId, - v_nodes: Vec, - new_node_id: NodeId, - ) -> () { - self.message_cluster_replace_node(cluster_id, v_nodes, new_node_id) - .unwrap() + node_key: NodeKey, + status_in_cluster: NodeStatusInCluster, + ) -> Result<()> { + self.message_cluster_set_node_status(cluster_id, node_key, status_in_cluster) } - /// Trigger the distribution of revenues from the cluster to the providers. + /// Changes CDN node status. + /// + /// This endpoint changes CDN node status in a cluster. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// * `cdn_node_key` - Public Key associated with the CDN node. + /// * `status` - Status for the targeting CDN node, can be one of the following: ACTIVE, ADDING, DELETING, OFFLINE. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterCdnNodeStatusSet` event on successful CDN status change. + /// + /// # Errors + /// + /// * `OnlyClusterManager` error if the caller is not the cluster manager. + /// * `ClusterDoesNotExist` error if the cluster does not exist. + /// * `CdnNodeIsNotAddedToCluster(ClusterId)` error if the CDN node is not in this cluster. #[ink(message)] - pub fn cluster_distribute_revenues(&mut self, cluster_id: ClusterId) { - self.message_cluster_distribute_revenues(cluster_id) - .unwrap() + pub fn cluster_set_cdn_node_status( + &mut self, + cluster_id: ClusterId, + cdn_node_key: CdnNodeKey, + status_in_cluster: NodeStatusInCluster, + ) -> Result<()> { + self.message_cluster_set_cdn_node_status(cluster_id, cdn_node_key, status_in_cluster) } - /// Change the `cluster_params`, which is configuration used by clients and nodes. + /// Sets the resource used per virual node in cluster. /// - /// See the [data structure of ClusterParams](https://docs.cere.network/ddc/protocols/contract-params-schema) - #[ink(message, payable)] - pub fn cluster_change_params(&mut self, cluster_id: ClusterId, params: ClusterParams) { - self.message_cluster_change_params(cluster_id, params) - .unwrap(); + /// This endpoint sets the resource value that is being used by each virtual node in the cluster. + /// If there are existing virtual nodes in the cluster the resource for its physical nodes will be recalculated. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// * `new_resource_per_v_node` - Resource value that will be allocated for every virtual node in the cluster. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `ClusterNodeReplaced` event on successful virtual node reassignment. + /// + /// # Errors + /// + /// * `ClusterDoesNotExist` error if the cluster does not exist. + /// * `OnlyClusterManager` error if the caller is not the cluster manager. + /// * `NodeDoesNotExist` error if the new Storage node does not exist. + /// * `VNodeIsNotAssignedToNode(ClusterId, VNodeToken)` error if the there is some virtual node that is being reasigned, but this virtual node is not assigned to any physical node. + /// * `InsufficientClusterResources` - error if there is not enough resources in the cluster. + /// * `InsufficientNodeResources` - error if there is not enough resources in a physical node. + #[ink(message)] + pub fn cluster_set_resource_per_v_node( + &mut self, + cluster_id: ClusterId, + new_resource_per_v_node: Resource, + ) -> Result<()> { + self.message_cluster_set_resource_per_v_node(cluster_id, new_resource_per_v_node) } - /// Get the current status of a cluster. + /// Gets a cluster. + /// + /// This endpoint gets the targeting cluster along with its parameters, Storage and CDN nodes. + /// + /// # Parameters + /// + /// * `cluster_id` - ID of the targeting cluster. + /// + /// # Output + /// + /// Returns `ClusterInfo` data transfer object. + /// + /// # Errors + /// + /// * `ClusterDoesNotExist` error if the cluster does not exist. #[ink(message)] - pub fn cluster_get(&self, cluster_id: ClusterId) -> Result { + pub fn cluster_get(&self, cluster_id: ClusterId) -> Result { self.message_cluster_get(cluster_id) } - /// Iterate through all clusters. + /// Gets a paginated list of clusters. /// + /// This endpoint gets a paginated list of clusters along with their parameters, Storage and CDN nodes. /// The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`. /// The optimal `limit` depends on the size of params. /// - /// The results can be filtered by manager. Note that paging must still be completed fully. + /// # Parameters + /// + /// * `offset` - starting offset. + /// * `limit` - page limit. + /// * `filter_manager_id` - optional filter by cluster manager. + /// + /// # Errors + /// + /// No errors. In case a pagination param is out of bounds, an empty list will be returned. #[ink(message)] pub fn cluster_list( &self, offset: u32, limit: u32, filter_manager_id: Option, - ) -> (Vec, u32) { + ) -> (Vec, u32) { self.message_cluster_list(offset, limit, filter_manager_id) } - } - // ---- End Cluster ---- - - // ---- CDN Cluster ---- - /// A new cluster was created. - #[ink(event)] - #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] - pub struct CdnClusterCreated { - #[ink(topic)] - cluster_id: ClusterId, - #[ink(topic)] - manager: AccountId, - } - - /// The respective share of revenues of a CDN cluster for a provider was distributed. - #[ink(event)] - #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] - pub struct CdnClusterDistributeRevenues { - #[ink(topic)] - cluster_id: ClusterId, - #[ink(topic)] - provider_id: AccountId, - } - - impl DdcBucket { - /// Create a new cluster and return its `cluster_id`. - /// - /// The caller will be its first manager. - /// - /// The CDN node ids are provided, which will form a cluster. - #[ink(message, payable)] - pub fn cdn_cluster_create(&mut self, cdn_node_ids: Vec) -> ClusterId { - self.message_cdn_cluster_create(cdn_node_ids).unwrap() + /// Trigger the distribution of revenues from the cluster to the providers. + #[ink(message)] + pub fn cluster_distribute_revenues(&mut self, cluster_id: ClusterId) -> Result<()> { + self.message_cluster_distribute_revenues(cluster_id) } /// Set rate for streaming (price per gb) #[ink(message, payable)] - pub fn cdn_set_rate(&mut self, cluster_id: ClusterId, usd_per_gb: Balance) -> () { - self.message_cdn_set_rate(cluster_id, usd_per_gb).unwrap() + pub fn cdn_set_rate(&mut self, cluster_id: ClusterId, usd_per_gb: Balance) -> Result<()> { + self.message_cdn_set_rate(cluster_id, usd_per_gb) } /// Get rate for streaming (price per gb) #[ink(message, payable)] - pub fn cdn_get_rate(&self, cluster_id: ClusterId) -> Balance { - self.message_cdn_get_rate(cluster_id).unwrap() + pub fn cdn_get_rate(&self, cluster_id: ClusterId) -> Result { + self.message_cdn_get_rate(cluster_id) } /// As validator, charge payments from users and allocate undistributed payments to CDN nodes. /// /// As a result CDN cluster revenue increases, which can be distributed between CDN node providers via method cdn_cluster_distribute_revenues. #[ink(message)] - pub fn cdn_cluster_put_revenue( + pub fn cluster_put_cdn_revenue( &mut self, cluster_id: ClusterId, aggregates_accounts: Vec<(AccountId, u128)>, - aggregates_nodes: Vec<(u32, u128)>, + aggregates_nodes: Vec<(CdnNodeKey, u128)>, aggregates_buckets: Vec<(BucketId, Resource)>, era: u64, - ) -> () { - self.message_cdn_cluster_put_revenue( + ) -> Result<()> { + self.message_cluster_put_cdn_revenue( cluster_id, aggregates_accounts, aggregates_nodes, aggregates_buckets, - era, - ) - .unwrap() - } - - /// Trigger the distribution of revenues from the cluster to the CDN node providers. - /// - /// Anyone can call this method. - /// - /// Undistributed payments will be trasnferred, CDN cluster revenue will decrease. - #[ink(message)] - pub fn cdn_cluster_distribute_revenues(&mut self, cluster_id: ClusterId) { - self.message_cdn_cluster_distribute_revenues(cluster_id) - .unwrap() - } - - /// Get the current status of a cluster. - #[ink(message)] - pub fn cdn_cluster_get(&self, cluster_id: ClusterId) -> Result { - self.message_cdn_cluster_get(cluster_id) + era, + ) } - /// Iterate through all clusters. + /// Trigger the distribution of revenues from the cluster to the CDN node providers. /// - /// The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`. - /// The optimal `limit` depends on the size of params. + /// Anyone can call this method. /// - /// The results can be filtered by manager. Note that paging must still be completed fully. + /// Undistributed payments will be trasnferred, CDN cluster revenue will decrease. #[ink(message)] - pub fn cdn_cluster_list( - &self, - offset: u32, - limit: u32, - filter_manager_id: Option, - ) -> (Vec, u32) { - self.message_cdn_cluster_list(offset, limit, filter_manager_id) + pub fn cluster_distribute_cdn_revenue(&mut self, cluster_id: ClusterId) -> Result<()> { + self.message_cluster_distribute_cdn_revenue(cluster_id) } } - // ---- End CDN Cluster ---- + // ---- End Cluster ---- // ---- Committer ---- impl DdcBucket { /// CDN node operator sets the commit for current era. #[ink(message)] - pub fn set_commit(&mut self, cdn_owner: AccountId, node_id: NodeId, commit: Commit) { - self.message_set_commit(cdn_owner, node_id, commit); + pub fn set_commit( + &mut self, + cdn_owner: AccountId, + cdn_node_key: CdnNodeKey, + commit: Commit, + ) -> Result<()> { + self.message_set_commit(cdn_owner, cdn_node_key, commit) } /// Return the last commit submitted by CDN node operator #[ink(message)] - pub fn get_commit(&self, cdn_owner: AccountId) -> Vec<(NodeId, Commit)> { + pub fn get_commit(&self, cdn_owner: AccountId) -> Vec<(CdnNodeKey, Commit)> { self.message_get_commit(cdn_owner) } /// Return last era validated per CDN node #[ink(message)] - pub fn get_validated_commit(&self, node: NodeId) -> EraAndTimestamp { - self.message_get_validated_commit(node) + pub fn get_validated_commit(&self, cdn_node_key: CdnNodeKey) -> EraAndTimestamp { + self.message_get_validated_commit(cdn_node_key) } /// Set the new configs for era #[ink(message)] - pub fn set_era(&mut self, era_config: EraConfig) -> () { - self.message_set_era(era_config).unwrap(); + pub fn set_era(&mut self, era_config: EraConfig) -> Result<()> { + self.message_set_era(era_config) } /// Return current status of an era @@ -620,81 +989,162 @@ pub mod ddc_bucket { #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] pub struct CdnNodeCreated { #[ink(topic)] - node_id: NodeId, + cdn_node_key: CdnNodeKey, #[ink(topic)] provider_id: AccountId, + cdn_node_params: CdnNodeParams, undistributed_payment: Balance, } + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct CdnNodeRemoved { + #[ink(topic)] + cdn_node_key: CdnNodeKey, + } + + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct CdnNodeParamsSet { + #[ink(topic)] + cdn_node_key: CdnNodeKey, + cdn_node_params: CdnNodeParams, + } + impl DdcBucket { - /// As node provider, authorize a cluster manager to use his nodes. + /// Creates a CDN node + /// + /// This endpoint creates a CDN node with specific parameters. + /// The caller will be the node owner (node provider). + /// + /// # Parameters + /// + /// * `cdn_node_key` - Public Keys of the CDN node that should be treated as node identifier. + /// * `cdn_node_params` - [CDN node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format. + /// + /// # Output + /// + /// Returns Public Key of the created CDN node. + /// + /// # Events + /// + /// * `CdnNodeCreated` event on successful CDN node creation. + /// + /// # Errors + /// + /// * `CdnNodeAlreadyExists` error if a CDN node with the same Public Key is already created. + /// * `InvalidParams(message)` error if there is some invalid configuration parameter. #[ink(message, payable)] - pub fn cdn_node_trust_manager(&mut self, manager: AccountId) { - self.message_cdn_node_trust_manager(manager, true).unwrap(); + pub fn cdn_node_create( + &mut self, + cdn_node_key: CdnNodeKey, + cdn_node_params: CdnNodeParams, + ) -> Result { + self.message_cdn_node_create(cdn_node_key, cdn_node_params) } - /// As node provider, revoke the authorization of a cluster manager to use his nodes. + /// Removes a CDN node. + /// + /// This endpoint removes the targeting CDN Node if it is not added to some cluster. + /// Only a node that is not a member of some cluster can be removed. + /// + /// # Parameters + /// + /// * `cdn_node_key` - Public Key associated with the CDN node. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `CdnNodeRemoved` event on successful CDN node removal. + /// + /// # Errors + /// + /// * `OnlyCdnNodeProvider` error if the caller is not the CDN node owner. + /// * `CdnNodeDoesNotExist` error if the CDN node does not exist. + /// * `CdnNodeIsAddedToCluster(ClusterId)` error if the removing CDN node is added to some cluster. #[ink(message)] - pub fn cdn_node_distrust_manager(&mut self, manager: AccountId) { - self.message_cdn_node_trust_manager(manager, false).unwrap(); + pub fn cdn_node_remove(&mut self, cdn_node_key: CdnNodeKey) -> Result<()> { + self.message_remove_cdn_node(cdn_node_key) } - /// Create a new node and return its `node_id`. + /// Sets parameters for the targeting CDN node. /// - /// The caller will be its owner. + /// This endpoint updates [CDN node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format. + /// All CDN node parameters must be specified as the endpoint works using SET approach. /// - /// `node_params` is configuration used by clients and nodes. In particular, this contains the URL to the service. See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema) - #[ink(message, payable)] - pub fn cdn_node_create(&mut self, node_params: Params, pubkey: AccountId) -> NodeId { - self.message_cdn_node_create(node_params, pubkey).unwrap() - } - - /// Change the `node_params`, which is configuration used by clients and nodes. + /// # Parameters + /// + /// * `cdn_node_key` - Public Key associated with the CDN node. + /// * `cdn_node_params` - [CDN node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format. + /// + /// # Output + /// + /// Returns nothing. /// - /// See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema) + /// # Events + /// + /// * `CdnNodeParamsSet` event on successful CDN node params setting. + /// + /// # Errors + /// + /// * `OnlyCdnNodeProvider` error if the caller is not the CDN node owner. + /// * `CdnNodeDoesNotExist` error if the CDN node does not exist. #[ink(message, payable)] - pub fn cdn_node_change_params(&mut self, node_id: NodeId, params: NodeParams) { - self.message_cdn_node_change_params(node_id, params) - .unwrap(); - } - - /// Get the current state of the cdn node - #[ink(message)] - pub fn cdn_node_get(&self, node_id: NodeId) -> Result { - self.message_cdn_node_get(node_id) + pub fn cdn_node_set_params( + &mut self, + cdn_node_key: CdnNodeKey, + cdn_node_params: CdnNodeParams, + ) -> Result<()> { + self.message_cdn_node_set_params(cdn_node_key, cdn_node_params) } - /// Get the current state of a cdn node by a public key. + /// Gets a CDN node. + /// + /// This endpoint gets the targeting CDN node along with its parameters. + /// + /// # Parameters + /// + /// * `cdn_node_key` - Public Key associated with the CDN node. + /// + /// # Output + /// + /// Returns `CdnNodeInfo` data transfer object. + /// + /// # Errors + /// + /// * `CdnNodeDoesNotExist` error if the CDN node does not exist. #[ink(message)] - pub fn cdn_node_get_by_pubkey(&self, pubkey: AccountId) -> Result { - self.message_cdn_node_get_by_pub_key(pubkey) + pub fn cdn_node_get(&self, cdn_node_key: CdnNodeKey) -> Result { + self.message_cdn_node_get(cdn_node_key) } - /// Iterate through all nodes. + /// Gets a paginated list of CDN nodes. /// + /// This endpoint gets a paginated list of CDN nodes along with their parameters. /// The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`. /// The optimal `limit` depends on the size of params. /// - /// The results can be filtered by owner. Note that paging must still be completed fully. + /// # Parameters + /// + /// * `offset` - starting offset. + /// * `limit` - page limit. + /// * `filter_provider_id` - optional filter by CDN node owner. + /// + /// # Errors + /// + /// No errors. In case a pagination param is out of bounds, an empty list will be returned. #[ink(message)] pub fn cdn_node_list( &self, offset: u32, limit: u32, filter_provider_id: Option, - ) -> (Vec, u32) { + ) -> (Vec, u32) { self.message_cdn_node_list(offset, limit, filter_provider_id) } - - /// Remove cdn node by id - /// - /// Only the provider of the node can remove the node (not related to the public key) - /// - /// The underlying algorithm swaps the moved to be removed with the last one added, hence the id of the last one added will be updated - #[ink(message)] - pub fn cdn_node_remove(&mut self, node_id: NodeId) -> Result<()> { - self.message_remove_cdn_node(node_id) - } } // ---- End CDN Node ---- @@ -705,89 +1155,166 @@ pub mod ddc_bucket { #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] pub struct NodeCreated { #[ink(topic)] - node_id: NodeId, + node_key: NodeKey, #[ink(topic)] provider_id: AccountId, - rent_per_month: Balance, + rent_v_node_per_month: Balance, node_params: NodeParams, } - impl DdcBucket { - /// As node provider, authorize a cluster manager to use his nodes. - #[ink(message, payable)] - pub fn node_trust_manager(&mut self, manager: AccountId) { - self.message_node_trust_manager(manager, true).unwrap(); - } + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct NodeRemoved { + #[ink(topic)] + node_key: NodeKey, + } - /// As node provider, revoke the authorization of a cluster manager to use his nodes. - #[ink(message)] - pub fn node_distrust_manager(&mut self, manager: AccountId) { - self.message_node_trust_manager(manager, false).unwrap(); - } + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct NodeParamsSet { + #[ink(topic)] + node_key: NodeKey, + node_params: NodeParams, + } - /// Create a new node and return its `node_id`. + impl DdcBucket { + /// Creates a Storage node + /// + /// This endpoint creates a Storage node with specific parameters. + /// The caller will be the node owner (node provider). + /// + /// # Parameters + /// + /// * `node_key` - Public Keys of the Storage node that should be treated as node identifier. + /// * `node_params` - [Storage node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format. + /// * `capacity` - Measure used to evaluate physical node hardware resources. + /// * `rent_v_node_per_month` - Renting per month. + /// + /// # Output /// - /// The caller will be its owner. + /// Returns Public Key of the created Storage node. /// - /// `node_params` is configuration used by clients and nodes. In particular, this contains the URL to the service. See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema) + /// # Events + /// + /// * `NodeCreated` event on successful Storage node creation. + /// + /// # Errors + /// + /// * `NodeAlreadyExists` error if a Storage node with the same Public Key is already created. + /// * `InvalidParams(message)` error if there is some invalid configuration parameter. #[ink(message, payable)] pub fn node_create( &mut self, - rent_per_month: Balance, + node_key: NodeKey, node_params: NodeParams, capacity: Resource, - node_tag: NodeTag, - pubkey: AccountId, - ) -> NodeId { - self.message_node_create(rent_per_month, node_params, capacity, node_tag, pubkey) - .unwrap() + rent_v_node_per_month: Balance, + ) -> Result { + self.message_node_create(node_key, node_params, capacity, rent_v_node_per_month) } - /// Change the `node_params`, which is configuration used by clients and nodes. + /// Removes a Storage node. /// - /// See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema) - #[ink(message, payable)] - pub fn node_change_params(&mut self, node_id: NodeId, params: NodeParams) { - self.message_node_change_params(node_id, params).unwrap(); + /// This endpoint removes the targeting Storage Node if it is not added to some cluster. + /// Only a node that is not a member of some cluster can be removed. + /// + /// # Parameters + /// + /// * `node_key` - Public Key associated with the Storage node. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `NodeRemoved` event on successful Storage node removal. + /// + /// # Errors + /// + /// * `OnlyNodeProvider` error if the caller is not the Storage node owner. + /// * `NodeDoesNotExist` error if the Storage node does not exist. + /// * `NodeIsAddedToCluster(ClusterId)` error if the removing Storage node is added to some cluster. + #[ink(message)] + pub fn node_remove(&mut self, node_key: NodeKey) -> Result<()> { + self.message_node_remove(node_key) } - /// Get the current status of a node. - #[ink(message)] - pub fn node_get(&self, node_id: NodeId) -> Result { - self.message_node_get(node_id) + /// Sets parameters for the targeting Storage node. + /// + /// This endpoint updates [Storage node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format. + /// All Storage node parameters must be specified as the endpoint works using SET approach. + /// + /// # Parameters + /// + /// * `node_key` - Public Key associated with the Storage node. + /// * `node_params` - [Storage node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `NodeParamsSet` event on successful Storage node params setting. + /// + /// # Errors + /// + /// * `OnlyNodeProvider` error if the caller is not the Storage node owner. + /// * `NodeDoesNotExist` error if the Storage node does not exist. + #[ink(message, payable)] + pub fn node_set_params( + &mut self, + node_key: NodeKey, + node_params: NodeParams, + ) -> Result<()> { + self.message_node_set_params(node_key, node_params) } - /// Get the current status of a node by a public key. + /// Gets a Storage node. + /// + /// This endpoint gets the targeting Storage node along with its parameters. + /// + /// # Parameters + /// + /// * `node_key` - Public Key associated with the Storage node. + /// + /// # Output + /// + /// Returns `NodeInfo` data transfer object. + /// + /// # Errors + /// + /// * `NodeDoesNotExist` error if the Storage node does not exist. #[ink(message)] - pub fn node_get_by_pubkey(&self, pubkey: AccountId) -> Result { - self.message_node_get_by_pub_key(pubkey) + pub fn node_get(&self, node_key: NodeKey) -> Result { + self.message_node_get(node_key) } - /// Iterate through all nodes. + /// Gets a paginated list of Storage nodes. /// + /// This endpoint gets a paginated list of Storage nodes along with their parameters. /// The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`. /// The optimal `limit` depends on the size of params. /// - /// The results can be filtered by owner. Note that paging must still be completed fully. + /// # Parameters + /// + /// * `offset` - starting offset. + /// * `limit` - page limit. + /// * `filter_provider_id` - optional filter by Storage node owner. + /// + /// # Errors + /// + /// No errors. In case a pagination param is out of bounds, an empty list will be returned. #[ink(message)] pub fn node_list( &self, offset: u32, limit: u32, filter_provider_id: Option, - ) -> (Vec, u32) { + ) -> (Vec, u32) { self.message_node_list(offset, limit, filter_provider_id) } - - /// Remove node by id - /// - /// Only the provider of the node can remove the node (not related to the public key) - /// - /// The underlying algorithm swaps the moved to be removed with the last one added, hence the id of the last one added will be updated - #[ink(message)] - pub fn node_remove(&mut self, node_id: NodeId) -> Result<()> { - self.message_remove_node(node_id) - } } // ---- End Node ---- @@ -796,26 +1323,20 @@ pub mod ddc_bucket { impl DdcBucket { /// Get the Fee Percentage Basis Points that will be charged by the protocol #[ink(message)] - pub fn get_fee_bp(&self) -> u32 { - self.message_get_fee_bp() - } - - /// Return the last commit submitted by CDN node operator - #[ink(message)] - pub fn set_fee_bp(&mut self, fee_bp: u32) -> () { - self.message_set_fee_bp(fee_bp).unwrap(); + pub fn get_protocol_fee_bp(&self) -> u128 { + self.message_get_protocol_fee_bp() } /// Return fees accumulated by the protocol #[ink(message)] pub fn get_protocol_revenues(&self) -> Cash { - self.message_get_fee_revenues() + self.message_get_protocol_revenues() } - /// Pay the revenues accumulated by the protocol + /// Get the Fee Percentage Basis Points that will be charged by the protocol #[ink(message)] - pub fn protocol_withdraw_revenues(&mut self, amount: u128) -> () { - self.message_withdraw_revenues(amount).unwrap(); + pub fn get_network_fee_config(&self) -> NetworkFeeConfig { + self.message_get_network_fee_config() } } // ---- End Protocol ---- @@ -835,34 +1356,34 @@ pub mod ddc_bucket { /// As user, deposit tokens on the account of the caller from the transaction value. This deposit /// can be used to pay for the services to buckets of the account. #[ink(message, payable)] - pub fn account_deposit(&mut self) -> () { - self.message_account_deposit().unwrap() + pub fn account_deposit(&mut self) -> Result<()> { + self.message_account_deposit() } /// As user, bond some amount of tokens from the withdrawable balance. These funds will be used to pay for CDN node service. #[ink(message, payable)] - pub fn account_bond(&mut self, bond_amount: Balance) -> () { - self.message_account_bond(bond_amount).unwrap() + pub fn account_bond(&mut self, bond_amount: Balance) -> Result<()> { + self.message_account_bond(bond_amount) } /// As user, unbond a specified amount of tokens. The tokens will be locked for some time, as defined by contract owner. #[ink(message, payable)] - pub fn account_unbond(&mut self, amount_to_unbond: Cash) -> () { - self.message_account_unbond(amount_to_unbond).unwrap() + pub fn account_unbond(&mut self, amount_to_unbond: Cash) -> Result<()> { + self.message_account_unbond(amount_to_unbond) } /// As user, move the unbonded tokens back to withdrawable balance state. /// /// This can be triggered after unbonded_timestamp #[ink(message, payable)] - pub fn account_withdraw_unbonded(&mut self) -> () { - self.message_account_withdraw_unbonded().unwrap() + pub fn account_withdraw_unbonded(&mut self) -> Result<()> { + self.message_account_withdraw_unbonded() } /// Get the current status of an account. #[ink(message)] pub fn account_get(&self, account_id: AccountId) -> Result { - Ok(self.accounts.get(&account_id)?.clone()) + self.accounts.get(&account_id) } /// Get the current conversion rate between the native currency and an external currency (USD). @@ -875,8 +1396,8 @@ pub mod ddc_bucket { /// /// This requires the permission SetExchangeRate or SuperAdmin. #[ink(message)] - pub fn account_set_usd_per_cere(&mut self, usd_per_cere: Balance) { - self.message_account_set_usd_per_cere(usd_per_cere); + pub fn account_set_usd_per_cere(&mut self, usd_per_cere: Balance) -> Result<()> { + self.message_account_set_usd_per_cere(usd_per_cere) } } // ---- End Billing ---- @@ -885,7 +1406,7 @@ pub mod ddc_bucket { /// A permission was granted to the account. #[ink(event)] #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] - pub struct GrantPermission { + pub struct PermissionGranted { #[ink(topic)] account_id: AccountId, permission: Permission, @@ -894,50 +1415,254 @@ pub mod ddc_bucket { /// A permission was revoked from the account. #[ink(event)] #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] - pub struct RevokePermission { + pub struct PermissionRevoked { #[ink(topic)] account_id: AccountId, permission: Permission, } impl DdcBucket { - /// Check whether the given account has the given permission currently, - /// or the SuperAdmin permission. + /// Checks for permission existence. + /// + /// This endpoint checks whether the given account has the given permission. + /// Super-admin will always have all permissions. + /// + /// # Parameters + /// + /// * `account_id` - account to check permissions. + /// * `permission` - permission to check. + /// + /// # Output + /// + /// Returns true if the account has permissions, false otherwise. + /// + /// # Errors + /// + /// No errors. + #[ink(message)] + pub fn has_permission(&self, account_id: AccountId, permission: Permission) -> bool { + self.perms.has_permission(account_id, permission) + } + + /// Grants permissions for a cluster manager. + /// + /// This endpoint grants permissions for a cluster manager ro manage Storage or CDN node owner. + /// After the permission is granted, the cluster manager can add nodes to the cluster. + /// Permissions can be granted by Storage or CDN node owner. + /// + /// # Parameters + /// + /// * `manager_id` - cluster manager account. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `PermissionGranted` event on successful manager permissions granting + /// + /// # Errors + /// + /// No errors. The endpoint is idempotent. + #[ink(message, payable)] + pub fn grant_trusted_manager_permission(&mut self, manager_id: AccountId) -> Result<()> { + self.message_grant_trusted_manager_permission(manager_id) + } + + /// Revokes permissions from cluster manager. + /// + /// This endpoint revokes permissions from a cluster manager to manage Storage or CDN node owner. + /// After the permission is revoked, the cluster manager can add nodes to the cluster. + /// Permissions can be revoked by Storage or CDN node owner. + /// + /// # Parameters + /// + /// * `manager_id` - cluster manager account. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `PermissionRevoked` event on successful manager permissions revoking + /// + /// # Errors + /// + /// No errors. The endpoint is idempotent. #[ink(message)] - pub fn has_permission(&self, grantee: AccountId, permission: Permission) -> bool { - self.perms.has_permission(grantee, permission) + pub fn revoke_trusted_manager_permission(&mut self, manager_id: AccountId) -> Result<()> { + self.message_revoke_trusted_manager_permission(manager_id) } } // ---- End Permissions ---- + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct NodeOwnershipTransferred { + #[ink(topic)] + account_id: AccountId, + #[ink(topic)] + node_key: NodeKey, + } + + #[ink(event)] + #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] + pub struct CdnNodeOwnershipTransferred { + #[ink(topic)] + account_id: AccountId, + #[ink(topic)] + cdn_node_key: CdnNodeKey, + } + // ---- Admin ---- impl DdcBucket { - /// As SuperAdmin, grant any permission to any account. - #[ink(message, payable)] - pub fn admin_grant_permission(&mut self, grantee: AccountId, permission: Permission) { - self.message_admin_grant_permission(grantee, permission, true) - .unwrap(); + /// Grants any permission. + /// + /// This endpoint grants any permissions for any account by the Super-admin. + /// + /// # Parameters + /// + /// * `grantee` - account to grant permission. + /// * `permission` - permission type. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `PermissionGranted` event on successful permissions granting + /// + /// # Errors + /// + /// Returns `OnlySuperAdmin` error if the caller is not the Super-admin. + #[ink(message)] + pub fn admin_grant_permission( + &mut self, + grantee: AccountId, + permission: Permission, + ) -> Result<()> { + self.message_admin_grant_permission(grantee, permission) + } + + /// Revokes any permission. + /// + /// This endpoint revokes any permissions from any account by the Super-admin. + /// + /// # Parameters + /// + /// * `grantee` - account to revoke permission. + /// * `permission` - permission type. + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `PermissionRevoked` event on successful permissions revoking + /// + /// # Errors + /// + /// Returns `OnlySuperAdmin` error if the caller is not the Super-admin. + #[ink(message)] + pub fn admin_revoke_permission( + &mut self, + grantee: AccountId, + permission: Permission, + ) -> Result<()> { + self.message_admin_revoke_permission(grantee, permission) + } + + /// Transfers Storage node ownership. + /// + /// This endpoint transfers Storage node ownership from Super-admin account to the targeting account forever. + /// This action is usually required only once after the Storage node certification process. + /// + /// # Parameters + /// + /// * `node_key` - Public Key associated with the Storage node. + /// * `new_owner` - New Storage node owner + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `NodeOwnershipTransferred` event on successful Storage node ownership transfer + /// + /// # Errors + /// + /// * `OnlySuperAdmin` error if the caller is not the Super-admin. + /// * `NodeDoesNotExist` error if the Storage node does not exist. + /// * `NodeProviderIsNotSuperAdmin` error if the owner of the targeting node is not the Super-admin. + #[ink(message)] + pub fn admin_transfer_node_ownership( + &mut self, + node_key: NodeKey, + new_owner: AccountId, + ) -> Result<()> { + self.message_admin_transfer_node_ownership(node_key, new_owner) } - /// As SuperAdmin, revoke any permission to any account. + /// Transfers CDN node ownership. + /// + /// This endpoint transfers CDN node ownership from Super-admin account to the targeting account forever. + /// This action is usually required only once after the CDN node certification process. + /// + /// # Parameters + /// + /// * `cdn_node_key` - Public Key associated with the CDN node. + /// * `new_owner` - CDN node owner + /// + /// # Output + /// + /// Returns nothing. + /// + /// # Events + /// + /// * `CdnNodeOwnershipTransferred` event on successful CDN node ownership transfer + /// + /// # Errors + /// + /// * `OnlySuperAdmin` error if the caller is not the Super-admin. + /// * `CdnNodeDoesNotExist` error if the Storage node does not exist. + /// * `CdnNodeOwnerIsNotSuperAdmin` error if the owner of the targeting node is not the Super-admin. #[ink(message)] - pub fn admin_revoke_permission(&mut self, grantee: AccountId, permission: Permission) { - self.message_admin_grant_permission(grantee, permission, false) - .unwrap(); + pub fn admin_transfer_cdn_node_ownership( + &mut self, + cdn_node_key: CdnNodeKey, + new_owner: AccountId, + ) -> Result<()> { + self.message_admin_transfer_cdn_node_ownership(cdn_node_key, new_owner) } /// As SuperAdmin, withdraw the funds held in custody in this contract. /// /// This is a temporary measure to allow migrating the funds to a new version of the contract. #[ink(message)] - pub fn admin_withdraw(&mut self, amount: Balance) { - self.message_admin_withdraw(amount).unwrap(); + pub fn admin_withdraw(&mut self, amount: Balance) -> Result<()> { + self.message_admin_withdraw(amount) + } + + /// Pay the revenues accumulated by the protocol + #[ink(message)] + pub fn admin_withdraw_protocol_revenues(&mut self, amount: Balance) -> Result<()> { + self.message_admin_withdraw_revenues(amount) } /// As SuperAdmin, set the network and cluster fee configuration. #[ink(message)] - pub fn admin_set_fee_config(&mut self, config: FeeConfig) { - self.message_admin_set_fee_config(config).unwrap(); + pub fn admin_set_network_fee_config(&mut self, config: NetworkFeeConfig) -> Result<()> { + self.message_admin_set_network_fee_config(config) + } + + #[ink(message)] + pub fn admin_set_protocol_fee_bp(&mut self, protocol_fee_bp: BasisPoints) -> Result<()> { + self.message_admin_set_protocol_fee_bp(protocol_fee_bp) } } // ---- End Admin ---- @@ -952,35 +1677,84 @@ pub mod ddc_bucket { } // ---- End Accounts ---- - // ---- Utils ---- + // ---- Topology ---- + impl DdcBucket { + #[ink(message)] + pub fn get_v_nodes_by_cluster(&self, cluster_id: ClusterId) -> Vec { + self.message_get_v_nodes_by_cluster(cluster_id) + } + + #[ink(message)] + pub fn get_v_nodes_by_node(&self, node_key: NodeKey) -> Vec { + self.message_get_v_nodes_by_node(node_key) + } + + #[ink(message)] + pub fn get_node_by_v_node( + &self, + cluster_id: ClusterId, + v_node: VNodeToken, + ) -> Result { + self.message_get_node_by_v_node(cluster_id, v_node) + } + } + // ---- End Topology ---- + + // ---- Constants ---- /// One token with 10 decimals. pub const TOKEN: Balance = 10_000_000_000; - pub const DEFAULT_BASIS_POINTS: u32 = 500; + + pub type BasisPoints = u128; + pub const BASIS_POINTS: BasisPoints = 10_000; // 100% + pub const DEFAULT_PROTOCOL_FEE_BP: BasisPoints = 500; // 5 % + pub const DEFAULT_NETWORK_FEE_BP: BasisPoints = 0; // 0 % + pub const DEFAULT_CLUSTER_FEE_BP: BasisPoints = 0; // 0 % #[derive(Debug, PartialEq, Eq, Encode, Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum Error { - BucketDoesNotExist, - ClusterDoesNotExist, - ParamsTooBig, - VNodeDoesNotExist, - BondingPeriodNotFinished, - BucketClusterAlreadyConnected, - BucketClusterNotSetup, NodeDoesNotExist, + CdnNodeDoesNotExist, NodeAlreadyExists, - FlowDoesNotExist, + CdnNodeAlreadyExists, AccountDoesNotExist, ParamsDoesNotExist, - UnauthorizedProvider, - UnauthorizedOwner, - UnauthorizedClusterManager, - ClusterManagerIsNotTrusted, + ParamsSizeExceedsLimit, + OnlyOwner, + OnlyNodeProvider, + OnlyCdnNodeProvider, + OnlyClusterManager, + OnlyTrustedClusterManager, + OnlyValidator, + OnlySuperAdmin, + OnlyClusterManagerOrNodeProvider, + OnlyClusterManagerOrCdnNodeProvider, + Unauthorized, + ClusterDoesNotExist, + ClusterIsNotEmpty, + TopologyIsNotCreated(ClusterId), + TopologyAlreadyExists, + NodesSizeExceedsLimit, + CdnNodesSizeExceedsLimit, + VNodesSizeExceedsLimit, + AccountsSizeExceedsLimit, + NodeIsNotAddedToCluster(ClusterId), + NodeIsAddedToCluster(ClusterId), + CdnNodeIsNotAddedToCluster(ClusterId), + CdnNodeIsAddedToCluster(ClusterId), + VNodeDoesNotExistsInCluster(ClusterId), + VNodeIsNotAssignedToNode(ClusterId, VNodeToken), + VNodeIsAlreadyAssignedToNode(NodeKey), + AtLeastOneVNodeHasToBeAssigned(ClusterId, NodeKey), + NodeProviderIsNotSuperAdmin, + CdnNodeOwnerIsNotSuperAdmin, + BucketDoesNotExist, + BondingPeriodNotFinished, TransferFailed, InsufficientBalance, - InsufficientResources, - Unauthorized, - UnknownNode, + InsufficientNodeResources, + InsufficientClusterResources, + EraSettingFailed, } pub type Result = core::result::Result; diff --git a/bucket/scripts/.gitignore b/bucket/scripts/.gitignore deleted file mode 100644 index 1929c324..00000000 --- a/bucket/scripts/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -secrets.txt diff --git a/bucket/scripts/init.js b/bucket/scripts/init.js deleted file mode 100644 index 281fe009..00000000 --- a/bucket/scripts/init.js +++ /dev/null @@ -1,129 +0,0 @@ -import { config } from './config.js'; -import * as fs from 'fs'; - -import { ContractPromise } from '@polkadot/api-contract'; -import { ApiPromise, WsProvider, Keyring } from '@polkadot/api'; -import { cryptoWaitReady, mnemonicGenerate } from "@polkadot/util-crypto"; - -const INIT_ENV = process.env.INIT_ENV; -const SUPERADMIN_MNEMONIC = process.env.SUPERADMIN; -const CERE = 10_000_000_000n; - -const txOptions = { - storageDepositLimit: null, - gasLimit: 100_000_000_000n, -}; - -async function signAndSendPromise(txn, signer) { - return new Promise((res, rej) => { - txn - .signAndSend(signer, ({ events = [], status, blockNumber }) => { - if (status.isInvalid) { - console.log(" Transaction invalid"); - rej("Transaction invalid"); - } else if (status.isReady) { - console.log(" Transaction is ready"); - } else if (status.isBroadcast) { - console.log(" Transaction has been broadcasted"); - } else if (status.isInBlock) { - const blockHash = status.asInBlock.toHex(); - console.log(` Transaction is in block: ${blockHash} of ${blockNumber}`); - } else if (status.isFinalized) { - const blockHash = status.asFinalized.toHex(); - console.log(` Transaction has been included in blockHash ${blockHash} of ${blockNumber}`); - const treasuryDeposit = events.find( - (event) => event.event.toHuman().method === "Deposit" && event.event.toHuman().section === "treasury", - ); - const txFee = treasuryDeposit ? treasuryDeposit.event.toHuman().data.value : undefined; - const txFeeParsed = txFee ? (parseFloat(txFee.replace(" mCERE", "")) / 1000) * 2 : undefined; - - if (events.find(event => event.event.toHuman().method === "ExtrinsicSuccess")) res({ blockHash, txFeeParsed, events }); - else rej("No success found: " + blockHash); - } - }) - .catch((err) => rej(err)); - }); -} - -function createUser() { - const keyring = new Keyring({ type: "sr25519" }); - const mnemonic = mnemonicGenerate(12); - const account = keyring.addFromUri(mnemonic); - - return { - mnemonic: mnemonic, - address: account.address, - addressBase64: Buffer.from(account.publicKey).toString("base64"), - }; -} - -const cfg = config[INIT_ENV]; -if (cfg === undefined) { - console.error("Please provide INIT_ENV as one of ", Object.keys(config)); - process.exit(-1); -} -console.log(cfg); - -await cryptoWaitReady(); -const keyring = new Keyring({ type: "sr25519" }); -const alice = keyring.addFromUri("//Alice"); -const sadmin = keyring.addFromUri(SUPERADMIN_MNEMONIC); -console.log(`Superadmin: ${sadmin.address}`); - -// Contract metadata -const metadata = fs.readFileSync('./metadata.json', 'utf8'); - -// Construct -const wsProvider = new WsProvider(cfg.ws_provider); -const api = await ApiPromise.create({ provider: wsProvider }); -const contract = new ContractPromise(api, metadata, cfg.contract_address); - -console.log("1. adminGrantPermission"); -const res = await signAndSendPromise(await contract.tx.adminGrantPermission(txOptions, sadmin.address, "SuperAdmin"), sadmin); - -console.log("2. accountSetUsdPerCere"); -await signAndSendPromise(await contract.tx.accountSetUsdPerCere(txOptions, 1000n * CERE), sadmin); - -console.log("3. cdnNodeTrustManager"); -await signAndSendPromise(await contract.tx.cdnNodeTrustManager(txOptions, sadmin.address), sadmin); - -console.log("4. nodeTrustManager"); -await signAndSendPromise(await contract.tx.nodeTrustManager(txOptions, sadmin.address), sadmin); - -console.log("5. cdnNodeCreate"); -for (let i = 0; i < cfg.cdn_node_params.length; i++) { - await signAndSendPromise(await contract.tx.cdnNodeCreate(txOptions, JSON.stringify(cfg.cdn_node_params[i])), sadmin); -} - -for (let id in cfg.cdn_cluster) { - const clu = cfg.cdn_cluster[id]; - console.log("6. cdnClusterCreate, cluster: ", id, clu); - await signAndSendPromise(await contract.tx.cdnClusterCreate(txOptions, clu.cdn_nodes), sadmin); -} - -console.log("7. nodeCreate"); -for (let i = 0; i < cfg.storage_node_params.length; i++) { - const param = JSON.stringify(cfg.storage_node_params[i]); - const user = createUser(); - fs.appendFileSync('secrets.txt', `${user.address}: ${user.mnemonic} -- ${INIT_ENV} storage ${i}\n`); - console.log(` node ${i}: address ${user.address}, param ${param}`); - await signAndSendPromise(await contract.tx.nodeCreate(txOptions, 1n * CERE, param, 100000n, "ACTIVE", user.address), sadmin); -} - -for (let id in cfg.cluster) { - const clu = cfg.cluster[id]; - - console.log("8. clusterCreate, cluster: ", id, clu); - await signAndSendPromise(await contract.tx.clusterCreate(txOptions, alice.address, clu.vnodes, clu.storage_nodes, JSON.stringify(clu.param)), sadmin); - - console.log("9. clusterReserveResource, cluster: ", id); - await signAndSendPromise(await contract.tx.clusterReserveResource(txOptions, id, 100000n), sadmin); -} - -// console.log("cdnNodeChangeParams"); -// for (let i = 0; i < cfg.cdn_node_params.length; i++) { -// await signAndSendPromise(await contract.tx.cdnNodeChangeParams(txOptions, i+1, JSON.stringify(cfg.cdn_node_params[i])), sadmin); -// } - -//console.log(res.events.map(event => event.event.toHuman())); -process.exit(0); diff --git a/bucket/scripts/metadata.json b/bucket/scripts/metadata.json deleted file mode 100644 index 5233dd85..00000000 --- a/bucket/scripts/metadata.json +++ /dev/null @@ -1,4959 +0,0 @@ -{ - "source": { - "hash": "0x17173e6d800d4555fa6b27c1cb7a5bf509796c993c1ed7e1206e8ef27c21fb48", - "language": "ink! 3.4.0", - "compiler": "rustc 1.69.0-nightly" - }, - "contract": { - "name": "ddc_bucket", - "version": "0.5.2", - "authors": [ - "Aurélien Nicolas " - ], - "description": "DDC v2 Smart Contracts -- Orchestrate the network around clusters and buckets", - "license": "Apache-2.0" - }, - "V3": { - "spec": { - "constructors": [ - { - "args": [], - "docs": [ - "Create a new contract.", - "", - "The caller will be admin of the contract." - ], - "label": "new", - "payable": false, - "selector": "0x9bae9d5e" - } - ], - "docs": [], - "events": [ - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "owner_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " A bucket was created. The given account is its first owner and payer of resources." - ], - "label": "BucketCreated" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": false, - "label": "resource", - "type": { - "displayName": [ - "Resource" - ], - "type": 9 - } - } - ], - "docs": [ - " Some amount of resources of a cluster were allocated to a bucket." - ], - "label": "BucketAllocated" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - } - ], - "docs": [ - " The due costs of a bucket was settled from the bucket payer to the cluster." - ], - "label": "BucketSettlePayment" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "public_availability", - "type": { - "displayName": [ - "bool" - ], - "type": 3 - } - } - ], - "docs": [ - " The availiablity of the bucket was updated." - ], - "label": "BucketAvailabilityUpdated" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "manager", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "docs": [], - "indexed": false, - "label": "cluster_params", - "type": { - "displayName": [ - "ClusterParams" - ], - "type": 16 - } - } - ], - "docs": [ - " A new cluster was created." - ], - "label": "ClusterCreated" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - } - ], - "docs": [ - " A vnode was re-assigned to new node." - ], - "label": "ClusterNodeReplaced" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": false, - "label": "resource", - "type": { - "displayName": [ - "Resource" - ], - "type": 9 - } - } - ], - "docs": [ - " Some resources were reserved for the cluster from the nodes." - ], - "label": "ClusterReserveResource" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "provider_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " The share of revenues of a cluster for a provider was distributed." - ], - "label": "ClusterDistributeRevenues" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "manager", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " A new cluster was created." - ], - "label": "CdnClusterCreated" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "provider_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " The respective share of revenues of a CDN cluster for a provider was distributed." - ], - "label": "CdnClusterDistributeRevenues" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "provider_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "docs": [], - "indexed": false, - "label": "undistributed_payment", - "type": { - "displayName": [ - "Balance" - ], - "type": 12 - } - } - ], - "docs": [ - " A node was created. The given account is its owner and recipient of revenues." - ], - "label": "CdnNodeCreated" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - }, - { - "docs": [], - "indexed": true, - "label": "provider_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "docs": [], - "indexed": false, - "label": "rent_per_month", - "type": { - "displayName": [ - "Balance" - ], - "type": 12 - } - }, - { - "docs": [], - "indexed": false, - "label": "node_params", - "type": { - "displayName": [ - "NodeParams" - ], - "type": 16 - } - } - ], - "docs": [ - " A node was created. The given account is its owner and recipient of revenues." - ], - "label": "NodeCreated" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "account_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "docs": [], - "indexed": false, - "label": "value", - "type": { - "displayName": [ - "Balance" - ], - "type": 12 - } - } - ], - "docs": [ - " Tokens were deposited on an account." - ], - "label": "Deposit" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "account_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "docs": [], - "indexed": false, - "label": "permission", - "type": { - "displayName": [ - "Permission" - ], - "type": 77 - } - } - ], - "docs": [ - " A permission was granted to the account." - ], - "label": "GrantPermission" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "label": "account_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "docs": [], - "indexed": false, - "label": "permission", - "type": { - "displayName": [ - "Permission" - ], - "type": 77 - } - } - ], - "docs": [ - " A permission was revoked from the account." - ], - "label": "RevokePermission" - } - ], - "messages": [ - { - "args": [ - { - "label": "bucket_params", - "type": { - "displayName": [ - "BucketParams" - ], - "type": 16 - } - }, - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "label": "owner_id", - "type": { - "displayName": [ - "Option" - ], - "type": 43 - } - } - ], - "docs": [ - " Create a new bucket and return its `bucket_id`.", - "", - " The caller will be its first owner and payer of resources.", - "", - " `bucket_params` is configuration used by clients and nodes. See the [data structure of BucketParams](https://docs.cere.network/ddc/protocols/contract-params-schema)", - "", - " The bucket can be connected to a single cluster (currently). Allocate cluster resources with the function `bucket_alloc_into_cluster`" - ], - "label": "bucket_create", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [ - "BucketId" - ], - "type": 9 - }, - "selector": "0x0aeb2379" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "label": "owner_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Change owner of the bucket", - "", - " Provide the account of new owner" - ], - "label": "bucket_change_owner", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xc7d0c2cd" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "label": "resource", - "type": { - "displayName": [ - "Resource" - ], - "type": 9 - } - } - ], - "docs": [ - " Allocate some resources of a cluster to a bucket.", - "", - " The amount of resources is given per vnode (total resources will be `resource` times the number of vnodes)." - ], - "label": "bucket_alloc_into_cluster", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x4c482d19" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - } - ], - "docs": [ - " Settle the due costs of a bucket from its payer account to the cluster account." - ], - "label": "bucket_settle_payment", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0x15974555" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "label": "params", - "type": { - "displayName": [ - "BucketParams" - ], - "type": 16 - } - } - ], - "docs": [ - " Change the `bucket_params`, which is configuration used by clients and nodes.", - "", - " See the [data structure of BucketParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" - ], - "label": "bucket_change_params", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x9f2d075b" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - } - ], - "docs": [ - " Get the current status of a bucket." - ], - "label": "bucket_get", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 45 - }, - "selector": "0x3802cb77" - }, - { - "args": [ - { - "label": "offset", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - }, - { - "label": "limit", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - }, - { - "label": "filter_owner_id", - "type": { - "displayName": [ - "Option" - ], - "type": 43 - } - } - ], - "docs": [ - " Iterate through all buckets.", - "", - " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", - " The optimal `limit` depends on the size of params.", - "", - " The results can be filtered by owner. Note that paging must still be completed fully." - ], - "label": "bucket_list", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [], - "type": 49 - }, - "selector": "0x417ab584" - }, - { - "args": [ - { - "label": "owner_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Iterate through all buckets and return only those owned by owner", - "", - " This method returns bucket struct, not the status" - ], - "label": "bucket_list_for_account", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Vec" - ], - "type": 6 - }, - "selector": "0xc434cf57" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "label": "public_availability", - "type": { - "displayName": [ - "bool" - ], - "type": 3 - } - } - ], - "docs": [ - " Set availiablity of the bucket" - ], - "label": "bucket_set_availability", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x053eb3ce" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "label": "new_resource_cap", - "type": { - "displayName": [ - "Resource" - ], - "type": 9 - } - } - ], - "docs": [ - " Set max resource cap to be charged by CDN for public bucket" - ], - "label": "bucket_set_resource_cap", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x85010c6d" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - } - ], - "docs": [ - " Set permission for the reader of the bucket" - ], - "label": "get_bucket_writers", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "Vec" - ], - "type": 14 - }, - "selector": "0x499cd4b7" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "label": "writer", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Set permission for the writer of the bucket" - ], - "label": "bucket_set_writer_perm", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xea2e477a" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "label": "writer", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Revoke permission for the writer of the bucket" - ], - "label": "bucket_revoke_writer_perm", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x2b3d8dd1" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - } - ], - "docs": [ - " Set permission for the reader of the bucket" - ], - "label": "get_bucket_readers", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "Vec" - ], - "type": 14 - }, - "selector": "0xb9a7cc1c" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "label": "reader", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Set permission for the reader of the bucket" - ], - "label": "bucket_set_reader_perm", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xfc0e94ea" - }, - { - "args": [ - { - "label": "bucket_id", - "type": { - "displayName": [ - "BucketId" - ], - "type": 9 - } - }, - { - "label": "writer", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Revoke permission for the reader of the bucket" - ], - "label": "bucket_revoke_reader_perm", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xe9bfed5a" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "label": "node_ids", - "type": { - "displayName": [ - "Vec" - ], - "type": 20 - } - }, - { - "label": "v_nodes", - "type": { - "displayName": [ - "Vec" - ], - "type": 21 - } - } - ], - "docs": [ - " Removes a node to an existing cluster", - "", - " The caller will be its first manager." - ], - "label": "cluster_remove_node", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x793e0778" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "label": "node_ids", - "type": { - "displayName": [ - "Vec" - ], - "type": 20 - } - }, - { - "label": "v_nodes", - "type": { - "displayName": [ - "Vec" - ], - "type": 21 - } - } - ], - "docs": [ - " Adds node to an existing cluster", - "", - " The caller will be its first manager." - ], - "label": "cluster_add_node", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0xf7496bdc" - }, - { - "args": [ - { - "label": "_unused", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "label": "v_nodes", - "type": { - "displayName": [ - "Vec" - ], - "type": 21 - } - }, - { - "label": "node_ids", - "type": { - "displayName": [ - "Vec" - ], - "type": 20 - } - }, - { - "label": "cluster_params", - "type": { - "displayName": [ - "ClusterParams" - ], - "type": 16 - } - } - ], - "docs": [ - " Create a new cluster and return its `cluster_id`.", - "", - " The caller will be its first manager.", - "", - " The cluster is split in a number of vnodes. The vnodes are assigned to the given physical nodes in a round-robin. Only nodes of providers that trust the cluster manager can be used (see `node_trust_manager`). The assignment can be changed with the function `cluster_replace_node`.", - "", - " `cluster_params` is configuration used by clients and nodes. In particular, this describes the semantics of vnodes. See the [data structure of ClusterParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" - ], - "label": "cluster_create", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [ - "ClusterId" - ], - "type": 9 - }, - "selector": "0x4c0f21f6" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "label": "amount", - "type": { - "displayName": [ - "Resource" - ], - "type": 9 - } - } - ], - "docs": [ - " As manager, reserve more resources for the cluster from the free capacity of nodes.", - "", - " The amount of resources is given per vnode (total resources will be `resource` times the number of vnodes)." - ], - "label": "cluster_reserve_resource", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xb5e38125" - }, - { - "args": [ - { - "label": "node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - }, - { - "label": "new_tag", - "type": { - "displayName": [ - "NodeTag" - ], - "type": 31 - } - } - ], - "docs": [ - " As manager, change a node tag" - ], - "label": "cluster_change_node_tag", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x9640d48e" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "label": "v_nodes", - "type": { - "displayName": [ - "Vec" - ], - "type": 22 - } - }, - { - "label": "new_node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - } - ], - "docs": [ - " As manager, re-assign a vnode to another physical node.", - "", - " The cluster manager can only use nodes of providers that trust him (see `node_trust_manager`), or any nodes if he is also SuperAdmin." - ], - "label": "cluster_replace_node", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x48194ab1" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - } - ], - "docs": [ - " Trigger the distribution of revenues from the cluster to the providers." - ], - "label": "cluster_distribute_revenues", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0xe71e66fc" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "label": "params", - "type": { - "displayName": [ - "ClusterParams" - ], - "type": 16 - } - } - ], - "docs": [ - " Change the `cluster_params`, which is configuration used by clients and nodes.", - "", - " See the [data structure of ClusterParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" - ], - "label": "cluster_change_params", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x1207912c" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - } - ], - "docs": [ - " Get the current status of a cluster." - ], - "label": "cluster_get", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 51 - }, - "selector": "0xe75411f5" - }, - { - "args": [ - { - "label": "offset", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - }, - { - "label": "limit", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - }, - { - "label": "filter_manager_id", - "type": { - "displayName": [ - "Option" - ], - "type": 43 - } - } - ], - "docs": [ - " Iterate through all clusters.", - "", - " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", - " The optimal `limit` depends on the size of params.", - "", - " The results can be filtered by manager. Note that paging must still be completed fully." - ], - "label": "cluster_list", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [], - "type": 53 - }, - "selector": "0xd9db9d44" - }, - { - "args": [ - { - "label": "cdn_node_ids", - "type": { - "displayName": [ - "Vec" - ], - "type": 20 - } - } - ], - "docs": [ - " Create a new cluster and return its `cluster_id`.", - "", - " The caller will be its first manager.", - "", - " The CDN node ids are provided, which will form a cluster." - ], - "label": "cdn_cluster_create", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [ - "ClusterId" - ], - "type": 9 - }, - "selector": "0x4344cd7e" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "label": "usd_per_gb", - "type": { - "displayName": [ - "Balance" - ], - "type": 12 - } - } - ], - "docs": [ - " Set rate for streaming (price per gb)" - ], - "label": "cdn_set_rate", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x7578922a" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - } - ], - "docs": [ - " Get rate for streaming (price per gb)" - ], - "label": "cdn_get_rate", - "mutates": false, - "payable": true, - "returnType": { - "displayName": [ - "Balance" - ], - "type": 12 - }, - "selector": "0xa1e3ea8a" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - }, - { - "label": "aggregates_accounts", - "type": { - "displayName": [ - "Vec" - ], - "type": 55 - } - }, - { - "label": "aggregates_nodes", - "type": { - "displayName": [ - "Vec" - ], - "type": 57 - } - }, - { - "label": "aggregates_buckets", - "type": { - "displayName": [ - "Vec" - ], - "type": 59 - } - }, - { - "label": "era", - "type": { - "displayName": [ - "u64" - ], - "type": 23 - } - } - ], - "docs": [ - " As validator, charge payments from users and allocate undistributed payments to CDN nodes.", - "", - " As a result CDN cluster revenue increases, which can be distributed between CDN node providers via method cdn_cluster_distribute_revenues." - ], - "label": "cdn_cluster_put_revenue", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x7219be3f" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - } - ], - "docs": [ - " Trigger the distribution of revenues from the cluster to the CDN node providers.", - "", - " Anyone can call this method.", - "", - " Undistributed payments will be trasnferred, CDN cluster revenue will decrease." - ], - "label": "cdn_cluster_distribute_revenues", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0xfa8d570d" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 9 - } - } - ], - "docs": [ - " Get the current status of a cluster." - ], - "label": "cdn_cluster_get", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 61 - }, - "selector": "0x4b22fbf1" - }, - { - "args": [ - { - "label": "offset", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - }, - { - "label": "limit", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - }, - { - "label": "filter_manager_id", - "type": { - "displayName": [ - "Option" - ], - "type": 43 - } - } - ], - "docs": [ - " Iterate through all clusters.", - "", - " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", - " The optimal `limit` depends on the size of params.", - "", - " The results can be filtered by manager. Note that paging must still be completed fully." - ], - "label": "cdn_cluster_list", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [], - "type": 63 - }, - "selector": "0xb242a64f" - }, - { - "args": [ - { - "label": "cdn_owner", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "label": "node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - }, - { - "label": "commit", - "type": { - "displayName": [ - "Commit" - ], - "type": 37 - } - } - ], - "docs": [ - " CDN node operator sets the commit for current era." - ], - "label": "set_commit", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0xe445e1fd" - }, - { - "args": [ - { - "label": "cdn_owner", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Return the last commit submitted by CDN node operator" - ], - "label": "get_commit", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Vec" - ], - "type": 35 - }, - "selector": "0x5329f551" - }, - { - "args": [ - { - "label": "node", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - } - ], - "docs": [ - " Return last era validated per CDN node" - ], - "label": "get_validated_commit", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "EraAndTimestamp" - ], - "type": 40 - }, - "selector": "0x7d497bc1" - }, - { - "args": [ - { - "label": "era_config", - "type": { - "displayName": [ - "EraConfig" - ], - "type": 65 - } - } - ], - "docs": [ - " Set the new configs for era" - ], - "label": "set_era", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x49a5b8f7" - }, - { - "args": [], - "docs": [ - " Return current status of an era" - ], - "label": "get_era", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "EraStatus" - ], - "type": 66 - }, - "selector": "0x617f696b" - }, - { - "args": [], - "docs": [ - " Return current era settings" - ], - "label": "get_era_settings", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "EraConfig" - ], - "type": 65 - }, - "selector": "0x84b61468" - }, - { - "args": [ - { - "label": "manager", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " As node provider, authorize a cluster manager to use his nodes." - ], - "label": "cdn_node_trust_manager", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x372daa96" - }, - { - "args": [ - { - "label": "manager", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " As node provider, revoke the authorization of a cluster manager to use his nodes." - ], - "label": "cdn_node_distrust_manager", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0xf67f5438" - }, - { - "args": [ - { - "label": "node_params", - "type": { - "displayName": [ - "Params" - ], - "type": 16 - } - } - ], - "docs": [ - " Create a new node and return its `node_id`.", - "", - " The caller will be its owner.", - "", - " `node_params` is configuration used by clients and nodes. In particular, this contains the URL to the service. See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" - ], - "label": "cdn_node_create", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [ - "NodeId" - ], - "type": 9 - }, - "selector": "0xe8aa4ade" - }, - { - "args": [ - { - "label": "node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - }, - { - "label": "params", - "type": { - "displayName": [ - "NodeParams" - ], - "type": 16 - } - } - ], - "docs": [ - " Change the `node_params`, which is configuration used by clients and nodes.", - "", - " See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" - ], - "label": "cdn_node_change_params", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0xf52c20f5" - }, - { - "args": [ - { - "label": "node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - } - ], - "docs": [ - " Get the current state of the cdn node" - ], - "label": "cdn_node_get", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 68 - }, - "selector": "0xf9a5a813" - }, - { - "args": [ - { - "label": "offset", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - }, - { - "label": "limit", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - }, - { - "label": "filter_provider_id", - "type": { - "displayName": [ - "Option" - ], - "type": 43 - } - } - ], - "docs": [ - " Iterate through all nodes.", - "", - " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", - " The optimal `limit` depends on the size of params.", - "", - " The results can be filtered by owner. Note that paging must still be completed fully." - ], - "label": "cdn_node_list", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [], - "type": 70 - }, - "selector": "0xf8589aae" - }, - { - "args": [ - { - "label": "manager", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " As node provider, authorize a cluster manager to use his nodes." - ], - "label": "node_trust_manager", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x6fd54a01" - }, - { - "args": [ - { - "label": "manager", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " As node provider, revoke the authorization of a cluster manager to use his nodes." - ], - "label": "node_distrust_manager", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0x40912279" - }, - { - "args": [ - { - "label": "rent_per_month", - "type": { - "displayName": [ - "Balance" - ], - "type": 12 - } - }, - { - "label": "node_params", - "type": { - "displayName": [ - "NodeParams" - ], - "type": 16 - } - }, - { - "label": "capacity", - "type": { - "displayName": [ - "Resource" - ], - "type": 9 - } - }, - { - "label": "node_tag", - "type": { - "displayName": [ - "NodeTag" - ], - "type": 31 - } - }, - { - "label": "pubkey", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Create a new node and return its `node_id`.", - "", - " The caller will be its owner.", - "", - " `node_params` is configuration used by clients and nodes. In particular, this contains the URL to the service. See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" - ], - "label": "node_create", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [ - "NodeId" - ], - "type": 9 - }, - "selector": "0xb77ac1bb" - }, - { - "args": [ - { - "label": "node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - }, - { - "label": "params", - "type": { - "displayName": [ - "NodeParams" - ], - "type": 16 - } - } - ], - "docs": [ - " Change the `node_params`, which is configuration used by clients and nodes.", - "", - " See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" - ], - "label": "node_change_params", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x258ccb2a" - }, - { - "args": [ - { - "label": "node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 9 - } - } - ], - "docs": [ - " Get the current status of a node." - ], - "label": "node_get", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 72 - }, - "selector": "0x847f3997" - }, - { - "args": [ - { - "label": "pubkey", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Get the current status of a node by a public key." - ], - "label": "node_get_by_pubkey", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 72 - }, - "selector": "0x7f6c82d4" - }, - { - "args": [ - { - "label": "offset", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - }, - { - "label": "limit", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - }, - { - "label": "filter_provider_id", - "type": { - "displayName": [ - "Option" - ], - "type": 43 - } - } - ], - "docs": [ - " Iterate through all nodes.", - "", - " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", - " The optimal `limit` depends on the size of params.", - "", - " The results can be filtered by owner. Note that paging must still be completed fully." - ], - "label": "node_list", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [], - "type": 74 - }, - "selector": "0x423286d6" - }, - { - "args": [], - "docs": [ - " Get the Fee Percentage Basis Points that will be charged by the protocol" - ], - "label": "get_fee_bp", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "u32" - ], - "type": 9 - }, - "selector": "0x0d5daf5f" - }, - { - "args": [ - { - "label": "fee_bp", - "type": { - "displayName": [ - "u32" - ], - "type": 9 - } - } - ], - "docs": [ - " Return the last commit submitted by CDN node operator" - ], - "label": "set_fee_bp", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xc5e3e2ca" - }, - { - "args": [], - "docs": [ - " Return fees accumulated by the protocol" - ], - "label": "get_protocol_revenues", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Cash" - ], - "type": 19 - }, - "selector": "0x07c63885" - }, - { - "args": [ - { - "label": "amount", - "type": { - "displayName": [ - "u128" - ], - "type": 12 - } - } - ], - "docs": [ - " Pay the revenues accumulated by the protocol" - ], - "label": "protocol_withdraw_revenues", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x85c6fa6d" - }, - { - "args": [], - "docs": [ - " As user, deposit tokens on the account of the caller from the transaction value. This deposit", - " can be used to pay for the services to buckets of the account." - ], - "label": "account_deposit", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xc311af62" - }, - { - "args": [ - { - "label": "bond_amount", - "type": { - "displayName": [ - "Balance" - ], - "type": 12 - } - } - ], - "docs": [ - " As user, bond some amount of tokens from the withdrawable balance. These funds will be used to pay for CDN node service." - ], - "label": "account_bond", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xe9fad0bf" - }, - { - "args": [ - { - "label": "amount_to_unbond", - "type": { - "displayName": [ - "Cash" - ], - "type": 19 - } - } - ], - "docs": [ - " As user, unbond a specified amount of tokens. The tokens will be locked for some time, as defined by contract owner." - ], - "label": "account_unbond", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xf7ea2c67" - }, - { - "args": [], - "docs": [ - " As user, move the unbonded tokens back to withdrawable balance state.", - "", - " This can be triggered after unbonded_timestamp" - ], - "label": "account_withdraw_unbonded", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x98173716" - }, - { - "args": [ - { - "label": "account_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - } - ], - "docs": [ - " Get the current status of an account." - ], - "label": "account_get", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 76 - }, - "selector": "0x1d4220fa" - }, - { - "args": [], - "docs": [ - " Get the current conversion rate between the native currency and an external currency (USD)." - ], - "label": "account_get_usd_per_cere", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Balance" - ], - "type": 12 - }, - "selector": "0xe4a4652a" - }, - { - "args": [ - { - "label": "usd_per_cere", - "type": { - "displayName": [ - "Balance" - ], - "type": 12 - } - } - ], - "docs": [ - " As price oracle, set the current conversion rate between the native currency and an external currency (USD).", - "", - " This requires the permission SetExchangeRate or SuperAdmin." - ], - "label": "account_set_usd_per_cere", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0x48d45ee8" - }, - { - "args": [ - { - "label": "grantee", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "label": "permission", - "type": { - "displayName": [ - "Permission" - ], - "type": 77 - } - } - ], - "docs": [ - " Check whether the given account has the given permission currently,", - " or the SuperAdmin permission." - ], - "label": "has_permission", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "bool" - ], - "type": 3 - }, - "selector": "0xe0942492" - }, - { - "args": [ - { - "label": "grantee", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "label": "permission", - "type": { - "displayName": [ - "Permission" - ], - "type": 77 - } - } - ], - "docs": [ - " As SuperAdmin, grant any permission to any account." - ], - "label": "admin_grant_permission", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0xbe41ea55" - }, - { - "args": [ - { - "label": "grantee", - "type": { - "displayName": [ - "AccountId" - ], - "type": 8 - } - }, - { - "label": "permission", - "type": { - "displayName": [ - "Permission" - ], - "type": 77 - } - } - ], - "docs": [ - " As SuperAdmin, revoke any permission to any account." - ], - "label": "admin_revoke_permission", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0x6b150666" - }, - { - "args": [ - { - "label": "amount", - "type": { - "displayName": [ - "Balance" - ], - "type": 12 - } - } - ], - "docs": [ - " As SuperAdmin, withdraw the funds held in custody in this contract.", - "", - " This is a temporary measure to allow migrating the funds to a new version of the contract." - ], - "label": "admin_withdraw", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0x2f6e0868" - }, - { - "args": [ - { - "label": "config", - "type": { - "displayName": [ - "FeeConfig" - ], - "type": 78 - } - } - ], - "docs": [ - " As SuperAdmin, set the network and cluster fee configuration." - ], - "label": "admin_set_fee_config", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0x00d441e7" - }, - { - "args": [], - "docs": [ - " Get all Account IDs stored in the SC" - ], - "label": "get_accounts", - "mutates": false, - "payable": true, - "returnType": { - "displayName": [ - "Vec" - ], - "type": 14 - }, - "selector": "0xef03ead7" - } - ] - }, - "storage": { - "struct": { - "fields": [ - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0000000000000000000000000000000000000000000000000000000000000000", - "ty": 0 - } - }, - "name": null - } - ] - } - }, - "name": "perms" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0100000000000000000000000000000000000000000000000000000000000000", - "ty": 6 - } - }, - "name": null - } - ] - } - }, - "name": "buckets" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0200000000000000000000000000000000000000000000000000000000000000", - "ty": 13 - } - }, - "name": "writers" - }, - { - "layout": { - "cell": { - "key": "0x0300000000000000000000000000000000000000000000000000000000000000", - "ty": 13 - } - }, - "name": "readers" - } - ] - } - }, - "name": "buckets_perms" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0400000000000000000000000000000000000000000000000000000000000000", - "ty": 15 - } - }, - "name": null - } - ] - } - }, - "name": "bucket_params" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0500000000000000000000000000000000000000000000000000000000000000", - "ty": 17 - } - }, - "name": null - } - ] - } - }, - "name": "clusters" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0600000000000000000000000000000000000000000000000000000000000000", - "ty": 24 - } - }, - "name": null - } - ] - } - }, - "name": "cdn_clusters" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0700000000000000000000000000000000000000000000000000000000000000", - "ty": 15 - } - }, - "name": null - } - ] - } - }, - "name": "cluster_params" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0800000000000000000000000000000000000000000000000000000000000000", - "ty": 26 - } - }, - "name": null - } - ] - } - }, - "name": "cdn_nodes" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0900000000000000000000000000000000000000000000000000000000000000", - "ty": 15 - } - }, - "name": null - } - ] - } - }, - "name": "cdn_node_params" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0a00000000000000000000000000000000000000000000000000000000000000", - "ty": 28 - } - }, - "name": "account_node" - }, - { - "layout": { - "cell": { - "key": "0x0b00000000000000000000000000000000000000000000000000000000000000", - "ty": 29 - } - }, - "name": "nodes" - } - ] - } - }, - "name": "nodes" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0c00000000000000000000000000000000000000000000000000000000000000", - "ty": 15 - } - }, - "name": null - } - ] - } - }, - "name": "node_params" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0d00000000000000000000000000000000000000000000000000000000000000", - "ty": 32 - } - }, - "name": null - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0e00000000000000000000000000000000000000000000000000000000000000", - "ty": 12 - } - }, - "name": null - } - ] - } - }, - "name": null - }, - { - "layout": { - "cell": { - "key": "0x0f00000000000000000000000000000000000000000000000000000000000000", - "ty": 14 - } - }, - "name": null - } - ] - } - }, - "name": "accounts" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x1000000000000000000000000000000000000000000000000000000000000000", - "ty": 12 - } - }, - "name": "network_fee_bp" - }, - { - "layout": { - "cell": { - "key": "0x1100000000000000000000000000000000000000000000000000000000000000", - "ty": 8 - } - }, - "name": "network_fee_destination" - }, - { - "layout": { - "cell": { - "key": "0x1200000000000000000000000000000000000000000000000000000000000000", - "ty": 12 - } - }, - "name": "cluster_management_fee_bp" - } - ] - } - }, - "name": null - } - ] - } - }, - "name": "network_fee" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x1300000000000000000000000000000000000000000000000000000000000000", - "ty": 8 - } - }, - "name": "operator_id" - }, - { - "layout": { - "cell": { - "key": "0x1400000000000000000000000000000000000000000000000000000000000000", - "ty": 34 - } - }, - "name": "commits" - }, - { - "layout": { - "cell": { - "key": "0x1500000000000000000000000000000000000000000000000000000000000000", - "ty": 39 - } - }, - "name": "validated_commits" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x1600000000000000000000000000000000000000000000000000000000000000", - "ty": 23 - } - }, - "name": "start" - }, - { - "layout": { - "cell": { - "key": "0x1700000000000000000000000000000000000000000000000000000000000000", - "ty": 23 - } - }, - "name": "interval" - }, - { - "layout": { - "cell": { - "key": "0x1800000000000000000000000000000000000000000000000000000000000000", - "ty": 23 - } - }, - "name": "commit_duration" - }, - { - "layout": { - "cell": { - "key": "0x1900000000000000000000000000000000000000000000000000000000000000", - "ty": 23 - } - }, - "name": "validation_duration" - } - ] - } - }, - "name": "era_settings" - } - ] - } - }, - "name": "committer_store" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x1a00000000000000000000000000000000000000000000000000000000000000", - "ty": 8 - } - }, - "name": "admin" - }, - { - "layout": { - "cell": { - "key": "0x1b00000000000000000000000000000000000000000000000000000000000000", - "ty": 9 - } - }, - "name": "fee_bp" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x1c00000000000000000000000000000000000000000000000000000000000000", - "ty": 12 - } - }, - "name": null - } - ] - } - }, - "name": "revenues" - } - ] - } - }, - "name": "protocol_store" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x1d00000000000000000000000000000000000000000000000000000000000000", - "ty": 41 - } - }, - "name": null - } - ] - } - }, - "name": "topology_store" - } - ] - } - }, - "types": [ - { - "id": 0, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 4, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 1 - }, - { - "name": "V", - "type": 3 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 1, - "type": { - "def": { - "sequence": { - "type": 2 - } - } - } - }, - { - "id": 2, - "type": { - "def": { - "primitive": "u8" - } - } - }, - { - "id": 3, - "type": { - "def": { - "primitive": "bool" - } - } - }, - { - "id": 4, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 5, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_primitives", - "Key" - ] - } - }, - { - "id": 5, - "type": { - "def": { - "array": { - "len": 32, - "type": 2 - } - } - } - }, - { - "id": 6, - "type": { - "def": { - "sequence": { - "type": 7 - } - } - } - }, - { - "id": 7, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "owner_id", - "type": 8, - "typeName": "AccountId" - }, - { - "name": "cluster_id", - "type": 9, - "typeName": "ClusterId" - }, - { - "name": "flow", - "type": 10, - "typeName": "Flow" - }, - { - "name": "resource_reserved", - "type": 9, - "typeName": "Resource" - }, - { - "name": "public_availability", - "type": 3, - "typeName": "bool" - }, - { - "name": "resource_consumption_cap", - "type": 9, - "typeName": "Resource" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "bucket", - "entity", - "Bucket" - ] - } - }, - { - "id": 8, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 5, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_env", - "types", - "AccountId" - ] - } - }, - { - "id": 9, - "type": { - "def": { - "primitive": "u32" - } - } - }, - { - "id": 10, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "from", - "type": 8, - "typeName": "AccountId" - }, - { - "name": "schedule", - "type": 11, - "typeName": "Schedule" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "flow", - "Flow" - ] - } - }, - { - "id": 11, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "rate", - "type": 12, - "typeName": "Balance" - }, - { - "name": "offset", - "type": 12, - "typeName": "Balance" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "schedule", - "Schedule" - ] - } - }, - { - "id": 12, - "type": { - "def": { - "primitive": "u128" - } - } - }, - { - "id": 13, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 4, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 9 - }, - { - "name": "V", - "type": 14 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 14, - "type": { - "def": { - "sequence": { - "type": 8 - } - } - } - }, - { - "id": 15, - "type": { - "def": { - "sequence": { - "type": 16 - } - } - } - }, - { - "id": 16, - "type": { - "def": { - "primitive": "str" - } - } - }, - { - "id": 17, - "type": { - "def": { - "sequence": { - "type": 18 - } - } - } - }, - { - "id": 18, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "manager_id", - "type": 8, - "typeName": "AccountId" - }, - { - "name": "resource_per_vnode", - "type": 9, - "typeName": "Resource" - }, - { - "name": "resource_used", - "type": 9, - "typeName": "Resource" - }, - { - "name": "revenues", - "type": 19, - "typeName": "Cash" - }, - { - "name": "node_ids", - "type": 20, - "typeName": "Vec" - }, - { - "name": "v_nodes", - "type": 21, - "typeName": "Vec>" - }, - { - "name": "total_rent", - "type": 12, - "typeName": "Balance" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "cluster", - "entity", - "Cluster" - ] - } - }, - { - "id": 19, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "value", - "type": 12, - "typeName": "Balance" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "cash", - "Cash" - ] - } - }, - { - "id": 20, - "type": { - "def": { - "sequence": { - "type": 9 - } - } - } - }, - { - "id": 21, - "type": { - "def": { - "sequence": { - "type": 22 - } - } - } - }, - { - "id": 22, - "type": { - "def": { - "sequence": { - "type": 23 - } - } - } - }, - { - "id": 23, - "type": { - "def": { - "primitive": "u64" - } - } - }, - { - "id": 24, - "type": { - "def": { - "sequence": { - "type": 25 - } - } - } - }, - { - "id": 25, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "manager_id", - "type": 8, - "typeName": "AccountId" - }, - { - "name": "cdn_nodes", - "type": 20, - "typeName": "Vec" - }, - { - "name": "resources_used", - "type": 9, - "typeName": "Resource" - }, - { - "name": "revenues", - "type": 19, - "typeName": "Cash" - }, - { - "name": "usd_per_gb", - "type": 12, - "typeName": "Balance" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "cdn_cluster", - "entity", - "CdnCluster" - ] - } - }, - { - "id": 26, - "type": { - "def": { - "sequence": { - "type": 27 - } - } - } - }, - { - "id": 27, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "provider_id", - "type": 8, - "typeName": "ProviderId" - }, - { - "name": "undistributed_payment", - "type": 12, - "typeName": "Balance" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "cdn_node", - "entity", - "CdnNode" - ] - } - }, - { - "id": 28, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 4, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 8 - }, - { - "name": "V", - "type": 9 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 29, - "type": { - "def": { - "sequence": { - "type": 30 - } - } - } - }, - { - "id": 30, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "provider_id", - "type": 8, - "typeName": "ProviderId" - }, - { - "name": "rent_per_month", - "type": 12, - "typeName": "Balance" - }, - { - "name": "free_resource", - "type": 9, - "typeName": "Resource" - }, - { - "name": "node_tag", - "type": 31, - "typeName": "NodeTag" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "node", - "entity", - "Node" - ] - } - }, - { - "id": 31, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "UNKNOWN" - }, - { - "index": 1, - "name": "ACTIVE" - }, - { - "index": 2, - "name": "ADDING" - }, - { - "index": 3, - "name": "DELETING" - }, - { - "index": 4, - "name": "OFFLINE" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "node", - "entity", - "NodeTag" - ] - } - }, - { - "id": 32, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 4, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 8 - }, - { - "name": "V", - "type": 33 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 33, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "deposit", - "type": 19, - "typeName": "Cash" - }, - { - "name": "bonded", - "type": 19, - "typeName": "Cash" - }, - { - "name": "negative", - "type": 19, - "typeName": "Cash" - }, - { - "name": "unbonded_amount", - "type": 19, - "typeName": "Cash" - }, - { - "name": "unbonded_timestamp", - "type": 23, - "typeName": "u64" - }, - { - "name": "payable_schedule", - "type": 11, - "typeName": "Schedule" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "account", - "entity", - "Account" - ] - } - }, - { - "id": 34, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 4, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 8 - }, - { - "name": "V", - "type": 35 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 35, - "type": { - "def": { - "sequence": { - "type": 36 - } - } - } - }, - { - "id": 36, - "type": { - "def": { - "tuple": [ - 9, - 37 - ] - } - } - }, - { - "id": 37, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "hash", - "type": 38, - "typeName": "Hash" - }, - { - "name": "total_logs", - "type": 12, - "typeName": "u128" - }, - { - "name": "from_timestamp", - "type": 23, - "typeName": "u64" - }, - { - "name": "to_timestamp", - "type": 23, - "typeName": "u64" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "committer", - "store", - "Commit" - ] - } - }, - { - "id": 38, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 5, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_env", - "types", - "Hash" - ] - } - }, - { - "id": 39, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 4, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 9 - }, - { - "name": "V", - "type": 40 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 40, - "type": { - "def": { - "tuple": [ - 23, - 23 - ] - } - } - }, - { - "id": 41, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 4, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 42 - }, - { - "name": "V", - "type": 9 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 42, - "type": { - "def": { - "tuple": [ - 9, - 23 - ] - } - } - }, - { - "id": 43, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "None" - }, - { - "fields": [ - { - "type": 8 - } - ], - "index": 1, - "name": "Some" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 8 - } - ], - "path": [ - "Option" - ] - } - }, - { - "id": 44, - "type": { - "def": { - "tuple": [] - } - } - }, - { - "id": 45, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 46 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 48 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 46 - }, - { - "name": "E", - "type": 48 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 46, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "bucket_id", - "type": 9, - "typeName": "BucketId" - }, - { - "name": "bucket", - "type": 47, - "typeName": "BucketInStatus" - }, - { - "name": "params", - "type": 16, - "typeName": "BucketParams" - }, - { - "name": "writer_ids", - "type": 14, - "typeName": "Vec" - }, - { - "name": "reader_ids", - "type": 14, - "typeName": "Vec" - }, - { - "name": "rent_covered_until_ms", - "type": 23, - "typeName": "u64" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "bucket", - "entity", - "BucketStatus" - ] - } - }, - { - "id": 47, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "owner_id", - "type": 8, - "typeName": "AccountId" - }, - { - "name": "cluster_id", - "type": 9, - "typeName": "ClusterId" - }, - { - "name": "resource_reserved", - "type": 9, - "typeName": "Resource" - }, - { - "name": "public_availability", - "type": 3, - "typeName": "bool" - }, - { - "name": "resource_consumption_cap", - "type": 9, - "typeName": "Resource" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "bucket", - "entity", - "BucketInStatus" - ] - } - }, - { - "id": 48, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "BucketDoesNotExist" - }, - { - "index": 1, - "name": "ClusterDoesNotExist" - }, - { - "index": 2, - "name": "ParamsTooBig" - }, - { - "index": 3, - "name": "VNodeDoesNotExist" - }, - { - "index": 4, - "name": "BondingPeriodNotFinished" - }, - { - "index": 5, - "name": "BucketClusterAlreadyConnected" - }, - { - "index": 6, - "name": "BucketClusterNotSetup" - }, - { - "index": 7, - "name": "NodeDoesNotExist" - }, - { - "index": 8, - "name": "NodeAlreadyExists" - }, - { - "index": 9, - "name": "FlowDoesNotExist" - }, - { - "index": 10, - "name": "AccountDoesNotExist" - }, - { - "index": 11, - "name": "ParamsDoesNotExist" - }, - { - "index": 12, - "name": "UnauthorizedProvider" - }, - { - "index": 13, - "name": "UnauthorizedOwner" - }, - { - "index": 14, - "name": "UnauthorizedClusterManager" - }, - { - "index": 15, - "name": "ClusterManagerIsNotTrusted" - }, - { - "index": 16, - "name": "TransferFailed" - }, - { - "index": 17, - "name": "InsufficientBalance" - }, - { - "index": 18, - "name": "InsufficientResources" - }, - { - "index": 19, - "name": "Unauthorized" - }, - { - "index": 20, - "name": "UnknownNode" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "Error" - ] - } - }, - { - "id": 49, - "type": { - "def": { - "tuple": [ - 50, - 9 - ] - } - } - }, - { - "id": 50, - "type": { - "def": { - "sequence": { - "type": 46 - } - } - } - }, - { - "id": 51, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 52 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 48 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 52 - }, - { - "name": "E", - "type": 48 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 52, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "cluster_id", - "type": 9, - "typeName": "ClusterId" - }, - { - "name": "cluster", - "type": 18, - "typeName": "Cluster" - }, - { - "name": "params", - "type": 16, - "typeName": "Params" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "cluster", - "entity", - "ClusterStatus" - ] - } - }, - { - "id": 53, - "type": { - "def": { - "tuple": [ - 54, - 9 - ] - } - } - }, - { - "id": 54, - "type": { - "def": { - "sequence": { - "type": 52 - } - } - } - }, - { - "id": 55, - "type": { - "def": { - "sequence": { - "type": 56 - } - } - } - }, - { - "id": 56, - "type": { - "def": { - "tuple": [ - 8, - 12 - ] - } - } - }, - { - "id": 57, - "type": { - "def": { - "sequence": { - "type": 58 - } - } - } - }, - { - "id": 58, - "type": { - "def": { - "tuple": [ - 9, - 12 - ] - } - } - }, - { - "id": 59, - "type": { - "def": { - "sequence": { - "type": 60 - } - } - } - }, - { - "id": 60, - "type": { - "def": { - "tuple": [ - 9, - 9 - ] - } - } - }, - { - "id": 61, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 62 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 48 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 62 - }, - { - "name": "E", - "type": 48 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 62, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "cluster_id", - "type": 9, - "typeName": "ClusterId" - }, - { - "name": "cluster", - "type": 25, - "typeName": "CdnCluster" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "cdn_cluster", - "entity", - "CdnClusterStatus" - ] - } - }, - { - "id": 63, - "type": { - "def": { - "tuple": [ - 64, - 9 - ] - } - } - }, - { - "id": 64, - "type": { - "def": { - "sequence": { - "type": 62 - } - } - } - }, - { - "id": 65, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "start", - "type": 23, - "typeName": "u64" - }, - { - "name": "interval", - "type": 23, - "typeName": "u64" - }, - { - "name": "commit_duration", - "type": 23, - "typeName": "u64" - }, - { - "name": "validation_duration", - "type": 23, - "typeName": "u64" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "committer", - "store", - "EraConfig" - ] - } - }, - { - "id": 66, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "current_era", - "type": 23, - "typeName": "u64" - }, - { - "name": "current_phase", - "type": 67, - "typeName": "Phase" - }, - { - "name": "previous_era", - "type": 23, - "typeName": "u64" - }, - { - "name": "prev_era_from_timestamp", - "type": 23, - "typeName": "u64" - }, - { - "name": "prev_era_to_timestamp", - "type": 23, - "typeName": "u64" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "committer", - "store", - "EraStatus" - ] - } - }, - { - "id": 67, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Commit" - }, - { - "index": 1, - "name": "Valiadation" - }, - { - "index": 2, - "name": "Payout" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "committer", - "store", - "Phase" - ] - } - }, - { - "id": 68, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 69 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 48 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 69 - }, - { - "name": "E", - "type": 48 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 69, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "node_id", - "type": 9, - "typeName": "NodeId" - }, - { - "name": "node", - "type": 27, - "typeName": "CdnNode" - }, - { - "name": "params", - "type": 16, - "typeName": "Params" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "cdn_node", - "entity", - "CdnNodeStatus" - ] - } - }, - { - "id": 70, - "type": { - "def": { - "tuple": [ - 71, - 9 - ] - } - } - }, - { - "id": 71, - "type": { - "def": { - "sequence": { - "type": 69 - } - } - } - }, - { - "id": 72, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 73 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 48 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 73 - }, - { - "name": "E", - "type": 48 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 73, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "node_id", - "type": 9, - "typeName": "NodeId" - }, - { - "name": "node", - "type": 30, - "typeName": "Node" - }, - { - "name": "params", - "type": 16, - "typeName": "Params" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "node", - "entity", - "NodeStatus" - ] - } - }, - { - "id": 74, - "type": { - "def": { - "tuple": [ - 75, - 9 - ] - } - } - }, - { - "id": 75, - "type": { - "def": { - "sequence": { - "type": 73 - } - } - } - }, - { - "id": 76, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 33 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 48 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 33 - }, - { - "name": "E", - "type": 48 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 77, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 8, - "typeName": "AccountId" - } - ], - "index": 0, - "name": "ManagerTrustedBy" - }, - { - "index": 1, - "name": "SetExchangeRate" - }, - { - "index": 2, - "name": "SuperAdmin" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "perm", - "entity", - "Permission" - ] - } - }, - { - "id": 78, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "network_fee_bp", - "type": 12, - "typeName": "BasisPoints" - }, - { - "name": "network_fee_destination", - "type": 8, - "typeName": "AccountId" - }, - { - "name": "cluster_management_fee_bp", - "type": 12, - "typeName": "BasisPoints" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "network_fee", - "FeeConfig" - ] - } - } - ] - } -} \ No newline at end of file diff --git a/bucket/scripts/package-lock.json b/bucket/scripts/package-lock.json deleted file mode 100644 index 0411d6de..00000000 --- a/bucket/scripts/package-lock.json +++ /dev/null @@ -1,789 +0,0 @@ -{ - "name": "scripts", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "@polkadot/api": "^10.9.1", - "@polkadot/api-contract": "^10.9.1", - "@polkadot/util-crypto": "^12.3.2" - } - }, - "node_modules/@noble/curves": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", - "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", - "dependencies": { - "@noble/hashes": "1.3.1" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", - "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/api": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.9.1.tgz", - "integrity": "sha512-ND/2UqZBWvtt4PfV03OStTKg0mxmPk4UpMAgJKutdgsz/wP9CYJ1KbjwFgPNekL9JnzbKQsWyQNPVrcw7kQk8A==", - "dependencies": { - "@polkadot/api-augment": "10.9.1", - "@polkadot/api-base": "10.9.1", - "@polkadot/api-derive": "10.9.1", - "@polkadot/keyring": "^12.3.1", - "@polkadot/rpc-augment": "10.9.1", - "@polkadot/rpc-core": "10.9.1", - "@polkadot/rpc-provider": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-augment": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/types-create": "10.9.1", - "@polkadot/types-known": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "eventemitter3": "^5.0.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/api-augment": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.9.1.tgz", - "integrity": "sha512-kRZZvCFVcN4hAH4dJ+Qzfdy27/4EEq3oLDf3ihj0LTVrAezSWcKPGE3EVFy+Mn6Lo4SUc7RVyoKvIUhSk2l4Dg==", - "dependencies": { - "@polkadot/api-base": "10.9.1", - "@polkadot/rpc-augment": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-augment": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/api-base": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.9.1.tgz", - "integrity": "sha512-Q3m2KzlceMK2kX8bhnUZWk3RT6emmijeeFZZQgCePpEcrSeNjnqG4qjuTPgkveaOkUT8MAoDc5Avuzcc2jlW9g==", - "dependencies": { - "@polkadot/rpc-core": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/util": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/api-contract": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-contract/-/api-contract-10.9.1.tgz", - "integrity": "sha512-BJjFYSFtsUlClC3mgvPNQ5h/7LZd5gVfexwl+mDgLd/6SN4dUBQjIXhlIL5a1cGYjq3EROuu7t0agG2DIaiZMQ==", - "dependencies": { - "@polkadot/api": "10.9.1", - "@polkadot/api-augment": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/types-create": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/api-derive": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.9.1.tgz", - "integrity": "sha512-mRud1UZCFIc4Z63qAoGSIHh/foyUYADfy1RQYCmPpeFKfIdCIrHpd7xFdJXTOMYOS0BwlM6u4qli/ZT4XigezQ==", - "dependencies": { - "@polkadot/api": "10.9.1", - "@polkadot/api-augment": "10.9.1", - "@polkadot/api-base": "10.9.1", - "@polkadot/rpc-core": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/keyring": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.3.2.tgz", - "integrity": "sha512-NTdtDeI0DP9l/45hXynNABeP5VB8piw5YR+CbUxK2e36xpJWVXwbcOepzslg5ghE9rs8UKJb30Z/HqTU4sBY0Q==", - "dependencies": { - "@polkadot/util": "12.3.2", - "@polkadot/util-crypto": "12.3.2", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "12.3.2", - "@polkadot/util-crypto": "12.3.2" - } - }, - "node_modules/@polkadot/networks": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.3.2.tgz", - "integrity": "sha512-uCkyybKoeEm1daKr0uT/9oNDHDDzCy2/ZdVl346hQqfdR1Ct3BaxMjxqvdmb5N8aCw0cBWSfgsxAYtw8ESmllQ==", - "dependencies": { - "@polkadot/util": "12.3.2", - "@substrate/ss58-registry": "^1.40.0", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/rpc-augment": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.9.1.tgz", - "integrity": "sha512-MaLHkNlyqN20ZRYr6uNd1BZr1OsrnX9qLAmsl0mcrri1vPGRH6VHjfFH1RBLkikpWD82v17g0l2hLwdV1ZHMcw==", - "dependencies": { - "@polkadot/rpc-core": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/rpc-core": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.9.1.tgz", - "integrity": "sha512-ZtA8B8SfXSAwVkBlCcKRHw0eSM7ec/sbiNOM5GasXPeRujUgT7lOwSH2GbUZSqe9RfRDMp6DvO9c2JoGc3LLWw==", - "dependencies": { - "@polkadot/rpc-augment": "10.9.1", - "@polkadot/rpc-provider": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/util": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/rpc-provider": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.9.1.tgz", - "integrity": "sha512-4QzT2QzD+320+eT6b79sGAA85Tt3Bb8fQvse4r5Mom2iiBd2SO81vOhxSAOaIe4GUsw25VzFJmsbe7+OObItdg==", - "dependencies": { - "@polkadot/keyring": "^12.3.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-support": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "@polkadot/x-fetch": "^12.3.1", - "@polkadot/x-global": "^12.3.1", - "@polkadot/x-ws": "^12.3.1", - "eventemitter3": "^5.0.1", - "mock-socket": "^9.2.1", - "nock": "^13.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - }, - "optionalDependencies": { - "@substrate/connect": "0.7.26" - } - }, - "node_modules/@polkadot/types": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.9.1.tgz", - "integrity": "sha512-AG33i2ZGGfq7u+5rkAdGrXAQHHl844/Yv+junH5ZzX69xiCoWO1bH/yzDUNBdpki2GlACWvF9nLYh3F2tVF93w==", - "dependencies": { - "@polkadot/keyring": "^12.3.1", - "@polkadot/types-augment": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/types-create": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/types-augment": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.9.1.tgz", - "integrity": "sha512-OY9/jTMFRFqYdkUnfcGwqMLC64A0Q25bjvCuVQCVjsPFKE3wl0Kt5rNT01eV2UmLXrR6fY0xWbR2w80bLA7CIQ==", - "dependencies": { - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/types-codec": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.9.1.tgz", - "integrity": "sha512-mJ5OegKGraY1FLvEa8FopRCr3pQrhDkcn5RNOjmgJQozENVeRaxhk0NwxYz7IojFvSDnKnc6lNQfKaaSe5pLHg==", - "dependencies": { - "@polkadot/util": "^12.3.1", - "@polkadot/x-bigint": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/types-create": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.9.1.tgz", - "integrity": "sha512-OVz50MGTTuiuVnRP/zAx4CTuLioc0hsiwNwqN2lNhmIJGtnQ4Vy/7mQRsIWehiYz6g0Vzzm5B3qWkTXO1NSN5w==", - "dependencies": { - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/types-known": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.9.1.tgz", - "integrity": "sha512-zCMVWc4pJtkbMFPu72bD4IhvV/gkHXPX3C5uu92WdmCfnn0vEIEsMKWlVXVVvQQZKAqvs/awpqIfrUtEViOGEA==", - "dependencies": { - "@polkadot/networks": "^12.3.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/types-create": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/types-support": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.9.1.tgz", - "integrity": "sha512-XsieuLDsszvMZQlleacQBfx07i/JkwQV/UxH9q8Hz7Okmaz9pEVEW1h3ka2/cPuC7a4l32JhaORBUYshBZNdJg==", - "dependencies": { - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/util": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.3.2.tgz", - "integrity": "sha512-y/JShcGyOamCUiSIg++XZuLHt1ktSKBaSH2K5Nw5NXlgP0+7am+GZzqPB8fQ4qhYLruEOv+YRiz0GC1Zr9S+wg==", - "dependencies": { - "@polkadot/x-bigint": "12.3.2", - "@polkadot/x-global": "12.3.2", - "@polkadot/x-textdecoder": "12.3.2", - "@polkadot/x-textencoder": "12.3.2", - "@types/bn.js": "^5.1.1", - "bn.js": "^5.2.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/util-crypto": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.3.2.tgz", - "integrity": "sha512-pTpx+YxolY0BDT4RcGmgeKbHHD/dI6Ll9xRsqmVdIjpcVVY20uDNTyXs81ZNtfKgyod1y9JQkfNv2Dz9iEpTkQ==", - "dependencies": { - "@noble/curves": "1.1.0", - "@noble/hashes": "1.3.1", - "@polkadot/networks": "12.3.2", - "@polkadot/util": "12.3.2", - "@polkadot/wasm-crypto": "^7.2.1", - "@polkadot/wasm-util": "^7.2.1", - "@polkadot/x-bigint": "12.3.2", - "@polkadot/x-randomvalues": "12.3.2", - "@scure/base": "1.1.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "12.3.2" - } - }, - "node_modules/@polkadot/wasm-bridge": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.2.1.tgz", - "integrity": "sha512-uV/LHREDBGBbHrrv7HTki+Klw0PYZzFomagFWII4lp6Toj/VCvRh5WMzooVC+g/XsBGosAwrvBhoModabyHx+A==", - "dependencies": { - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.2.1.tgz", - "integrity": "sha512-SA2+33S9TAwGhniKgztVN6pxUKpGfN4Tre/eUZGUfpgRkT92wIUT2GpGWQE+fCCqGQgADrNiBcwt6XwdPqMQ4Q==", - "dependencies": { - "@polkadot/wasm-bridge": "7.2.1", - "@polkadot/wasm-crypto-asmjs": "7.2.1", - "@polkadot/wasm-crypto-init": "7.2.1", - "@polkadot/wasm-crypto-wasm": "7.2.1", - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-asmjs": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.2.1.tgz", - "integrity": "sha512-z/d21bmxyVfkzGsKef/FWswKX02x5lK97f4NPBZ9XBeiFkmzlXhdSnu58/+b1sKsRAGdW/Rn/rTNRDhW0GqCAg==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-init": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.2.1.tgz", - "integrity": "sha512-GcEXtwN9LcSf32V9zSaYjHImFw16hCyo2Xzg4GLLDPPeaAAfbFr2oQMgwyDbvBrBjLKHVHjsPZyGhXae831amw==", - "dependencies": { - "@polkadot/wasm-bridge": "7.2.1", - "@polkadot/wasm-crypto-asmjs": "7.2.1", - "@polkadot/wasm-crypto-wasm": "7.2.1", - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-wasm": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.2.1.tgz", - "integrity": "sha512-DqyXE4rSD0CVlLIw88B58+HHNyrvm+JAnYyuEDYZwCvzUWOCNos/DDg9wi/K39VAIsCCKDmwKqkkfIofuOj/lA==", - "dependencies": { - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/wasm-util": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.2.1.tgz", - "integrity": "sha512-FBSn/3aYJzhN0sYAYhHB8y9JL8mVgxLy4M1kUXYbyo+8GLRQEN5rns8Vcb8TAlIzBWgVTOOptYBvxo0oj0h7Og==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/x-bigint": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.3.2.tgz", - "integrity": "sha512-JLqLgfGXe/x+hZJETd5ZqfpVsbwyMsH5Nn1Q20ineMMjXN/ig+kVR8Mc15LXBMuw4g7LldFW6UUrotWnuMI8Yw==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/x-fetch": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.3.2.tgz", - "integrity": "sha512-3IEuZ5S+RI/t33NsdPLIIa5COfDCfpUW2sbaByEczn75aD1jLqJZSEDwiBniJ2osyNd4uUxBf6e5jw7LAZeZJg==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "node-fetch": "^3.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/x-global": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.3.2.tgz", - "integrity": "sha512-yVZq6oIegjlyh5rUZiTklgu+fL+W/DG1ypEa02683tUCB3avV5cA3PAHKptMSlb6FpweHu37lKKrqfAWrraDxg==", - "dependencies": { - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/x-randomvalues": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.3.2.tgz", - "integrity": "sha512-ywjIs8CWpvOGmq+3cGCNPOHxAjPHdBUiXyDccftx5BRVdmtbt36gK/V84bKr6Xs73FGu0jprUAOSRRsLZX/3dg==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "12.3.2", - "@polkadot/wasm-util": "*" - } - }, - "node_modules/@polkadot/x-textdecoder": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.3.2.tgz", - "integrity": "sha512-lY5bfA5xArJRWEJlYOlQQMJeTjWD8s0yMhchirVgf5xj8Id9vPGeUoneH+VFDEwgXxrqBvDFJ4smN4T/r6a/fg==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/x-textencoder": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.3.2.tgz", - "integrity": "sha512-iP3qEBiHzBckQ9zeY7ZHRWuu7mCEg5SMpOugs6UODRk8sx6KHzGQYlghBbWLit0uppPDVE0ifEwZ2n73djJHWQ==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/x-ws": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.3.2.tgz", - "integrity": "sha512-yM9Z64pLNlHpJE43+Xtr+iUXmYpFFY5u5hrke2PJt13O48H8f9Vb9cRaIh94appLyICoS0aekGhDkGH+MCspBA==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3", - "ws": "^8.13.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@scure/base": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/@substrate/connect": { - "version": "0.7.26", - "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.26.tgz", - "integrity": "sha512-uuGSiroGuKWj1+38n1kY5HReer5iL9bRwPCzuoLtqAOmI1fGI0hsSI2LlNQMAbfRgr7VRHXOk5MTuQf5ulsFRw==", - "optional": true, - "dependencies": { - "@substrate/connect-extension-protocol": "^1.0.1", - "eventemitter3": "^4.0.7", - "smoldot": "1.0.4" - } - }, - "node_modules/@substrate/connect-extension-protocol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz", - "integrity": "sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg==", - "optional": true - }, - "node_modules/@substrate/connect/node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "optional": true - }, - "node_modules/@substrate/ss58-registry": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.40.0.tgz", - "integrity": "sha512-QuU2nBql3J4KCnOWtWDw4n1K4JU0T79j54ZZvm/9nhsX6AIar13FyhsaBfs6QkJ2ixTQAnd7TocJIoJRWbqMZA==" - }, - "node_modules/@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", - "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/mock-socket": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.2.1.tgz", - "integrity": "sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/nock": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.1.tgz", - "integrity": "sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw==", - "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.21", - "propagate": "^2.0.0" - }, - "engines": { - "node": ">= 10.13" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", - "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "optional": true - }, - "node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/smoldot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-1.0.4.tgz", - "integrity": "sha512-N3TazI1C4GGrseFH/piWyZCCCRJTRx2QhDfrUKRT4SzILlW5m8ayZ3QTKICcz1C/536T9cbHHJyP7afxI6Mi1A==", - "optional": true, - "dependencies": { - "pako": "^2.0.4", - "ws": "^8.8.1" - } - }, - "node_modules/tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" - }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - } - } -} diff --git a/bucket/scripts/package.json b/bucket/scripts/package.json deleted file mode 100644 index bc305466..00000000 --- a/bucket/scripts/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "type": "module", - "dependencies": { - "@polkadot/api": "^10.9.1", - "@polkadot/api-contract": "^10.9.1", - "@polkadot/util-crypto": "^12.3.2" - } -} diff --git a/build-and-deploy.sh b/build-and-deploy.sh index 343ba632..83316aae 100755 --- a/build-and-deploy.sh +++ b/build-and-deploy.sh @@ -5,10 +5,7 @@ set -eu cargo +nightly-2023-02-07 test cargo +nightly-2023-02-07 contract build --release --manifest-path bucket/Cargo.toml -cargo +nightly-2023-02-07 contract build --release --manifest-path ddc_nft_registry/Cargo.toml cp target/ink/ddc_bucket/metadata.json scripts/sdk/src/abi/ddc_bucket.json -cp target/ink/ddc_nft_registry/metadata.json scripts/sdk/src/abi/ddc_nft_registry.json -yarn --cwd scripts deploy-ddc-bucket -yarn --cwd scripts deploy-ddc-nft-registry \ No newline at end of file +yarn --cwd scripts deploy-ddc-bucket \ No newline at end of file diff --git a/ddc_nft_registry/Cargo.toml b/ddc_nft_registry/Cargo.toml deleted file mode 100755 index f3248ff8..00000000 --- a/ddc_nft_registry/Cargo.toml +++ /dev/null @@ -1,66 +0,0 @@ -[package] -name = "ddc_nft_registry" -version = "0.5.2" -authors = ["Aurélien Nicolas ", "Anton Volk "] -edition = "2021" - -[dependencies] -scale = { package = "parity-scale-codec", version = "=3.1.2", default-features = false, features = ["derive"] } -scale-info = { version = "=2.0.1", default-features = false, features = ["derive"], optional = true } - -ink_primitives = { version = "3.4.0", default-features = false } -ink_metadata = { version = "3.4.0", default-features = false, features = ["derive"], optional = true } -ink_env = { version = "3.4.0", default-features = false } -ink_storage = { version = "3.4.0", default-features = false } -ink_lang = { version = "3.4.0", default-features = false } -ink_prelude = { version = "3.4.0", default-features = false } - -# Transitive dependencies necessary to fix the version. -ink_allocator = { version = "3.4.0", default-features = false, optional = true } -ink_lang_codegen = { version = "3.4.0", default-features = false, optional = true } -ink_storage_derive = { version = "3.4.0", default-features = false, optional = true } -ink_lang_ir = { version = "3.4.0", default-features = false, optional = true } -ink_lang_macro = { version = "3.4.0", default-features = false, optional = true } - -serde = { version = "1.0.136", optional = true } -serde_json = { version = "1.0.79", optional = true } - -[lib] -name = "ddc_nft_registry" -path = "lib.rs" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["std"] -std = [ - "scale/std", - "scale-info/std", - "ink_metadata/std", - "ink_env/std", - "ink_storage/std", - "ink_primitives/std", - "ink_prelude/std", - # Transitive dependencies. - "ink_allocator/std", - "ink_lang_codegen/std", - #"ink_storage_derive/std", - "ink_lang_ir/std", - "ink_lang_macro/std", - - # For tests - "serde", - "serde_json", -] -ink-as-dependency = [] - - -# Needed until https://github.com/paritytech/ink/issues/364 is resolved. -[profile.release] -overflow-checks = false -[profile.dev] -overflow-checks = false diff --git a/ddc_nft_registry/ddc_nft_registry/attachment/entity.rs b/ddc_nft_registry/ddc_nft_registry/attachment/entity.rs deleted file mode 100644 index c9fdc792..00000000 --- a/ddc_nft_registry/ddc_nft_registry/attachment/entity.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! The data structure of Attachments. - -use ink_storage::traits::{PackedLayout, SpreadLayout}; -use scale::{Decode, Encode}; -use ink_prelude::string::String; -use crate::ddc_nft_registry::contract_fee::{SIZE_ACCOUNT_ID, SIZE_HASHMAP, SIZE_PER_RECORD, SIZE_VEC}; -use crate::ddc_nft_registry::AccountId; - -pub type NftId = String; -pub type AssetId = String; -pub type Proof = String; - -#[derive(Clone, PartialEq, Encode, Decode, SpreadLayout, PackedLayout)] -#[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] -pub struct Attachment { - pub reporter_id: AccountId, - pub nft_id: NftId, - pub asset_id: AssetId, - pub proof: Proof, -} - -impl Attachment { - pub fn record_size(&self) -> usize { - return SIZE_PER_RECORD + SIZE_HASHMAP // Map overhead - + SIZE_VEC // Key string - + SIZE_VEC * 3 // Value strings - + SIZE_ACCOUNT_ID // Reporter ID - + self.nft_id.len() * 2 // NFT ID in key and in value - + self.asset_id.len() // Asset ID - + self.proof.len(); // Proof data - } -} - -#[derive(Clone, PartialEq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] -pub struct AttachmentStatus { - pub attachment: Attachment, -} diff --git a/ddc_nft_registry/ddc_nft_registry/attachment/messages.rs b/ddc_nft_registry/ddc_nft_registry/attachment/messages.rs deleted file mode 100644 index bdfc9a87..00000000 --- a/ddc_nft_registry/ddc_nft_registry/attachment/messages.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! The public interface to manage Attachments. - -use ink_lang::codegen::{EmitEvent, StaticEnv}; - -use crate::ddc_nft_registry::{Attach, DdcNftRegistry, Result}; -use crate::ddc_nft_registry::attachment::entity::{AssetId, AttachmentStatus, NftId, Proof}; - -impl DdcNftRegistry { - pub fn message_attach(&mut self, nft_id: NftId, asset_id: AssetId, proof: Proof) -> Result<()> { - let reporter_id = Self::env().caller(); - let attachment = self.attachments.create(reporter_id, nft_id, asset_id, proof)?; - Self::capture_fee_and_refund(attachment.record_size())?; - Self::env().emit_event(Attach { reporter_id, nft_id: attachment.nft_id, asset_id: attachment.asset_id, proof: attachment.proof }); - Ok(()) - } - - pub fn message_report(&mut self, nft_id: NftId, asset_id: AssetId, proof: Proof) -> Result<()> { - let reporter_id = Self::env().caller(); - Self::env().emit_event(Attach { reporter_id, nft_id, asset_id, proof }); - Ok(()) - } - - pub fn message_get_by_nft_id(&mut self, nft_id: NftId) -> Result { - let attachment = self.attachments.get_by_nft_id(nft_id)?; - Ok(AttachmentStatus { attachment }) - } - -} diff --git a/ddc_nft_registry/ddc_nft_registry/attachment/mod.rs b/ddc_nft_registry/ddc_nft_registry/attachment/mod.rs deleted file mode 100644 index bdb87cee..00000000 --- a/ddc_nft_registry/ddc_nft_registry/attachment/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Attachment management. - -pub mod messages; -pub mod entity; -pub mod store; diff --git a/ddc_nft_registry/ddc_nft_registry/attachment/store.rs b/ddc_nft_registry/ddc_nft_registry/attachment/store.rs deleted file mode 100644 index 0353050f..00000000 --- a/ddc_nft_registry/ddc_nft_registry/attachment/store.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! The store to create and access Attachments. - -use ink_storage::Mapping; -use ink_storage::traits::{SpreadAllocate, SpreadLayout, StorageLayout}; - -use crate::ddc_nft_registry::{AccountId, Error, Error::*}; -use crate::ddc_nft_registry::attachment::entity::{AssetId, NftId, Proof}; - -use super::entity::Attachment; - -#[derive(SpreadAllocate, SpreadLayout, Default)] -#[cfg_attr(feature = "std", derive(StorageLayout, Debug))] -pub struct AttachmentStore(pub Mapping); - -impl AttachmentStore { - #[must_use] - pub fn create(&mut self, reporter_id: AccountId, nft_id: NftId, asset_id: AssetId, proof: Proof) -> Result { - let attachment = Attachment { - reporter_id, - nft_id, - asset_id, - proof, - }; - - // If exists, check that this is the same reporter. - if let Some(previous) = self.0.get(&attachment.nft_id) { - if previous.reporter_id != reporter_id { - return Err(UnauthorizedUpdate); - } - } - - self.0.insert(attachment.nft_id.clone(), &attachment); - Ok(attachment) - } - - pub fn get_by_nft_id(&mut self, nft_id: NftId) -> Result { - return self.0.get(&nft_id).map(|a| a.clone()).ok_or(AttachmentDoesNotExist); - } -} diff --git a/ddc_nft_registry/ddc_nft_registry/cash/entity.rs b/ddc_nft_registry/ddc_nft_registry/cash/entity.rs deleted file mode 100644 index 2cc4b479..00000000 --- a/ddc_nft_registry/ddc_nft_registry/cash/entity.rs +++ /dev/null @@ -1,79 +0,0 @@ -//! Cash and Payable represent money and debt. -//! -//! These data structures facilitate the correctness of money-related calculations using the Rust type system. - -use ink_storage::traits::{PackedLayout, SpreadLayout}; -use scale::{Decode, Encode}; - -use crate::ddc_nft_registry::{Balance, InsufficientBalance, Result}; - -// TODO: remove Clone. -/// Cash represents some value that was taken from someone, and that must be credited to someone. -#[must_use] -#[derive(Clone, PartialEq, Encode, Decode, SpreadLayout, PackedLayout)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct Cash(pub Balance); - -/// Payable represents some value that was credited to someone, and that must be paid by someone. -/// Payable must be covered by Cash at all times to guarantee the balance of the contract. -#[must_use] -#[derive(PartialEq, Encode, Decode, SpreadLayout, PackedLayout)] -#[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] -pub struct Payable(pub Balance); - -impl Cash { - pub fn borrow_payable_cash(amount: Balance) -> (Payable, Cash) { - (Payable(amount), Cash(amount)) - } - - #[must_use] - pub fn consume(self) -> Balance { self.0 } - - pub fn peek(&self) -> Balance { self.0 } - - pub fn increase(&mut self, cash: Cash) { - self.0 += cash.consume(); - } - - pub fn pay(&mut self, payable: Payable) -> Result<()> { - if self.peek() >= payable.peek() { - self.pay_unchecked(payable); - Ok(()) - } else { - Err(InsufficientBalance) - } - } - - pub fn pay_unchecked(&mut self, payable: Payable) { - self.0 -= payable.consume(); - } -} - -impl Payable { - pub fn new(amount: Balance) -> Self { - Self(amount) - } - - #[must_use] - pub fn consume(self) -> Balance { self.0 } - - pub fn peek(&self) -> Balance { self.0 } -} - -// Implement TypeInfo with a field "value" to work with polkadot.js. -#[cfg(feature = "std")] -impl ::scale_info::TypeInfo for Cash { - type Identity = Self; - fn type_info() -> ::scale_info::Type { - ::scale_info::Type::builder() - .path(::scale_info::Path::new( - "Cash", - "ddc_bucket::ddc_bucket::cash", - )) - .type_params([]) - .composite( - ::scale_info::build::Fields::named() - .field(|f| f.ty::().name("value").type_name("Balance")) - ) - } -} diff --git a/ddc_nft_registry/ddc_nft_registry/cash/messages.rs b/ddc_nft_registry/ddc_nft_registry/cash/messages.rs deleted file mode 100644 index 631fbf7b..00000000 --- a/ddc_nft_registry/ddc_nft_registry/cash/messages.rs +++ /dev/null @@ -1,19 +0,0 @@ -use ink_lang::codegen::StaticEnv; - -use crate::ddc_nft_registry::{AccountId, DdcNftRegistry, Result}; - -use super::entity::Cash; - -impl DdcNftRegistry { - pub fn receive_cash() -> Cash { - Cash(Self::env().transferred_value()) - } - - pub fn send_cash(destination: AccountId, cash: Cash) -> Result<()> { - if cash.peek() == 0 { return Ok(()); } - match Self::env().transfer(destination, cash.consume()) { - Err(_e) => panic!("Transfer failed"), // Err(Error::TransferFailed), - Ok(_v) => Ok(()), - } - } -} diff --git a/ddc_nft_registry/ddc_nft_registry/cash/mod.rs b/ddc_nft_registry/ddc_nft_registry/cash/mod.rs deleted file mode 100644 index 065d9338..00000000 --- a/ddc_nft_registry/ddc_nft_registry/cash/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod entity; -pub mod messages; diff --git a/ddc_nft_registry/ddc_nft_registry/contract_fee.rs b/ddc_nft_registry/ddc_nft_registry/contract_fee.rs deleted file mode 100644 index e92c73d6..00000000 --- a/ddc_nft_registry/ddc_nft_registry/contract_fee.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! This module captures fees necessary to pay for the storage of the contract state. - -use ink_lang::codegen::StaticEnv; - -use crate::ddc_nft_registry::{Balance, DdcNftRegistry, Result, TOKEN}; -use crate::ddc_nft_registry::cash::entity::Payable; - -pub const FEE_PER_BYTE: Balance = TOKEN / 100; - -/// The minimum size assumed for a new object to cover the overhead of storage keys, -/// in addition to the size of the object itself. -pub const SIZE_PER_RECORD: usize = 32; - -/// The size to cover the overhead of HashMap. -pub const SIZE_HASHMAP: usize = 32; - -pub const SIZE_ACCOUNT_ID: usize = 32; -pub const SIZE_NFT_ID: usize = 8 + 20 + 8; -pub const SIZE_PROOF: usize = 20; -pub const SIZE_VEC: usize = 4; -pub const SIZE_BALANCE: usize = 8; -pub const SIZE_INDEX: usize = 4; -pub const SIZE_RESOURCE: usize = 4; - -impl DdcNftRegistry { - pub fn capture_fee_and_refund(record_size: usize) -> Result<()> { - let mut received_value = Self::receive_cash(); - let fee = calculate_contract_fee(record_size); - received_value.pay(fee)?; - let caller = Self::env().caller(); - Self::send_cash(caller, received_value) - } -} - -pub fn calculate_contract_fee(record_size: usize) -> Payable { - Payable::new(FEE_PER_BYTE * record_size as Balance) -} diff --git a/ddc_nft_registry/ddc_nft_registry/tests/env_utils.rs b/ddc_nft_registry/ddc_nft_registry/tests/env_utils.rs deleted file mode 100644 index cc5cfd32..00000000 --- a/ddc_nft_registry/ddc_nft_registry/tests/env_utils.rs +++ /dev/null @@ -1,63 +0,0 @@ -#![allow(unused_variables, dead_code)] - -pub use ink_env::{ - call, - DefaultEnvironment, - test, - test::{advance_block, default_accounts, DefaultAccounts, recorded_events}, -}; -use scale::Decode; - -use crate::ddc_nft_registry::*; - -pub type Event = ::Type; - -/// Recommended contract fee for all operations with reasonable data amounts. -pub const CONTRACT_FEE_LIMIT: Balance = 10 * TOKEN; - -pub fn get_accounts() -> DefaultAccounts { - test::default_accounts::() -} - -pub fn set_caller_value(account: AccountId, value: Balance) { - set_caller(account); - set_value(value); -} - -pub fn set_value(value: Balance) { - test::set_value_transferred::(value); -} - -pub fn set_callee(account: AccountId) { - test::set_callee::(account); -} - -pub fn set_caller(account: AccountId) { - test::set_caller::(account); -} - -pub fn balance_of(account: AccountId) -> Balance { - test::get_account_balance::(account).unwrap() -} - -pub fn set_balance(account: AccountId, balance: Balance) { - test::set_account_balance::(account, balance); -} - -pub fn decode_event(event: &ink_env::test::EmittedEvent) -> Event { - ::decode(&mut &event.data[..]) - .expect("encountered invalid contract event data buffer") -} - -pub fn get_events() -> Vec { - let raw_events = recorded_events().collect::>(); - raw_events.iter().map(decode_event).collect() -} - -pub fn admin_id() -> AccountId { - get_accounts().alice -} - -pub fn contract_id() -> AccountId { - AccountId::from([0x09; 32]) -} diff --git a/ddc_nft_registry/ddc_nft_registry/tests/mod.rs b/ddc_nft_registry/ddc_nft_registry/tests/mod.rs deleted file mode 100644 index db6dc403..00000000 --- a/ddc_nft_registry/ddc_nft_registry/tests/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod env_utils; - -mod test_contract; diff --git a/ddc_nft_registry/ddc_nft_registry/tests/test_contract.rs b/ddc_nft_registry/ddc_nft_registry/tests/test_contract.rs deleted file mode 100644 index c155e991..00000000 --- a/ddc_nft_registry/ddc_nft_registry/tests/test_contract.rs +++ /dev/null @@ -1,127 +0,0 @@ -use ink_lang as ink; - -use crate::ddc_nft_registry::*; - -use super::env_utils::*; - -fn setup() -> DdcNftRegistry { - set_caller(admin_id()); - set_callee(contract_id()); - let contract = DdcNftRegistry::new(); - set_balance(contract_id(), 10); - contract -} - -#[ink::test] -fn attach_works() { - let mut contract = setup(); - - set_balance(get_accounts().alice, 1000 * TOKEN); - let reporter_id = get_accounts().alice; - let nft_id = "0000000000000030ABCD1234ABCD1234ABCD1234ABCD1234ABCD12340000003132333435"; - let asset_id = "4321DCBA4321DCBA4321DCBA4321DCBA4321DCBA"; - let proof = "certified by cere"; - - // Attach asset_id to nft_id - set_caller_value(reporter_id, 1000 * TOKEN); - contract.attach(nft_id.to_string(), asset_id.to_string(), proof.to_string()); - - // Verify attachment of asset_id to nft_id - let ev = get_events().pop().unwrap(); - assert!(matches!(ev, Event::Attach(ev) if ev == - Attach { - reporter_id, - nft_id: nft_id.to_string(), - asset_id: asset_id.to_string(), - proof: proof.to_string(), - })); - - let attachment_status = contract.get_by_nft_id(nft_id.to_string()); - assert_eq!(attachment_status.attachment.nft_id, nft_id.to_string()); - assert_eq!(attachment_status.attachment.asset_id, asset_id.to_string()); - assert_eq!(attachment_status.attachment.proof, proof.to_string()); - - // Attach different attachment to nft_id - let new_asset_id = "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef"; - set_caller_value(reporter_id, 900 * TOKEN); - contract.attach(nft_id.to_string(), new_asset_id.to_string(), proof.to_string()); - - // Verify attachment of new_asset_id to nft_id - let ev = get_events().pop().unwrap(); - assert!(matches!(ev, Event::Attach(ev) if ev == - Attach { - reporter_id, - nft_id: nft_id.to_string(), - asset_id: new_asset_id.to_string(), - proof: proof.to_string(), - })); - - let new_attachment_status = contract.get_by_nft_id(nft_id.to_string()); - assert_eq!(new_attachment_status.attachment.nft_id, nft_id.to_string()); - assert_eq!(new_attachment_status.attachment.asset_id, new_asset_id.to_string()); - assert_eq!(new_attachment_status.attachment.proof, proof.to_string()); -} - -#[ink::test] -fn reattach_only_owner() { - let mut contract = setup(); - - set_balance(get_accounts().alice, 1000 * TOKEN); - let reporter_id = get_accounts().alice; - let nft_id = "0000000000000030ABCD1234ABCD1234ABCD1234ABCD1234ABCD12340000003132333435"; - let asset_id = "4321DCBA4321DCBA4321DCBA4321DCBA4321DCBA"; - let proof = "certified by cere"; - - // Attach asset_id to nft_id - set_caller_value(reporter_id, 1000 * TOKEN); - contract.attach(nft_id.to_string(), asset_id.to_string(), proof.to_string()); - let attachment_status = contract.get_by_nft_id(nft_id.to_string()); - - // Try to attach a different attachment from another account. - let new_asset_id = "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef"; - let not_reporter_id = get_accounts().bob; - set_caller_value(not_reporter_id, 900 * TOKEN); - let result = contract.message_attach(nft_id.to_string(), new_asset_id.to_string(), proof.to_string()); - assert_eq!(result, Err(UnauthorizedUpdate)); - - // The stored attachment did not change. - let new_attachment_status = contract.get_by_nft_id(nft_id.to_string()); - assert_eq!(new_attachment_status, attachment_status); -} - - -#[ink::test] -fn report_works() { - let mut contract = setup(); - - set_balance(get_accounts().alice, 1000 * TOKEN); - let reporter_id = get_accounts().alice; - let nft_id = "0000000000000030ABCD1234ABCD1234ABCD1234ABCD1234ABCD12340000003132333435"; - let asset_id = "4321DCBA4321DCBA4321DCBA4321DCBA4321DCBA"; - let proof = "certified by cere"; - - // Attach asset_id to nft_id - set_caller_value(reporter_id, 1000 * TOKEN); - contract.attach(nft_id.to_string(), asset_id.to_string(), proof.to_string()); - let attachment_status = contract.get_by_nft_id(nft_id.to_string()); - - // Report (but not attach) a different attachment from another account. - let new_asset_id = "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef"; - let new_reporter_id = get_accounts().bob; - set_caller_value(new_reporter_id, 900 * TOKEN); - contract.report(nft_id.to_string(), new_asset_id.to_string(), proof.to_string()); - - // Verify attachment of new_asset_id to nft_id - let ev = get_events().pop().unwrap(); - assert!(matches!(ev, Event::Attach(ev) if ev == - Attach { - reporter_id: new_reporter_id, - nft_id: nft_id.to_string(), - asset_id: new_asset_id.to_string(), - proof: proof.to_string(), - })); - - // The stored attachment did not change. - let new_attachment_status = contract.get_by_nft_id(nft_id.to_string()); - assert_eq!(new_attachment_status, attachment_status); -} diff --git a/ddc_nft_registry/lib.rs b/ddc_nft_registry/lib.rs deleted file mode 100755 index ad74a858..00000000 --- a/ddc_nft_registry/lib.rs +++ /dev/null @@ -1,108 +0,0 @@ -//! The DDC smart contract implementing bucket-based services. - -#![cfg_attr(not(feature = "std"), no_std)] -#![feature(proc_macro_hygiene)] // for tests in a separate file -#![deny(unused_must_use, unused_variables)] - -use ink_lang as ink; - -#[ink::contract] -pub mod ddc_nft_registry { - use ink_prelude::string::String; - use scale::{Decode, Encode}; - - use Error::*; - - use crate::ddc_nft_registry::attachment::entity::AttachmentStatus; - use crate::ddc_nft_registry::attachment::store::AttachmentStore; - use ink_storage::traits::SpreadAllocate; - - pub mod cash; - pub mod contract_fee; - pub mod attachment; - - // ---- Global state ---- - #[ink(storage)] - #[derive(Default, SpreadAllocate)] - pub struct DdcNftRegistry { - attachments: AttachmentStore, - } - - impl DdcNftRegistry { - #[ink(constructor)] - pub fn new() -> Self { - ink_lang::utils::initialize_contract(|_| {}) - } - } - // ---- End global state ---- - - // ---- Bucket ---- - - #[ink(event)] - #[cfg_attr(feature = "std", derive(PartialEq, Debug, scale_info::TypeInfo))] - pub struct Attach { - reporter_id: AccountId, - nft_id: String, - asset_id: String, - proof: String, - } - - impl DdcNftRegistry { - /// Report and attach an asset ID to an NFT ID. - /// - /// All attachments are recorded as events. - /// There is absolutely no validation, any account can "attach" some asset ID. - /// Events should be filtered by reporter_id, or by analyzing the proof (not specified here). - /// - /// The latest attachment is also recorded in contract storage. - /// The latest asset ID can be queried from get_by_nft_id. - /// The first reporter for an NFT ID can also update the asset ID. - #[ink(message, payable)] - pub fn attach(&mut self, nft_id: String, asset_id: String, proof: String) { - self.message_attach(nft_id, asset_id, proof).unwrap() - } - - /// Report the attachment of an asset ID to an NFT ID. - /// - /// This is recorded only as a contract event. - /// This can *not* be queried from get_by_nft_id. - /// - /// There is absolutely no validation, any account can "report" some asset ID. - /// Events should be filtered by reporter_id, or by analyzing the proof (not specified here). - #[ink(message, payable)] - pub fn report(&mut self, nft_id: String, asset_id: String, proof: String) { - self.message_report(nft_id, asset_id, proof).unwrap() - } - - #[ink(message)] - pub fn get_by_nft_id(&mut self, nft_id: String) -> AttachmentStatus { - self.message_get_by_nft_id(nft_id).unwrap() - } - } - // ---- End Bucket ---- - - - // ---- Utils ---- - /// One token with 10 decimals. - pub const TOKEN: Balance = 10_000_000_000; - - #[derive(Debug, PartialEq, Eq, Encode, Decode)] - #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] - pub enum Error { - InsufficientBalance, - AttachmentDoesNotExist, - UnauthorizedUpdate, - } - - pub type Result = core::result::Result; - - impl From for ink_env::Error { - fn from(_: Error) -> Self { - ink_env::Error::Unknown - } - } - // ---- End Utils ---- - - #[cfg(test)] - mod tests; -} diff --git a/scripts/.gitignore b/scripts/.gitignore index 99600f94..87b5d6ff 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1,2 +1,3 @@ node_modules -yarn-error.log \ No newline at end of file +yarn-error.log +secrets.txt \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md index 81fe3d62..3dad84ec 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -6,33 +6,23 @@ Also, check the [config file](./sdk/src/config/index.js), to ensure that you are ## Examples -The [examples](./examples/) folder contains scripts that demonstrate how to properly send transacrions to DDC Bucket and DDC NFT Registry contracts using the [@polkadot/api](https://polkadot.js.org/docs/api/) library. These simple scenarious should help other team members to clarify the business logic flow, and quickly detect issues related to various business constraints and infrastructue constraints (i.e. gas price, attached tokens, method signature, etc.). Scripts should be updated while the contract is evolving to reflect the actual logic. +The [examples](./examples/) folder contains scripts that demonstrate how to properly send transacrions to DDC Bucket contracts using the [@polkadot/api](https://polkadot.js.org/docs/api/) library. These simple scenarious should help other team members to clarify the business logic flow, and quickly detect issues related to various business constraints and infrastructue constraints (i.e. gas price, attached tokens, method signature, etc.). Scripts should be updated while the contract is evolving to reflect the actual logic. #### DDC Bucket scenario Run the script as: ``` -yarn run demo-ddc-bucket +ENV=devnet yarn run demo-ddc-bucket ``` The execution progress will be displayed in the console along with links to explorer that will help you to investigate the details of each transaction -#### DDC NFT Registry scenario - -Run the script as: -``` -yarn run demo-nft-registry -``` - -The execution progress will be displayed in the console along with links to explorer that will help you to investigate the details of each transaction - - #### Display DDC Bucket state Run the script as: ``` -yarn run print-ddc-bucket +ENV=devnet yarn run print-ddc-bucket ``` @@ -52,15 +42,6 @@ yarn run deploy-ddc-bucket Optionally, the command can accept a code hash as the first parameter, and constructor name as the second parameter. In order to use these options, your contract artifacts [must be registered](./sdk/src/deploymentRegistry.js) to retrieve the required metadata from artifacts. -#### DDC NFT Registry deployment - -Run the script as: -``` -yarn run deploy-ddc-nft-registry -``` -Optionally, the command can accept a code hash as the first parameter, and constructor name as the second parameter. In order to use these options, your contract artifacts [must be registered](./sdk/src/deploymentRegistry.js) to retrieve the required metadata from artifacts. - - #### Build and Deploy all contracts To run both building and deployment for all contracts, you can use the [build-and-deploy.sh](./../build-and-deploy.sh) script. diff --git a/bucket/scripts/config.js b/scripts/ddc-setup/ddcConfig.js similarity index 99% rename from bucket/scripts/config.js rename to scripts/ddc-setup/ddcConfig.js index 0323cfe1..0517238f 100644 --- a/bucket/scripts/config.js +++ b/scripts/ddc-setup/ddcConfig.js @@ -1,6 +1,6 @@ const GEN = 0x10000000000000000n; // UINT64_MAX -export const config = { +module.exports = { devnet: { ws_provider: "wss://archive.devnet.cere.network/ws", contract_address: "6SfBsKbfPUTN35GCcqAHSMY4MemedK2A73VeJ34Z2FV6PB4r", diff --git a/scripts/ddc-setup/ddcSetup.js b/scripts/ddc-setup/ddcSetup.js new file mode 100644 index 00000000..4d6db103 --- /dev/null +++ b/scripts/ddc-setup/ddcSetup.js @@ -0,0 +1,145 @@ +const fs = require("fs"); +const { + connect, + accountFromUri, + sendTx, + getContract, + CERE, + MGAS, + ddcBucket, + randomAccount, + deploymentRegistry, + config +} = require("../sdk/src"); +const ddcConfig = require('./ddcConfig.js'); +const log = console.log; + +const DDC_BUCKET_CONTRACT_NAME = config.DDC_BUCKET_CONTRACT_NAME; +const ENV = process.env.ENV; +const SUPERADMIN_MNEMONIC = process.env.SUPERADMIN; + + +const ddcEnvConfig = ddcConfig[ENV]; +if (!ddcEnvConfig) { + console.error("Please provide ENV as one of ", Object.keys(ddcConfig)); + process.exit(-1); +} +console.log(ddcEnvConfig); + +if (!SUPERADMIN_MNEMONIC) { + console.error("Please provide SUPERADMIN seed"); + process.exit(-1); +} + +deploymentRegistry.initContract( + DDC_BUCKET_CONTRACT_NAME, + ENV, + ddcEnvConfig.contract_address +); + + +async function main() { + const {api, chainName, getExplorerUrl} = await connect(ddcEnvConfig.ws_provider); + log("Connected to blockchain:", chainName); + + const sadmin = accountFromUri(SUPERADMIN_MNEMONIC); + console.log(`Superadmin: ${sadmin.address}`); + + const bucketContract = getContract(DDC_BUCKET_CONTRACT_NAME, ENV, api); + log("Using bucket contract", DDC_BUCKET_CONTRACT_NAME, "at", bucketContract.address.toString()); + + const txOptions = { + storageDepositLimit: null, + gasLimit: 100_000_000_000n, + }; + + { + log("1. accountSetUsdPerCere"); + const tx = bucketContract.tx.accountSetUsdPerCere( + txOptions, + 1000n * CERE + ); + + const result = await sendTx(sadmin, tx); + log(getExplorerUrl(result), "\n"); + } + + { + log("2. grantTrustedManagerPermission"); + const tx = bucketContract.tx.grantTrustedManagerPermission( + txOptions, + sadmin.address + ); + const result = await sendTx(sadmin, tx); + log(getExplorerUrl(result), "\n"); + } + + const cdnNodesKeys = [] + { + console.log("3. cdnNodeCreate"); + for (let i = 0; i < ddcEnvConfig.cdn_node_params.length; i++) { + const cdnNodeKey = ddcEnvConfig.cdn_node_params[i].publicKey; + cdnNodesKeys.push(cdnNodeKey); + + const tx = bucketContract.tx.cdnNodeCreate( + txOptions, + cdnNodeKey, + JSON.stringify(ddcEnvConfig.cdn_node_params[i]) + ); + + const result = await sendTx(sadmin, tx); + log(getExplorerUrl(result), "\n"); + } + } + + const storageNodesKeys = [] + { + console.log("4. nodeCreate"); + for (let i = 0; i < ddcEnvConfig.storage_node_params.length; i++) { + const param = JSON.stringify(ddcEnvConfig.storage_node_params[i]); + const user = randomAccount(); + + fs.appendFileSync('secrets.txt', `${user.address}: ${user.mnemonic} -- ${ENV} storage ${i}\n`); + console.log(` node ${i}: address ${user.address}, param ${param}`); + + const storageNodeKey = user.address; + storageNodesKeys.push(storageNodeKey); + + const tx = bucketContract.tx.nodeCreate( + txOptions, + storageNodeKey, + param, + 100000n, + 1n * CERE + ); + + const result = await sendTx(sadmin, tx); + log(getExplorerUrl(result), "\n"); + } + } + + const clustersIds = []; + { + for (let key in ddcEnvConfig.cluster) { + console.log("5. clusterCreate "); + + const tx1 = bucketContract.tx.clusterCreate( + txOptions, + JSON.stringify(ddcEnvConfig.cluster[key].param), + 100000n + ); + + const result1 = await sendTx(sadmin, tx1); + log(getExplorerUrl(result1), "\n"); + let { clusterId } = ddcBucket.findClusterCreatedEvent(result1.contractEvents || []); + clustersIds.push(clusterId); + } + } + + // TODO: Add Storage nodes and CDN nodes to clusters + + process.exit(0); +} + + +main().then(log, log); \ No newline at end of file diff --git a/scripts/deployment/ddcBucketDeploy.js b/scripts/deployment/ddcBucketDeploy.js index 9868c316..04424abc 100644 --- a/scripts/deployment/ddcBucketDeploy.js +++ b/scripts/deployment/ddcBucketDeploy.js @@ -2,13 +2,10 @@ const { deployContract } = require("./deploy"); const { config } = require("./../sdk"); const log = console.log; +const CODE_HASH = process.env.CODE_HASH || null; async function main() { - const args = process.argv.slice(2); - const codeHash = args.length > 0 ? args[0] : null; - const constructorName = args.length > 1 ? args[1] : "new"; - - await deployContract(config.DDC_BUCKET_CONTRACT_NAME, codeHash, constructorName); + await deployContract(config.DDC_BUCKET_CONTRACT_NAME, CODE_HASH); process.exit(0); } diff --git a/scripts/deployment/ddcNftRegistryDeploy.js b/scripts/deployment/ddcNftRegistryDeploy.js deleted file mode 100644 index 68b30971..00000000 --- a/scripts/deployment/ddcNftRegistryDeploy.js +++ /dev/null @@ -1,15 +0,0 @@ -const { deployContract } = require("./deploy"); -const { config } = require("./../sdk"); -const log = console.log; - - -async function main() { - const args = process.argv.slice(2); - const codeHash = args.length > 0 ? args[0] : null; - const constructorName = args.length > 1 ? args[1] : "new"; - - await deployContract(config.DDC_NFT_REGISTRY_CONTRACT_NAME, codeHash, constructorName); - process.exit(0); -} - -main().then(log, log); diff --git a/scripts/deployment/deploy.js b/scripts/deployment/deploy.js index db7ddbdb..4c5accd8 100644 --- a/scripts/deployment/deploy.js +++ b/scripts/deployment/deploy.js @@ -15,7 +15,11 @@ const log = console.log; const SEED = config.ACTOR_SEED; -const RPC = config.DEVNET_RPC_ENDPOINT; +const RPC = process.env.ENV == 'devnet' + ? config.DEVNET_RPC_ENDPOINT + : process.env.ENV == 'testnet' + ? config.TESTNET_RPC_ENDPOINT + : config.LOCAL_RPC_ENDPOINT; const deployContract = async ( contractName, @@ -30,7 +34,7 @@ const deployContract = async ( log("From account", account.address); if (maybeCodeHash) { - deploymentRegistry.initContracts(); + deploymentRegistry.initDefaultContracts(); } await deploy( diff --git a/scripts/examples/ddcBucketDemo.js b/scripts/examples/ddcBucketDemo.js index 254065c7..cbf86674 100644 --- a/scripts/examples/ddcBucketDemo.js +++ b/scripts/examples/ddcBucketDemo.js @@ -17,9 +17,13 @@ const log = console.log; const BUCKET_CONTRACT_NAME = config.DDC_BUCKET_CONTRACT_NAME; const SEED = config.ACTOR_SEED; -const RPC = config.DEVNET_RPC_ENDPOINT; +const RPC = process.env.ENV == 'devnet' + ? config.DEVNET_RPC_ENDPOINT + : process.env.ENV == 'testnet' + ? config.TESTNET_RPC_ENDPOINT + : config.LOCAL_RPC_ENDPOINT; -deploymentRegistry.initContracts(); +deploymentRegistry.initDefaultContracts(); async function main() { const {api, chainName, getExplorerUrl} = await connect(RPC); @@ -42,142 +46,239 @@ async function main() { }; // Test data. - const managerId = account.address; + const clusterManagerId = account.address; + const nodePubKey = randomAccount().address; + const cdnNodePubKey = randomAccount().address; + const anyAccountId = account.address; - const rentPerMonth = 10n * CERE; - const nodeParams = "{\"url\":\"https://ddc-123.cere.network/bucket/{BUCKET_ID}\"}"; - const capacity = 1e6; - const vNodes = [1,2,3,4,5,6]; - const clusterResource = 10; + const bucketResource = 5; const bucketParams = "{}"; - const nodePubKey = randomAccount().address; - let nodeId; + let nodeKey; { - log("Setup a node…"); - const tx = bucketContract.tx - .nodeCreate(txOptionsPay, rentPerMonth, nodeParams, capacity, 'ADDING', nodePubKey); + log("Create a Storage node..."); + const nodeParams = "{\"url\":\"https://ddc-123.cere.network/storage/0\"}"; + const capacity = 1e6; + const rentPerMonth = 10n * CERE; + const tx = bucketContract.tx.nodeCreate( + txOptionsPay, + nodePubKey, + nodeParams, + capacity, + rentPerMonth, + ); const result = await sendTx(account, tx); printGas(result); log(getExplorerUrl(result)); const events = printEvents(result); - nodeId = ddcBucket.findCreatedNodeId(events); - log("New NodeId", nodeId, "\n"); + let event = ddcBucket.findNodeCreatedEvent(events); + nodeKey = event.nodeKey; + log("NodeCreated", event, "\n"); } - + + let cdnNodeKey; { - log("Trust the cluster manager…"); - const tx = bucketContract.tx - .nodeTrustManager(txOptionsPay, managerId); + log("Create a CDN node..."); + const cdnNodeParams = "{\"url\":\"https://ddc-123.cere.network/cdn/0\"}"; + const tx = bucketContract.tx.cdnNodeCreate( + txOptionsPay, + cdnNodePubKey, + cdnNodeParams + ); const result = await sendTx(account, tx); printGas(result); log(getExplorerUrl(result)); - printEvents(result); - log(); + const events = printEvents(result); + let event = ddcBucket.findCdnNodeCreatedEvent(events); + cdnNodeKey = event.cdnNodeKey; + log("CdnNodeCreated", event, "\n"); } let clusterId; { - log("Setup a cluster…"); + log("Setup a cluster..."); let clusterParams = "{}"; - const tx = bucketContract.tx - .clusterCreate(txOptionsPay, managerId, vNodes, [nodeId], clusterParams); + const resourcePerVNode = 10; + const tx = bucketContract.tx.clusterCreate( + txOptionsPay, + clusterParams, + resourcePerVNode + ); const result = await sendTx(account, tx); printGas(result); log(getExplorerUrl(result)); const events = printEvents(result); - clusterId = ddcBucket.findCreatedClusterId(events); - log("New ClusterId", clusterId, "\n"); + let event = ddcBucket.findClusterCreatedEvent(events); + clusterId = event.clusterId; + log("ClusterCreated", event, "\n"); + } + + let trustedManagerId; + { + log("Trust the cluster manager..."); + const tx = bucketContract.tx.grantTrustedManagerPermission( + txOptionsPay, + clusterManagerId + ); + const result = await sendTx(account, tx); + printGas(result); + log(getExplorerUrl(result)); + const events = printEvents(result); + let event = ddcBucket.findPermissionGrantedEvent(events); + trustedManagerId = event.accountId; + log("PermissionGranted", event, "\n"); } { - log("Reserve some resources for the cluster…"); - const tx = bucketContract.tx - .clusterReserveResource(txOptions, clusterId, clusterResource); + log("Adding Storage node to the cluster..."); + const vNodes = [1,2,3,4,5,6]; + const tx = bucketContract.tx.clusterAddNode( + txOptionsPay, + clusterId, + nodeKey, + vNodes + ); const result = await sendTx(account, tx); printGas(result); log(getExplorerUrl(result)); - printEvents(result); - log(); + const events = printEvents(result); + let event = ddcBucket.findClusterNodeAddedEvent(events); + log("ClusterNodeAdded", event, "\n"); } { - log("Changing node tag…"); - const tx = bucketContract.tx - .clusterChangeNodeTag(txOptions, nodeId, 'ACTIVE'); + log("Adding CDN node to the cluster..."); + const tx = bucketContract.tx.clusterAddCdnNode( + txOptionsPay, + clusterId, + cdnNodeKey, + ); const result = await sendTx(account, tx); printGas(result); log(getExplorerUrl(result)); - printEvents(result); - log(); + const events = printEvents(result); + let event = ddcBucket.findClusterCdnNodeAddedEvent(events); + cdnNodeKey = event.cdnNodeKey; + log("ClusterCdnNodeAdded", event, "\n"); + } + + { + log("Changing Storage node status..."); + const tx = bucketContract.tx.clusterSetNodeStatus( + txOptions, + clusterId, + nodeKey, + 'ACTIVE' + ); + + const result = await sendTx(account, tx); + printGas(result); + log(getExplorerUrl(result)); + const events = printEvents(result); + let event = ddcBucket.findClusterNodeStatusSetEvent(events); + log("ClusterNodeStatusSet", event, "\n"); + } + + { + log("Changing CDN node status..."); + const tx = bucketContract.tx.clusterSetCdnNodeStatus( + txOptions, + clusterId, + cdnNodeKey, + 'ACTIVE' + ); + + const result = await sendTx(account, tx); + printGas(result); + log(getExplorerUrl(result)); + const events = printEvents(result); + let event = ddcBucket.findClusterCdnNodeStatusSetEvent(events); + log("ClusterCdnNodeStatusSet", event, "\n"); } let bucketId; { - log("Create a bucket…"); - const tx = bucketContract.tx - .bucketCreate(txOptionsPay, bucketParams, clusterId, null); + log("Create a bucket..."); + const tx = bucketContract.tx.bucketCreate( + txOptionsPay, + bucketParams, + clusterId, + null + ); const result = await sendTx(account, tx); printGas(result); log(getExplorerUrl(result)); const events = printEvents(result); - bucketId = ddcBucket.findCreatedBucketId(events); - log("New BucketId", bucketId, "\n"); + let event = ddcBucket.findBucketCreatedEvent(events); + bucketId = event.bucketId; + log("BucketCreated", event, "\n"); } { - log("Topup the account…"); - const tx = bucketContract.tx - .accountDeposit(txOptionsPay); + log("Topup the account..."); + const tx = bucketContract.tx.accountDeposit( + txOptionsPay + ); const result = await sendTx(account, tx); printGas(result); log(getExplorerUrl(result)); - printEvents(result); - log(); + const events = printEvents(result); + let event = ddcBucket.findDepositEvent(events); + log("Deposit", event, "\n"); } { - log("Allocate some resources for the bucket…"); - const tx = bucketContract.tx - .bucketAllocIntoCluster(txOptions, bucketId, bucketResource); + log("Allocate some resources for the bucket..."); + const tx = bucketContract.tx.bucketAllocIntoCluster( + txOptions, + bucketId, + bucketResource + ); const result = await sendTx(account, tx); printGas(result); log(getExplorerUrl(result)); - printEvents(result); - log(); + const events = printEvents(result); + let event = ddcBucket.findBucketAllocatedEvent(events); + log("BucketAllocated", event, "\n"); } { - log("Collect payment from Bucket to Cluster…"); - const tx = bucketContract.tx - .bucketSettlePayment(txOptions, bucketId); + log("Collect payment from Bucket to Cluster..."); + const tx = bucketContract.tx.bucketSettlePayment( + txOptions, + bucketId + ); const result = await sendTx(account, tx); printGas(result); log(getExplorerUrl(result)); - printEvents(result); - log(); + const events = printEvents(result); + let event = ddcBucket.findBucketSettlePaymentEvent(events) + log("BucketSettlePayment", event, "\n"); } { - log("Distribute payment from Cluster to Providers…"); - const tx = bucketContract.tx - .clusterDistributeRevenues(txOptions, clusterId); + log("Distribute payment from Cluster to Providers..."); + const tx = bucketContract.tx.clusterDistributeRevenues( + txOptions, + clusterId + ); const result = await sendTx(account, tx); printGas(result); log(getExplorerUrl(result)); - printEvents(result); - log(); + const events = printEvents(result); + let event = ddcBucket.findClusterDistributeRevenuesEvent(events); + log("ClusterDistributeRevenues", event, "\n"); } log(" ---- \n"); @@ -185,7 +286,7 @@ async function main() { { log("Read the node status…"); const {result, output} = await bucketContract.query - .nodeGet(anyAccountId, txOptions, nodeId); + .nodeGet(anyAccountId, txOptions, nodeKey); if (!result.isOk) assert.fail(result.asErr); log("Node", output.toHuman(), "\n"); diff --git a/scripts/examples/ddcBucketPrint.js b/scripts/examples/ddcBucketPrint.js index ea6f98b5..a24b5e65 100644 --- a/scripts/examples/ddcBucketPrint.js +++ b/scripts/examples/ddcBucketPrint.js @@ -10,10 +10,15 @@ const log = console.log; const CONTRACT_NAME = config.DDC_BUCKET_CONTRACT_NAME; -const RPC = config.DEVNET_RPC_ENDPOINT; +const RPC = process.env.ENV == 'devnet' + ? config.DEVNET_RPC_ENDPOINT + : process.env.ENV == 'testnet' + ? config.TESTNET_RPC_ENDPOINT + : config.LOCAL_RPC_ENDPOINT; + const ACCOUNT_FILTER = null; // get data about all accounts. -deploymentRegistry.initContracts(); +deploymentRegistry.initDefaultContracts(); async function main() { const {api, chainName} = await connect(RPC); @@ -108,17 +113,17 @@ function printGraph(clusters, nodes, buckets) { log(); for (status of nodes) { - let {nodeId, node} = status; + let {nodeKey, node} = status; // Define - log(`Node_${nodeId}[(Node ${nodeId})]`); + log(`Node_${nodeKey}[(Node ${nodeKey})]`); // Node to Provider. - log(`Node_${nodeId} -. owned by .-> Account_${node.providerId.substr(0, 8)}`); + log(`Node_${nodeKey} -. owned by .-> Account_${node.providerId.substr(0, 8)}`); log(); } for (status of clusters) { - let {clusterId, cluster} = status; + let {clusterId, cluster, clusterVNodes} = status; // Define log(`Cluster_${clusterId}((Cluster ${clusterId}))`); @@ -126,8 +131,8 @@ function printGraph(clusters, nodes, buckets) { log(`Cluster_${clusterId} -. managed by ..-> Account_${cluster.managerId.substr(0, 8)}`); // Cluster to Node. - for (i = 0; i < cluster.vNodes.length; i++) { - let vNodeId = cluster.vNodes[i]; + for (i = 0; i < clusterVNodes.length; i++) { + let vNodeId = clusterVNodes[i]; log(`Cluster_${clusterId} -- P${i} --> Node_${vNodeId}`); } log(); diff --git a/scripts/examples/ddcNftRegistryDemo.js b/scripts/examples/ddcNftRegistryDemo.js deleted file mode 100644 index c56f2743..00000000 --- a/scripts/examples/ddcNftRegistryDemo.js +++ /dev/null @@ -1,77 +0,0 @@ -const { - connect, - accountFromUri, - sendTx, - getContract, - CERE, - MGAS, - ddcNftRegistry, - lodash: _, - deploymentRegistry, - config -} = require("./../sdk"); - -const log = console.log; - -const NFT_REGISTRY_CONTRACT_NAME = config.DDC_NFT_REGISTRY_CONTRACT_NAME; -const SEED = config.ACTOR_SEED; -const RPC = config.DEVNET_RPC_ENDPOINT; - -deploymentRegistry.initContracts(); - -async function main() { - const {api, chainName, getExplorerUrl} = await connect(RPC); - log("Connected to blockchain:", chainName); - - const account = accountFromUri(SEED); - log("From account", account.address); - - const txOptions = { - value: 0n, - gasLimit: 200_000n * MGAS, - }; - - const txOptionsPay = { - value: 10n * CERE, - gasLimit: 200_000n * MGAS, - }; - - // NFT registry - const registryContract = getContract(NFT_REGISTRY_CONTRACT_NAME, chainName, api); - log("Using nft registry contract", NFT_REGISTRY_CONTRACT_NAME, "at", registryContract.address.toString()); - - // Test data. - const nftId = "0000000000000030ABCD1234ABCD1234ABCD1234ABCD1234ABCD12340000003132333435"; - const assetId = "ddc:1234"; - const proofVal = "cere_tx"; - - { - log("Attach asset…"); - const tx = registryContract.tx.attach(txOptionsPay, nftId, assetId, proofVal); - - const result = await sendTx(account, tx); - printGas(result); - log(getExplorerUrl(result)); - const events = printEvents(result); - - - const { nft_id, asset_id, proof } = ddcNftRegistry.findCreatedAttachment(events); - log(`New attach: nft_id=${nft_id}, asset_id=${asset_id}, proof=${proof}\n`); - } - - process.exit(0); -} - -function printEvents(result) { - const events = result.contractEvents || []; - //log("EVENTS", JSON.stringify(events, null, 4)); - log(events.length, "events"); - return events; -} - -function printGas(result) { - let gas = _.get(result, "dispatchInfo.weight", 0); - log(parseInt(gas / 1e6), "MGas"); -} - -main().then(log, log); diff --git a/scripts/package.json b/scripts/package.json index d7122bb0..b54cd421 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -5,10 +5,9 @@ "license": "Apache-2.0", "scripts": { "ensure-deps": "yarn --cwd sdk", - "demo-ddc-bucket": "yarn ensure-deps && node examples/ddcBucketDemo.js", - "demo-nft-registry": "yarn ensure-deps && node examples/ddcNftRegistryDemo.js", + "setup-ddc-bucket": "yarn ensure-deps && node ddc-setup/ddcSetup.js", "deploy-ddc-bucket": "yarn ensure-deps && node deployment/ddcBucketDeploy.js", - "deploy-ddc-nft-registry": "yarn ensure-deps && node deployment/ddcNftRegistryDeploy.js", + "demo-ddc-bucket": "yarn ensure-deps && node examples/ddcBucketDemo.js", "print-ddc-bucket": "yarn ensure-deps && node examples/ddcBucketPrint.js" } } \ No newline at end of file diff --git a/scripts/sdk/package.json b/scripts/sdk/package.json index 39ba53bd..93f2f888 100644 --- a/scripts/sdk/package.json +++ b/scripts/sdk/package.json @@ -5,8 +5,8 @@ "main": "src/index.js", "license": "Apache-2.0", "dependencies": { - "@polkadot/api": "8.2.1", - "@polkadot/api-contract": "8.2.1", + "@polkadot/api": "9.4.1", + "@polkadot/api-contract": "9.4.1", "@polkadot/util-crypto": "9.7.2", "lodash": "^4.17.21" } diff --git a/scripts/sdk/src/abi/ddc_bucket.json b/scripts/sdk/src/abi/ddc_bucket.json index b8a1dc70..95f5fbf5 100644 --- a/scripts/sdk/src/abi/ddc_bucket.json +++ b/scripts/sdk/src/abi/ddc_bucket.json @@ -1,6 +1,6 @@ { "source": { - "hash": "0x89bcf7ab14df699f511b76c97cec6ab5b79f0bb690424523f14711a59159c7f6", + "hash": "0x3fb93f4d51fa512d9873fb945365bc5e5a3d168a9e745ffc19e0cd4e25ecbb68", "language": "ink! 3.4.0", "compiler": "rustc 1.69.0-nightly" }, @@ -40,7 +40,7 @@ "displayName": [ "BucketId" ], - "type": 5 + "type": 6 } }, { @@ -51,7 +51,7 @@ "displayName": [ "AccountId" ], - "type": 2 + "type": 9 } } ], @@ -70,7 +70,7 @@ "displayName": [ "BucketId" ], - "type": 5 + "type": 6 } }, { @@ -81,7 +81,7 @@ "displayName": [ "ClusterId" ], - "type": 5 + "type": 6 } }, { @@ -92,7 +92,7 @@ "displayName": [ "Resource" ], - "type": 5 + "type": 6 } } ], @@ -111,7 +111,7 @@ "displayName": [ "BucketId" ], - "type": 5 + "type": 6 } }, { @@ -122,7 +122,7 @@ "displayName": [ "ClusterId" ], - "type": 5 + "type": 6 } } ], @@ -141,7 +141,7 @@ "displayName": [ "BucketId" ], - "type": 5 + "type": 6 } }, { @@ -152,7 +152,7 @@ "displayName": [ "bool" ], - "type": 9 + "type": 3 } } ], @@ -161,6 +161,34 @@ ], "label": "BucketAvailabilityUpdated" }, + { + "args": [ + { + "docs": [], + "indexed": true, + "label": "bucket_id", + "type": { + "displayName": [ + "BucketId" + ], + "type": 6 + } + }, + { + "docs": [], + "indexed": false, + "label": "bucket_params", + "type": { + "displayName": [ + "BucketParams" + ], + "type": 13 + } + } + ], + "docs": [], + "label": "BucketParamsSet" + }, { "args": [ { @@ -171,18 +199,18 @@ "displayName": [ "ClusterId" ], - "type": 5 + "type": 6 } }, { "docs": [], "indexed": true, - "label": "manager", + "label": "manager_id", "type": { "displayName": [ "AccountId" ], - "type": 2 + "type": 9 } }, { @@ -193,13 +221,11 @@ "displayName": [ "ClusterParams" ], - "type": 14 + "type": 13 } } ], - "docs": [ - " A new cluster was created." - ], + "docs": [], "label": "ClusterCreated" }, { @@ -212,25 +238,34 @@ "displayName": [ "ClusterId" ], - "type": 5 + "type": 6 } }, { "docs": [], "indexed": true, - "label": "node_id", + "label": "node_key", + "type": { + "displayName": [ + "NodeKey" + ], + "type": 9 + } + }, + { + "docs": [], + "indexed": false, + "label": "v_nodes", "type": { "displayName": [ - "NodeId" + "Vec" ], - "type": 5 + "type": 27 } } ], - "docs": [ - " A vnode was re-assigned to new node." - ], - "label": "ClusterNodeReplaced" + "docs": [], + "label": "ClusterNodeAdded" }, { "args": [ @@ -242,25 +277,23 @@ "displayName": [ "ClusterId" ], - "type": 5 + "type": 6 } }, { "docs": [], - "indexed": false, - "label": "resource", + "indexed": true, + "label": "node_key", "type": { "displayName": [ - "Resource" + "NodeKey" ], - "type": 5 + "type": 9 } } ], - "docs": [ - " Some resources were reserved for the cluster from the nodes." - ], - "label": "ClusterReserveResource" + "docs": [], + "label": "ClusterNodeRemoved" }, { "args": [ @@ -272,25 +305,23 @@ "displayName": [ "ClusterId" ], - "type": 5 + "type": 6 } }, { "docs": [], "indexed": true, - "label": "provider_id", + "label": "cdn_node_key", "type": { "displayName": [ - "AccountId" + "CdnNodeKey" ], - "type": 2 + "type": 9 } } ], - "docs": [ - " The share of revenues of a cluster for a provider was distributed." - ], - "label": "ClusterDistributeRevenues" + "docs": [], + "label": "ClusterCdnNodeAdded" }, { "args": [ @@ -302,25 +333,23 @@ "displayName": [ "ClusterId" ], - "type": 5 + "type": 6 } }, { "docs": [], "indexed": true, - "label": "manager", + "label": "cdn_node_key", "type": { "displayName": [ - "AccountId" + "CdnNodeKey" ], - "type": 2 + "type": 9 } } ], - "docs": [ - " A new cluster was created." - ], - "label": "CdnClusterCreated" + "docs": [], + "label": "ClusterCdnNodeRemoved" }, { "args": [ @@ -332,701 +361,680 @@ "displayName": [ "ClusterId" ], - "type": 5 + "type": 6 } }, { "docs": [], - "indexed": true, - "label": "provider_id", + "indexed": false, + "label": "cluster_params", "type": { "displayName": [ - "AccountId" + "ClusterParams" ], - "type": 2 + "type": 13 } } ], - "docs": [ - " The respective share of revenues of a CDN cluster for a provider was distributed." + "docs": [], + "label": "ClusterParamsSet" + }, + { + "args": [ + { + "docs": [], + "indexed": true, + "label": "cluster_id", + "type": { + "displayName": [ + "ClusterId" + ], + "type": 6 + } + } ], - "label": "CdnClusterDistributeRevenues" + "docs": [], + "label": "ClusterRemoved" }, { "args": [ { "docs": [], "indexed": true, - "label": "node_id", + "label": "node_key", "type": { "displayName": [ - "NodeId" + "NodeKey" ], - "type": 5 + "type": 9 } }, { "docs": [], "indexed": true, - "label": "provider_id", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" ], - "type": 2 + "type": 6 } }, { "docs": [], "indexed": false, - "label": "undistributed_payment", + "label": "status", "type": { "displayName": [ - "Balance" + "NodeStatusInCluster" ], - "type": 8 + "type": 23 } } ], - "docs": [ - " A node was created. The given account is its owner and recipient of revenues." - ], - "label": "CdnNodeCreated" + "docs": [], + "label": "ClusterNodeStatusSet" }, { "args": [ { "docs": [], "indexed": true, - "label": "node_id", + "label": "cdn_node_key", "type": { "displayName": [ - "NodeId" + "CdnNodeKey" ], - "type": 5 + "type": 9 } }, { "docs": [], "indexed": true, - "label": "provider_id", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" ], - "type": 2 + "type": 6 } }, { "docs": [], "indexed": false, - "label": "rent_per_month", + "label": "status", "type": { "displayName": [ - "Balance" + "NodeStatusInCluster" + ], + "type": 23 + } + } + ], + "docs": [], + "label": "ClusterCdnNodeStatusSet" + }, + { + "args": [ + { + "docs": [], + "indexed": true, + "label": "cluster_id", + "type": { + "displayName": [ + "ClusterId" + ], + "type": 6 + } + }, + { + "docs": [], + "indexed": true, + "label": "node_key", + "type": { + "displayName": [ + "NodeKey" ], - "type": 8 + "type": 9 } }, { "docs": [], "indexed": false, - "label": "node_params", + "label": "v_nodes", "type": { "displayName": [ - "NodeParams" + "Vec" ], - "type": 14 + "type": 27 } } ], "docs": [ - " A node was created. The given account is its owner and recipient of revenues." + " A vnode was re-assigned to new node." ], - "label": "NodeCreated" + "label": "ClusterNodeReplaced" }, { "args": [ { "docs": [], "indexed": true, - "label": "account_id", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" + ], + "type": 6 + } + }, + { + "docs": [], + "indexed": true, + "label": "node_key", + "type": { + "displayName": [ + "NodeKey" ], - "type": 2 + "type": 9 } }, { "docs": [], "indexed": false, - "label": "value", + "label": "v_nodes", "type": { "displayName": [ - "Balance" + "Vec" ], - "type": 8 + "type": 27 } } ], "docs": [ - " Tokens were deposited on an account." + " A vnode was re-assigned to new node." ], - "label": "Deposit" + "label": "ClusterNodeReset" }, { "args": [ { "docs": [], "indexed": true, - "label": "account_id", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" ], - "type": 2 + "type": 6 } }, { "docs": [], "indexed": false, - "label": "permission", + "label": "resource", "type": { "displayName": [ - "Permission" + "Resource" ], - "type": 77 + "type": 6 } } ], "docs": [ - " A permission was granted to the account." + " Some resources were reserved for the cluster from the nodes." ], - "label": "GrantPermission" + "label": "ClusterReserveResource" }, { "args": [ { "docs": [], "indexed": true, - "label": "account_id", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" ], - "type": 2 + "type": 6 } }, { "docs": [], - "indexed": false, - "label": "permission", + "indexed": true, + "label": "provider_id", "type": { "displayName": [ - "Permission" + "AccountId" ], - "type": 77 + "type": 9 } } ], "docs": [ - " A permission was revoked from the account." + " The share of revenues of a cluster for a provider was distributed." ], - "label": "RevokePermission" - } - ], - "messages": [ + "label": "ClusterDistributeRevenues" + }, { "args": [ { - "label": "bucket_params", - "type": { - "displayName": [ - "BucketParams" - ], - "type": 14 - } - }, - { + "docs": [], + "indexed": true, "label": "cluster_id", "type": { "displayName": [ "ClusterId" ], - "type": 5 + "type": 6 } }, { - "label": "owner_id", + "docs": [], + "indexed": true, + "label": "provider_id", "type": { "displayName": [ - "Option" + "AccountId" ], - "type": 43 + "type": 9 } } ], - "docs": [ - " Create a new bucket and return its `bucket_id`.", - "", - " The caller will be its first owner and payer of resources.", - "", - " `bucket_params` is configuration used by clients and nodes. See the [data structure of BucketParams](https://docs.cere.network/ddc/protocols/contract-params-schema)", - "", - " The bucket can be connected to a single cluster (currently). Allocate cluster resources with the function `bucket_alloc_into_cluster`" - ], - "label": "bucket_create", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [ - "BucketId" - ], - "type": 5 - }, - "selector": "0x0aeb2379" + "docs": [], + "label": "ClusterDistributeCdnRevenues" }, { "args": [ { - "label": "bucket_id", + "docs": [], + "indexed": true, + "label": "cdn_node_key", "type": { "displayName": [ - "BucketId" + "CdnNodeKey" ], - "type": 5 + "type": 9 } }, { - "label": "owner_id", + "docs": [], + "indexed": true, + "label": "provider_id", "type": { "displayName": [ "AccountId" ], - "type": 2 + "type": 9 } - } - ], - "docs": [ - " Change owner of the bucket", - "", - " Provide the account of new owner" - ], - "label": "bucket_change_owner", - "mutates": true, - "payable": true, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xc7d0c2cd" - }, - { - "args": [ + }, { - "label": "bucket_id", + "docs": [], + "indexed": false, + "label": "cdn_node_params", "type": { "displayName": [ - "BucketId" + "CdnNodeParams" ], - "type": 5 + "type": 13 } }, { - "label": "resource", + "docs": [], + "indexed": false, + "label": "undistributed_payment", "type": { "displayName": [ - "Resource" + "Balance" ], - "type": 5 + "type": 12 } } ], "docs": [ - " Allocate some resources of a cluster to a bucket.", - "", - " The amount of resources is given per vnode (total resources will be `resource` times the number of vnodes)." + " A node was created. The given account is its owner and recipient of revenues." ], - "label": "bucket_alloc_into_cluster", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x4c482d19" + "label": "CdnNodeCreated" }, { "args": [ { - "label": "bucket_id", + "docs": [], + "indexed": true, + "label": "cdn_node_key", "type": { "displayName": [ - "BucketId" + "CdnNodeKey" ], - "type": 5 + "type": 9 } } ], - "docs": [ - " Settle the due costs of a bucket from its payer account to the cluster account." - ], - "label": "bucket_settle_payment", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0x15974555" + "docs": [], + "label": "CdnNodeRemoved" }, { "args": [ { - "label": "bucket_id", + "docs": [], + "indexed": true, + "label": "cdn_node_key", "type": { "displayName": [ - "BucketId" + "CdnNodeKey" ], - "type": 5 + "type": 9 } }, { - "label": "params", + "docs": [], + "indexed": false, + "label": "cdn_node_params", "type": { "displayName": [ - "BucketParams" + "CdnNodeParams" ], - "type": 14 + "type": 13 } } ], - "docs": [ - " Change the `bucket_params`, which is configuration used by clients and nodes.", - "", - " See the [data structure of BucketParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" - ], - "label": "bucket_change_params", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x9f2d075b" + "docs": [], + "label": "CdnNodeParamsSet" }, { "args": [ { - "label": "bucket_id", + "docs": [], + "indexed": true, + "label": "node_key", "type": { "displayName": [ - "BucketId" + "NodeKey" ], - "type": 5 + "type": 9 } - } - ], - "docs": [ - " Get the current status of a bucket." - ], - "label": "bucket_get", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 45 - }, - "selector": "0x3802cb77" - }, - { - "args": [ + }, { - "label": "offset", + "docs": [], + "indexed": true, + "label": "provider_id", "type": { "displayName": [ - "u32" + "AccountId" ], - "type": 5 + "type": 9 } }, { - "label": "limit", + "docs": [], + "indexed": false, + "label": "rent_v_node_per_month", "type": { "displayName": [ - "u32" + "Balance" ], - "type": 5 + "type": 12 } }, { - "label": "filter_owner_id", + "docs": [], + "indexed": false, + "label": "node_params", "type": { "displayName": [ - "Option" + "NodeParams" ], - "type": 43 + "type": 13 } } ], "docs": [ - " Iterate through all buckets.", - "", - " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", - " The optimal `limit` depends on the size of params.", - "", - " The results can be filtered by owner. Note that paging must still be completed fully." + " A node was created. The given account is its owner and recipient of revenues." ], - "label": "bucket_list", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [], - "type": 49 - }, - "selector": "0x417ab584" + "label": "NodeCreated" }, { "args": [ { - "label": "owner_id", + "docs": [], + "indexed": true, + "label": "node_key", "type": { "displayName": [ - "AccountId" + "NodeKey" ], - "type": 2 + "type": 9 } } ], - "docs": [ - " Iterate through all buckets and return only those owned by owner", - "", - " This method returns bucket struct, not the status" - ], - "label": "bucket_list_for_account", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Vec" - ], - "type": 0 - }, - "selector": "0xc434cf57" + "docs": [], + "label": "NodeRemoved" }, { "args": [ { - "label": "bucket_id", + "docs": [], + "indexed": true, + "label": "node_key", "type": { "displayName": [ - "BucketId" + "NodeKey" ], - "type": 5 + "type": 9 } }, { - "label": "public_availability", + "docs": [], + "indexed": false, + "label": "node_params", "type": { "displayName": [ - "bool" + "NodeParams" ], - "type": 9 + "type": 13 } } ], - "docs": [ - " Set availiablity of the bucket" - ], - "label": "bucket_set_availability", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x053eb3ce" + "docs": [], + "label": "NodeParamsSet" }, { "args": [ { - "label": "bucket_id", + "docs": [], + "indexed": true, + "label": "account_id", "type": { "displayName": [ - "BucketId" + "AccountId" ], - "type": 5 + "type": 9 } }, { - "label": "new_resource_cap", + "docs": [], + "indexed": false, + "label": "value", "type": { "displayName": [ - "Resource" + "Balance" ], - "type": 5 + "type": 12 } } ], "docs": [ - " Set max resource cap to be charged by CDN for public bucket" + " Tokens were deposited on an account." ], - "label": "bucket_set_resource_cap", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x85010c6d" + "label": "Deposit" }, { "args": [ { - "label": "bucket_id", + "docs": [], + "indexed": true, + "label": "account_id", "type": { "displayName": [ - "BucketId" + "AccountId" + ], + "type": 9 + } + }, + { + "docs": [], + "indexed": false, + "label": "permission", + "type": { + "displayName": [ + "Permission" ], - "type": 5 + "type": 75 } } ], "docs": [ - " Set permission for the reader of the bucket" + " A permission was granted to the account." ], - "label": "get_bucket_writers", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "Vec" - ], - "type": 11 - }, - "selector": "0x499cd4b7" + "label": "PermissionGranted" }, { "args": [ { - "label": "bucket_id", + "docs": [], + "indexed": true, + "label": "account_id", "type": { "displayName": [ - "BucketId" + "AccountId" ], - "type": 5 + "type": 9 } }, { - "label": "writer", + "docs": [], + "indexed": false, + "label": "permission", "type": { "displayName": [ - "AccountId" + "Permission" ], - "type": 2 + "type": 75 } } ], "docs": [ - " Set permission for the writer of the bucket" + " A permission was revoked from the account." ], - "label": "bucket_set_writer_perm", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0xea2e477a" + "label": "PermissionRevoked" }, { "args": [ { - "label": "bucket_id", + "docs": [], + "indexed": true, + "label": "account_id", "type": { "displayName": [ - "BucketId" + "AccountId" ], - "type": 5 + "type": 9 } }, { - "label": "writer", + "docs": [], + "indexed": true, + "label": "node_key", "type": { "displayName": [ - "AccountId" + "NodeKey" ], - "type": 2 + "type": 9 } } ], - "docs": [ - " Revoke permission for the writer of the bucket" - ], - "label": "bucket_revoke_writer_perm", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x2b3d8dd1" + "docs": [], + "label": "NodeOwnershipTransferred" }, { "args": [ { - "label": "bucket_id", + "docs": [], + "indexed": true, + "label": "account_id", "type": { "displayName": [ - "BucketId" + "AccountId" + ], + "type": 9 + } + }, + { + "docs": [], + "indexed": true, + "label": "cdn_node_key", + "type": { + "displayName": [ + "CdnNodeKey" ], - "type": 5 + "type": 9 } } ], - "docs": [ - " Set permission for the reader of the bucket" - ], - "label": "get_bucket_readers", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "Vec" - ], - "type": 11 - }, - "selector": "0xb9a7cc1c" - }, + "docs": [], + "label": "CdnNodeOwnershipTransferred" + } + ], + "messages": [ { "args": [ { - "label": "bucket_id", + "label": "bucket_params", "type": { "displayName": [ - "BucketId" + "BucketParams" ], - "type": 5 + "type": 13 } }, { - "label": "reader", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" + ], + "type": 6 + } + }, + { + "label": "owner_id", + "type": { + "displayName": [ + "Option" ], - "type": 2 + "type": 41 } } ], "docs": [ - " Set permission for the reader of the bucket" + " Create a new bucket and return its `bucket_id`.", + "", + " The caller will be its first owner and payer of resources.", + "", + " `bucket_params` is configuration used by clients and nodes. See the [data structure of BucketParams](https://docs.cere.network/ddc/protocols/contract-params-schema)", + "", + " The bucket can be connected to a single cluster (currently). Allocate cluster resources with the function `bucket_alloc_into_cluster`" ], - "label": "bucket_set_reader_perm", + "label": "bucket_create", "mutates": true, - "payable": false, + "payable": true, "returnType": { - "displayName": [], - "type": 44 + "displayName": [ + "Result" + ], + "type": 42 }, - "selector": "0xfc0e94ea" + "selector": "0x0aeb2379" }, { "args": [ @@ -1036,560 +1044,624 @@ "displayName": [ "BucketId" ], - "type": 5 + "type": 6 } }, { - "label": "writer", + "label": "owner_id", "type": { "displayName": [ "AccountId" ], - "type": 2 + "type": 9 } } ], "docs": [ - " Revoke permission for the reader of the bucket" + " Change owner of the bucket", + "", + " Provide the account of new owner" ], - "label": "bucket_revoke_reader_perm", + "label": "bucket_change_owner", "mutates": true, - "payable": false, + "payable": true, "returnType": { - "displayName": [], + "displayName": [ + "Result" + ], "type": 44 }, - "selector": "0xe9bfed5a" + "selector": "0xc7d0c2cd" }, { "args": [ { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 5 - } - }, - { - "label": "node_ids", + "label": "bucket_id", "type": { "displayName": [ - "Vec" + "BucketId" ], - "type": 18 + "type": 6 } }, { - "label": "v_nodes", + "label": "resource", "type": { "displayName": [ - "Vec" + "Resource" ], - "type": 19 + "type": 6 } } ], "docs": [ - " Removes a node to an existing cluster", + " Allocate some resources of a cluster to a bucket.", "", - " The caller will be its first manager." + " The amount of resources is given per vnode (total resources will be `resource` times the number of vnodes)." ], - "label": "cluster_remove_node", + "label": "bucket_alloc_into_cluster", "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x793e0778" + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x4c482d19" }, { "args": [ { - "label": "cluster_id", + "label": "bucket_id", "type": { "displayName": [ - "ClusterId" + "BucketId" ], - "type": 5 + "type": 6 } - }, + } + ], + "docs": [ + " Settle the due costs of a bucket from its payer account to the cluster account." + ], + "label": "bucket_settle_payment", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x15974555" + }, + { + "args": [ { - "label": "node_ids", + "label": "bucket_id", "type": { "displayName": [ - "Vec" + "BucketId" ], - "type": 18 + "type": 6 } }, { - "label": "v_nodes", + "label": "params", "type": { "displayName": [ - "Vec" + "BucketParams" ], - "type": 19 + "type": 13 } } ], "docs": [ - " Adds node to an existing cluster", + " Change the `bucket_params`, which is configuration used by clients and nodes.", "", - " The caller will be its first manager." + " See the [data structure of BucketParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" ], - "label": "cluster_add_node", + "label": "bucket_change_params", "mutates": true, "payable": true, - "returnType": null, - "selector": "0xf7496bdc" + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x9f2d075b" }, { "args": [ { - "label": "_unused", + "label": "bucket_id", "type": { "displayName": [ - "AccountId" + "BucketId" ], - "type": 2 + "type": 6 } - }, + } + ], + "docs": [ + " Get the current status of a bucket." + ], + "label": "bucket_get", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 46 + }, + "selector": "0x3802cb77" + }, + { + "args": [ { - "label": "v_nodes", + "label": "offset", "type": { "displayName": [ - "Vec" + "u32" ], - "type": 19 + "type": 6 } }, { - "label": "node_ids", + "label": "limit", "type": { "displayName": [ - "Vec" + "u32" ], - "type": 18 + "type": 6 } }, { - "label": "cluster_params", + "label": "filter_owner_id", "type": { "displayName": [ - "ClusterParams" + "Option" ], - "type": 14 + "type": 41 } } ], "docs": [ - " Create a new cluster and return its `cluster_id`.", - "", - " The caller will be its first manager.", + " Iterate through all buckets.", "", - " The cluster is split in a number of vnodes. The vnodes are assigned to the given physical nodes in a round-robin. Only nodes of providers that trust the cluster manager can be used (see `node_trust_manager`). The assignment can be changed with the function `cluster_replace_node`.", + " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", + " The optimal `limit` depends on the size of params.", "", - " `cluster_params` is configuration used by clients and nodes. In particular, this describes the semantics of vnodes. See the [data structure of ClusterParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" + " The results can be filtered by owner. Note that paging must still be completed fully." ], - "label": "cluster_create", - "mutates": true, - "payable": true, + "label": "bucket_list", + "mutates": false, + "payable": false, "returnType": { - "displayName": [ - "ClusterId" - ], - "type": 5 + "displayName": [], + "type": 49 }, - "selector": "0x4c0f21f6" + "selector": "0x417ab584" }, { "args": [ { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 5 - } - }, - { - "label": "amount", + "label": "owner_id", "type": { "displayName": [ - "Resource" + "AccountId" ], - "type": 5 + "type": 9 } } ], "docs": [ - " As manager, reserve more resources for the cluster from the free capacity of nodes.", + " Iterate through all buckets and return only those owned by owner", "", - " The amount of resources is given per vnode (total resources will be `resource` times the number of vnodes)." + " This method returns bucket struct, not the status" ], - "label": "cluster_reserve_resource", - "mutates": true, + "label": "bucket_list_for_account", + "mutates": false, "payable": false, "returnType": { - "displayName": [], - "type": 44 + "displayName": [ + "Vec" + ], + "type": 51 }, - "selector": "0xb5e38125" + "selector": "0xc434cf57" }, { "args": [ { - "label": "node_id", + "label": "bucket_id", "type": { "displayName": [ - "NodeId" + "BucketId" ], - "type": 5 + "type": 6 } }, { - "label": "new_tag", + "label": "public_availability", "type": { "displayName": [ - "NodeTag" + "bool" ], - "type": 29 + "type": 3 } } ], "docs": [ - " As manager, change a node tag" + " Set availiablity of the bucket" ], - "label": "cluster_change_node_tag", + "label": "bucket_set_availability", "mutates": true, "payable": false, "returnType": { - "displayName": [], + "displayName": [ + "Result" + ], "type": 44 }, - "selector": "0x9640d48e" + "selector": "0x053eb3ce" }, { "args": [ { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 5 - } - }, - { - "label": "v_nodes", + "label": "bucket_id", "type": { "displayName": [ - "Vec" + "BucketId" ], - "type": 20 + "type": 6 } }, { - "label": "new_node_id", + "label": "new_resource_cap", "type": { "displayName": [ - "NodeId" + "Resource" ], - "type": 5 + "type": 6 } } ], "docs": [ - " As manager, re-assign a vnode to another physical node.", - "", - " The cluster manager can only use nodes of providers that trust him (see `node_trust_manager`), or any nodes if he is also SuperAdmin." + " Set max resource cap to be charged by CDN for public bucket" ], - "label": "cluster_replace_node", + "label": "bucket_set_resource_cap", "mutates": true, "payable": false, "returnType": { - "displayName": [], + "displayName": [ + "Result" + ], "type": 44 }, - "selector": "0x48194ab1" + "selector": "0x85010c6d" }, { "args": [ { - "label": "cluster_id", + "label": "bucket_id", "type": { "displayName": [ - "ClusterId" + "BucketId" ], - "type": 5 + "type": 6 } } ], "docs": [ - " Trigger the distribution of revenues from the cluster to the providers." + " Set permission for the reader of the bucket" ], - "label": "cluster_distribute_revenues", + "label": "get_bucket_writers", "mutates": true, "payable": false, - "returnType": null, - "selector": "0xe71e66fc" + "returnType": { + "displayName": [ + "Vec" + ], + "type": 15 + }, + "selector": "0x499cd4b7" }, { "args": [ { - "label": "cluster_id", + "label": "bucket_id", "type": { "displayName": [ - "ClusterId" + "BucketId" ], - "type": 5 + "type": 6 } }, { - "label": "params", + "label": "writer", "type": { "displayName": [ - "ClusterParams" + "AccountId" ], - "type": 14 + "type": 9 } } ], "docs": [ - " Change the `cluster_params`, which is configuration used by clients and nodes.", - "", - " See the [data structure of ClusterParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" + " Set permission for the writer of the bucket" ], - "label": "cluster_change_params", + "label": "bucket_set_writer_perm", "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x1207912c" - }, - { - "args": [ - { - "label": "cluster_id", - "type": { - "displayName": [ - "ClusterId" - ], - "type": 5 - } - } - ], - "docs": [ - " Get the current status of a cluster." - ], - "label": "cluster_get", - "mutates": false, "payable": false, "returnType": { "displayName": [ "Result" ], - "type": 51 + "type": 44 }, - "selector": "0xe75411f5" + "selector": "0xea2e477a" }, { "args": [ { - "label": "offset", - "type": { - "displayName": [ - "u32" - ], - "type": 5 - } - }, - { - "label": "limit", + "label": "bucket_id", "type": { "displayName": [ - "u32" + "BucketId" ], - "type": 5 + "type": 6 } }, { - "label": "filter_manager_id", + "label": "writer", "type": { "displayName": [ - "Option" + "AccountId" ], - "type": 43 + "type": 9 } } ], "docs": [ - " Iterate through all clusters.", - "", - " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", - " The optimal `limit` depends on the size of params.", - "", - " The results can be filtered by manager. Note that paging must still be completed fully." + " Revoke permission for the writer of the bucket" ], - "label": "cluster_list", - "mutates": false, + "label": "bucket_revoke_writer_perm", + "mutates": true, "payable": false, "returnType": { - "displayName": [], - "type": 53 + "displayName": [ + "Result" + ], + "type": 44 }, - "selector": "0xd9db9d44" + "selector": "0x2b3d8dd1" }, { "args": [ { - "label": "cdn_node_ids", + "label": "bucket_id", "type": { "displayName": [ - "Vec" + "BucketId" ], - "type": 18 + "type": 6 } } ], "docs": [ - " Create a new cluster and return its `cluster_id`.", - "", - " The caller will be its first manager.", - "", - " The CDN node ids are provided, which will form a cluster." + " Set permission for the reader of the bucket" ], - "label": "cdn_cluster_create", + "label": "get_bucket_readers", "mutates": true, - "payable": true, + "payable": false, "returnType": { "displayName": [ - "ClusterId" + "Vec" ], - "type": 5 + "type": 15 }, - "selector": "0x4344cd7e" + "selector": "0xb9a7cc1c" }, { "args": [ { - "label": "cluster_id", + "label": "bucket_id", "type": { "displayName": [ - "ClusterId" + "BucketId" ], - "type": 5 + "type": 6 } }, { - "label": "usd_per_gb", + "label": "reader", "type": { "displayName": [ - "Balance" + "AccountId" ], - "type": 8 + "type": 9 } } ], "docs": [ - " Set rate for streaming (price per gb)" + " Set permission for the reader of the bucket" ], - "label": "cdn_set_rate", + "label": "bucket_set_reader_perm", "mutates": true, - "payable": true, + "payable": false, "returnType": { - "displayName": [], + "displayName": [ + "Result" + ], "type": 44 }, - "selector": "0x7578922a" + "selector": "0xfc0e94ea" }, { "args": [ { - "label": "cluster_id", + "label": "bucket_id", "type": { "displayName": [ - "ClusterId" + "BucketId" ], - "type": 5 + "type": 6 + } + }, + { + "label": "writer", + "type": { + "displayName": [ + "AccountId" + ], + "type": 9 } } ], "docs": [ - " Get rate for streaming (price per gb)" + " Revoke permission for the reader of the bucket" ], - "label": "cdn_get_rate", - "mutates": false, - "payable": true, + "label": "bucket_revoke_reader_perm", + "mutates": true, + "payable": false, "returnType": { "displayName": [ - "Balance" + "Result" ], - "type": 8 + "type": 44 }, - "selector": "0xa1e3ea8a" + "selector": "0xe9bfed5a" }, { "args": [ { - "label": "cluster_id", + "label": "cluster_params", "type": { "displayName": [ - "ClusterId" + "ClusterParams" ], - "type": 5 + "type": 13 } }, { - "label": "aggregates_accounts", + "label": "resource_per_v_node", "type": { "displayName": [ - "Vec" + "Resource" ], - "type": 55 + "type": 6 } - }, + } + ], + "docs": [ + " Creates a cluster of Storage nodes and CDN nodes.", + "", + " This endpoint creates a cluster of Storage nodes and CDN nodes with specific parameters.", + " The caller will be the cluster manager (cluster owner). In order to add a Storage or CDN node, the manager must be authorized by the node owner using the `trust_manager` endpoint or be the node owner.", + "", + " # Parameters", + "", + " * `cluster_params` - [Cluster parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#cluster-parameters) in protobuf format.", + " * `resource_per_v_node` - Resource value that will be allocated for every virtual node in the cluster.", + "", + " # Output", + "", + " Returns ID of the created cluster.", + "", + " # Events", + "", + " * `ClusterCreated` event on successful cluster creation.", + "", + " # Errors", + "", + " * `InvalidClusterParams` error if there is an invalid cluster parameter." + ], + "label": "cluster_create", + "mutates": true, + "payable": true, + "returnType": { + "displayName": [ + "Result" + ], + "type": 42 + }, + "selector": "0x4c0f21f6" + }, + { + "args": [ { - "label": "aggregates_nodes", + "label": "cluster_id", "type": { "displayName": [ - "Vec" + "ClusterId" ], - "type": 57 + "type": 6 } }, { - "label": "aggregates_buckets", + "label": "node_key", "type": { "displayName": [ - "Vec" + "NodeKey" ], - "type": 59 + "type": 9 } }, { - "label": "era", + "label": "v_nodes", "type": { "displayName": [ - "u64" + "Vec" ], - "type": 21 + "type": 27 } } ], "docs": [ - " As validator, charge payments from users and allocate undistributed payments to CDN nodes.", + " Adds a Storage node to the targeting cluster.", "", - " As a result CDN cluster revenue increases, which can be distributed between CDN node providers via method cdn_cluster_distribute_revenues." + " This endpoint adds a physical Storage node along with its virtual nodes to the targeting cluster.", + " Virtual nodes determines a token (position) on the ring in terms of Consistent Hashing.", + " The Storage node can be added to the cluster by cluster manager only.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + " * `node_key` - Public Key associated with the Storage node.", + " * `v_nodes` - List of tokens (positions) related to the Storage node.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `ClusterNodeAdded` event on successful Storage node addition.", + "", + " # Errors", + "", + " * `ClusterDoesNotExist` error if the cluster does not exist.", + " * `OnlyTrustedClusterManager` error if the caller is not a trusted cluster manager.", + " * `NodeDoesNotExist` error if the adding Storage node does not exist.", + " * `NodeIsAddedToCluster(ClusterId)` error if the adding Storage node is already added to this or another cluster.", + " * `AtLeastOneVNodeHasToBeAssigned(ClusterId, NodeKey)` error if there is a Storage node without any virtual nodes in the cluster.", + " * `VNodesSizeExceedsLimit` error if virtual nodes length exceeds storage capacity.", + " * `InsufficientNodeResources` - error if there is not enough resources in a physical node." ], - "label": "cdn_cluster_put_revenue", + "label": "cluster_add_node", "mutates": true, - "payable": false, + "payable": true, "returnType": { - "displayName": [], + "displayName": [ + "Result" + ], "type": 44 }, - "selector": "0x7219be3f" + "selector": "0xf7496bdc" }, { "args": [ @@ -1599,988 +1671,2152 @@ "displayName": [ "ClusterId" ], - "type": 5 + "type": 6 } - } - ], - "docs": [ - " Trigger the distribution of revenues from the cluster to the CDN node providers.", - "", - " Anyone can call this method.", - "", - " Undistributed payments will be trasnferred, CDN cluster revenue will decrease." - ], - "label": "cdn_cluster_distribute_revenues", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0xfa8d570d" - }, - { - "args": [ + }, { - "label": "cluster_id", + "label": "node_key", "type": { "displayName": [ - "ClusterId" + "NodeKey" ], - "type": 5 + "type": 9 } } ], "docs": [ - " Get the current status of a cluster." + " Removes a Storage node from the targeting cluster.", + "", + " This endpoint removes a physical Storage node along with its virtual nodes from the targeting cluster.", + " The Storage node can be removed from the cluster either by cluster manager or by the node owner.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + " * `node_key` - Public Key associated with the Storage node.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `ClusterNodeRemoved` event on successful Storage node removal.", + "", + " # Errors", + "", + " * `ClusterDoesNotExist` error if the cluster does not exist.", + " * `OnlyClusterManagerOrNodeProvider` error if the caller is not the cluster manager or node owner.", + " * `NodeDoesNotExist` error if the removing Storage node does not exist.", + " * `NodeIsNotAddedToCluster(ClusterId)` error if the removing Storage node is not in this cluster." ], - "label": "cdn_cluster_get", - "mutates": false, + "label": "cluster_remove_node", + "mutates": true, "payable": false, "returnType": { "displayName": [ "Result" ], - "type": 61 + "type": 44 }, - "selector": "0x4b22fbf1" + "selector": "0x793e0778" }, { "args": [ { - "label": "offset", + "label": "cluster_id", "type": { "displayName": [ - "u32" + "ClusterId" ], - "type": 5 + "type": 6 } }, { - "label": "limit", + "label": "v_nodes", "type": { "displayName": [ - "u32" + "Vec" ], - "type": 5 + "type": 27 } }, { - "label": "filter_manager_id", + "label": "new_node_key", "type": { "displayName": [ - "Option" + "NodeKey" ], - "type": 43 + "type": 9 } } ], "docs": [ - " Iterate through all clusters.", + " Reasignes existing virtual nodes in the targeting cluster.", "", - " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", - " The optimal `limit` depends on the size of params.", + " This endpoint reasignes existing virtual nodes to another physical Storage node within the same cluster.", + " All specifying virtual nodes must pre-exist in the cluster and the new physical Storage node must be added to the cluster preliminary.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + " * `v_nodes` - List of tokens (positions) to reasign for the new physical Storage node.", + " * `new_node_key` - Public Key associated with the Storage node that is being reasigned to the specified virtual nodes.", + "", + " # Output", + "", + " Returns nothing.", "", - " The results can be filtered by manager. Note that paging must still be completed fully." + " # Events", + "", + " * `ClusterNodeReplaced` event on successful virtual node reassignment.", + "", + " # Errors", + "", + " * `ClusterDoesNotExist` error if the cluster does not exist.", + " * `OnlyClusterManager` error if the caller is not the cluster manager.", + " * `NodeDoesNotExist` error if the new Storage node does not exist.", + " * `NodeIsNotAddedToCluster(ClusterId)` error if the new Storage node is not added to this cluster.", + " * `NodeIsAddedToCluster(ClusterId)` error if the new Storage node is in another cluster.", + " * `VNodeIsNotAssignedToNode(ClusterId, VNodeToken)` error if the there is some virtual node that is being reasigned, but this virtual node is not assigned to any physical node.", + " * `VNodeIsAlreadyAssignedToNode(NodeKey)` - error if there is some virtual node that is already assigned to other physical node within the same cluster.", + " * `AtLeastOneVNodeHasToBeAssigned(ClusterId, NodeKey)` error if there is a Storage node without any virtual nodes in the cluster.", + " * `VNodesSizeExceedsLimit` error if virtual nodes length exceeds storage capacity." ], - "label": "cdn_cluster_list", - "mutates": false, + "label": "cluster_replace_node", + "mutates": true, "payable": false, "returnType": { - "displayName": [], - "type": 63 - }, - "selector": "0xb242a64f" + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x48194ab1" }, { "args": [ { - "label": "cdn_owner", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" ], - "type": 2 + "type": 6 } }, { - "label": "node_id", + "label": "node_key", "type": { "displayName": [ - "NodeId" + "NodeKey" ], - "type": 5 + "type": 9 } }, { - "label": "commit", + "label": "new_v_nodes", "type": { "displayName": [ - "Commit" + "Vec" ], - "type": 37 + "type": 27 } } ], "docs": [ - " CDN node operator sets the commit for current era." - ], - "label": "set_commit", + " Reeset a Storage node in the targeting cluster.", + "", + " This endpoint resets virtual nodes on a physical Storage node in the targeting cluster.", + " Virtual nodes determines a token (position) on the ring in terms of Consistent Hashing.", + " The Storage node can be reset in the cluster by cluster manager only.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + " * `node_key` - Public Key associated with the Storage node.", + " * `new_v_nodes` - List of tokens (positions) related to the Storage node to reset.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `ClusterNodeAdded` event on successful Storage node addition.", + "", + " # Errors", + "", + " * `ClusterDoesNotExist` error if the cluster does not exist.", + " * `OnlyTrustedClusterManager` error if the caller is not a trusted cluster manager.", + " * `NodeDoesNotExist` error if the adding Storage node does not exist.", + " * `NodeIsAddedToCluster(ClusterId)` error if the adding Storage node is already added to this or another cluster.", + " * `AtLeastOneVNodeHasToBeAssigned(ClusterId, NodeKey)` error if there is a Storage node without any virtual nodes in the cluster.", + " * `VNodesSizeExceedsLimit` error if virtual nodes length exceeds storage capacity.", + " * `InsufficientNodeResources` - error if there is not enough resources in a physical node." + ], + "label": "cluster_reset_node", "mutates": true, "payable": false, - "returnType": null, - "selector": "0xe445e1fd" + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0xa78b2e19" }, { "args": [ { - "label": "cdn_owner", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" ], - "type": 2 + "type": 6 } - } - ], - "docs": [ - " Return the last commit submitted by CDN node operator" - ], - "label": "get_commit", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Vec" - ], - "type": 35 - }, - "selector": "0x5329f551" - }, - { - "args": [ + }, { - "label": "node", + "label": "cdn_node_key", "type": { "displayName": [ - "NodeId" + "CdnNodeKey" ], - "type": 5 + "type": 9 } } ], "docs": [ - " Return last era validated per CDN node" + " Adds a CDN node to the targeting cluster.", + "", + " This endpoint adds a CDN node to the targeting cluster.", + " The CDN node can be added to the cluster by cluster manager only.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + " * `cdn_node_key` - Public Key associated with the CDN node.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `ClusterCdnNodeAdded` event on successful CDN node addition.", + "", + " # Errors", + "", + " * `ClusterDoesNotExist` error if the cluster does not exist.", + " * `OnlyTrustedClusterManager` error if the caller is not a trusted cluster manager.", + " * `CdnNodeDoesNotExist` error if the adding CDN node does not exist.", + " * `CdnNodeIsAddedToCluster(ClusterId)` error if the adding CDN node is already added to this or another cluster." ], - "label": "get_validated_commit", - "mutates": false, - "payable": false, + "label": "cluster_add_cdn_node", + "mutates": true, + "payable": true, "returnType": { "displayName": [ - "EraAndTimestamp" + "Result" ], - "type": 40 + "type": 44 }, - "selector": "0x7d497bc1" + "selector": "0x0b4199f3" }, { "args": [ { - "label": "era_config", + "label": "cluster_id", "type": { "displayName": [ - "EraConfig" + "ClusterId" + ], + "type": 6 + } + }, + { + "label": "cdn_node_key", + "type": { + "displayName": [ + "CdnNodeKey" ], - "type": 65 + "type": 9 } } ], "docs": [ - " Set the new configs for era" + " Removes a CDN node from the targeting cluster.", + "", + " This endpoint removes a CDN node the targeting cluster.", + " The CDN node can be removed from the cluster either by cluster manager or by the node owner.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + " * `cdn_node_key` - Public Key associated with the CDN node.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `ClusterCdnNodeRemoved` event on successful CDN node removal.", + "", + " # Errors", + "", + " * `ClusterDoesNotExist` error if the cluster does not exist.", + " * `OnlyClusterManagerOrCdnNodeProvider` error if the caller is not the cluster manager or node owner.", + " * `CdnNodeDoesNotExist` error if the removing CDN node does not exist.", + " * `CdnNodeIsNotAddedToCluster(ClusterId)` error if the removing CDN node is not in this cluster." ], - "label": "set_era", + "label": "cluster_remove_cdn_node", "mutates": true, "payable": false, - "returnType": { - "displayName": [], - "type": 44 - }, - "selector": "0x49a5b8f7" - }, - { - "args": [], - "docs": [ - " Return current status of an era" - ], - "label": "get_era", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "EraStatus" - ], - "type": 66 - }, - "selector": "0x617f696b" - }, - { - "args": [], - "docs": [ - " Return current era settings" - ], - "label": "get_era_settings", - "mutates": false, - "payable": false, "returnType": { "displayName": [ - "EraConfig" + "Result" ], - "type": 65 + "type": 44 }, - "selector": "0x84b61468" + "selector": "0xff8531d8" }, { "args": [ { - "label": "manager", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" ], - "type": 2 + "type": 6 } - } - ], - "docs": [ - " As node provider, authorize a cluster manager to use his nodes." - ], - "label": "cdn_node_trust_manager", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x372daa96" - }, - { - "args": [ + }, { - "label": "manager", + "label": "cluster_params", "type": { "displayName": [ - "AccountId" + "ClusterParams" ], - "type": 2 + "type": 13 } } ], "docs": [ - " As node provider, revoke the authorization of a cluster manager to use his nodes." + " Sets parameters for the targeting cluster.", + "", + " This endpoint updates [cluster parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#cluster-parameters) in protobuf format.", + " All cluster parameters must be specified as the endpoint works using SET approach.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + " * `cluster_params` - [Cluster parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#cluster-parameters) in protobuf format.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `ClusterParamsSet` event on successful cluster params setting.", + "", + " # Errors", + "", + " * `OnlyClusterManager` error if the caller is not the cluster manager.", + " * `ClusterDoesNotExist` error if the cluster does not exist." ], - "label": "cdn_node_distrust_manager", + "label": "cluster_set_params", "mutates": true, - "payable": false, - "returnType": null, - "selector": "0xf67f5438" + "payable": true, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x7dac5f9a" }, { "args": [ { - "label": "node_params", + "label": "cluster_id", "type": { "displayName": [ - "Params" + "ClusterId" ], - "type": 14 + "type": 6 } } ], "docs": [ - " Create a new node and return its `node_id`.", + " Removes a cluster.", + "", + " This endpoint removes the cluster if it does not contain any nodes.", + " Only an empty cluster can be removed.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", "", - " The caller will be its owner.", + " * `ClusterRemoved` event on successful cluster removal.", "", - " `node_params` is configuration used by clients and nodes. In particular, this contains the URL to the service. See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" + " # Errors", + "", + " * `OnlyClusterManager` error if the caller is not the cluster manager.", + " * `ClusterDoesNotExist` error if the cluster does not exist.", + " * `ClusterIsNotEmpty` error if the removing cluster contains some Storage or CDN nodes." ], - "label": "cdn_node_create", + "label": "cluster_remove", "mutates": true, - "payable": true, + "payable": false, "returnType": { "displayName": [ - "NodeId" + "Result" ], - "type": 5 + "type": 44 }, - "selector": "0xe8aa4ade" + "selector": "0x2248742a" }, { "args": [ { - "label": "node_id", + "label": "cluster_id", "type": { "displayName": [ - "NodeId" + "ClusterId" ], - "type": 5 + "type": 6 } }, { - "label": "params", + "label": "node_key", "type": { "displayName": [ - "NodeParams" + "NodeKey" ], - "type": 14 + "type": 9 } - } - ], - "docs": [ - " Change the `node_params`, which is configuration used by clients and nodes.", - "", - " See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" - ], - "label": "cdn_node_change_params", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0xf52c20f5" - }, - { - "args": [ + }, { - "label": "node_id", + "label": "status_in_cluster", "type": { "displayName": [ - "NodeId" + "NodeStatusInCluster" ], - "type": 5 + "type": 23 } } ], "docs": [ - " Get the current state of the cdn node" + " Changes Storage node status.", + "", + " This endpoint changes Storage node status in a cluster.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + " * `node_key` - Public Key associated with the Storage node.", + " * `status` - Status for the targeting Storage node, can be one of the following: ACTIVE, ADDING, DELETING, OFFLINE.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `ClusterNodeStatusSet` event on successful Storage status change.", + "", + " # Errors", + "", + " * `OnlyClusterManager` error if the caller is not the cluster manager.", + " * `ClusterDoesNotExist` error if the cluster does not exist.", + " * `NodeIsNotAddedToCluster(ClusterId)` error if the Storage node is not in this cluster." ], - "label": "cdn_node_get", - "mutates": false, + "label": "cluster_set_node_status", + "mutates": true, "payable": false, "returnType": { "displayName": [ "Result" ], - "type": 68 + "type": 44 }, - "selector": "0xf9a5a813" + "selector": "0x8078df7f" }, { "args": [ { - "label": "offset", + "label": "cluster_id", "type": { "displayName": [ - "u32" + "ClusterId" ], - "type": 5 + "type": 6 } }, { - "label": "limit", + "label": "cdn_node_key", "type": { "displayName": [ - "u32" + "CdnNodeKey" ], - "type": 5 + "type": 9 } }, { - "label": "filter_provider_id", + "label": "status_in_cluster", "type": { "displayName": [ - "Option" + "NodeStatusInCluster" ], - "type": 43 + "type": 23 } } ], "docs": [ - " Iterate through all nodes.", + " Changes CDN node status.", "", - " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", - " The optimal `limit` depends on the size of params.", + " This endpoint changes CDN node status in a cluster.", "", - " The results can be filtered by owner. Note that paging must still be completed fully." + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + " * `cdn_node_key` - Public Key associated with the CDN node.", + " * `status` - Status for the targeting CDN node, can be one of the following: ACTIVE, ADDING, DELETING, OFFLINE.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `ClusterCdnNodeStatusSet` event on successful CDN status change.", + "", + " # Errors", + "", + " * `OnlyClusterManager` error if the caller is not the cluster manager.", + " * `ClusterDoesNotExist` error if the cluster does not exist.", + " * `CdnNodeIsNotAddedToCluster(ClusterId)` error if the CDN node is not in this cluster." ], - "label": "cdn_node_list", - "mutates": false, + "label": "cluster_set_cdn_node_status", + "mutates": true, "payable": false, "returnType": { - "displayName": [], - "type": 70 + "displayName": [ + "Result" + ], + "type": 44 }, - "selector": "0xf8589aae" + "selector": "0x577027ba" }, { "args": [ { - "label": "manager", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" ], - "type": 2 + "type": 6 } - } - ], - "docs": [ - " As node provider, authorize a cluster manager to use his nodes." - ], - "label": "node_trust_manager", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x6fd54a01" - }, - { - "args": [ + }, { - "label": "manager", + "label": "new_resource_per_v_node", "type": { "displayName": [ - "AccountId" + "Resource" ], - "type": 2 + "type": 6 } } ], "docs": [ - " As node provider, revoke the authorization of a cluster manager to use his nodes." - ], - "label": "node_distrust_manager", + " Sets the resource used per virual node in cluster.", + "", + " This endpoint sets the resource value that is being used by each virtual node in the cluster.", + " If there are existing virtual nodes in the cluster the resource for its physical nodes will be recalculated.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + " * `new_resource_per_v_node` - Resource value that will be allocated for every virtual node in the cluster.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `ClusterNodeReplaced` event on successful virtual node reassignment.", + "", + " # Errors", + "", + " * `ClusterDoesNotExist` error if the cluster does not exist.", + " * `OnlyClusterManager` error if the caller is not the cluster manager.", + " * `NodeDoesNotExist` error if the new Storage node does not exist.", + " * `VNodeIsNotAssignedToNode(ClusterId, VNodeToken)` error if the there is some virtual node that is being reasigned, but this virtual node is not assigned to any physical node.", + " * `InsufficientClusterResources` - error if there is not enough resources in the cluster.", + " * `InsufficientNodeResources` - error if there is not enough resources in a physical node." + ], + "label": "cluster_set_resource_per_v_node", "mutates": true, "payable": false, - "returnType": null, - "selector": "0x40912279" + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0xbca5ef71" }, { "args": [ { - "label": "rent_per_month", + "label": "cluster_id", "type": { "displayName": [ - "Balance" + "ClusterId" ], - "type": 8 + "type": 6 } - }, + } + ], + "docs": [ + " Gets a cluster.", + "", + " This endpoint gets the targeting cluster along with its parameters, Storage and CDN nodes.", + "", + " # Parameters", + "", + " * `cluster_id` - ID of the targeting cluster.", + "", + " # Output", + "", + " Returns `ClusterInfo` data transfer object.", + "", + " # Errors", + "", + " * `ClusterDoesNotExist` error if the cluster does not exist." + ], + "label": "cluster_get", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 52 + }, + "selector": "0xe75411f5" + }, + { + "args": [ { - "label": "node_params", + "label": "offset", "type": { "displayName": [ - "NodeParams" + "u32" ], - "type": 14 + "type": 6 } }, { - "label": "capacity", + "label": "limit", "type": { "displayName": [ - "Resource" + "u32" ], - "type": 5 + "type": 6 } }, { - "label": "node_tag", + "label": "filter_manager_id", "type": { "displayName": [ - "NodeTag" + "Option" ], - "type": 29 + "type": 41 } - }, + } + ], + "docs": [ + " Gets a paginated list of clusters.", + "", + " This endpoint gets a paginated list of clusters along with their parameters, Storage and CDN nodes.", + " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", + " The optimal `limit` depends on the size of params.", + "", + " # Parameters", + "", + " * `offset` - starting offset.", + " * `limit` - page limit.", + " * `filter_manager_id` - optional filter by cluster manager.", + "", + " # Errors", + "", + " No errors. In case a pagination param is out of bounds, an empty list will be returned." + ], + "label": "cluster_list", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [], + "type": 54 + }, + "selector": "0xd9db9d44" + }, + { + "args": [ { - "label": "pubkey", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" ], - "type": 2 + "type": 6 } } ], "docs": [ - " Create a new node and return its `node_id`.", - "", - " The caller will be its owner.", - "", - " `node_params` is configuration used by clients and nodes. In particular, this contains the URL to the service. See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" + " Trigger the distribution of revenues from the cluster to the providers." ], - "label": "node_create", + "label": "cluster_distribute_revenues", "mutates": true, - "payable": true, + "payable": false, "returnType": { "displayName": [ - "NodeId" + "Result" ], - "type": 5 + "type": 44 }, - "selector": "0xb77ac1bb" + "selector": "0xe71e66fc" }, { "args": [ { - "label": "node_id", + "label": "cluster_id", "type": { "displayName": [ - "NodeId" + "ClusterId" ], - "type": 5 + "type": 6 } }, { - "label": "params", + "label": "usd_per_gb", "type": { "displayName": [ - "NodeParams" + "Balance" ], - "type": 14 + "type": 12 } } ], "docs": [ - " Change the `node_params`, which is configuration used by clients and nodes.", - "", - " See the [data structure of NodeParams](https://docs.cere.network/ddc/protocols/contract-params-schema)" + " Set rate for streaming (price per gb)" ], - "label": "node_change_params", + "label": "cdn_set_rate", "mutates": true, "payable": true, - "returnType": null, - "selector": "0x258ccb2a" - }, - { - "args": [ - { - "label": "node_id", - "type": { - "displayName": [ - "NodeId" - ], - "type": 5 - } - } - ], - "docs": [ - " Get the current status of a node." - ], - "label": "node_get", - "mutates": false, - "payable": false, "returnType": { "displayName": [ "Result" ], - "type": 72 + "type": 44 }, - "selector": "0x847f3997" + "selector": "0x7578922a" }, { "args": [ { - "label": "pubkey", + "label": "cluster_id", "type": { "displayName": [ - "AccountId" + "ClusterId" ], - "type": 2 + "type": 6 } } ], "docs": [ - " Get the current status of a node by a public key." + " Get rate for streaming (price per gb)" ], - "label": "node_get_by_pubkey", + "label": "cdn_get_rate", "mutates": false, - "payable": false, + "payable": true, "returnType": { "displayName": [ "Result" ], - "type": 72 + "type": 56 }, - "selector": "0x7f6c82d4" + "selector": "0xa1e3ea8a" }, { "args": [ { - "label": "offset", + "label": "cluster_id", "type": { "displayName": [ - "u32" + "ClusterId" ], - "type": 5 + "type": 6 } }, { - "label": "limit", + "label": "aggregates_accounts", "type": { "displayName": [ - "u32" + "Vec" ], - "type": 5 + "type": 57 } }, { - "label": "filter_provider_id", + "label": "aggregates_nodes", "type": { "displayName": [ - "Option" + "Vec" + ], + "type": 57 + } + }, + { + "label": "aggregates_buckets", + "type": { + "displayName": [ + "Vec" + ], + "type": 59 + } + }, + { + "label": "era", + "type": { + "displayName": [ + "u64" ], - "type": 43 + "type": 28 } } ], "docs": [ - " Iterate through all nodes.", - "", - " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", - " The optimal `limit` depends on the size of params.", + " As validator, charge payments from users and allocate undistributed payments to CDN nodes.", "", - " The results can be filtered by owner. Note that paging must still be completed fully." + " As a result CDN cluster revenue increases, which can be distributed between CDN node providers via method cdn_cluster_distribute_revenues." ], - "label": "node_list", - "mutates": false, + "label": "cluster_put_cdn_revenue", + "mutates": true, "payable": false, "returnType": { - "displayName": [], - "type": 74 + "displayName": [ + "Result" + ], + "type": 44 }, - "selector": "0x423286d6" + "selector": "0xeb8ec51c" }, { - "args": [], + "args": [ + { + "label": "cluster_id", + "type": { + "displayName": [ + "ClusterId" + ], + "type": 6 + } + } + ], "docs": [ - " Get the Fee Percentage Basis Points that will be charged by the protocol" + " Trigger the distribution of revenues from the cluster to the CDN node providers.", + "", + " Anyone can call this method.", + "", + " Undistributed payments will be trasnferred, CDN cluster revenue will decrease." ], - "label": "get_fee_bp", - "mutates": false, + "label": "cluster_distribute_cdn_revenue", + "mutates": true, "payable": false, "returnType": { "displayName": [ - "u32" + "Result" ], - "type": 5 + "type": 44 }, - "selector": "0x0d5daf5f" + "selector": "0x804ac6e0" }, { "args": [ { - "label": "fee_bp", + "label": "cdn_owner", "type": { "displayName": [ - "u32" + "AccountId" + ], + "type": 9 + } + }, + { + "label": "cdn_node_key", + "type": { + "displayName": [ + "CdnNodeKey" + ], + "type": 9 + } + }, + { + "label": "commit", + "type": { + "displayName": [ + "Commit" ], - "type": 5 + "type": 37 } } ], "docs": [ - " Return the last commit submitted by CDN node operator" + " CDN node operator sets the commit for current era." ], - "label": "set_fee_bp", + "label": "set_commit", "mutates": true, "payable": false, "returnType": { - "displayName": [], + "displayName": [ + "Result" + ], "type": 44 }, - "selector": "0xc5e3e2ca" + "selector": "0xe445e1fd" }, { - "args": [], + "args": [ + { + "label": "cdn_owner", + "type": { + "displayName": [ + "AccountId" + ], + "type": 9 + } + } + ], "docs": [ - " Return fees accumulated by the protocol" + " Return the last commit submitted by CDN node operator" ], - "label": "get_protocol_revenues", + "label": "get_commit", "mutates": false, "payable": false, "returnType": { "displayName": [ - "Cash" + "Vec" ], - "type": 17 + "type": 35 }, - "selector": "0x07c63885" + "selector": "0x5329f551" }, { "args": [ { - "label": "amount", + "label": "cdn_node_key", "type": { "displayName": [ - "u128" + "CdnNodeKey" ], - "type": 8 + "type": 9 } } ], "docs": [ - " Pay the revenues accumulated by the protocol" + " Return last era validated per CDN node" ], - "label": "protocol_withdraw_revenues", - "mutates": true, + "label": "get_validated_commit", + "mutates": false, "payable": false, "returnType": { - "displayName": [], - "type": 44 + "displayName": [ + "EraAndTimestamp" + ], + "type": 40 }, - "selector": "0x85c6fa6d" + "selector": "0x7d497bc1" }, { - "args": [], + "args": [ + { + "label": "era_config", + "type": { + "displayName": [ + "EraConfig" + ], + "type": 61 + } + } + ], "docs": [ - " As user, deposit tokens on the account of the caller from the transaction value. This deposit", - " can be used to pay for the services to buckets of the account." + " Set the new configs for era" ], - "label": "account_deposit", + "label": "set_era", "mutates": true, - "payable": true, + "payable": false, "returnType": { - "displayName": [], + "displayName": [ + "Result" + ], "type": 44 }, - "selector": "0xc311af62" + "selector": "0x49a5b8f7" + }, + { + "args": [], + "docs": [ + " Return current status of an era" + ], + "label": "get_era", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "EraStatus" + ], + "type": 62 + }, + "selector": "0x617f696b" + }, + { + "args": [], + "docs": [ + " Return current era settings" + ], + "label": "get_era_settings", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "EraConfig" + ], + "type": 61 + }, + "selector": "0x84b61468" }, { "args": [ { - "label": "bond_amount", + "label": "cdn_node_key", "type": { "displayName": [ - "Balance" + "CdnNodeKey" ], - "type": 8 + "type": 9 + } + }, + { + "label": "cdn_node_params", + "type": { + "displayName": [ + "CdnNodeParams" + ], + "type": 13 } } ], "docs": [ - " As user, bond some amount of tokens from the withdrawable balance. These funds will be used to pay for CDN node service." + " Creates a CDN node", + "", + " This endpoint creates a CDN node with specific parameters.", + " The caller will be the node owner (node provider).", + "", + " # Parameters", + "", + " * `cdn_node_key` - Public Keys of the CDN node that should be treated as node identifier.", + " * `cdn_node_params` - [CDN node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format.", + "", + " # Output", + "", + " Returns Public Key of the created CDN node.", + "", + " # Events", + "", + " * `CdnNodeCreated` event on successful CDN node creation.", + "", + " # Errors", + "", + " * `CdnNodeAlreadyExists` error if a CDN node with the same Public Key is already created.", + " * `InvalidParams(message)` error if there is some invalid configuration parameter." ], - "label": "account_bond", + "label": "cdn_node_create", "mutates": true, "payable": true, "returnType": { - "displayName": [], - "type": 44 + "displayName": [ + "Result" + ], + "type": 64 }, - "selector": "0xe9fad0bf" + "selector": "0xe8aa4ade" }, { "args": [ { - "label": "amount_to_unbond", + "label": "cdn_node_key", "type": { "displayName": [ - "Cash" + "CdnNodeKey" ], - "type": 17 + "type": 9 } } ], "docs": [ - " As user, unbond a specified amount of tokens. The tokens will be locked for some time, as defined by contract owner." + " Removes a CDN node.", + "", + " This endpoint removes the targeting CDN Node if it is not added to some cluster.", + " Only a node that is not a member of some cluster can be removed.", + "", + " # Parameters", + "", + " * `cdn_node_key` - Public Key associated with the CDN node.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `CdnNodeRemoved` event on successful CDN node removal.", + "", + " # Errors", + "", + " * `OnlyCdnNodeProvider` error if the caller is not the CDN node owner.", + " * `CdnNodeDoesNotExist` error if the CDN node does not exist.", + " * `CdnNodeIsAddedToCluster(ClusterId)` error if the removing CDN node is added to some cluster." ], - "label": "account_unbond", + "label": "cdn_node_remove", "mutates": true, - "payable": true, + "payable": false, "returnType": { - "displayName": [], + "displayName": [ + "Result" + ], "type": 44 }, - "selector": "0xf7ea2c67" + "selector": "0xe068fb34" }, { - "args": [], + "args": [ + { + "label": "cdn_node_key", + "type": { + "displayName": [ + "CdnNodeKey" + ], + "type": 9 + } + }, + { + "label": "cdn_node_params", + "type": { + "displayName": [ + "CdnNodeParams" + ], + "type": 13 + } + } + ], "docs": [ - " As user, move the unbonded tokens back to withdrawable balance state.", + " Sets parameters for the targeting CDN node.", "", - " This can be triggered after unbonded_timestamp" + " This endpoint updates [CDN node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format.", + " All CDN node parameters must be specified as the endpoint works using SET approach.", + "", + " # Parameters", + "", + " * `cdn_node_key` - Public Key associated with the CDN node.", + " * `cdn_node_params` - [CDN node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `CdnNodeParamsSet` event on successful CDN node params setting.", + "", + " # Errors", + "", + " * `OnlyCdnNodeProvider` error if the caller is not the CDN node owner.", + " * `CdnNodeDoesNotExist` error if the CDN node does not exist." ], - "label": "account_withdraw_unbonded", + "label": "cdn_node_set_params", "mutates": true, "payable": true, "returnType": { - "displayName": [], + "displayName": [ + "Result" + ], "type": 44 }, - "selector": "0x98173716" + "selector": "0xdf8b696e" }, { "args": [ { - "label": "account_id", + "label": "cdn_node_key", "type": { "displayName": [ - "AccountId" + "CdnNodeKey" ], - "type": 2 + "type": 9 } } ], "docs": [ - " Get the current status of an account." + " Gets a CDN node.", + "", + " This endpoint gets the targeting CDN node along with its parameters.", + "", + " # Parameters", + "", + " * `cdn_node_key` - Public Key associated with the CDN node.", + "", + " # Output", + "", + " Returns `CdnNodeInfo` data transfer object.", + "", + " # Errors", + "", + " * `CdnNodeDoesNotExist` error if the CDN node does not exist." ], - "label": "account_get", + "label": "cdn_node_get", "mutates": false, "payable": false, "returnType": { "displayName": [ "Result" ], - "type": 76 - }, - "selector": "0x1d4220fa" - }, - { - "args": [], - "docs": [ - " Get the current conversion rate between the native currency and an external currency (USD)." - ], - "label": "account_get_usd_per_cere", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "Balance" - ], - "type": 8 + "type": 65 }, - "selector": "0xe4a4652a" + "selector": "0xf9a5a813" }, { "args": [ { - "label": "usd_per_cere", + "label": "offset", "type": { "displayName": [ - "Balance" + "u32" ], - "type": 8 + "type": 6 } - } - ], - "docs": [ - " As price oracle, set the current conversion rate between the native currency and an external currency (USD).", - "", - " This requires the permission SetExchangeRate or SuperAdmin." - ], - "label": "account_set_usd_per_cere", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0x48d45ee8" - }, - { - "args": [ + }, { - "label": "grantee", + "label": "limit", "type": { "displayName": [ - "AccountId" + "u32" ], - "type": 2 + "type": 6 } }, { - "label": "permission", + "label": "filter_provider_id", "type": { "displayName": [ - "Permission" + "Option" ], - "type": 77 + "type": 41 } } ], "docs": [ - " Check whether the given account has the given permission currently,", - " or the SuperAdmin permission." + " Gets a paginated list of CDN nodes.", + "", + " This endpoint gets a paginated list of CDN nodes along with their parameters.", + " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", + " The optimal `limit` depends on the size of params.", + "", + " # Parameters", + "", + " * `offset` - starting offset.", + " * `limit` - page limit.", + " * `filter_provider_id` - optional filter by CDN node owner.", + "", + " # Errors", + "", + " No errors. In case a pagination param is out of bounds, an empty list will be returned." ], - "label": "has_permission", + "label": "cdn_node_list", "mutates": false, "payable": false, "returnType": { - "displayName": [ - "bool" - ], - "type": 9 + "displayName": [], + "type": 67 }, - "selector": "0xe0942492" + "selector": "0xf8589aae" }, { "args": [ { - "label": "grantee", + "label": "node_key", "type": { "displayName": [ - "AccountId" + "NodeKey" ], - "type": 2 + "type": 9 } }, { - "label": "permission", + "label": "node_params", "type": { "displayName": [ - "Permission" + "NodeParams" ], - "type": 77 + "type": 13 } - } - ], - "docs": [ - " As SuperAdmin, grant any permission to any account." - ], - "label": "admin_grant_permission", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0xbe41ea55" - }, - { - "args": [ + }, { - "label": "grantee", + "label": "capacity", "type": { "displayName": [ - "AccountId" + "Resource" ], - "type": 2 + "type": 6 } }, { - "label": "permission", + "label": "rent_v_node_per_month", "type": { "displayName": [ - "Permission" + "Balance" ], - "type": 77 + "type": 12 } } ], "docs": [ - " As SuperAdmin, revoke any permission to any account." + " Creates a Storage node", + "", + " This endpoint creates a Storage node with specific parameters.", + " The caller will be the node owner (node provider).", + "", + " # Parameters", + "", + " * `node_key` - Public Keys of the Storage node that should be treated as node identifier.", + " * `node_params` - [Storage node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format.", + " * `capacity` - Measure used to evaluate physical node hardware resources.", + " * `rent_v_node_per_month` - Renting per month.", + "", + " # Output", + "", + " Returns Public Key of the created Storage node.", + "", + " # Events", + "", + " * `NodeCreated` event on successful Storage node creation.", + "", + " # Errors", + "", + " * `NodeAlreadyExists` error if a Storage node with the same Public Key is already created.", + " * `InvalidParams(message)` error if there is some invalid configuration parameter." ], - "label": "admin_revoke_permission", + "label": "node_create", "mutates": true, - "payable": false, - "returnType": null, - "selector": "0x6b150666" + "payable": true, + "returnType": { + "displayName": [ + "Result" + ], + "type": 64 + }, + "selector": "0xb77ac1bb" }, { "args": [ { - "label": "amount", + "label": "node_key", "type": { "displayName": [ - "Balance" + "NodeKey" ], - "type": 8 + "type": 9 + } + } + ], + "docs": [ + " Removes a Storage node.", + "", + " This endpoint removes the targeting Storage Node if it is not added to some cluster.", + " Only a node that is not a member of some cluster can be removed.", + "", + " # Parameters", + "", + " * `node_key` - Public Key associated with the Storage node.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `NodeRemoved` event on successful Storage node removal.", + "", + " # Errors", + "", + " * `OnlyNodeProvider` error if the caller is not the Storage node owner.", + " * `NodeDoesNotExist` error if the Storage node does not exist.", + " * `NodeIsAddedToCluster(ClusterId)` error if the removing Storage node is added to some cluster." + ], + "label": "node_remove", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x42e5f273" + }, + { + "args": [ + { + "label": "node_key", + "type": { + "displayName": [ + "NodeKey" + ], + "type": 9 + } + }, + { + "label": "node_params", + "type": { + "displayName": [ + "NodeParams" + ], + "type": 13 + } + } + ], + "docs": [ + " Sets parameters for the targeting Storage node.", + "", + " This endpoint updates [Storage node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format.", + " All Storage node parameters must be specified as the endpoint works using SET approach.", + "", + " # Parameters", + "", + " * `node_key` - Public Key associated with the Storage node.", + " * `node_params` - [Storage node parameters](https://docs.cere.network/ddc/protocols/contract-params-schema#node-params.proto) in protobuf format.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `NodeParamsSet` event on successful Storage node params setting.", + "", + " # Errors", + "", + " * `OnlyNodeProvider` error if the caller is not the Storage node owner.", + " * `NodeDoesNotExist` error if the Storage node does not exist." + ], + "label": "node_set_params", + "mutates": true, + "payable": true, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0xfb74fd2e" + }, + { + "args": [ + { + "label": "node_key", + "type": { + "displayName": [ + "NodeKey" + ], + "type": 9 + } + } + ], + "docs": [ + " Gets a Storage node.", + "", + " This endpoint gets the targeting Storage node along with its parameters.", + "", + " # Parameters", + "", + " * `node_key` - Public Key associated with the Storage node.", + "", + " # Output", + "", + " Returns `NodeInfo` data transfer object.", + "", + " # Errors", + "", + " * `NodeDoesNotExist` error if the Storage node does not exist." + ], + "label": "node_get", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 69 + }, + "selector": "0x847f3997" + }, + { + "args": [ + { + "label": "offset", + "type": { + "displayName": [ + "u32" + ], + "type": 6 + } + }, + { + "label": "limit", + "type": { + "displayName": [ + "u32" + ], + "type": 6 + } + }, + { + "label": "filter_provider_id", + "type": { + "displayName": [ + "Option" + ], + "type": 41 + } + } + ], + "docs": [ + " Gets a paginated list of Storage nodes.", + "", + " This endpoint gets a paginated list of Storage nodes along with their parameters.", + " The algorithm for paging is: start with `offset = 1` and `limit = 20`. The function returns a `(results, max_id)`. Call again with `offset += limit`, until `offset >= max_id`.", + " The optimal `limit` depends on the size of params.", + "", + " # Parameters", + "", + " * `offset` - starting offset.", + " * `limit` - page limit.", + " * `filter_provider_id` - optional filter by Storage node owner.", + "", + " # Errors", + "", + " No errors. In case a pagination param is out of bounds, an empty list will be returned." + ], + "label": "node_list", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [], + "type": 71 + }, + "selector": "0x423286d6" + }, + { + "args": [], + "docs": [ + " Get the Fee Percentage Basis Points that will be charged by the protocol" + ], + "label": "get_protocol_fee_bp", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "u128" + ], + "type": 12 + }, + "selector": "0x650857a8" + }, + { + "args": [], + "docs": [ + " Return fees accumulated by the protocol" + ], + "label": "get_protocol_revenues", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "Cash" + ], + "type": 18 + }, + "selector": "0x07c63885" + }, + { + "args": [], + "docs": [ + " Get the Fee Percentage Basis Points that will be charged by the protocol" + ], + "label": "get_network_fee_config", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "NetworkFeeConfig" + ], + "type": 73 + }, + "selector": "0xd1503f07" + }, + { + "args": [], + "docs": [ + " As user, deposit tokens on the account of the caller from the transaction value. This deposit", + " can be used to pay for the services to buckets of the account." + ], + "label": "account_deposit", + "mutates": true, + "payable": true, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0xc311af62" + }, + { + "args": [ + { + "label": "bond_amount", + "type": { + "displayName": [ + "Balance" + ], + "type": 12 + } + } + ], + "docs": [ + " As user, bond some amount of tokens from the withdrawable balance. These funds will be used to pay for CDN node service." + ], + "label": "account_bond", + "mutates": true, + "payable": true, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0xe9fad0bf" + }, + { + "args": [ + { + "label": "amount_to_unbond", + "type": { + "displayName": [ + "Cash" + ], + "type": 18 + } + } + ], + "docs": [ + " As user, unbond a specified amount of tokens. The tokens will be locked for some time, as defined by contract owner." + ], + "label": "account_unbond", + "mutates": true, + "payable": true, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0xf7ea2c67" + }, + { + "args": [], + "docs": [ + " As user, move the unbonded tokens back to withdrawable balance state.", + "", + " This can be triggered after unbonded_timestamp" + ], + "label": "account_withdraw_unbonded", + "mutates": true, + "payable": true, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x98173716" + }, + { + "args": [ + { + "label": "account_id", + "type": { + "displayName": [ + "AccountId" + ], + "type": 9 + } + } + ], + "docs": [ + " Get the current status of an account." + ], + "label": "account_get", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 74 + }, + "selector": "0x1d4220fa" + }, + { + "args": [], + "docs": [ + " Get the current conversion rate between the native currency and an external currency (USD)." + ], + "label": "account_get_usd_per_cere", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "Balance" + ], + "type": 12 + }, + "selector": "0xe4a4652a" + }, + { + "args": [ + { + "label": "usd_per_cere", + "type": { + "displayName": [ + "Balance" + ], + "type": 12 + } + } + ], + "docs": [ + " As price oracle, set the current conversion rate between the native currency and an external currency (USD).", + "", + " This requires the permission SetExchangeRate or SuperAdmin." + ], + "label": "account_set_usd_per_cere", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x48d45ee8" + }, + { + "args": [ + { + "label": "account_id", + "type": { + "displayName": [ + "AccountId" + ], + "type": 9 + } + }, + { + "label": "permission", + "type": { + "displayName": [ + "Permission" + ], + "type": 75 + } + } + ], + "docs": [ + " Checks for permission existence.", + "", + " This endpoint checks whether the given account has the given permission.", + " Super-admin will always have all permissions.", + "", + " # Parameters", + "", + " * `account_id` - account to check permissions.", + " * `permission` - permission to check.", + "", + " # Output", + "", + " Returns true if the account has permissions, false otherwise.", + "", + " # Errors", + "", + " No errors." + ], + "label": "has_permission", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "bool" + ], + "type": 3 + }, + "selector": "0xe0942492" + }, + { + "args": [ + { + "label": "manager_id", + "type": { + "displayName": [ + "AccountId" + ], + "type": 9 + } + } + ], + "docs": [ + " Grants permissions for a cluster manager.", + "", + " This endpoint grants permissions for a cluster manager ro manage Storage or CDN node owner.", + " After the permission is granted, the cluster manager can add nodes to the cluster.", + " Permissions can be granted by Storage or CDN node owner.", + "", + " # Parameters", + "", + " * `manager_id` - cluster manager account.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `PermissionGranted` event on successful manager permissions granting", + "", + " # Errors", + "", + " No errors. The endpoint is idempotent." + ], + "label": "grant_trusted_manager_permission", + "mutates": true, + "payable": true, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0xea0cbdcd" + }, + { + "args": [ + { + "label": "manager_id", + "type": { + "displayName": [ + "AccountId" + ], + "type": 9 + } + } + ], + "docs": [ + " Revokes permissions from cluster manager.", + "", + " This endpoint revokes permissions from a cluster manager to manage Storage or CDN node owner.", + " After the permission is revoked, the cluster manager can add nodes to the cluster.", + " Permissions can be revoked by Storage or CDN node owner.", + "", + " # Parameters", + "", + " * `manager_id` - cluster manager account.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `PermissionRevoked` event on successful manager permissions revoking", + "", + " # Errors", + "", + " No errors. The endpoint is idempotent." + ], + "label": "revoke_trusted_manager_permission", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x83532355" + }, + { + "args": [ + { + "label": "grantee", + "type": { + "displayName": [ + "AccountId" + ], + "type": 9 + } + }, + { + "label": "permission", + "type": { + "displayName": [ + "Permission" + ], + "type": 75 + } + } + ], + "docs": [ + " Grants any permission.", + "", + " This endpoint grants any permissions for any account by the Super-admin.", + "", + " # Parameters", + "", + " * `grantee` - account to grant permission.", + " * `permission` - permission type.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `PermissionGranted` event on successful permissions granting", + "", + " # Errors", + "", + " Returns `OnlySuperAdmin` error if the caller is not the Super-admin." + ], + "label": "admin_grant_permission", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0xbe41ea55" + }, + { + "args": [ + { + "label": "grantee", + "type": { + "displayName": [ + "AccountId" + ], + "type": 9 + } + }, + { + "label": "permission", + "type": { + "displayName": [ + "Permission" + ], + "type": 75 + } + } + ], + "docs": [ + " Revokes any permission.", + "", + " This endpoint revokes any permissions from any account by the Super-admin.", + "", + " # Parameters", + "", + " * `grantee` - account to revoke permission.", + " * `permission` - permission type.", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `PermissionRevoked` event on successful permissions revoking", + "", + " # Errors", + "", + " Returns `OnlySuperAdmin` error if the caller is not the Super-admin." + ], + "label": "admin_revoke_permission", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x6b150666" + }, + { + "args": [ + { + "label": "node_key", + "type": { + "displayName": [ + "NodeKey" + ], + "type": 9 + } + }, + { + "label": "new_owner", + "type": { + "displayName": [ + "AccountId" + ], + "type": 9 + } + } + ], + "docs": [ + " Transfers Storage node ownership.", + "", + " This endpoint transfers Storage node ownership from Super-admin account to the targeting account forever.", + " This action is usually required only once after the Storage node certification process.", + "", + " # Parameters", + "", + " * `node_key` - Public Key associated with the Storage node.", + " * `new_owner` - New Storage node owner", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `NodeOwnershipTransferred` event on successful Storage node ownership transfer", + "", + " # Errors", + "", + " * `OnlySuperAdmin` error if the caller is not the Super-admin.", + " * `NodeDoesNotExist` error if the Storage node does not exist.", + " * `NodeProviderIsNotSuperAdmin` error if the owner of the targeting node is not the Super-admin." + ], + "label": "admin_transfer_node_ownership", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x783b382d" + }, + { + "args": [ + { + "label": "cdn_node_key", + "type": { + "displayName": [ + "CdnNodeKey" + ], + "type": 9 + } + }, + { + "label": "new_owner", + "type": { + "displayName": [ + "AccountId" + ], + "type": 9 + } + } + ], + "docs": [ + " Transfers CDN node ownership.", + "", + " This endpoint transfers CDN node ownership from Super-admin account to the targeting account forever.", + " This action is usually required only once after the CDN node certification process.", + "", + " # Parameters", + "", + " * `cdn_node_key` - Public Key associated with the CDN node.", + " * `new_owner` - CDN node owner", + "", + " # Output", + "", + " Returns nothing.", + "", + " # Events", + "", + " * `CdnNodeOwnershipTransferred` event on successful CDN node ownership transfer", + "", + " # Errors", + "", + " * `OnlySuperAdmin` error if the caller is not the Super-admin.", + " * `CdnNodeDoesNotExist` error if the Storage node does not exist.", + " * `CdnNodeOwnerIsNotSuperAdmin` error if the owner of the targeting node is not the Super-admin." + ], + "label": "admin_transfer_cdn_node_ownership", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0xcd9821be" + }, + { + "args": [ + { + "label": "amount", + "type": { + "displayName": [ + "Balance" + ], + "type": 12 } } ], @@ -2592,45 +3828,186 @@ "label": "admin_withdraw", "mutates": true, "payable": false, - "returnType": null, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, "selector": "0x2f6e0868" }, { "args": [ { - "label": "config", + "label": "amount", + "type": { + "displayName": [ + "Balance" + ], + "type": 12 + } + } + ], + "docs": [ + " Pay the revenues accumulated by the protocol" + ], + "label": "admin_withdraw_protocol_revenues", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0xa781da48" + }, + { + "args": [ + { + "label": "config", + "type": { + "displayName": [ + "NetworkFeeConfig" + ], + "type": 73 + } + } + ], + "docs": [ + " As SuperAdmin, set the network and cluster fee configuration." + ], + "label": "admin_set_network_fee_config", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x14649395" + }, + { + "args": [ + { + "label": "protocol_fee_bp", + "type": { + "displayName": [ + "BasisPoints" + ], + "type": 12 + } + } + ], + "docs": [], + "label": "admin_set_protocol_fee_bp", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 44 + }, + "selector": "0x00e00535" + }, + { + "args": [], + "docs": [ + " Get all Account IDs stored in the SC" + ], + "label": "get_accounts", + "mutates": false, + "payable": true, + "returnType": { + "displayName": [ + "Vec" + ], + "type": 15 + }, + "selector": "0xef03ead7" + }, + { + "args": [ + { + "label": "cluster_id", + "type": { + "displayName": [ + "ClusterId" + ], + "type": 6 + } + } + ], + "docs": [], + "label": "get_v_nodes_by_cluster", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "Vec" + ], + "type": 27 + }, + "selector": "0x94eb8da6" + }, + { + "args": [ + { + "label": "node_key", + "type": { + "displayName": [ + "NodeKey" + ], + "type": 9 + } + } + ], + "docs": [], + "label": "get_v_nodes_by_node", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "Vec" + ], + "type": 27 + }, + "selector": "0xf0dfbd13" + }, + { + "args": [ + { + "label": "cluster_id", + "type": { + "displayName": [ + "ClusterId" + ], + "type": 6 + } + }, + { + "label": "v_node", "type": { "displayName": [ - "FeeConfig" + "VNodeToken" ], - "type": 78 + "type": 28 } } ], - "docs": [ - " As SuperAdmin, set the network and cluster fee configuration." - ], - "label": "admin_set_fee_config", - "mutates": true, - "payable": false, - "returnType": null, - "selector": "0x00d441e7" - }, - { - "args": [], - "docs": [ - " Get all Account IDs stored in the SC" - ], - "label": "get_accounts", + "docs": [], + "label": "get_node_by_v_node", "mutates": false, - "payable": true, + "payable": false, "returnType": { "displayName": [ - "Vec" + "Result" ], - "type": 11 + "type": 64 }, - "selector": "0xef03ead7" + "selector": "0xea47048e" } ] }, @@ -2648,12 +4025,12 @@ "ty": 0 } }, - "name": null + "name": "perms" } ] } }, - "name": "buckets" + "name": "perms" }, { "layout": { @@ -2663,60 +4040,42 @@ "layout": { "cell": { "key": "0x0100000000000000000000000000000000000000000000000000000000000000", - "ty": 10 + "ty": 6 } }, - "name": "writers" + "name": "next_bucket_id" }, { "layout": { "cell": { "key": "0x0200000000000000000000000000000000000000000000000000000000000000", - "ty": 10 + "ty": 7 } }, - "name": "readers" - } - ] - } - }, - "name": "buckets_perms" - }, - { - "layout": { - "struct": { - "fields": [ + "name": "buckets" + }, { "layout": { "cell": { "key": "0x0300000000000000000000000000000000000000000000000000000000000000", - "ty": 13 + "ty": 14 } }, - "name": null - } - ] - } - }, - "name": "bucket_params" - }, - { - "layout": { - "struct": { - "fields": [ + "name": "writers" + }, { "layout": { "cell": { "key": "0x0400000000000000000000000000000000000000000000000000000000000000", - "ty": 15 + "ty": 14 } }, - "name": null + "name": "readers" } ] } }, - "name": "clusters" + "name": "buckets" }, { "layout": { @@ -2726,33 +4085,24 @@ "layout": { "cell": { "key": "0x0500000000000000000000000000000000000000000000000000000000000000", - "ty": 22 + "ty": 6 } }, - "name": null - } - ] - } - }, - "name": "cdn_clusters" - }, - { - "layout": { - "struct": { - "fields": [ + "name": "next_cluster_id" + }, { "layout": { "cell": { "key": "0x0600000000000000000000000000000000000000000000000000000000000000", - "ty": 13 + "ty": 16 } }, - "name": null + "name": "clusters" } ] } }, - "name": "cluster_params" + "name": "clusters" }, { "layout": { @@ -2762,33 +4112,24 @@ "layout": { "cell": { "key": "0x0700000000000000000000000000000000000000000000000000000000000000", - "ty": 24 + "ty": 19 } }, - "name": null - } - ] - } - }, - "name": "cdn_nodes" - }, - { - "layout": { - "struct": { - "fields": [ + "name": "cdn_nodes" + }, { "layout": { "cell": { "key": "0x0800000000000000000000000000000000000000000000000000000000000000", - "ty": 13 + "ty": 15 } }, - "name": null + "name": "keys" } ] } }, - "name": "cdn_node_params" + "name": "cdn_nodes" }, { "layout": { @@ -2798,19 +4139,19 @@ "layout": { "cell": { "key": "0x0900000000000000000000000000000000000000000000000000000000000000", - "ty": 26 + "ty": 24 } }, - "name": "account_node" + "name": "nodes" }, { "layout": { "cell": { "key": "0x0a00000000000000000000000000000000000000000000000000000000000000", - "ty": 27 + "ty": 15 } }, - "name": "nodes" + "name": "keys" } ] } @@ -2825,60 +4166,33 @@ "layout": { "cell": { "key": "0x0b00000000000000000000000000000000000000000000000000000000000000", - "ty": 13 + "ty": 26 } }, - "name": null - } - ] - } - }, - "name": "node_params" - }, - { - "layout": { - "struct": { - "fields": [ + "name": "v_nodes_map" + }, { "layout": { "cell": { "key": "0x0c00000000000000000000000000000000000000000000000000000000000000", - "ty": 30 - } - }, - "name": null - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0d00000000000000000000000000000000000000000000000000000000000000", - "ty": 8 - } - }, - "name": null - } - ] + "ty": 29 } }, - "name": null + "name": "nodes_map" }, { "layout": { "cell": { - "key": "0x0e00000000000000000000000000000000000000000000000000000000000000", - "ty": 11 + "key": "0x0d00000000000000000000000000000000000000000000000000000000000000", + "ty": 31 } }, - "name": null + "name": "cluster_v_nodes_map" } ] } }, - "name": "accounts" + "name": "topology" }, { "layout": { @@ -2887,61 +4201,25 @@ { "layout": { "cell": { - "key": "0x0f00000000000000000000000000000000000000000000000000000000000000", + "key": "0x0e00000000000000000000000000000000000000000000000000000000000000", "ty": 32 } }, - "name": null - } - ] - } - }, - "name": "perms" - }, - { - "layout": { - "struct": { - "fields": [ + "name": "accounts" + }, { "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x1000000000000000000000000000000000000000000000000000000000000000", - "ty": 8 - } - }, - "name": "network_fee_bp" - }, - { - "layout": { - "cell": { - "key": "0x1100000000000000000000000000000000000000000000000000000000000000", - "ty": 2 - } - }, - "name": "network_fee_destination" - }, - { - "layout": { - "cell": { - "key": "0x1200000000000000000000000000000000000000000000000000000000000000", - "ty": 8 - } - }, - "name": "cluster_management_fee_bp" - } - ] + "cell": { + "key": "0x0f00000000000000000000000000000000000000000000000000000000000000", + "ty": 15 } }, - "name": null + "name": "accounts_keys" } ] } }, - "name": "network_fee" + "name": "accounts" }, { "layout": { @@ -2950,8 +4228,8 @@ { "layout": { "cell": { - "key": "0x1300000000000000000000000000000000000000000000000000000000000000", - "ty": 2 + "key": "0x1000000000000000000000000000000000000000000000000000000000000000", + "ty": 9 } }, "name": "operator_id" @@ -2959,7 +4237,7 @@ { "layout": { "cell": { - "key": "0x1400000000000000000000000000000000000000000000000000000000000000", + "key": "0x1100000000000000000000000000000000000000000000000000000000000000", "ty": 34 } }, @@ -2968,7 +4246,7 @@ { "layout": { "cell": { - "key": "0x1500000000000000000000000000000000000000000000000000000000000000", + "key": "0x1200000000000000000000000000000000000000000000000000000000000000", "ty": 39 } }, @@ -2981,8 +4259,8 @@ { "layout": { "cell": { - "key": "0x1600000000000000000000000000000000000000000000000000000000000000", - "ty": 21 + "key": "0x1300000000000000000000000000000000000000000000000000000000000000", + "ty": 28 } }, "name": "start" @@ -2990,8 +4268,8 @@ { "layout": { "cell": { - "key": "0x1700000000000000000000000000000000000000000000000000000000000000", - "ty": 21 + "key": "0x1400000000000000000000000000000000000000000000000000000000000000", + "ty": 28 } }, "name": "interval" @@ -2999,8 +4277,8 @@ { "layout": { "cell": { - "key": "0x1800000000000000000000000000000000000000000000000000000000000000", - "ty": 21 + "key": "0x1500000000000000000000000000000000000000000000000000000000000000", + "ty": 28 } }, "name": "commit_duration" @@ -3008,8 +4286,8 @@ { "layout": { "cell": { - "key": "0x1900000000000000000000000000000000000000000000000000000000000000", - "ty": 21 + "key": "0x1600000000000000000000000000000000000000000000000000000000000000", + "ty": 28 } }, "name": "validation_duration" @@ -3022,7 +4300,7 @@ ] } }, - "name": "committer_store" + "name": "committer" }, { "layout": { @@ -3031,20 +4309,20 @@ { "layout": { "cell": { - "key": "0x1a00000000000000000000000000000000000000000000000000000000000000", - "ty": 2 + "key": "0x1700000000000000000000000000000000000000000000000000000000000000", + "ty": 12 } }, - "name": "admin" + "name": "protocol_fee_bp" }, { "layout": { "cell": { - "key": "0x1b00000000000000000000000000000000000000000000000000000000000000", - "ty": 5 + "key": "0x1800000000000000000000000000000000000000000000000000000000000000", + "ty": 9 } }, - "name": "fee_bp" + "name": "protocol_fee_destination" }, { "layout": { @@ -3053,8 +4331,8 @@ { "layout": { "cell": { - "key": "0x1c00000000000000000000000000000000000000000000000000000000000000", - "ty": 8 + "key": "0x1900000000000000000000000000000000000000000000000000000000000000", + "ty": 12 } }, "name": null @@ -3063,79 +4341,238 @@ } }, "name": "revenues" - } - ] - } - }, - "name": "protocol_store" - }, - { - "layout": { - "struct": { - "fields": [ + }, { "layout": { - "cell": { - "key": "0x1d00000000000000000000000000000000000000000000000000000000000000", - "ty": 41 + "struct": { + "fields": [ + { + "layout": { + "cell": { + "key": "0x1a00000000000000000000000000000000000000000000000000000000000000", + "ty": 12 + } + }, + "name": "rate" + } + ] + } + }, + "name": "curr_converter" + }, + { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "cell": { + "key": "0x1b00000000000000000000000000000000000000000000000000000000000000", + "ty": 12 + } + }, + "name": "network_fee_bp" + }, + { + "layout": { + "cell": { + "key": "0x1c00000000000000000000000000000000000000000000000000000000000000", + "ty": 9 + } + }, + "name": "network_fee_destination" + }, + { + "layout": { + "cell": { + "key": "0x1d00000000000000000000000000000000000000000000000000000000000000", + "ty": 12 + } + }, + "name": "cluster_management_fee_bp" + } + ] } }, - "name": null + "name": "network_fee_config" } ] } }, - "name": "topology_store" + "name": "protocol" + } + ] + } + }, + "types": [ + { + "id": 0, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "offset_key", + "type": 4, + "typeName": "Key" + } + ] + } + }, + "params": [ + { + "name": "K", + "type": 1 + }, + { + "name": "V", + "type": 3 + } + ], + "path": [ + "ink_storage", + "lazy", + "mapping", + "Mapping" + ] + } + }, + { + "id": 1, + "type": { + "def": { + "sequence": { + "type": 2 + } + } + } + }, + { + "id": 2, + "type": { + "def": { + "primitive": "u8" } - ] - } - }, - "types": [ + } + }, { - "id": 0, + "id": 3, "type": { "def": { - "sequence": { - "type": 1 + "primitive": "bool" + } + } + }, + { + "id": 4, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 5, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "Key" + ] + } + }, + { + "id": 5, + "type": { + "def": { + "array": { + "len": 32, + "type": 2 } } } }, { - "id": 1, + "id": 6, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 7, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "offset_key", + "type": 4, + "typeName": "Key" + } + ] + } + }, + "params": [ + { + "name": "K", + "type": 6 + }, + { + "name": "V", + "type": 8 + } + ], + "path": [ + "ink_storage", + "lazy", + "mapping", + "Mapping" + ] + } + }, + { + "id": 8, "type": { "def": { "composite": { "fields": [ { "name": "owner_id", - "type": 2, + "type": 9, "typeName": "AccountId" }, { "name": "cluster_id", - "type": 5, + "type": 6, "typeName": "ClusterId" }, { "name": "flow", - "type": 6, + "type": 10, "typeName": "Flow" }, { "name": "resource_reserved", - "type": 5, + "type": 6, "typeName": "Resource" }, { "name": "public_availability", - "type": 9, + "type": 3, "typeName": "bool" }, { "name": "resource_consumption_cap", - "type": 5, + "type": 6, "typeName": "Resource" + }, + { + "name": "bucket_params", + "type": 13, + "typeName": "BucketParams" } ] } @@ -3150,13 +4587,13 @@ } }, { - "id": 2, + "id": 9, "type": { "def": { "composite": { "fields": [ { - "type": 3, + "type": 5, "typeName": "[u8; 32]" } ] @@ -3170,47 +4607,204 @@ } }, { - "id": 3, + "id": 10, "type": { "def": { - "array": { - "len": 32, - "type": 4 + "composite": { + "fields": [ + { + "name": "from", + "type": 9, + "typeName": "AccountId" + }, + { + "name": "schedule", + "type": 11, + "typeName": "Schedule" + } + ] + } + }, + "path": [ + "ddc_bucket", + "ddc_bucket", + "flow", + "Flow" + ] + } + }, + { + "id": 11, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "rate", + "type": 12, + "typeName": "Balance" + }, + { + "name": "offset", + "type": 12, + "typeName": "Balance" + } + ] } + }, + "path": [ + "ddc_bucket", + "ddc_bucket", + "schedule", + "Schedule" + ] + } + }, + { + "id": 12, + "type": { + "def": { + "primitive": "u128" } } }, { - "id": 4, + "id": 13, "type": { "def": { - "primitive": "u8" + "primitive": "str" } } }, { - "id": 5, + "id": 14, "type": { "def": { - "primitive": "u32" + "composite": { + "fields": [ + { + "name": "offset_key", + "type": 4, + "typeName": "Key" + } + ] + } + }, + "params": [ + { + "name": "K", + "type": 6 + }, + { + "name": "V", + "type": 15 + } + ], + "path": [ + "ink_storage", + "lazy", + "mapping", + "Mapping" + ] + } + }, + { + "id": 15, + "type": { + "def": { + "sequence": { + "type": 9 + } } } }, { - "id": 6, + "id": 16, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "offset_key", + "type": 4, + "typeName": "Key" + } + ] + } + }, + "params": [ + { + "name": "K", + "type": 6 + }, + { + "name": "V", + "type": 17 + } + ], + "path": [ + "ink_storage", + "lazy", + "mapping", + "Mapping" + ] + } + }, + { + "id": 17, "type": { "def": { "composite": { "fields": [ { - "name": "from", - "type": 2, - "typeName": "AccountId" + "name": "manager_id", + "type": 9, + "typeName": "AccountId" + }, + { + "name": "cluster_params", + "type": 13, + "typeName": "ClusterParams" + }, + { + "name": "nodes_keys", + "type": 15, + "typeName": "Vec" + }, + { + "name": "resource_per_v_node", + "type": 6, + "typeName": "Resource" + }, + { + "name": "resource_used", + "type": 6, + "typeName": "Resource" + }, + { + "name": "revenues", + "type": 18, + "typeName": "Cash" + }, + { + "name": "total_rent", + "type": 12, + "typeName": "Balance" + }, + { + "name": "cdn_nodes_keys", + "type": 15, + "typeName": "Vec" + }, + { + "name": "cdn_revenues", + "type": 18, + "typeName": "Cash" }, { - "name": "schedule", - "type": 7, - "typeName": "Schedule" + "name": "cdn_usd_per_gb", + "type": 12, + "typeName": "Balance" } ] } @@ -3218,25 +4812,21 @@ "path": [ "ddc_bucket", "ddc_bucket", - "flow", - "Flow" + "cluster", + "entity", + "Cluster" ] } }, { - "id": 7, + "id": 18, "type": { "def": { "composite": { "fields": [ { - "name": "rate", - "type": 8, - "typeName": "Balance" - }, - { - "name": "offset", - "type": 8, + "name": "value", + "type": 12, "typeName": "Balance" } ] @@ -3245,36 +4835,20 @@ "path": [ "ddc_bucket", "ddc_bucket", - "schedule", - "Schedule" + "cash", + "Cash" ] } }, { - "id": 8, - "type": { - "def": { - "primitive": "u128" - } - } - }, - { - "id": 9, - "type": { - "def": { - "primitive": "bool" - } - } - }, - { - "id": 10, + "id": 19, "type": { "def": { "composite": { "fields": [ { "name": "offset_key", - "type": 12, + "type": 4, "typeName": "Key" } ] @@ -3283,11 +4857,11 @@ "params": [ { "name": "K", - "type": 5 + "type": 9 }, { "name": "V", - "type": 11 + "type": 20 } ], "path": [ @@ -3299,102 +4873,135 @@ } }, { - "id": 11, - "type": { - "def": { - "sequence": { - "type": 2 - } - } - } - }, - { - "id": 12, + "id": 20, "type": { "def": { "composite": { "fields": [ { - "type": 3, - "typeName": "[u8; 32]" + "name": "provider_id", + "type": 9, + "typeName": "ProviderId" + }, + { + "name": "undistributed_payment", + "type": 12, + "typeName": "Balance" + }, + { + "name": "cdn_node_params", + "type": 13, + "typeName": "CdnNodeParams" + }, + { + "name": "cluster_id", + "type": 21, + "typeName": "Option" + }, + { + "name": "status_in_cluster", + "type": 22, + "typeName": "Option" } ] } }, "path": [ - "ink_primitives", - "Key" + "ddc_bucket", + "ddc_bucket", + "cdn_node", + "entity", + "CdnNode" ] } }, { - "id": 13, + "id": 21, "type": { "def": { - "sequence": { - "type": 14 + "variant": { + "variants": [ + { + "index": 0, + "name": "None" + }, + { + "fields": [ + { + "type": 6 + } + ], + "index": 1, + "name": "Some" + } + ] } - } - } - }, - { - "id": 14, - "type": { - "def": { - "primitive": "str" - } + }, + "params": [ + { + "name": "T", + "type": 6 + } + ], + "path": [ + "Option" + ] } }, { - "id": 15, + "id": 22, "type": { "def": { - "sequence": { - "type": 16 + "variant": { + "variants": [ + { + "index": 0, + "name": "None" + }, + { + "fields": [ + { + "type": 23 + } + ], + "index": 1, + "name": "Some" + } + ] } - } + }, + "params": [ + { + "name": "T", + "type": 23 + } + ], + "path": [ + "Option" + ] } }, { - "id": 16, + "id": 23, "type": { "def": { - "composite": { - "fields": [ - { - "name": "manager_id", - "type": 2, - "typeName": "AccountId" - }, - { - "name": "resource_per_vnode", - "type": 5, - "typeName": "Resource" - }, - { - "name": "resource_used", - "type": 5, - "typeName": "Resource" - }, + "variant": { + "variants": [ { - "name": "revenues", - "type": 17, - "typeName": "Cash" + "index": 0, + "name": "ADDING" }, { - "name": "node_ids", - "type": 18, - "typeName": "Vec" + "index": 1, + "name": "ACTIVE" }, { - "name": "v_nodes", - "type": 19, - "typeName": "Vec>" + "index": 2, + "name": "DELETING" }, { - "name": "total_rent", - "type": 8, - "typeName": "Balance" + "index": 3, + "name": "OFFLINE" } ] } @@ -3402,112 +5009,79 @@ "path": [ "ddc_bucket", "ddc_bucket", - "cluster", + "node", "entity", - "Cluster" + "NodeStatusInCluster" ] } }, { - "id": 17, + "id": 24, "type": { "def": { "composite": { "fields": [ { - "name": "value", - "type": 8, - "typeName": "Balance" + "name": "offset_key", + "type": 4, + "typeName": "Key" } ] } }, + "params": [ + { + "name": "K", + "type": 9 + }, + { + "name": "V", + "type": 25 + } + ], "path": [ - "ddc_bucket", - "ddc_bucket", - "cash", - "Cash" + "ink_storage", + "lazy", + "mapping", + "Mapping" ] } }, { - "id": 18, - "type": { - "def": { - "sequence": { - "type": 5 - } - } - } - }, - { - "id": 19, - "type": { - "def": { - "sequence": { - "type": 20 - } - } - } - }, - { - "id": 20, - "type": { - "def": { - "sequence": { - "type": 21 - } - } - } - }, - { - "id": 21, - "type": { - "def": { - "primitive": "u64" - } - } - }, - { - "id": 22, - "type": { - "def": { - "sequence": { - "type": 23 - } - } - } - }, - { - "id": 23, + "id": 25, "type": { "def": { "composite": { "fields": [ { - "name": "manager_id", - "type": 2, - "typeName": "AccountId" + "name": "provider_id", + "type": 9, + "typeName": "ProviderId" }, { - "name": "cdn_nodes", - "type": 18, - "typeName": "Vec" + "name": "rent_v_node_per_month", + "type": 12, + "typeName": "Balance" }, { - "name": "resources_used", - "type": 5, + "name": "free_resource", + "type": 6, "typeName": "Resource" }, { - "name": "revenues", - "type": 17, - "typeName": "Cash" + "name": "node_params", + "type": 13, + "typeName": "NodeParams" }, { - "name": "usd_per_gb", - "type": 8, - "typeName": "Balance" + "name": "cluster_id", + "type": 21, + "typeName": "Option" + }, + { + "name": "status_in_cluster", + "type": 22, + "typeName": "Option" } ] } @@ -3515,59 +5089,114 @@ "path": [ "ddc_bucket", "ddc_bucket", - "cdn_cluster", + "node", "entity", - "CdnCluster" + "Node" ] } }, { - "id": 24, + "id": 26, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "offset_key", + "type": 4, + "typeName": "Key" + } + ] + } + }, + "params": [ + { + "name": "K", + "type": 9 + }, + { + "name": "V", + "type": 27 + } + ], + "path": [ + "ink_storage", + "lazy", + "mapping", + "Mapping" + ] + } + }, + { + "id": 27, "type": { "def": { "sequence": { - "type": 25 + "type": 28 } } } }, { - "id": 25, + "id": 28, + "type": { + "def": { + "primitive": "u64" + } + } + }, + { + "id": 29, "type": { "def": { "composite": { "fields": [ { - "name": "provider_id", - "type": 2, - "typeName": "ProviderId" - }, - { - "name": "undistributed_payment", - "type": 8, - "typeName": "Balance" + "name": "offset_key", + "type": 4, + "typeName": "Key" } ] } }, + "params": [ + { + "name": "K", + "type": 30 + }, + { + "name": "V", + "type": 9 + } + ], "path": [ - "ddc_bucket", - "ddc_bucket", - "cdn_node", - "entity", - "CdnNode" + "ink_storage", + "lazy", + "mapping", + "Mapping" ] } }, { - "id": 26, + "id": 30, + "type": { + "def": { + "tuple": [ + 6, + 28 + ] + } + } + }, + { + "id": 31, "type": { "def": { "composite": { "fields": [ { "name": "offset_key", - "type": 12, + "type": 4, "typeName": "Key" } ] @@ -3576,11 +5205,11 @@ "params": [ { "name": "K", - "type": 2 + "type": 6 }, { "name": "V", - "type": 5 + "type": 27 } ], "path": [ @@ -3592,78 +5221,72 @@ } }, { - "id": 27, - "type": { - "def": { - "sequence": { - "type": 28 - } - } - } - }, - { - "id": 28, + "id": 32, "type": { "def": { "composite": { "fields": [ { - "name": "provider_id", - "type": 2, - "typeName": "ProviderId" - }, - { - "name": "rent_per_month", - "type": 8, - "typeName": "Balance" - }, - { - "name": "free_resource", - "type": 5, - "typeName": "Resource" - }, - { - "name": "node_tag", - "type": 29, - "typeName": "NodeTag" + "name": "offset_key", + "type": 4, + "typeName": "Key" } ] } }, + "params": [ + { + "name": "K", + "type": 9 + }, + { + "name": "V", + "type": 33 + } + ], "path": [ - "ddc_bucket", - "ddc_bucket", - "node", - "entity", - "Node" + "ink_storage", + "lazy", + "mapping", + "Mapping" ] } }, { - "id": 29, + "id": 33, "type": { "def": { - "variant": { - "variants": [ + "composite": { + "fields": [ { - "index": 0, - "name": "UNKNOWN" + "name": "deposit", + "type": 18, + "typeName": "Cash" }, { - "index": 1, - "name": "ACTIVE" + "name": "bonded", + "type": 18, + "typeName": "Cash" }, { - "index": 2, - "name": "ADDING" + "name": "negative", + "type": 18, + "typeName": "Cash" }, { - "index": 3, - "name": "DELETING" + "name": "unbonded_amount", + "type": 18, + "typeName": "Cash" }, { - "index": 4, - "name": "OFFLINE" + "name": "unbonded_timestamp", + "type": 28, + "typeName": "u64" + }, + { + "name": "payable_schedule", + "type": 11, + "typeName": "Schedule" } ] } @@ -3671,21 +5294,21 @@ "path": [ "ddc_bucket", "ddc_bucket", - "node", + "account", "entity", - "NodeTag" + "Account" ] } }, { - "id": 30, + "id": 34, "type": { "def": { "composite": { "fields": [ { "name": "offset_key", - "type": 12, + "type": 4, "typeName": "Key" } ] @@ -3694,11 +5317,11 @@ "params": [ { "name": "K", - "type": 2 + "type": 9 }, { "name": "V", - "type": 31 + "type": 35 } ], "path": [ @@ -3710,62 +5333,93 @@ } }, { - "id": 31, + "id": 35, + "type": { + "def": { + "sequence": { + "type": 36 + } + } + } + }, + { + "id": 36, + "type": { + "def": { + "tuple": [ + 9, + 37 + ] + } + } + }, + { + "id": 37, "type": { "def": { "composite": { "fields": [ { - "name": "deposit", - "type": 17, - "typeName": "Cash" - }, - { - "name": "bonded", - "type": 17, - "typeName": "Cash" + "name": "hash", + "type": 38, + "typeName": "Hash" }, { - "name": "negative", - "type": 17, - "typeName": "Cash" + "name": "total_logs", + "type": 12, + "typeName": "u128" }, { - "name": "unbonded_amount", - "type": 17, - "typeName": "Cash" + "name": "from_timestamp", + "type": 28, + "typeName": "u64" }, { - "name": "unbonded_timestamp", - "type": 21, + "name": "to_timestamp", + "type": 28, "typeName": "u64" - }, + } + ] + } + }, + "path": [ + "ddc_bucket", + "ddc_bucket", + "committer", + "store", + "Commit" + ] + } + }, + { + "id": 38, + "type": { + "def": { + "composite": { + "fields": [ { - "name": "payable_schedule", - "type": 7, - "typeName": "Schedule" + "type": 5, + "typeName": "[u8; 32]" } ] } }, "path": [ - "ddc_bucket", - "ddc_bucket", - "account", - "entity", - "Account" + "ink_env", + "types", + "Hash" ] } }, { - "id": 32, + "id": 39, "type": { "def": { "composite": { "fields": [ { "name": "offset_key", - "type": 12, + "type": 4, "typeName": "Key" } ] @@ -3774,11 +5428,11 @@ "params": [ { "name": "K", - "type": 33 + "type": 9 }, { "name": "V", - "type": 9 + "type": 40 } ], "path": [ @@ -3790,230 +5444,360 @@ } }, { - "id": 33, + "id": 40, "type": { "def": { - "sequence": { - "type": 4 - } + "tuple": [ + 28, + 28 + ] } } }, { - "id": 34, + "id": 41, "type": { "def": { - "composite": { - "fields": [ + "variant": { + "variants": [ { - "name": "offset_key", - "type": 12, - "typeName": "Key" + "index": 0, + "name": "None" + }, + { + "fields": [ + { + "type": 9 + } + ], + "index": 1, + "name": "Some" } ] } }, "params": [ { - "name": "K", - "type": 2 - }, - { - "name": "V", - "type": 35 + "name": "T", + "type": 9 } ], "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" + "Option" ] } }, { - "id": 35, + "id": 42, "type": { "def": { - "sequence": { - "type": 36 + "variant": { + "variants": [ + { + "fields": [ + { + "type": 6 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 43 + } + ], + "index": 1, + "name": "Err" + } + ] } - } - } - }, - { - "id": 36, - "type": { - "def": { - "tuple": [ - 5, - 37 - ] - } + }, + "params": [ + { + "name": "T", + "type": 6 + }, + { + "name": "E", + "type": 43 + } + ], + "path": [ + "Result" + ] } }, { - "id": 37, + "id": 43, "type": { "def": { - "composite": { - "fields": [ + "variant": { + "variants": [ { - "name": "hash", - "type": 38, - "typeName": "Hash" + "index": 0, + "name": "NodeDoesNotExist" + }, + { + "index": 1, + "name": "CdnNodeDoesNotExist" + }, + { + "index": 2, + "name": "NodeAlreadyExists" + }, + { + "index": 3, + "name": "CdnNodeAlreadyExists" + }, + { + "index": 4, + "name": "AccountDoesNotExist" + }, + { + "index": 5, + "name": "ParamsDoesNotExist" + }, + { + "index": 6, + "name": "ParamsSizeExceedsLimit" + }, + { + "index": 7, + "name": "OnlyOwner" + }, + { + "index": 8, + "name": "OnlyNodeProvider" + }, + { + "index": 9, + "name": "OnlyCdnNodeProvider" + }, + { + "index": 10, + "name": "OnlyClusterManager" + }, + { + "index": 11, + "name": "OnlyTrustedClusterManager" + }, + { + "index": 12, + "name": "OnlyValidator" + }, + { + "index": 13, + "name": "OnlySuperAdmin" + }, + { + "index": 14, + "name": "OnlyClusterManagerOrNodeProvider" + }, + { + "index": 15, + "name": "OnlyClusterManagerOrCdnNodeProvider" + }, + { + "index": 16, + "name": "Unauthorized" + }, + { + "index": 17, + "name": "ClusterDoesNotExist" + }, + { + "index": 18, + "name": "ClusterIsNotEmpty" + }, + { + "fields": [ + { + "type": 6, + "typeName": "ClusterId" + } + ], + "index": 19, + "name": "TopologyIsNotCreated" + }, + { + "index": 20, + "name": "TopologyAlreadyExists" + }, + { + "index": 21, + "name": "NodesSizeExceedsLimit" + }, + { + "index": 22, + "name": "CdnNodesSizeExceedsLimit" + }, + { + "index": 23, + "name": "VNodesSizeExceedsLimit" + }, + { + "index": 24, + "name": "AccountsSizeExceedsLimit" + }, + { + "fields": [ + { + "type": 6, + "typeName": "ClusterId" + } + ], + "index": 25, + "name": "NodeIsNotAddedToCluster" + }, + { + "fields": [ + { + "type": 6, + "typeName": "ClusterId" + } + ], + "index": 26, + "name": "NodeIsAddedToCluster" + }, + { + "fields": [ + { + "type": 6, + "typeName": "ClusterId" + } + ], + "index": 27, + "name": "CdnNodeIsNotAddedToCluster" + }, + { + "fields": [ + { + "type": 6, + "typeName": "ClusterId" + } + ], + "index": 28, + "name": "CdnNodeIsAddedToCluster" + }, + { + "fields": [ + { + "type": 6, + "typeName": "ClusterId" + } + ], + "index": 29, + "name": "VNodeDoesNotExistsInCluster" + }, + { + "fields": [ + { + "type": 6, + "typeName": "ClusterId" + }, + { + "type": 28, + "typeName": "VNodeToken" + } + ], + "index": 30, + "name": "VNodeIsNotAssignedToNode" + }, + { + "fields": [ + { + "type": 9, + "typeName": "NodeKey" + } + ], + "index": 31, + "name": "VNodeIsAlreadyAssignedToNode" + }, + { + "fields": [ + { + "type": 6, + "typeName": "ClusterId" + }, + { + "type": 9, + "typeName": "NodeKey" + } + ], + "index": 32, + "name": "AtLeastOneVNodeHasToBeAssigned" + }, + { + "index": 33, + "name": "NodeProviderIsNotSuperAdmin" + }, + { + "index": 34, + "name": "CdnNodeOwnerIsNotSuperAdmin" + }, + { + "index": 35, + "name": "BucketDoesNotExist" }, { - "name": "total_logs", - "type": 8, - "typeName": "u128" + "index": 36, + "name": "BondingPeriodNotFinished" }, { - "name": "from_timestamp", - "type": 21, - "typeName": "u64" + "index": 37, + "name": "TransferFailed" }, { - "name": "to_timestamp", - "type": 21, - "typeName": "u64" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "committer", - "store", - "Commit" - ] - } - }, - { - "id": 38, - "type": { - "def": { - "composite": { - "fields": [ + "index": 38, + "name": "InsufficientBalance" + }, { - "type": 3, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_env", - "types", - "Hash" - ] - } - }, - { - "id": 39, - "type": { - "def": { - "composite": { - "fields": [ + "index": 39, + "name": "InsufficientNodeResources" + }, { - "name": "offset_key", - "type": 12, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 5 - }, - { - "name": "V", - "type": 40 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 40, - "type": { - "def": { - "tuple": [ - 21, - 21 - ] - } - } - }, - { - "id": 41, - "type": { - "def": { - "composite": { - "fields": [ + "index": 40, + "name": "InsufficientClusterResources" + }, { - "name": "offset_key", - "type": 12, - "typeName": "Key" + "index": 41, + "name": "EraSettingFailed" } ] } }, - "params": [ - { - "name": "K", - "type": 42 - }, - { - "name": "V", - "type": 5 - } - ], "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" + "ddc_bucket", + "ddc_bucket", + "Error" ] } }, { - "id": 42, - "type": { - "def": { - "tuple": [ - 5, - 21 - ] - } - } - }, - { - "id": 43, + "id": 44, "type": { "def": { "variant": { "variants": [ { + "fields": [ + { + "type": 45 + } + ], "index": 0, - "name": "None" + "name": "Ok" }, { "fields": [ { - "type": 2 + "type": 43 } ], "index": 1, - "name": "Some" + "name": "Err" } ] } @@ -4021,16 +5805,20 @@ "params": [ { "name": "T", - "type": 2 + "type": 45 + }, + { + "name": "E", + "type": 43 } ], "path": [ - "Option" + "Result" ] } }, { - "id": 44, + "id": 45, "type": { "def": { "tuple": [] @@ -4038,7 +5826,7 @@ } }, { - "id": 45, + "id": 46, "type": { "def": { "variant": { @@ -4046,7 +5834,7 @@ { "fields": [ { - "type": 46 + "type": 47 } ], "index": 0, @@ -4055,7 +5843,7 @@ { "fields": [ { - "type": 48 + "type": 43 } ], "index": 1, @@ -4067,11 +5855,11 @@ "params": [ { "name": "T", - "type": 46 + "type": 47 }, { "name": "E", - "type": 48 + "type": 43 } ], "path": [ @@ -4080,39 +5868,39 @@ } }, { - "id": 46, + "id": 47, "type": { "def": { "composite": { "fields": [ { "name": "bucket_id", - "type": 5, + "type": 6, "typeName": "BucketId" }, { "name": "bucket", - "type": 47, + "type": 48, "typeName": "BucketInStatus" }, { "name": "params", - "type": 14, + "type": 13, "typeName": "BucketParams" }, { "name": "writer_ids", - "type": 11, + "type": 15, "typeName": "Vec" }, { "name": "reader_ids", - "type": 11, + "type": 15, "typeName": "Vec" }, { "name": "rent_covered_until_ms", - "type": 21, + "type": 28, "typeName": "u64" } ] @@ -4128,34 +5916,34 @@ } }, { - "id": 47, + "id": 48, "type": { "def": { "composite": { "fields": [ { "name": "owner_id", - "type": 2, + "type": 9, "typeName": "AccountId" }, { "name": "cluster_id", - "type": 5, + "type": 6, "typeName": "ClusterId" }, { "name": "resource_reserved", - "type": 5, + "type": 6, "typeName": "Resource" }, { "name": "public_availability", - "type": 9, + "type": 3, "typeName": "bool" }, { "name": "resource_consumption_cap", - "type": 5, + "type": 6, "typeName": "Resource" } ] @@ -4170,113 +5958,13 @@ ] } }, - { - "id": 48, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "BucketDoesNotExist" - }, - { - "index": 1, - "name": "ClusterDoesNotExist" - }, - { - "index": 2, - "name": "ParamsTooBig" - }, - { - "index": 3, - "name": "VNodeDoesNotExist" - }, - { - "index": 4, - "name": "BondingPeriodNotFinished" - }, - { - "index": 5, - "name": "BucketClusterAlreadyConnected" - }, - { - "index": 6, - "name": "BucketClusterNotSetup" - }, - { - "index": 7, - "name": "NodeDoesNotExist" - }, - { - "index": 8, - "name": "NodeAlreadyExists" - }, - { - "index": 9, - "name": "FlowDoesNotExist" - }, - { - "index": 10, - "name": "AccountDoesNotExist" - }, - { - "index": 11, - "name": "ParamsDoesNotExist" - }, - { - "index": 12, - "name": "UnauthorizedProvider" - }, - { - "index": 13, - "name": "UnauthorizedOwner" - }, - { - "index": 14, - "name": "UnauthorizedClusterManager" - }, - { - "index": 15, - "name": "ClusterManagerIsNotTrusted" - }, - { - "index": 16, - "name": "TransferFailed" - }, - { - "index": 17, - "name": "InsufficientBalance" - }, - { - "index": 18, - "name": "InsufficientResources" - }, - { - "index": 19, - "name": "Unauthorized" - }, - { - "index": 20, - "name": "UnknownNode" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "Error" - ] - } - }, { "id": 49, "type": { "def": { "tuple": [ 50, - 5 + 6 ] } } @@ -4286,13 +5974,23 @@ "type": { "def": { "sequence": { - "type": 46 + "type": 47 + } + } + } + }, + { + "id": 51, + "type": { + "def": { + "sequence": { + "type": 8 } } } }, { - "id": 51, + "id": 52, "type": { "def": { "variant": { @@ -4300,7 +5998,7 @@ { "fields": [ { - "type": 52 + "type": 53 } ], "index": 0, @@ -4309,7 +6007,7 @@ { "fields": [ { - "type": 48 + "type": 43 } ], "index": 1, @@ -4321,11 +6019,11 @@ "params": [ { "name": "T", - "type": 52 + "type": 53 }, { "name": "E", - "type": 48 + "type": 43 } ], "path": [ @@ -4334,25 +6032,25 @@ } }, { - "id": 52, + "id": 53, "type": { "def": { "composite": { "fields": [ { "name": "cluster_id", - "type": 5, + "type": 6, "typeName": "ClusterId" }, { "name": "cluster", - "type": 16, + "type": 17, "typeName": "Cluster" }, { - "name": "params", - "type": 14, - "typeName": "Params" + "name": "cluster_v_nodes", + "type": 27, + "typeName": "Vec" } ] } @@ -4362,96 +6060,33 @@ "ddc_bucket", "cluster", "entity", - "ClusterStatus" + "ClusterInfo" ] } }, { - "id": 53, + "id": 54, "type": { "def": { "tuple": [ - 54, - 5 + 55, + 6 ] } } }, - { - "id": 54, - "type": { - "def": { - "sequence": { - "type": 52 - } - } - } - }, { "id": 55, "type": { "def": { "sequence": { - "type": 56 + "type": 53 } } } }, { "id": 56, - "type": { - "def": { - "tuple": [ - 2, - 8 - ] - } - } - }, - { - "id": 57, - "type": { - "def": { - "sequence": { - "type": 58 - } - } - } - }, - { - "id": 58, - "type": { - "def": { - "tuple": [ - 5, - 8 - ] - } - } - }, - { - "id": 59, - "type": { - "def": { - "sequence": { - "type": 60 - } - } - } - }, - { - "id": 60, - "type": { - "def": { - "tuple": [ - 5, - 5 - ] - } - } - }, - { - "id": 61, "type": { "def": { "variant": { @@ -4459,7 +6094,7 @@ { "fields": [ { - "type": 62 + "type": 12 } ], "index": 0, @@ -4468,7 +6103,7 @@ { "fields": [ { - "type": 48 + "type": 43 } ], "index": 1, @@ -4480,11 +6115,11 @@ "params": [ { "name": "T", - "type": 62 + "type": 12 }, { "name": "E", - "type": 48 + "type": 43 } ], "path": [ @@ -4493,78 +6128,71 @@ } }, { - "id": 62, + "id": 57, "type": { "def": { - "composite": { - "fields": [ - { - "name": "cluster_id", - "type": 5, - "typeName": "ClusterId" - }, - { - "name": "cluster", - "type": 23, - "typeName": "CdnCluster" - } - ] + "sequence": { + "type": 58 } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "cdn_cluster", - "entity", - "CdnClusterStatus" - ] + } } }, { - "id": 63, + "id": 58, "type": { "def": { "tuple": [ - 64, - 5 + 9, + 12 ] } } }, { - "id": 64, + "id": 59, "type": { "def": { "sequence": { - "type": 62 + "type": 60 } } } }, { - "id": 65, + "id": 60, + "type": { + "def": { + "tuple": [ + 6, + 6 + ] + } + } + }, + { + "id": 61, "type": { "def": { "composite": { "fields": [ { "name": "start", - "type": 21, + "type": 28, "typeName": "u64" }, { "name": "interval", - "type": 21, + "type": 28, "typeName": "u64" }, { "name": "commit_duration", - "type": 21, + "type": 28, "typeName": "u64" }, { "name": "validation_duration", - "type": 21, + "type": 28, "typeName": "u64" } ] @@ -4580,34 +6208,34 @@ } }, { - "id": 66, + "id": 62, "type": { "def": { "composite": { "fields": [ { "name": "current_era", - "type": 21, + "type": 28, "typeName": "u64" }, { "name": "current_phase", - "type": 67, + "type": 63, "typeName": "Phase" }, { "name": "previous_era", - "type": 21, + "type": 28, "typeName": "u64" }, { "name": "prev_era_from_timestamp", - "type": 21, + "type": 28, "typeName": "u64" }, { "name": "prev_era_to_timestamp", - "type": 21, + "type": 28, "typeName": "u64" } ] @@ -4623,7 +6251,7 @@ } }, { - "id": 67, + "id": 63, "type": { "def": { "variant": { @@ -4653,7 +6281,49 @@ } }, { - "id": 68, + "id": 64, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 9 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 43 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 9 + }, + { + "name": "E", + "type": 43 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 65, "type": { "def": { "variant": { @@ -4661,7 +6331,7 @@ { "fields": [ { - "type": 69 + "type": 66 } ], "index": 0, @@ -4670,7 +6340,7 @@ { "fields": [ { - "type": 48 + "type": 43 } ], "index": 1, @@ -4682,11 +6352,11 @@ "params": [ { "name": "T", - "type": 69 + "type": 66 }, { "name": "E", - "type": 48 + "type": 43 } ], "path": [ @@ -4695,25 +6365,20 @@ } }, { - "id": 69, + "id": 66, "type": { "def": { "composite": { "fields": [ { - "name": "node_id", - "type": 5, - "typeName": "NodeId" + "name": "cdn_node_key", + "type": 9, + "typeName": "CdnNodeKey" }, { - "name": "node", - "type": 25, + "name": "cdn_node", + "type": 20, "typeName": "CdnNode" - }, - { - "name": "params", - "type": 14, - "typeName": "Params" } ] } @@ -4723,33 +6388,33 @@ "ddc_bucket", "cdn_node", "entity", - "CdnNodeStatus" + "CdnNodeInfo" ] } }, { - "id": 70, + "id": 67, "type": { "def": { "tuple": [ - 71, - 5 + 68, + 6 ] } } }, { - "id": 71, + "id": 68, "type": { "def": { "sequence": { - "type": 69 + "type": 66 } } } }, { - "id": 72, + "id": 69, "type": { "def": { "variant": { @@ -4757,7 +6422,7 @@ { "fields": [ { - "type": 73 + "type": 70 } ], "index": 0, @@ -4766,7 +6431,7 @@ { "fields": [ { - "type": 48 + "type": 43 } ], "index": 1, @@ -4778,11 +6443,11 @@ "params": [ { "name": "T", - "type": 73 + "type": 70 }, { "name": "E", - "type": 48 + "type": 43 } ], "path": [ @@ -4791,25 +6456,25 @@ } }, { - "id": 73, + "id": 70, "type": { "def": { "composite": { "fields": [ { - "name": "node_id", - "type": 5, - "typeName": "NodeId" + "name": "node_key", + "type": 9, + "typeName": "NodeKey" }, { "name": "node", - "type": 28, + "type": 25, "typeName": "Node" }, { - "name": "params", - "type": 14, - "typeName": "Params" + "name": "v_nodes", + "type": 27, + "typeName": "Vec" } ] } @@ -4819,33 +6484,66 @@ "ddc_bucket", "node", "entity", - "NodeStatus" + "NodeInfo" ] } }, { - "id": 74, + "id": 71, "type": { "def": { "tuple": [ - 75, - 5 + 72, + 6 ] } } }, { - "id": 75, + "id": 72, "type": { "def": { "sequence": { - "type": 73 + "type": 70 } } } }, { - "id": 76, + "id": 73, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "network_fee_bp", + "type": 12, + "typeName": "BasisPoints" + }, + { + "name": "network_fee_destination", + "type": 9, + "typeName": "AccountId" + }, + { + "name": "cluster_management_fee_bp", + "type": 12, + "typeName": "BasisPoints" + } + ] + } + }, + "path": [ + "ddc_bucket", + "ddc_bucket", + "protocol", + "store", + "NetworkFeeConfig" + ] + } + }, + { + "id": 74, "type": { "def": { "variant": { @@ -4853,7 +6551,7 @@ { "fields": [ { - "type": 31 + "type": 33 } ], "index": 0, @@ -4862,7 +6560,7 @@ { "fields": [ { - "type": 48 + "type": 43 } ], "index": 1, @@ -4874,11 +6572,11 @@ "params": [ { "name": "T", - "type": 31 + "type": 33 }, { "name": "E", - "type": 48 + "type": 43 } ], "path": [ @@ -4887,7 +6585,7 @@ } }, { - "id": 77, + "id": 75, "type": { "def": { "variant": { @@ -4895,12 +6593,12 @@ { "fields": [ { - "type": 2, + "type": 9, "typeName": "AccountId" } ], "index": 0, - "name": "ManagerTrustedBy" + "name": "ClusterManagerTrustedBy" }, { "index": 1, @@ -4909,6 +6607,10 @@ { "index": 2, "name": "SuperAdmin" + }, + { + "index": 3, + "name": "Validator" } ] } @@ -4921,38 +6623,6 @@ "Permission" ] } - }, - { - "id": 78, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "network_fee_bp", - "type": 8, - "typeName": "BasisPoints" - }, - { - "name": "network_fee_destination", - "type": 2, - "typeName": "AccountId" - }, - { - "name": "cluster_management_fee_bp", - "type": 8, - "typeName": "BasisPoints" - } - ] - } - }, - "path": [ - "ddc_bucket", - "ddc_bucket", - "network_fee", - "FeeConfig" - ] - } } ] } diff --git a/scripts/sdk/src/abi/ddc_nft_registry.json b/scripts/sdk/src/abi/ddc_nft_registry.json deleted file mode 100644 index c2f45d2d..00000000 --- a/scripts/sdk/src/abi/ddc_nft_registry.json +++ /dev/null @@ -1,384 +0,0 @@ -{ - "source": { - "hash": "0x6c9783a07ddbc3bad9d23f4e8ce41aafa1d2fa509fa4a397c4b21a5ab4e56cbc", - "language": "ink! 3.4.0", - "compiler": "rustc 1.69.0-nightly" - }, - "contract": { - "name": "ddc_nft_registry", - "version": "0.5.2", - "authors": [ - "Aurélien Nicolas ", - "Anton Volk " - ] - }, - "V3": { - "spec": { - "constructors": [ - { - "args": [], - "docs": [], - "label": "new", - "payable": false, - "selector": "0x9bae9d5e" - } - ], - "docs": [], - "events": [ - { - "args": [ - { - "docs": [], - "indexed": false, - "label": "reporter_id", - "type": { - "displayName": [ - "AccountId" - ], - "type": 3 - } - }, - { - "docs": [], - "indexed": false, - "label": "nft_id", - "type": { - "displayName": [ - "String" - ], - "type": 1 - } - }, - { - "docs": [], - "indexed": false, - "label": "asset_id", - "type": { - "displayName": [ - "String" - ], - "type": 1 - } - }, - { - "docs": [], - "indexed": false, - "label": "proof", - "type": { - "displayName": [ - "String" - ], - "type": 1 - } - } - ], - "docs": [], - "label": "Attach" - } - ], - "messages": [ - { - "args": [ - { - "label": "nft_id", - "type": { - "displayName": [ - "String" - ], - "type": 1 - } - }, - { - "label": "asset_id", - "type": { - "displayName": [ - "String" - ], - "type": 1 - } - }, - { - "label": "proof", - "type": { - "displayName": [ - "String" - ], - "type": 1 - } - } - ], - "docs": [ - " Report and attach an asset ID to an NFT ID.", - "", - " All attachments are recorded as events.", - " There is absolutely no validation, any account can \"attach\" some asset ID.", - " Events should be filtered by reporter_id, or by analyzing the proof (not specified here).", - "", - " The latest attachment is also recorded in contract storage.", - " The latest asset ID can be queried from get_by_nft_id.", - " The first reporter for an NFT ID can also update the asset ID." - ], - "label": "attach", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0x82e05a4c" - }, - { - "args": [ - { - "label": "nft_id", - "type": { - "displayName": [ - "String" - ], - "type": 1 - } - }, - { - "label": "asset_id", - "type": { - "displayName": [ - "String" - ], - "type": 1 - } - }, - { - "label": "proof", - "type": { - "displayName": [ - "String" - ], - "type": 1 - } - } - ], - "docs": [ - " Report the attachment of an asset ID to an NFT ID.", - "", - " This is recorded only as a contract event.", - " This can *not* be queried from get_by_nft_id.", - "", - " There is absolutely no validation, any account can \"report\" some asset ID.", - " Events should be filtered by reporter_id, or by analyzing the proof (not specified here)." - ], - "label": "report", - "mutates": true, - "payable": true, - "returnType": null, - "selector": "0xf30f464c" - }, - { - "args": [ - { - "label": "nft_id", - "type": { - "displayName": [ - "String" - ], - "type": 1 - } - } - ], - "docs": [], - "label": "get_by_nft_id", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "AttachmentStatus" - ], - "type": 7 - }, - "selector": "0xb5c41aa2" - } - ] - }, - "storage": { - "struct": { - "fields": [ - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0000000000000000000000000000000000000000000000000000000000000000", - "ty": 0 - } - }, - "name": null - } - ] - } - }, - "name": "attachments" - } - ] - } - }, - "types": [ - { - "id": 0, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 6, - "typeName": "Key" - } - ] - } - }, - "params": [ - { - "name": "K", - "type": 1 - }, - { - "name": "V", - "type": 2 - } - ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] - } - }, - { - "id": 1, - "type": { - "def": { - "primitive": "str" - } - } - }, - { - "id": 2, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "reporter_id", - "type": 3, - "typeName": "AccountId" - }, - { - "name": "nft_id", - "type": 1, - "typeName": "NftId" - }, - { - "name": "asset_id", - "type": 1, - "typeName": "AssetId" - }, - { - "name": "proof", - "type": 1, - "typeName": "Proof" - } - ] - } - }, - "path": [ - "ddc_nft_registry", - "ddc_nft_registry", - "attachment", - "entity", - "Attachment" - ] - } - }, - { - "id": 3, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 4, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_env", - "types", - "AccountId" - ] - } - }, - { - "id": 4, - "type": { - "def": { - "array": { - "len": 32, - "type": 5 - } - } - } - }, - { - "id": 5, - "type": { - "def": { - "primitive": "u8" - } - } - }, - { - "id": 6, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 4, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_primitives", - "Key" - ] - } - }, - { - "id": 7, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "attachment", - "type": 2, - "typeName": "Attachment" - } - ] - } - }, - "path": [ - "ddc_nft_registry", - "ddc_nft_registry", - "attachment", - "entity", - "AttachmentStatus" - ] - } - } - ] - } -} \ No newline at end of file diff --git a/scripts/sdk/src/bucket/ddcBucket.js b/scripts/sdk/src/bucket/ddcBucket.js index 2414e1de..2df8a158 100644 --- a/scripts/sdk/src/bucket/ddcBucket.js +++ b/scripts/sdk/src/bucket/ddcBucket.js @@ -1,28 +1,135 @@ const _ = require("lodash"); -function findCreatedId(events, eventName) { +function findEvent(events, eventName) { const event = _.find(events, ["event.identifier", eventName]); - const id = _.get(event, "args[0]"); - return id && id.toString(); + if (!event) + throw new Error("Event '" + eventName + "' is not found"); + return event; } -function findCreatedNodeId(events) { - return findCreatedId(events, "NodeCreated"); +function findNodeCreatedEvent(events) { + const event = findEvent(events, "NodeCreated"); + const nodeKey = _.get(event, "args[0]").toString(); + const providerId = _.get(event, "args[1]").toString(); + const rentPerMonth = _.get(event, "args[2]").toNumber(); + const nodeParams = _.get(event, "args[3]").toString(); + return { nodeKey, providerId, rentPerMonth, nodeParams }; } -function findCreatedClusterId(events) { - return findCreatedId(events, "ClusterCreated"); +function findCdnNodeCreatedEvent(events) { + const event = findEvent(events, "CdnNodeCreated"); + const cdnNodeKey = _.get(event, "args[0]").toString(); + const providerId = _.get(event, "args[1]").toString(); + const cdnNodeParams = _.get(event, "args[2]").toString(); + const undistributedPayment = _.get(event, "args[3]").toNumber(); + return { cdnNodeKey, providerId, cdnNodeParams, undistributedPayment }; } -function findCreatedBucketId(events) { - return findCreatedId(events, "BucketCreated"); +function findClusterCreatedEvent(events) { + const event = findEvent(events, "ClusterCreated"); + const clusterId = _.get(event, "args[0]").toNumber(); + const managerId = _.get(event, "args[1]").toString(); + const clusterParams = _.get(event, "args[2]").toString(); + return { clusterId, managerId, clusterParams }; +} + +function findBucketCreatedEvent(events) { + const event = findEvent(events, "BucketCreated"); + const bucketId = _.get(event, "args[0]").toNumber(); + const ownerId = _.get(event, "args[1]").toString(); + return { bucketId, ownerId }; +} + +function findPermissionGrantedEvent(events) { + const event = findEvent(events, "PermissionGranted"); + const accountId = _.get(event, "args[0]").toString(); + const permission = _.get(event, "args[1]").toString(); + return { accountId, permission }; +} + +function findClusterReserveResourceEvent(events) { + const event = findEvent(events, "ClusterReserveResource"); + const clusterId = _.get(event, "args[0]").toNumber(); + const reserved = _.get(event, "args[1]").toNumber(); + return { clusterId, reserved }; +} + +function findClusterNodeStatusSetEvent(events) { + const event = findEvent(events, "ClusterNodeStatusSet"); + const nodeKey = _.get(event, "args[0]").toString(); + const clusterId = _.get(event, "args[1]").toNumber(); + const status = _.get(event, "args[2]").toString(); + return { nodeKey, clusterId, status }; +} + +function findClusterCdnNodeStatusSetEvent(events) { + const event = findEvent(events, "ClusterCdnNodeStatusSet"); + const cdnNodeKey = _.get(event, "args[0]").toString(); + const clusterId = _.get(event, "args[1]").toNumber(); + const status = _.get(event, "args[2]").toString(); + return { cdnNodeKey, clusterId, status }; +} + +function findClusterNodeAddedEvent(events) { + const event = findEvent(events, "ClusterNodeAdded"); + const clusterId = _.get(event, "args[0]").toNumber(); + const nodeKey = _.get(event, "args[1]").toString(); + const vNodes = _.get(event, "args[2]").toString(); + return { nodeKey, clusterId, vNodes }; +} + +function findClusterCdnNodeAddedEvent(events) { + const event = findEvent(events, "ClusterCdnNodeAdded"); + const clusterId = _.get(event, "args[0]").toNumber(); + const cdnNodeKey = _.get(event, "args[1]").toString(); + return { cdnNodeKey, clusterId }; +} + +function findDepositEvent(events) { + const event = findEvent(events, "Deposit"); + const accountId = _.get(event, "args[0]").toString(); + const value = _.get(event, "args[1]").toNumber(); + return { accountId, value }; +} + +function findBucketAllocatedEvent(events) { + const event = findEvent(events, "BucketAllocated"); + const bucketId = _.get(event, "args[0]").toNumber(); + const clusterId = _.get(event, "args[1]").toNumber(); + const resource = _.get(event, "args[2]").toNumber(); + return { bucketId, clusterId, resource }; +} + +function findBucketSettlePaymentEvent(events) { + const event = findEvent(events, "BucketSettlePayment"); + const bucketId = _.get(event, "args[0]").toNumber(); + const clusterId = _.get(event, "args[1]").toNumber(); + return { bucketId, clusterId }; +} + +function findClusterDistributeRevenuesEvent(events) { + const event = findEvent(events, "ClusterDistributeRevenues"); + const clusterId = _.get(event, "args[0]").toNumber(); + const providerId = _.get(event, "args[1]").toString(); + return { clusterId, providerId }; } module.exports = { - findCreatedId, - findCreatedNodeId, - findCreatedClusterId, - findCreatedBucketId, + findEvent, + findNodeCreatedEvent, + findCdnNodeCreatedEvent, + findClusterCreatedEvent, + findBucketCreatedEvent, + findPermissionGrantedEvent, + findClusterReserveResourceEvent, + findClusterNodeStatusSetEvent, + findClusterCdnNodeStatusSetEvent, + findClusterNodeAddedEvent, + findClusterCdnNodeAddedEvent, + findDepositEvent, + findBucketAllocatedEvent, + findBucketSettlePaymentEvent, + findClusterDistributeRevenuesEvent, }; diff --git a/scripts/sdk/src/config/index.js b/scripts/sdk/src/config/index.js index 20f45a51..8c6b577a 100644 --- a/scripts/sdk/src/config/index.js +++ b/scripts/sdk/src/config/index.js @@ -4,43 +4,34 @@ const ACTOR_SEED = "//Alice"; const EXPLORER_URL = "https://explorer.cere.network"; const DDC_BUCKET_CONTRACT_NAME = "ddc_bucket"; -const DDC_NFT_REGISTRY_CONTRACT_NAME = "ddc_nft_registry"; const DEVNET_RPC_ENDPOINT = "wss://archive.devnet.cere.network/ws/"; const DEVNET_DDC_BUCKET_ADDR = "6TCrAcm51jYJwNbEc7zYFBvuBaPru9B1cAkU4PpgoTpTy9VZ"; -const DEVNET_DDC_NFT_REGISTRY_ADDR = "6V1Bf84xDSRWBHjWS3PQCqVJeaJx5imPKGBnnr7WJXUUVGBi"; const DEVNET_CHAIN_NAME = "Cere Devnet"; -const TESTNET_RPC_ENDPOINT = "wss://archive.devnet.cere.network/ws/"; +const TESTNET_RPC_ENDPOINT = "wss://archive.testnet.cere.network/ws/"; const TESTNET_DDC_BUCKET_ADDR = "6TyNotWczZXMaXkF6mJ2cBMS6e1hgu5hFYuTpfg9vtH35gu3"; -const TESTNET_DDC_NFT_REGISTRY_ADDR = "6VFZJT9Kox8iZN4DL27GNFCpfTWHXbDst2WC7jpiJXv36eGR"; const TESTNET_CHAIN_NAME = "Cere Testnet"; const LOCAL_RPC_ENDPOINT = "ws://127.0.0.1:9944/"; -const LOCAL_DDC_BUCKET_ADDR = ""; -const LOCAL_DDC_NFT_REGISTRY_ADDR = ""; +const LOCAL_DDC_BUCKET_ADDR = ""; // add your local address const LOCAL_CHAIN_NAME = "Development"; - module.exports = { ACTOR_SEED, EXPLORER_URL, DDC_BUCKET_CONTRACT_NAME, - DDC_NFT_REGISTRY_CONTRACT_NAME, DEVNET_RPC_ENDPOINT, DEVNET_DDC_BUCKET_ADDR, - DEVNET_DDC_NFT_REGISTRY_ADDR, DEVNET_CHAIN_NAME, TESTNET_RPC_ENDPOINT, TESTNET_DDC_BUCKET_ADDR, - TESTNET_DDC_NFT_REGISTRY_ADDR, TESTNET_CHAIN_NAME, LOCAL_RPC_ENDPOINT, LOCAL_DDC_BUCKET_ADDR, - LOCAL_DDC_NFT_REGISTRY_ADDR, LOCAL_CHAIN_NAME } \ No newline at end of file diff --git a/scripts/sdk/src/ddcNftRegistry/nftRegistry.js b/scripts/sdk/src/ddcNftRegistry/nftRegistry.js deleted file mode 100644 index 5d98f618..00000000 --- a/scripts/sdk/src/ddcNftRegistry/nftRegistry.js +++ /dev/null @@ -1,21 +0,0 @@ -const _ = require("lodash"); - - -function findByEventName(events, eventName) { - const event = _.find(events, ["event.identifier", eventName]); - const nft_id = _.get(event, "args[0]"); - const asset_id = _.get(event, "args[1]"); - const proof = _.get(event, "args[2]"); - return { - nft_id, asset_id, proof - }; -} - -function findCreatedAttachment(events) { - return findByEventName(events, "Attach"); -} - - -module.exports = { - findCreatedAttachment, -}; diff --git a/scripts/sdk/src/deploymentRegistry.js b/scripts/sdk/src/deploymentRegistry.js index 9e88acfa..a5963326 100644 --- a/scripts/sdk/src/deploymentRegistry.js +++ b/scripts/sdk/src/deploymentRegistry.js @@ -2,10 +2,9 @@ const {registerABI} = require("./abiRegistry.js"); const {registerContract} = require("./contractRegistry.js"); const config = require("./config"); const ddcBucketAbi = require("./abi/ddc_bucket.json"); -const ddcNftRegistryAbi = require("./abi/ddc_nft_registry.json"); -function initContracts() { +function initDefaultContracts() { registerABI(config.DDC_BUCKET_CONTRACT_NAME, ddcBucketAbi); @@ -21,24 +20,24 @@ function initContracts() { config.TESTNET_DDC_BUCKET_ADDR ); - - registerABI(config.DDC_NFT_REGISTRY_CONTRACT_NAME, ddcNftRegistryAbi); - registerContract( - config.DDC_NFT_REGISTRY_CONTRACT_NAME, - config.DEVNET_CHAIN_NAME, - config.DEVNET_DDC_NFT_REGISTRY_ADDR + config.DDC_BUCKET_CONTRACT_NAME, + config.LOCAL_CHAIN_NAME, + config.LOCAL_DDC_BUCKET_ADDR ); + +} +function initContract(name, env, address) { + registerABI(name, ddcBucketAbi); registerContract( - config.DDC_NFT_REGISTRY_CONTRACT_NAME, - config.TESTNET_CHAIN_NAME, - config.TESTNET_DDC_NFT_REGISTRY_ADDR + name, + env, + address ); - } - module.exports = { - initContracts, + initDefaultContracts, + initContract } diff --git a/scripts/sdk/src/index.js b/scripts/sdk/src/index.js index f8662ea3..86b7ffc9 100644 --- a/scripts/sdk/src/index.js +++ b/scripts/sdk/src/index.js @@ -7,6 +7,5 @@ Object.assign(module.exports, require("./contractRegistry.js")); Object.assign(module.exports, require("./polkadotWrappers.js")); module.exports.ddcBucket = require("./bucket/ddcBucket.js"); module.exports.ddcBucketQuery = require("./bucket/ddcBucketQuery.js"); -module.exports.ddcNftRegistry = require("./ddcNftRegistry/nftRegistry.js"); module.exports.config = require("./config"); module.exports.lodash = require("lodash"); \ No newline at end of file diff --git a/scripts/sdk/src/polkadotWrappers.js b/scripts/sdk/src/polkadotWrappers.js index e36e6eb5..7151fc61 100644 --- a/scripts/sdk/src/polkadotWrappers.js +++ b/scripts/sdk/src/polkadotWrappers.js @@ -22,10 +22,15 @@ async function connect(rpcUrl) { } function randomAccount() { - const mnemonic = mnemonicGenerate(12); const keyring = new Keyring({type: 'sr25519'}); - const account =keyring.addFromMnemonic(mnemonic); - return account; + const mnemonic = mnemonicGenerate(12); + const account = keyring.addFromMnemonic(mnemonic); + + return { + account, + mnemonic, + address: account.address, + }; } function accountFromUri(uri) { diff --git a/scripts/sdk/yarn.lock b/scripts/sdk/yarn.lock index b01b379f..7012ce2d 100644 --- a/scripts/sdk/yarn.lock +++ b/scripts/sdk/yarn.lock @@ -2,110 +2,136 @@ # yarn lockfile v1 -"@babel/runtime@^7.17.9", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.6": +"@babel/runtime@^7.18.6", "@babel/runtime@^7.20.6": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.19.0", "@babel/runtime@^7.20.13": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" + integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== + dependencies: + regenerator-runtime "^0.13.11" + "@noble/hashes@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== +"@noble/hashes@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" + integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== + "@noble/secp256k1@1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.0.tgz#602afbbfcfb7e169210469b697365ef740d7e930" integrity sha512-DWSsg8zMHOYMYBqIQi96BQuthZrp98LCeMNcUOaffCIVYQ5yxDbNikLF+H7jEnmNNmXbtVic46iCuVWzar+MgA== -"@polkadot/api-augment@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-8.2.1.tgz#27173ecbb700b33d224f4b55a2fa6f5f76d776a1" - integrity sha512-kNS4Ff1KLL6FyR04s4CIR0wVi3I36ErvOUZnZqaHE3DRUBWSzmocAkXaHrwzYDs6nseoL98jtnEkBMRO50gJWw== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/api-base" "8.2.1" - "@polkadot/rpc-augment" "8.2.1" - "@polkadot/types" "8.2.1" - "@polkadot/types-augment" "8.2.1" - "@polkadot/types-codec" "8.2.1" - "@polkadot/util" "^9.0.1" - -"@polkadot/api-base@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-8.2.1.tgz#60dd3aa72d8fd3727822e67c11c190b91af80c1f" - integrity sha512-z614lpNagsuFH47++CNOYaraET4lWRA2Y+d+CqMoWlCgAtKDuqraZfjF7SzYMGp67zSD+FYFSimReWPh5LMV/Q== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/rpc-core" "8.2.1" - "@polkadot/types" "8.2.1" - "@polkadot/util" "^9.0.1" - rxjs "^7.5.5" - -"@polkadot/api-contract@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-8.2.1.tgz#ff00472057a2b0566e052d9082ac9ea13cc82916" - integrity sha512-y0HXHg3+8iodcdlyeXSn/ZZ2tuGs0dz8ehp09u6asZLyenWME4mi1ERSg4+WspHcQxR99TgAqfOZpCfDo1Yi8A== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/api" "8.2.1" - "@polkadot/types" "8.2.1" - "@polkadot/types-codec" "8.2.1" - "@polkadot/types-create" "8.2.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" - rxjs "^7.5.5" - -"@polkadot/api-derive@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-8.2.1.tgz#a4ad04b441d29b52bc0acf51a4872f5ab0d1bfcd" - integrity sha512-rLM99VyZMqsbYZf0ZxEYvKQCKjggJitDklHyemaVP1E2czOI+sKAy5R+oTyOXsAEk5QD2Bh6bLT6eDuuSDvdrA== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/api" "8.2.1" - "@polkadot/api-augment" "8.2.1" - "@polkadot/api-base" "8.2.1" - "@polkadot/rpc-core" "8.2.1" - "@polkadot/types" "8.2.1" - "@polkadot/types-codec" "8.2.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" - rxjs "^7.5.5" - -"@polkadot/api@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-8.2.1.tgz#dc78650b317467f874d0c381a849b378ace62800" - integrity sha512-zq1sOb1dPIyUwClx5NAqWlhB/b6t6eiSDJyZdSzvWz9XL1YWcd7/m7VtrTcMQbvC8EyMKW09Xiv5G2MEI3bhVw== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/api-augment" "8.2.1" - "@polkadot/api-base" "8.2.1" - "@polkadot/api-derive" "8.2.1" - "@polkadot/keyring" "^9.0.1" - "@polkadot/rpc-augment" "8.2.1" - "@polkadot/rpc-core" "8.2.1" - "@polkadot/rpc-provider" "8.2.1" - "@polkadot/types" "8.2.1" - "@polkadot/types-augment" "8.2.1" - "@polkadot/types-codec" "8.2.1" - "@polkadot/types-create" "8.2.1" - "@polkadot/types-known" "8.2.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" +"@noble/secp256k1@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + +"@polkadot/api-augment@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-9.4.1.tgz#2e2d00b0a89eb2b2df8a318b57f6aca4c5e0d402" + integrity sha512-I+jcPAeMUQn0YSs/JZhUsa9+SRcfGjlB0MtzkPb2w1uZhtXgg1bY36q6IXzhEHKxfjjUIjgITp/bSjJWlISzjw== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/api-base" "9.4.1" + "@polkadot/rpc-augment" "9.4.1" + "@polkadot/types" "9.4.1" + "@polkadot/types-augment" "9.4.1" + "@polkadot/types-codec" "9.4.1" + "@polkadot/util" "^10.1.8" + +"@polkadot/api-base@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-9.4.1.tgz#515271d12cf02d816e281dbb189dbbe00d5a9d77" + integrity sha512-APEnF3EviNMI6wKDIqU9xKOpgXhNJoT4ax4qegTqIWltx7aFMLFM8RYPPtMwSF+bm8z5w8aeEJ1H+vVlp4ovQw== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/rpc-core" "9.4.1" + "@polkadot/types" "9.4.1" + "@polkadot/util" "^10.1.8" + rxjs "^7.5.6" + +"@polkadot/api-contract@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-9.4.1.tgz#411269749f30c455effaef982443989a6cd775fe" + integrity sha512-wDGwGJhvkLStudmlsDaP6pQr4sjLRxtTrhfFU3paDkmAtOJoiUKWSiX62vGv+YMBoRQGvFGcWil5h8PNtTe45A== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/api" "9.4.1" + "@polkadot/types" "9.4.1" + "@polkadot/types-codec" "9.4.1" + "@polkadot/types-create" "9.4.1" + "@polkadot/util" "^10.1.8" + "@polkadot/util-crypto" "^10.1.8" + rxjs "^7.5.6" + +"@polkadot/api-derive@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-9.4.1.tgz#f02f43bc561ec680102531029b2d788f05214eac" + integrity sha512-RgQtiVi+u2fxxleVZ1dxu7FPdj3YMGbwpaBDc9fr2/ys6Jna5myN1ZsGlO7LzUrGGzug1t8HwHmsy9Ssnlod7w== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/api" "9.4.1" + "@polkadot/api-augment" "9.4.1" + "@polkadot/api-base" "9.4.1" + "@polkadot/rpc-core" "9.4.1" + "@polkadot/types" "9.4.1" + "@polkadot/types-codec" "9.4.1" + "@polkadot/util" "^10.1.8" + "@polkadot/util-crypto" "^10.1.8" + rxjs "^7.5.6" + +"@polkadot/api@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-9.4.1.tgz#a926312f80a63893a278336a9bacf83c2efde1ad" + integrity sha512-4xjt5M8f5K8dFvlRXGCjQO8ALplR7fG5TbOBmFdebbL9AinZkOCzKZg9J/nCUzwIYns8syjHFgakDchCkIIBTA== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/api-augment" "9.4.1" + "@polkadot/api-base" "9.4.1" + "@polkadot/api-derive" "9.4.1" + "@polkadot/keyring" "^10.1.8" + "@polkadot/rpc-augment" "9.4.1" + "@polkadot/rpc-core" "9.4.1" + "@polkadot/rpc-provider" "9.4.1" + "@polkadot/types" "9.4.1" + "@polkadot/types-augment" "9.4.1" + "@polkadot/types-codec" "9.4.1" + "@polkadot/types-create" "9.4.1" + "@polkadot/types-known" "9.4.1" + "@polkadot/util" "^10.1.8" + "@polkadot/util-crypto" "^10.1.8" eventemitter3 "^4.0.7" - rxjs "^7.5.5" + rxjs "^7.5.6" -"@polkadot/keyring@^9.0.1": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-9.7.2.tgz#3e252bdcabce4f4e74b8fbcd98d77bb0205af21e" - integrity sha512-qY5baU1qduwTE04Cyrqtf2pCpsIk7Z5vi45CD9U3cbkKXaJoNUqIpfKoL8Vh/yVJBwhclMdxV9E2rEJs8Iv4bg== +"@polkadot/keyring@^10.1.8": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-10.4.2.tgz#793377fdb9076df0af771df11388faa6be03c70d" + integrity sha512-7iHhJuXaHrRTG6cJDbZE9G+c1ts1dujp0qbO4RfAPmT7YUvphHvAtCKueN9UKPz5+TYDL+rP/jDEaSKU8jl/qQ== dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/util" "9.7.2" - "@polkadot/util-crypto" "9.7.2" + "@babel/runtime" "^7.20.13" + "@polkadot/util" "10.4.2" + "@polkadot/util-crypto" "10.4.2" + +"@polkadot/networks@10.4.2", "@polkadot/networks@^10.1.8": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.4.2.tgz#d7878c6aad8173c800a21140bfe5459261724456" + integrity sha512-FAh/znrEvWBiA/LbcT5GXHsCFUl//y9KqxLghSr/CreAmAergiJNT0MVUezC7Y36nkATgmsr4ylFwIxhVtuuCw== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/util" "10.4.2" + "@substrate/ss58-registry" "^1.38.0" -"@polkadot/networks@9.7.2", "@polkadot/networks@^9.0.1": +"@polkadot/networks@9.7.2": version "9.7.2" resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-9.7.2.tgz#9064f0578b293245bee263367d6f1674eb06e506" integrity sha512-oMAdF8Y9CLBI0EUZBcycHcvbQQdbkJHevPJ/lwnZXJTaueXuav/Xm2yiFj5J3V8meIjLocURlMawgsAVItXOBQ== @@ -114,110 +140,128 @@ "@polkadot/util" "9.7.2" "@substrate/ss58-registry" "^1.23.0" -"@polkadot/rpc-augment@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-8.2.1.tgz#ad161f9c523ace32e41aa68ad23745d9ac5103f7" - integrity sha512-Fr0wB275SjXqdhY3A4cazKCmOVIIIX3NQSiXiqpsFYMpSgmPPMNwgDPjxngJ7aI3lqMUGGC1sSD4vkSkDntfnQ== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/rpc-core" "8.2.1" - "@polkadot/types" "8.2.1" - "@polkadot/types-codec" "8.2.1" - "@polkadot/util" "^9.0.1" - -"@polkadot/rpc-core@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-8.2.1.tgz#54a0fcce7a58843be60d90317f0c4d0b8486d0a4" - integrity sha512-nRhUHQ5nkbbeFh/lV3OrCHJ/SAgV1KT7onPEVkSGG+OzuyY9yvGsDUbAtqfo+r2kBEDB6RhjV4Sx1HSeNHmQWg== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/rpc-augment" "8.2.1" - "@polkadot/rpc-provider" "8.2.1" - "@polkadot/types" "8.2.1" - "@polkadot/util" "^9.0.1" - rxjs "^7.5.5" - -"@polkadot/rpc-provider@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-8.2.1.tgz#d9471691a0b3d5e385b162087aa420d8cefb97ef" - integrity sha512-aGmwAA588me4DASe01wmkcY75ABVhcRlFvozIIDtvmlCk2/4uWk4RSLMcdbhedK+XQurh1w6W9jKAddAofCAIA== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/keyring" "^9.0.1" - "@polkadot/types" "8.2.1" - "@polkadot/types-support" "8.2.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" - "@polkadot/x-fetch" "^9.0.1" - "@polkadot/x-global" "^9.0.1" - "@polkadot/x-ws" "^9.0.1" - "@substrate/connect" "0.7.2" +"@polkadot/rpc-augment@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-9.4.1.tgz#ae5563a9850950a84ca986e991e17be663b0278f" + integrity sha512-ZBI5YrVE5U23Zta+XrQt9qYyf0FHgalXvlCzytjNo/VpHhZ371yisBbOuoetyP14rnF+RWux3rgU4Y1c9LNN+Q== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/rpc-core" "9.4.1" + "@polkadot/types" "9.4.1" + "@polkadot/types-codec" "9.4.1" + "@polkadot/util" "^10.1.8" + +"@polkadot/rpc-core@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-9.4.1.tgz#21c3e74996105c7fee082e6ebb8302deb8b0d86e" + integrity sha512-nGiebijY+DW7qmyB5O0iIlwVpI+m3olWJrMZT0gy7eUehwhsdjWNo3CjePb+EWtdlSjhpcxTwKCc4EaikWQCyQ== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/rpc-augment" "9.4.1" + "@polkadot/rpc-provider" "9.4.1" + "@polkadot/types" "9.4.1" + "@polkadot/util" "^10.1.8" + rxjs "^7.5.6" + +"@polkadot/rpc-provider@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-9.4.1.tgz#3f05f9f70bb36d38503acf99852460cffac29af1" + integrity sha512-JBq049KGpM6pDkgbxiADZG1cuTbBcfEJer466SVp54fGaF7ZfpWN6yb9pQNNkHvvx6rzPnxrTxWpqJXHlI+fmw== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/keyring" "^10.1.8" + "@polkadot/types" "9.4.1" + "@polkadot/types-support" "9.4.1" + "@polkadot/util" "^10.1.8" + "@polkadot/util-crypto" "^10.1.8" + "@polkadot/x-fetch" "^10.1.8" + "@polkadot/x-global" "^10.1.8" + "@polkadot/x-ws" "^10.1.8" + "@substrate/connect" "0.7.13" eventemitter3 "^4.0.7" - mock-socket "^9.1.3" - nock "^13.2.4" - -"@polkadot/types-augment@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-8.2.1.tgz#79670f13323d9811ec5552ace8d39a24b6038ba2" - integrity sha512-+uMC4JTYTghhbwyvvdjUnLcV3HeUZOqscfVYPYPtwctblXZRZae77b8TD1H+NqgBhloJPC8I8uBVflRIb+HcpA== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/types" "8.2.1" - "@polkadot/types-codec" "8.2.1" - "@polkadot/util" "^9.0.1" - -"@polkadot/types-codec@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-8.2.1.tgz#88fe747e1d94f1b68871494c5ad09d4607f4e060" - integrity sha512-dG5QUCNVmRA8BuQQdxU+2DB4lp+kbO22rDimJMNCr7lOELCIkbo+GFNpWhPInIq+aLrTTAyyv0VguJ3ciQVFJA== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/util" "^9.0.1" - -"@polkadot/types-create@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-8.2.1.tgz#1a8c538bd9f8b3bc03759e1219b894f7d8c4fff6" - integrity sha512-pdzSZgozhe/2EXnnXlxEoBiFwcg19mZ+o0ICdM+cpHayume5C3DrJzIRhQpKu7ndUyEb+MorC5aZn9Cj8Kdp0Q== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/types-codec" "8.2.1" - "@polkadot/util" "^9.0.1" - -"@polkadot/types-known@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-8.2.1.tgz#9b17f711b5d48b4558e2755b9f03e3c20f10ef85" - integrity sha512-9d+LOffogl7HOJ9ooiaOjfbZttF3LgjkVMTU1BQduu5Ln/o6Bk5ARCDeJuSEwYIegtqxrwpxlJabUuUT9xNmew== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/networks" "^9.0.1" - "@polkadot/types" "8.2.1" - "@polkadot/types-codec" "8.2.1" - "@polkadot/types-create" "8.2.1" - "@polkadot/util" "^9.0.1" - -"@polkadot/types-support@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-8.2.1.tgz#2024592d554119246be5b96edbb2b80a2df04c6d" - integrity sha512-vXvcbioDOVGcSpKIq4UrMVfbQPLsLEnhmn0BRGQotACfWGtBqRr1En1SIfSo5nJ/gyBjoq4AUsdUQII2Sa4Drw== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/util" "^9.0.1" - -"@polkadot/types@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-8.2.1.tgz#6d25ba48d8c925f05155ce1e922b36281b562d24" - integrity sha512-Qvgvxt5GNb9RRXDNIXB9Gu+E4Vw/9aRKw76mamWqFgtRONOJ8l6HgKgWvzsCZ/lBIKLi5NE/BKPIkOhZAWfDmA== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/keyring" "^9.0.1" - "@polkadot/types-augment" "8.2.1" - "@polkadot/types-codec" "8.2.1" - "@polkadot/types-create" "8.2.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" - rxjs "^7.5.5" - -"@polkadot/util-crypto@9.7.2", "@polkadot/util-crypto@^9.0.1": + mock-socket "^9.1.5" + nock "^13.2.9" + +"@polkadot/types-augment@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-9.4.1.tgz#c886597daa3e2304748a6701ac6eb5043dc2bfda" + integrity sha512-3GHoXSSUNVQF1iFJgTWoxo/VM5JLv8kJZjJR8T5mzPjqJQLa1zejkPqF7nvmJq39R/AarrNqg6R0xE6usZAtRw== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/types" "9.4.1" + "@polkadot/types-codec" "9.4.1" + "@polkadot/util" "^10.1.8" + +"@polkadot/types-codec@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-9.4.1.tgz#451d0d4207b3fef2141a51a1e9a8d0245236017a" + integrity sha512-S1aFDDK4GL/9SEFYZ6ZAzCvh2QFO/mf3dbTYZLFD7JHQLcbAgTi5E6oz+hWYzsJYW5SzTDW4VdIPoAjY+28UEA== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/util" "^10.1.8" + "@polkadot/x-bigint" "^10.1.8" + +"@polkadot/types-create@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-9.4.1.tgz#1889d74dfb31aa84ecaad40d5c88560c0c51cac0" + integrity sha512-XDGrefz0L8vRl5wjEtiCK8i/kwdcMCqm8vgCmXyMOsZxWkObzK9bex/EhdM/8kMBtXn/b4uQn67ZU89dptMWXw== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/types-codec" "9.4.1" + "@polkadot/util" "^10.1.8" + +"@polkadot/types-known@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-9.4.1.tgz#4b37eaf2886c437725640876d55f6d4af6308b2b" + integrity sha512-1WE+3IEaWaxRvASa6lQgE2bbIaqyesZsArd1QAlsWbOVlV/mSPeRXgBKwF+uDpWOXojZ+oKsOEnBf23BHGdIPg== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/networks" "^10.1.8" + "@polkadot/types" "9.4.1" + "@polkadot/types-codec" "9.4.1" + "@polkadot/types-create" "9.4.1" + "@polkadot/util" "^10.1.8" + +"@polkadot/types-support@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-9.4.1.tgz#7ea34676a5f50d74ccecb20d0dd80176f7ec2bf9" + integrity sha512-5udvaZU/KvEDzhDTcU8Qv9WG/3mu6nwx7m7PqDDywzUo/MAy2zI4OT6JBHcvJIk8Qm3zVQkVftohR8CbF9FeSg== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/util" "^10.1.8" + +"@polkadot/types@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-9.4.1.tgz#a713efdb5aa736a736ad186df82dd4c6a82ad0a6" + integrity sha512-vUPHqlBS5neNmI+8IgJxywCL3izdCgd3SrMKWM/xlxzhGh8zfBXaGiRu1sreEk4yuNCAMMWYYDp+eqecZ6wXGA== + dependencies: + "@babel/runtime" "^7.19.0" + "@polkadot/keyring" "^10.1.8" + "@polkadot/types-augment" "9.4.1" + "@polkadot/types-codec" "9.4.1" + "@polkadot/types-create" "9.4.1" + "@polkadot/util" "^10.1.8" + "@polkadot/util-crypto" "^10.1.8" + rxjs "^7.5.6" + +"@polkadot/util-crypto@10.4.2", "@polkadot/util-crypto@^10.1.8": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.4.2.tgz#871fb69c65768bd48c57bb5c1f76a85d979fb8b5" + integrity sha512-RxZvF7C4+EF3fzQv8hZOLrYCBq5+wA+2LWv98nECkroChY3C2ZZvyWDqn8+aonNULt4dCVTWDZM0QIY6y4LUAQ== + dependencies: + "@babel/runtime" "^7.20.13" + "@noble/hashes" "1.2.0" + "@noble/secp256k1" "1.7.1" + "@polkadot/networks" "10.4.2" + "@polkadot/util" "10.4.2" + "@polkadot/wasm-crypto" "^6.4.1" + "@polkadot/x-bigint" "10.4.2" + "@polkadot/x-randomvalues" "10.4.2" + "@scure/base" "1.1.1" + ed2curve "^0.3.0" + tweetnacl "^1.0.3" + +"@polkadot/util-crypto@9.7.2": version "9.7.2" resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-9.7.2.tgz#0a097f4e197cd344d101ab748a740c2d99a4c5b9" integrity sha512-tfz6mJtPwoNteivKCmR+QklC4mr1/hGZRsDJLWKaFhanDinYZ3V2pJM1EbCI6WONLuuzlTxsDXjAffWzzRqlPA== @@ -234,7 +278,20 @@ ed2curve "^0.3.0" tweetnacl "^1.0.3" -"@polkadot/util@9.7.2", "@polkadot/util@^9.0.1": +"@polkadot/util@10.4.2", "@polkadot/util@^10.1.8": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.4.2.tgz#df41805cb27f46b2b4dad24c371fa2a68761baa1" + integrity sha512-0r5MGICYiaCdWnx+7Axlpvzisy/bi1wZGXgCSw5+ZTyPTOqvsYRqM2X879yxvMsGfibxzWqNzaiVjToz1jvUaA== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/x-bigint" "10.4.2" + "@polkadot/x-global" "10.4.2" + "@polkadot/x-textdecoder" "10.4.2" + "@polkadot/x-textencoder" "10.4.2" + "@types/bn.js" "^5.1.1" + bn.js "^5.2.1" + +"@polkadot/util@9.7.2": version "9.7.2" resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-9.7.2.tgz#0f97fa92b273e6ce4b53fe869a957ac99342007d" integrity sha512-ivTmA+KkPCq5i3O0Gk+dTds/hwdwlYCh89aKfeaG9ni3XHUbbuBgTqHneo648HqxwAwSAyiDiwE9EdXrzAdO4Q== @@ -280,7 +337,7 @@ "@babel/runtime" "^7.20.6" "@polkadot/wasm-util" "6.4.1" -"@polkadot/wasm-crypto@^6.2.2": +"@polkadot/wasm-crypto@^6.2.2", "@polkadot/wasm-crypto@^6.4.1": version "6.4.1" resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-6.4.1.tgz#79310e23ad1ca62362ba893db6a8567154c2536a" integrity sha512-FH+dcDPdhSLJvwL0pMLtn/LIPd62QDPODZRCmDyw+pFjLOMaRBc7raomWUOqyRWJTnqVf/iscc2rLVLNMyt7ag== @@ -299,6 +356,14 @@ dependencies: "@babel/runtime" "^7.20.6" +"@polkadot/x-bigint@10.4.2", "@polkadot/x-bigint@^10.1.8": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.4.2.tgz#7eb2ec732259df48b5a00f07879a1331e05606ec" + integrity sha512-awRiox+/XSReLzimAU94fPldowiwnnMUkQJe8AebYhNocAj6SJU00GNoj6j6tAho6yleOwrTJXZaWFBaQVJQNg== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" + "@polkadot/x-bigint@9.7.2": version "9.7.2" resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-9.7.2.tgz#ec79977335dce173a81e45247bdfd46f3b301702" @@ -307,23 +372,38 @@ "@babel/runtime" "^7.18.6" "@polkadot/x-global" "9.7.2" -"@polkadot/x-fetch@^9.0.1": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-9.7.2.tgz#bc4091603114a4182ab06b8d7d127a3dd730cf48" - integrity sha512-ysXpPNq2S+L98hKow3d59prU4QFRg5N86pMkJdONc4VxtKITVY2MfdLVCqfEOOFuuwCzE7Sfmx53I4XpDgbP7A== +"@polkadot/x-fetch@^10.1.8": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-10.4.2.tgz#bc6ba70de71a252472fbe36180511ed920e05f05" + integrity sha512-Ubb64yaM4qwhogNP+4mZ3ibRghEg5UuCYRMNaCFoPgNAY8tQXuDKrHzeks3+frlmeH9YRd89o8wXLtWouwZIcw== dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/x-global" "9.7.2" + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" "@types/node-fetch" "^2.6.2" - node-fetch "^2.6.7" + node-fetch "^3.3.0" + +"@polkadot/x-global@10.4.2", "@polkadot/x-global@^10.1.8": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.4.2.tgz#5662366e3deda0b4c8f024b2d902fa838f9e60a4" + integrity sha512-g6GXHD/ykZvHap3M6wh19dO70Zm43l4jEhlxf5LtTo5/0/UporFCXr2YJYZqfbn9JbQwl1AU+NroYio+vtJdiA== + dependencies: + "@babel/runtime" "^7.20.13" -"@polkadot/x-global@9.7.2", "@polkadot/x-global@^9.0.1": +"@polkadot/x-global@9.7.2": version "9.7.2" resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-9.7.2.tgz#9847fd1da13989f321ca621e85477ba70fd8d55a" integrity sha512-3NN5JhjosaelaFWBJSlv9mb/gDAlt7RuZ8NKlOjB+LQHd9g6ZbnYi5wwjW+i/x/3E4IVbBx66uvWgNaw7IBrkg== dependencies: "@babel/runtime" "^7.18.6" +"@polkadot/x-randomvalues@10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.4.2.tgz#895f1220d5a4522a83d8d5014e3c1e03b129893e" + integrity sha512-mf1Wbpe7pRZHO0V3V89isPLqZOy5XGX2bCqsfUWHgb1NvV1MMx5TjVjdaYyNlGTiOkAmJKlOHshcfPU2sYWpNg== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" + "@polkadot/x-randomvalues@9.7.2": version "9.7.2" resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-9.7.2.tgz#d580b0e9149ea22b2afebba5d7b1368371f7086d" @@ -332,6 +412,14 @@ "@babel/runtime" "^7.18.6" "@polkadot/x-global" "9.7.2" +"@polkadot/x-textdecoder@10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.4.2.tgz#93202f3e5ad0e7f75a3fa02d2b8a3343091b341b" + integrity sha512-d3ADduOKUTU+cliz839+KCFmi23pxTlabH7qh7Vs1GZQvXOELWdqFOqakdiAjtMn68n1KVF4O14Y+OUm7gp/zA== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" + "@polkadot/x-textdecoder@9.7.2": version "9.7.2" resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-9.7.2.tgz#c94ea6c8f510fdf579659248ede9421854e32b42" @@ -340,6 +428,14 @@ "@babel/runtime" "^7.18.6" "@polkadot/x-global" "9.7.2" +"@polkadot/x-textencoder@10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.4.2.tgz#cd2e6c8a66b0b400a73f0164e99c510fb5c83501" + integrity sha512-mxcQuA1exnyv74Kasl5vxBq01QwckG088lYjc3KwmND6+pPrW2OWagbxFX5VFoDLDAE+UJtnUHsjdWyOTDhpQA== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" + "@polkadot/x-textencoder@9.7.2": version "9.7.2" resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-9.7.2.tgz#2ae29fa5ca2c0353e7a1913eef710b2d45bdf0b2" @@ -348,13 +444,13 @@ "@babel/runtime" "^7.18.6" "@polkadot/x-global" "9.7.2" -"@polkadot/x-ws@^9.0.1": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-9.7.2.tgz#b35470b487d19be48c1fe39e1c73c623bf548b7e" - integrity sha512-yF2qKL00SGivbima22jxoBNYCZFI8Ph7dmnVm7fDztVtO8Fc2x30Lj3a8+qsSOrynLyJHAh2bjjQxpPmDCB9tw== +"@polkadot/x-ws@^10.1.8": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-10.4.2.tgz#4e9d88f37717570ccf942c6f4f63b06260f45025" + integrity sha512-3gHSTXAWQu1EMcMVTF5QDKHhEHzKxhAArweEyDXE7VsgKUP/ixxw4hVZBrkX122iI5l5mjSiooRSnp/Zl3xqDQ== dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/x-global" "9.7.2" + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" "@types/websocket" "^1.0.5" websocket "^1.0.34" @@ -363,35 +459,39 @@ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== -"@substrate/connect-extension-protocol@^1.0.0": +"@substrate/connect-extension-protocol@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz#fa5738039586c648013caa6a0c95c43265dbe77d" integrity sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg== -"@substrate/connect@0.7.2": - version "0.7.2" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.2.tgz#a2440a7a85a75acbc839745b301d5b8b81cbac5d" - integrity sha512-8GWdrN7qbClYLa9LmETJnywT5fknEQeMw+QKvkUMvsHKegHD0Zkhi0K484mKxRN9RGwcPsKHPj1gMk8xlZuJ9g== +"@substrate/connect@0.7.13": + version "0.7.13" + resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.13.tgz#b87e6e9c60a4d7c5c945142e168e413f91bf6580" + integrity sha512-mGvvxLVVm2Zziff8SpTybeIy+jq0LN81kPqDp/bGwtxAnRctbCsERChAu4Q5HN27cCjI1iNdzSz5ihF/DP5SdQ== dependencies: - "@substrate/connect-extension-protocol" "^1.0.0" - "@substrate/smoldot-light" "0.6.15" + "@substrate/connect-extension-protocol" "^1.0.1" + "@substrate/smoldot-light" "0.6.33" eventemitter3 "^4.0.7" -"@substrate/smoldot-light@0.6.15": - version "0.6.15" - resolved "https://registry.yarnpkg.com/@substrate/smoldot-light/-/smoldot-light-0.6.15.tgz#f3fd2a9fa2e3a579d2bf0c13590fb48db4935f9f" - integrity sha512-c2tJCSp9Litsn/p8wY1FfEqIkJI8Peh89BU7T43bruWRO2SSgLVh0cIVbOCY4en90tIOX4W0CueRWFBRQz7BjQ== +"@substrate/smoldot-light@0.6.33": + version "0.6.33" + resolved "https://registry.yarnpkg.com/@substrate/smoldot-light/-/smoldot-light-0.6.33.tgz#887e5af3d1dada334d8d564f8cd3a93bdf7139bf" + integrity sha512-b1bgIcd6369Xa1Y2MelBD3xmyX2FeHnjq8xS0ohy5uC6bR1v6kEkEyCbcIJE1+GlwDzlfaNxbJjwJPIv8FaRYA== dependencies: - buffer "^6.0.1" pako "^2.0.4" - websocket "^1.0.32" + ws "^8.8.1" "@substrate/ss58-registry@^1.23.0": version "1.40.0" resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.40.0.tgz#2223409c496271df786c1ca8496898896595441e" integrity sha512-QuU2nBql3J4KCnOWtWDw4n1K4JU0T79j54ZZvm/9nhsX6AIar13FyhsaBfs6QkJ2ixTQAnd7TocJIoJRWbqMZA== -"@types/bn.js@^5.1.0": +"@substrate/ss58-registry@^1.38.0": + version "1.41.0" + resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.41.0.tgz#dd18e132f44b73c3cd31cf0db489c10af70bef36" + integrity sha512-TLz5VkEaJRNFzf1Oiix9gqknKer3aKbLfjK9XHBFCIhdxlQpI+S6lZGu3wT4DHAGXPakYfXb8+9ZIOtWLcQ/2Q== + +"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": version "5.1.1" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== @@ -423,24 +523,11 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -buffer@^6.0.1: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - bufferutil@^4.0.1: version "4.0.7" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" @@ -463,6 +550,11 @@ d@1, d@^1.0.1: es5-ext "^0.10.50" type "^1.0.1" +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + debug@^2.2.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -527,6 +619,14 @@ ext@^1.1.2: dependencies: type "^2.7.2" +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -536,10 +636,12 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" ip-regex@^4.3.0: version "4.3.0" @@ -573,7 +675,7 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" -mock-socket@^9.1.3: +mock-socket@^9.1.5: version "9.2.1" resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.2.1.tgz#cc9c0810aa4d0afe02d721dcb2b7e657c00e2282" integrity sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag== @@ -593,7 +695,7 @@ next-tick@^1.1.0: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== -nock@^13.2.4: +nock@^13.2.9: version "13.3.1" resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.1.tgz#f22d4d661f7a05ebd9368edae1b5dc0a62d758fc" integrity sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw== @@ -603,12 +705,19 @@ nock@^13.2.4: lodash "^4.17.21" propagate "^2.0.0" -node-fetch@^2.6.7: - version "2.6.11" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25" - integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w== +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e" + integrity sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow== dependencies: - whatwg-url "^5.0.0" + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" node-gyp-build@^4.3.0: version "4.6.0" @@ -630,18 +739,13 @@ regenerator-runtime@^0.13.11: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -rxjs@^7.5.5: +rxjs@^7.5.6: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - tslib@^2.1.0: version "2.5.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338" @@ -676,12 +780,12 @@ utf-8-validate@^5.0.2: dependencies: node-gyp-build "^4.3.0" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== +web-streams-polyfill@^3.0.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" + integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== -websocket@^1.0.32, websocket@^1.0.34: +websocket@^1.0.34: version "1.0.34" resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== @@ -693,13 +797,10 @@ websocket@^1.0.32, websocket@^1.0.34: utf-8-validate "^5.0.2" yaeti "^0.0.6" -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" +ws@^8.8.1: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== yaeti@^0.0.6: version "0.0.6"