diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a6e627e34f..24a957cedf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,8 +15,8 @@ * @futurechimp @mmsinclair # Rust rules: -*.rs @durch @futurechimp @jstuczyn @neacsu -Cargo.* @durch @futurechimp @jstuczyn @neacsu +*.rs @durch @futurechimp @jstuczyn @neacsu @octol +Cargo.* @durch @futurechimp @jstuczyn @neacsu @octol # JS rules: *.js @mmsinclair @fmtabbara @Aid19801 diff --git a/.github/workflows/nym-wallet-publish-macos.yml b/.github/workflows/nym-wallet-publish-macos.yml new file mode 100644 index 0000000000..7cd484a7ee --- /dev/null +++ b/.github/workflows/nym-wallet-publish-macos.yml @@ -0,0 +1,77 @@ +name: Publish Nym Wallet (MacOS) +on: + release: + types: [created] + +defaults: + run: + working-directory: nym-wallet + +jobs: + publish-tauri: + strategy: + fail-fast: false + matrix: + platform: [macos-latest] + + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + + - name: Check the release tag starts with `nym-wallet-` + if: startsWith(github.ref, 'refs/tags/nym-wallet-') == false + uses: actions/github-script@v3 + with: + script: | + core.setFailed('Release tag did not start with nym-wallet-...') + + - name: Node v16 + uses: actions/setup-node@v1 + with: + node-version: 16.x + - name: Install Rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: Install the Apple developer certificate for code signing + env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + run: | + # create variables + CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + + # import certificate and provisioning profile from secrets + echo -n "$APPLE_CERTIFICATE" | base64 --decode --output $CERTIFICATE_PATH + + # create temporary keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # import certificate to keychain + security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + - name: Install app dependencies and build it + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + run: yarn && yarn build + + - name: Upload to release based on tag name + uses: softprops/action-gh-release@v1 + with: + files: nym-wallet/target/release/bundle/dmg/*.dmg + + - name: Clean up keychain + if: ${{ always() }} + run: | + security delete-keychain $RUNNER_TEMP/app-signing.keychain-db diff --git a/.github/workflows/nym-wallet-publish-ubuntu.yml b/.github/workflows/nym-wallet-publish-ubuntu.yml new file mode 100644 index 0000000000..3b2c37ef20 --- /dev/null +++ b/.github/workflows/nym-wallet-publish-ubuntu.yml @@ -0,0 +1,46 @@ +name: Publish Nym Wallet (Ubuntu) +on: + release: + types: [created] + +defaults: + run: + working-directory: nym-wallet + +jobs: + publish-tauri: + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest] + + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + + - name: Tauri dependencies + run: > + sudo apt-get update && + sudo apt-get install -y webkit2gtk-4.0 + - name: Check the release tag starts with `nym-wallet-` + if: startsWith(github.ref, 'refs/tags/nym-wallet-') == false + uses: actions/github-script@v3 + with: + script: | + core.setFailed('Release tag did not start with nym-wallet-...') + + - name: Node v16 + uses: actions/setup-node@v1 + with: + node-version: 16.x + - name: Install Rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: Install app dependencies and build it + run: yarn && yarn build + + - name: Upload to release based on tag name + uses: softprops/action-gh-release@v1 + with: + files: nym-wallet/target/release/bundle/appimage/*.AppImage diff --git a/.github/workflows/nym-wallet-publish.yml b/.github/workflows/nym-wallet-publish.yml deleted file mode 100644 index 95544979af..0000000000 --- a/.github/workflows/nym-wallet-publish.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Publish Nym Wallet -on: - push: - tags: - - nym-wallet-* - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Run a one-line script - run: echo Hello, world! diff --git a/.github/workflows/wasm_client_build.yml b/.github/workflows/wasm_client_build.yml index cd064c4748..2761aabcd2 100644 --- a/.github/workflows/wasm_client_build.yml +++ b/.github/workflows/wasm_client_build.yml @@ -19,30 +19,27 @@ jobs: override: true components: rustfmt, clippy -# token credentials (non-coconut) don't work for wasm right now -# - uses: actions-rs/cargo@v1 -# with: -# command: build -# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown + - uses: actions-rs/cargo@v1 + with: + command: build + args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown - uses: actions-rs/cargo@v1 with: command: build args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut -# for some reason this does not seem to work correctly, leave it for later, building is good enough for now -# - uses: actions-rs/cargo@v1 -# with: -# command: test -# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown + - uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path clients/webassembly/Cargo.toml - uses: actions-rs/cargo@v1 with: command: fmt args: --manifest-path clients/webassembly/Cargo.toml -- --check -# for some reason this does not seem to work correctly, leave it for later, building is good enough for now -# - uses: actions-rs/cargo@v1 -# with: -# command: clippy -# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings + - uses: actions-rs/cargo@v1 + with: + command: clippy + args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index f8213321ad..f3d0b5aa78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -638,11 +638,41 @@ dependencies = [ "atty", "bitflags", "strsim 0.8.0", - "textwrap", + "textwrap 0.11.0", "unicode-width", "vec_map", ] +[[package]] +name = "clap" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "strsim 0.10.0", + "termcolor", + "textwrap 0.14.2", +] + +[[package]] +name = "clap_derive" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "517358c28fcef6607bf6f76108e02afad7e82297d132a6b846dcc1fc3efcd153" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "client-core" version = "0.12.0" @@ -772,13 +802,38 @@ dependencies = [ ] [[package]] -name = "console_error_panic_hook" -version = "0.1.6" +name = "console-api" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" +checksum = "14f67643a7d716307ad10b3e3aef02826382acbe349a3e7605ac57556148bc87" dependencies = [ - "cfg-if 0.1.10", - "wasm-bindgen", + "prost", + "prost-types", + "tonic", + "tonic-build", + "tracing-core", +] + +[[package]] +name = "console-subscriber" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829835c211a0247cd11e65e13cec8696b879374879c35ce162ce8098b23c90d4" +dependencies = [ + "console-api", + "crossbeam-channel", + "futures", + "hdrhistogram", + "humantime 2.1.0", + "serde", + "serde_json", + "thread_local", + "tokio", + "tokio-stream", + "tonic", + "tracing", + "tracing-core", + "tracing-subscriber", ] [[package]] @@ -947,9 +1002,9 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.0.0-beta3" +version = "1.0.0-beta4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a380b87642204557629c9b72988c47b55fbfe6d474960adba56b22331504956a" +checksum = "f903ebbabc0d4880dbc76148efb8be8fc29fa4bf294c440c3d70da1c8bcafff7" dependencies = [ "digest 0.9.0", "ed25519-zebra", @@ -960,18 +1015,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.0.0-beta3" +version = "1.0.0-beta4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866713b2fe13f23038c7d8824c3059d1f28dd94685fb406d1533c4eeeefeefae" +checksum = "832bebef577ecb394603de8e2bf0de429b74aa364e17dec18e15ce37e71b0cae" dependencies = [ "syn", ] [[package]] name = "cosmwasm-std" -version = "1.0.0-beta3" +version = "1.0.0-beta4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dbb9939b31441dfa9af3ec9740c8a24d585688401eff1b6b386abb7ad0d10a8" +checksum = "6238c45840cc9de5a39f0f619e3a4f7c38c5d2c6ac9e3e4d72751ee045e6d7da" dependencies = [ "base64", "cosmwasm-crypto", @@ -1036,7 +1091,7 @@ checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" dependencies = [ "atty", "cast", - "clap", + "clap 2.33.3", "criterion-plot", "csv", "itertools", @@ -1198,7 +1253,7 @@ checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa", + "itoa 0.4.8", "matches", "phf 0.8.0", "proc-macro2", @@ -1225,7 +1280,7 @@ checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" dependencies = [ "bstr", "csv-core", - "itoa", + "itoa 0.4.8", "ryu", "serde", ] @@ -1941,6 +1996,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixedbitset" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" + [[package]] name = "flate2" version = "1.0.22" @@ -2157,6 +2218,10 @@ name = "futures-timer" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +dependencies = [ + "gloo-timers", + "send_wrapper", +] [[package]] name = "futures-util" @@ -2446,7 +2511,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aad66361f66796bfc73f530c51ef123970eb895ffba991a234fcf7bea89e518" dependencies = [ "anyhow", - "heck", + "heck 0.3.3", "proc-macro-crate 1.1.0", "proc-macro-error", "proc-macro2", @@ -2493,6 +2558,19 @@ dependencies = [ "regex", ] +[[package]] +name = "gloo-timers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f16c88aa13d2656ef20d1c042086b8767bbe2bdb62526894275a1b062161b2e" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gobject-sys" version = "0.10.0" @@ -2586,7 +2664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21de1da96dc117443fb03c2e270b2d34b7de98d0a79a19bbb689476173745b79" dependencies = [ "anyhow", - "heck", + "heck 0.3.3", "proc-macro-crate 1.1.0", "proc-macro-error", "proc-macro2", @@ -2596,9 +2674,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.4" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f3675cfef6a30c8031cf9e6493ebdc3bb3272a3fea3923c4210d1830e6a472" +checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689" dependencies = [ "bytes", "fnv", @@ -2651,6 +2729,19 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "hdrhistogram" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6490be71f07a5f62b564bc58e36953f675833df11c7e4a0647bee7a07ca1ec5e" +dependencies = [ + "base64", + "byteorder", + "flate2", + "nom", + "num-traits", +] + [[package]] name = "headers" version = "0.3.4" @@ -2685,6 +2776,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -2748,14 +2845,14 @@ checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" dependencies = [ "bytes", "fnv", - "itoa", + "itoa 0.4.8", ] [[package]] name = "http-body" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ "bytes", "http", @@ -2807,9 +2904,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.13" +version = "0.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d1cfb9e4f68655fa04c01f59edb405b6074a0f7118ea881e5026e4a1cd8593" +checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" dependencies = [ "bytes", "futures-channel", @@ -2820,7 +2917,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa", + "itoa 0.4.8", "pin-project-lite", "socket2", "tokio", @@ -2865,6 +2962,18 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -3076,6 +3185,12 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + [[package]] name = "javascriptcore-rs" version = "0.14.0" @@ -3198,9 +3313,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.102" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103" +checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9" [[package]] name = "libgit2-sys" @@ -3348,12 +3463,6 @@ dependencies = [ "autocfg 1.0.1", ] -[[package]] -name = "memory_units" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" - [[package]] name = "mime" version = "0.3.16" @@ -3432,6 +3541,7 @@ dependencies = [ "serde", "serde_repr", "thiserror", + "time 0.3.6", "ts-rs", ] @@ -3481,6 +3591,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "native-tls" version = "0.2.8" @@ -3552,7 +3668,6 @@ dependencies = [ "cfg-if 1.0.0", "hex-literal", "serde", - "time 0.3.3", "url", ] @@ -3683,11 +3798,20 @@ dependencies = [ "syn", ] +[[package]] +name = "num_threads" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71a1eb3a36534514077c1e079ada2fb170ef30c47d203aa6916138cf882ecd52" +dependencies = [ + "libc", +] + [[package]] name = "nym-client" version = "0.12.1" dependencies = [ - "clap", + "clap 2.33.3", "client-core", "coconut-interface", "config", @@ -3717,30 +3841,6 @@ dependencies = [ "websocket-requests", ] -[[package]] -name = "nym-client-wasm" -version = "0.12.0" -dependencies = [ - "coconut-interface", - "console_error_panic_hook", - "credentials", - "crypto", - "futures", - "gateway-client", - "js-sys", - "nymsphinx", - "rand 0.7.3", - "serde", - "topology", - "url", - "validator-client", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test", - "wasm-utils", - "wee_alloc", -] - [[package]] name = "nym-gateway" version = "0.12.1" @@ -3748,7 +3848,7 @@ dependencies = [ "bandwidth-claim-contract", "bip39", "bs58", - "clap", + "clap 2.33.3", "coconut-interface", "colored", "config", @@ -3788,8 +3888,9 @@ dependencies = [ name = "nym-mixnode" version = "0.12.1" dependencies = [ + "anyhow", "bs58", - "clap", + "clap 3.0.10", "colored", "config", "crypto", @@ -3797,11 +3898,14 @@ dependencies = [ "dotenv", "futures", "humantime-serde", + "lazy_static", "log", "mixnet-client", "mixnode-common", "nonexhaustive-delayqueue", "nymsphinx", + "nymsphinx-params", + "nymsphinx-types", "pemstore", "pretty_env_logger", "rand 0.7.3", @@ -3822,7 +3926,7 @@ dependencies = [ name = "nym-network-requester" version = "0.12.0" dependencies = [ - "clap", + "clap 2.33.3", "dirs", "futures", "ipnetwork", @@ -3843,7 +3947,7 @@ dependencies = [ name = "nym-socks5-client" version = "0.12.1" dependencies = [ - "clap", + "clap 2.33.3", "client-core", "coconut-interface", "config", @@ -3880,9 +3984,11 @@ version = "0.12.0" dependencies = [ "anyhow", "attohttpc 0.18.0", - "clap", + "cfg-if 1.0.0", + "clap 2.33.3", "coconut-interface", "config", + "console-subscriber", "credentials", "crypto", "dirs", @@ -3898,7 +4004,6 @@ dependencies = [ "pretty_env_logger", "rand 0.7.3", "rand 0.8.4", - "rand_chacha 0.3.1", "reqwest", "rocket", "rocket_cors", @@ -3907,7 +4012,7 @@ dependencies = [ "serde_json", "sqlx", "thiserror", - "time 0.3.3", + "time 0.3.6", "tokio", "topology", "url", @@ -4161,6 +4266,15 @@ dependencies = [ "log", ] +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + [[package]] name = "owning_ref" version = "0.4.1" @@ -4393,6 +4507,16 @@ dependencies = [ "sha-1 0.8.2", ] +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "phf" version = "0.8.0" @@ -4735,6 +4859,26 @@ dependencies = [ "prost-derive", ] +[[package]] +name = "prost-build" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" +dependencies = [ + "bytes", + "heck 0.3.3", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "regex", + "tempfile", + "which", +] + [[package]] name = "prost-derive" version = "0.9.0" @@ -5658,6 +5802,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + [[package]] name = "serde" version = "1.0.130" @@ -5669,9 +5819,9 @@ dependencies = [ [[package]] name = "serde-json-wasm" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50eef3672ec8fa45f3457fd423ba131117786784a895548021976117c1ded449" +checksum = "042ac496d97e5885149d34139bad1d617192770d7eb8f1866da2317ff4501853" dependencies = [ "serde", ] @@ -5723,7 +5873,7 @@ version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" dependencies = [ - "itoa", + "itoa 0.4.8", "ryu", "serde", ] @@ -5746,7 +5896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" dependencies = [ "dtoa", - "itoa", + "itoa 0.4.8", "serde", "url", ] @@ -5758,7 +5908,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" dependencies = [ "form_urlencoded", - "itoa", + "itoa 0.4.8", "ryu", "serde", ] @@ -5851,6 +6001,15 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -6079,7 +6238,7 @@ dependencies = [ "futures-util", "hashlink", "hex", - "itoa", + "itoa 0.4.8", "libc", "libsqlite3-sys", "log", @@ -6110,7 +6269,7 @@ dependencies = [ "dotenv", "either", "futures", - "heck", + "heck 0.3.3", "once_cell", "proc-macro2", "quote", @@ -6304,7 +6463,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2", "quote", "syn", @@ -6316,7 +6475,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2", "quote", "syn", @@ -6510,7 +6669,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" dependencies = [ - "heck", + "heck 0.3.3", "pkg-config", "strum 0.18.0", "strum_macros 0.18.0", @@ -6527,7 +6686,7 @@ checksum = "480c269f870722b3b08d2f13053ce0c2ab722839f472863c3e2d61ff3a1c2fa6" dependencies = [ "anyhow", "cfg-expr", - "heck", + "heck 0.3.3", "itertools", "pkg-config", "strum 0.21.0", @@ -6774,7 +6933,7 @@ dependencies = [ "subtle 2.4.1", "subtle-encoding", "tendermint-proto", - "time 0.3.3", + "time 0.3.6", "zeroize", ] @@ -6807,7 +6966,7 @@ dependencies = [ "serde", "serde_bytes", "subtle-encoding", - "time 0.3.3", + "time 0.3.6", ] [[package]] @@ -6835,7 +6994,7 @@ dependencies = [ "tendermint-config", "tendermint-proto", "thiserror", - "time 0.3.3", + "time 0.3.6", "tokio", "tracing", "url", @@ -6872,6 +7031,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "textwrap" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" + [[package]] name = "thin-slice" version = "0.1.1" @@ -6935,12 +7100,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.3" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde1cf55178e0293453ba2cca0d5f8392a922e52aa958aee9c28ed02becc6d03" +checksum = "c8d54b9298e05179c335de2b9645d061255bcd5155f843b3e328d2cfe0a5b413" dependencies = [ - "itoa", + "itoa 1.0.1", "libc", + "num_threads", "serde", "time-macros 0.2.3", ] @@ -6995,11 +7161,10 @@ dependencies = [ [[package]] name = "tokio" -version = "1.12.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc" +checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" dependencies = [ - "autocfg 1.0.1", "bytes", "libc", "memchr", @@ -7010,14 +7175,25 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "tokio-macros", + "tracing", "winapi", ] [[package]] -name = "tokio-macros" -version = "1.3.0" +name = "tokio-io-timeout" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" dependencies = [ "proc-macro2", "quote", @@ -7107,6 +7283,49 @@ dependencies = [ "serde", ] +[[package]] +name = "tonic" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a" +dependencies = [ + "async-stream", + "async-trait", + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" +dependencies = [ + "proc-macro2", + "prost-build", + "quote", + "syn", +] + [[package]] name = "topology" version = "0.1.0" @@ -7121,6 +7340,33 @@ dependencies = [ "version-checker", ] +[[package]] +name = "tower" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5651b5f6860a99bd1adb59dbfe1db8beb433e73709d9032b413a77e2fb7c066a" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "pin-project-lite", + "rand 0.8.4", + "slab", + "tokio", + "tokio-stream", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" + [[package]] name = "tower-service" version = "0.3.1" @@ -7134,10 +7380,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84f96e095c0c82419687c20ddf5cb3eadb61f4e1405923c9dc8e53a1adacbda8" dependencies = [ "cfg-if 1.0.0", + "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" version = "0.1.20" @@ -7147,6 +7406,27 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77be66445c4eeebb934a7340f227bfe7b338173d3f8c00a60a5a58005c9faecf" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + [[package]] name = "try-lock" version = "0.2.3" @@ -7380,6 +7660,7 @@ dependencies = [ "url", "validator-api-requests", "vesting-contract", + "vesting-contract-common", ] [[package]] @@ -7454,7 +7735,12 @@ dependencies = [ name = "vesting-contract-common" version = "0.1.0" dependencies = [ + "config", "cosmwasm-std", + "cw-storage-plus", + "mixnet-contract-common", + "schemars", + "serde", ] [[package]] @@ -7564,30 +7850,6 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" -[[package]] -name = "wasm-bindgen-test" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96f1aa7971fdf61ef0f353602102dbea75a56e225ed036c1e3740564b91e6b7e" -dependencies = [ - "console_error_panic_hook", - "js-sys", - "scoped-tls", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6006f79628dfeb96a86d4db51fbf1344cd7fd8408f06fc9aa3c84913a4789688" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "wasm-utils" version = "0.1.0" @@ -7624,12 +7886,15 @@ dependencies = [ "ethereum-types", "futures", "futures-timer", + "getrandom 0.2.3", "headers", "hex", + "js-sys", "jsonrpc-core", "log", "parking_lot", "pin-project", + "rand 0.8.4", "reqwest", "rlp", "secp256k1", @@ -7641,6 +7906,8 @@ dependencies = [ "tokio-stream", "tokio-util", "url", + "wasm-bindgen", + "wasm-bindgen-futures", "web3-async-native-tls", ] @@ -7754,15 +8021,14 @@ dependencies = [ ] [[package]] -name = "wee_alloc" -version = "0.4.5" +name = "which" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" dependencies = [ - "cfg-if 0.1.10", + "either", + "lazy_static", "libc", - "memory_units", - "winapi", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 90a214fe8b..7cbdacab6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,13 +11,13 @@ panic = "abort" [workspace] +resolver = "2" members = [ "clients/client-core", "clients/native", "clients/native/websocket-requests", "clients/socks5", "clients/tauri-client/src-tauri", - "clients/webassembly", "common/client-libs/gateway-client", "common/client-libs/mixnet-client", "common/client-libs/validator-client", @@ -60,7 +60,6 @@ members = [ default-members = [ "clients/native", "clients/socks5", -# "clients/webassembly", "gateway", "service-providers/network-requester", "mixnode", @@ -68,4 +67,4 @@ default-members = [ "explorer-api", ] -exclude = ["explorer", "contracts", "tokenomics-py"] +exclude = ["explorer", "contracts", "tokenomics-py", "clients/webassembly"] diff --git a/Makefile b/Makefile index 847e8332d3..acb839b943 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -all: clippy-all test fmt +all: clippy-all test wasm fmt happy: clippy-happy test fmt clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet diff --git a/clients/client-core/Cargo.toml b/clients/client-core/Cargo.toml index e8a4aa39e7..464bf9d0a4 100644 --- a/clients/client-core/Cargo.toml +++ b/clients/client-core/Cargo.toml @@ -2,7 +2,7 @@ name = "client-core" version = "0.12.0" authors = ["Dave Hrycyszyn "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index fcdde9ea1c..bbb1ba86db 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -2,7 +2,7 @@ name = "nym-client" version = "0.12.1" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] -edition = "2018" +edition = "2021" rust-version = "1.56" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/clients/native/examples/js-examples/websocket/package-lock.json b/clients/native/examples/js-examples/websocket/package-lock.json index 8c08e7a741..f905c18f0c 100644 --- a/clients/native/examples/js-examples/websocket/package-lock.json +++ b/clients/native/examples/js-examples/websocket/package-lock.json @@ -2441,9 +2441,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "dev": true, "funding": [ { @@ -3806,9 +3806,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", + "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true, "peer": true, "bin": { @@ -3943,9 +3943,9 @@ } }, "node_modules/nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", "dependencies": { "boolbase": "^1.0.0" }, @@ -6085,9 +6085,9 @@ } }, "node_modules/url-parse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", - "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz", + "integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==", "dev": true, "dependencies": { "querystringify": "^2.1.1", @@ -8853,9 +8853,9 @@ } }, "follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "dev": true }, "for-in": { @@ -9871,9 +9871,9 @@ "optional": true }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", + "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true, "peer": true }, @@ -9984,9 +9984,9 @@ } }, "nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", "requires": { "boolbase": "^1.0.0" } @@ -11733,9 +11733,9 @@ } }, "url-parse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", - "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz", + "integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==", "dev": true, "requires": { "querystringify": "^2.1.1", diff --git a/clients/native/src/commands/mod.rs b/clients/native/src/commands/mod.rs index 24351254e0..fffcf47e96 100644 --- a/clients/native/src/commands/mod.rs +++ b/clients/native/src/commands/mod.rs @@ -32,7 +32,7 @@ fn parse_validators(raw: &str) -> Vec { .collect() } -pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config { +pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config { if let Some(raw_validators) = matches.value_of("validators") { config .get_base_mut() diff --git a/clients/native/src/commands/upgrade.rs b/clients/native/src/commands/upgrade.rs index c8a3bc44a5..2c90840f31 100644 --- a/clients/native/src/commands/upgrade.rs +++ b/clients/native/src/commands/upgrade.rs @@ -95,7 +95,7 @@ fn parse_package_version() -> Version { fn minor_0_12_upgrade( mut config: Config, - _matches: &ArgMatches, + _matches: &ArgMatches<'_>, config_version: &Version, package_version: &Version, ) -> Config { @@ -131,7 +131,7 @@ fn minor_0_12_upgrade( config } -fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version) { +fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Version) { loop { let config_version = parse_config_version(&config); @@ -151,7 +151,7 @@ fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version } } -pub fn execute(matches: &ArgMatches) { +pub fn execute(matches: &ArgMatches<'_>) { let package_version = parse_package_version(); let id = matches.value_of("id").unwrap(); diff --git a/clients/native/websocket-requests/Cargo.toml b/clients/native/websocket-requests/Cargo.toml index bd97d7fad6..74bb1ee500 100644 --- a/clients/native/websocket-requests/Cargo.toml +++ b/clients/native/websocket-requests/Cargo.toml @@ -2,7 +2,7 @@ name = "websocket-requests" version = "0.1.0" authors = ["Jędrzej Stuczyński "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index c256c005a0..b5116794ff 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -2,7 +2,7 @@ name = "nym-socks5-client" version = "0.12.1" authors = ["Dave Hrycyszyn "] -edition = "2018" +edition = "2021" rust-version = "1.56" [lib] diff --git a/clients/socks5/src/commands/mod.rs b/clients/socks5/src/commands/mod.rs index 9804d90704..29a2bcb76e 100644 --- a/clients/socks5/src/commands/mod.rs +++ b/clients/socks5/src/commands/mod.rs @@ -32,7 +32,7 @@ fn parse_validators(raw: &str) -> Vec { .collect() } -pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config { +pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config { if let Some(raw_validators) = matches.value_of("validators") { config .get_base_mut() diff --git a/clients/socks5/src/commands/upgrade.rs b/clients/socks5/src/commands/upgrade.rs index c8a3bc44a5..2c90840f31 100644 --- a/clients/socks5/src/commands/upgrade.rs +++ b/clients/socks5/src/commands/upgrade.rs @@ -95,7 +95,7 @@ fn parse_package_version() -> Version { fn minor_0_12_upgrade( mut config: Config, - _matches: &ArgMatches, + _matches: &ArgMatches<'_>, config_version: &Version, package_version: &Version, ) -> Config { @@ -131,7 +131,7 @@ fn minor_0_12_upgrade( config } -fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version) { +fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Version) { loop { let config_version = parse_config_version(&config); @@ -151,7 +151,7 @@ fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version } } -pub fn execute(matches: &ArgMatches) { +pub fn execute(matches: &ArgMatches<'_>) { let package_version = parse_package_version(); let id = matches.value_of("id").unwrap(); diff --git a/clients/tauri-client/src-tauri/Cargo.toml b/clients/tauri-client/src-tauri/Cargo.toml index c39fd4e600..075ec15f4d 100644 --- a/clients/tauri-client/src-tauri/Cargo.toml +++ b/clients/tauri-client/src-tauri/Cargo.toml @@ -6,7 +6,7 @@ authors = ["you"] license = "" repository = "" default-run = "app" -edition = "2018" +edition = "2021" build = "src/build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/clients/validator/src/index.ts b/clients/validator/src/index.ts index 76b475b8d3..5ed0cde383 100644 --- a/clients/validator/src/index.ts +++ b/clients/validator/src/index.ts @@ -180,8 +180,8 @@ export default class ValidatorClient implements INymClient { return this.client.getSybilResistancePercent(this.mixnetContract); } - public async getEpochRewardPercent(): Promise { - return this.client.getEpochRewardPercent(this.mixnetContract); + public async getIntervalRewardPercent(): Promise { + return this.client.getIntervalRewardPercent(this.mixnetContract); } public async getAllNymdMixnodes(): Promise { diff --git a/clients/validator/src/nymd-querier.ts b/clients/validator/src/nymd-querier.ts index 0db3ac3657..a0bdc911d3 100644 --- a/clients/validator/src/nymd-querier.ts +++ b/clients/validator/src/nymd-querier.ts @@ -18,7 +18,6 @@ import { PagedGatewayResponse, PagedMixDelegationsResponse, PagedMixnodeResponse, - RewardingIntervalResponse, RewardingStatus, } from './types'; @@ -79,12 +78,6 @@ export default class NymdQuerier implements INymdQuery { }); } - getCurrentRewardingInterval(mixnetContractAddress: string): Promise { - return this.client.queryContractSmart(mixnetContractAddress, { - current_rewarding_interval: {}, - }); - } - getAllNetworkDelegationsPaged( mixnetContractAddress: string, limit?: number, @@ -155,9 +148,9 @@ export default class NymdQuerier implements INymdQuery { }); } - getEpochRewardPercent(mixnetContractAddress: string): Promise { + getIntervalRewardPercent(mixnetContractAddress: string): Promise { return this.client.queryContractSmart(mixnetContractAddress, { - get_epoch_reward_percent: {}, + get_interval_reward_percent: {}, }); } diff --git a/clients/validator/src/query-client.ts b/clients/validator/src/query-client.ts index 49f4c127c2..dd423b1d4b 100644 --- a/clients/validator/src/query-client.ts +++ b/clients/validator/src/query-client.ts @@ -28,7 +28,6 @@ import { PagedGatewayResponse, PagedMixDelegationsResponse, PagedMixnodeResponse, - RewardingIntervalResponse, RewardingStatus, } from './types'; import ValidatorApiQuerier, { IValidatorApiQuery } from './validator-api-querier'; @@ -63,7 +62,6 @@ export interface INymdQuery { ownsMixNode(mixnetContractAddress: string, address: string): Promise; ownsGateway(mixnetContractAddress: string, address: string): Promise; getStateParams(mixnetContractAddress: string): Promise; - getCurrentRewardingInterval(mixnetContractAddress: string): Promise; getAllNetworkDelegationsPaged( mixnetContractAddress: string, @@ -87,7 +85,7 @@ export interface INymdQuery { getLayerDistribution(mixnetContractAddress: string): Promise; getRewardPool(mixnetContractAddress: string): Promise; getCirculatingSupply(mixnetContractAddress: string): Promise; - getEpochRewardPercent(mixnetContractAddress: string): Promise; + getIntervalRewardPercent(mixnetContractAddress: string): Promise; getSybilResistancePercent(mixnetContractAddress: string): Promise; getRewardingStatus( mixnetContractAddress: string, @@ -138,10 +136,6 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient return this.nymdQuerier.getStateParams(mixnetContractAddress); } - getCurrentRewardingInterval(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getCurrentRewardingInterval(mixnetContractAddress); - } - getAllNetworkDelegationsPaged( mixnetContractAddress: string, limit?: number, @@ -184,8 +178,8 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress); } - getEpochRewardPercent(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getEpochRewardPercent(mixnetContractAddress); + getIntervalRewardPercent(mixnetContractAddress: string): Promise { + return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress); } getSybilResistancePercent(mixnetContractAddress: string): Promise { diff --git a/clients/validator/src/signing-client.ts b/clients/validator/src/signing-client.ts index 8b027e9480..5c3551f69f 100644 --- a/clients/validator/src/signing-client.ts +++ b/clients/validator/src/signing-client.ts @@ -31,7 +31,6 @@ import { PagedGatewayResponse, PagedMixDelegationsResponse, PagedMixnodeResponse, - RewardingIntervalResponse, RewardingStatus, } from './types'; import ValidatorApiQuerier from './validator-api-querier'; @@ -257,10 +256,6 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig return this.nymdQuerier.getStateParams(mixnetContractAddress); } - getCurrentRewardingInterval(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getCurrentRewardingInterval(mixnetContractAddress); - } - getAllNetworkDelegationsPaged( mixnetContractAddress: string, limit?: number, @@ -303,8 +298,8 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress); } - getEpochRewardPercent(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getEpochRewardPercent(mixnetContractAddress); + getIntervalRewardPercent(mixnetContractAddress: string): Promise { + return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress); } getSybilResistancePercent(mixnetContractAddress: string): Promise { diff --git a/clients/validator/src/types.ts b/clients/validator/src/types.ts index 786b72ff6c..575702ec45 100644 --- a/clients/validator/src/types.ts +++ b/clients/validator/src/types.ts @@ -43,12 +43,6 @@ export type ContractStateParams = { mixnode_active_set_size: number; }; -export type RewardingIntervalResponse = { - current_rewarding_interval_starting_block: number; - current_rewarding_interval_nonce: number; - rewarding_in_progress: boolean; -}; - export type LayerDistribution = { gateways: number; layer1: number; diff --git a/clients/webassembly/Cargo.toml b/clients/webassembly/Cargo.toml index bfacaec629..72321e98e3 100644 --- a/clients/webassembly/Cargo.toml +++ b/clients/webassembly/Cargo.toml @@ -2,7 +2,7 @@ name = "nym-client-wasm" authors = ["Dave Hrycyszyn ", "Jedrzej Stuczynski "] version = "0.12.0" -edition = "2018" +edition = "2021" keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"] license = "Apache-2.0" repository = "https://github.com/nymtech/nym" @@ -20,7 +20,7 @@ coconut = ["coconut-interface", "credentials", "gateway-client/coconut"] [dependencies] futures = "0.3" serde = { version = "1.0", features = ["derive"] } -wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } +wasm-bindgen = { version = "=0.2.78", features = ["serde-serialize"] } wasm-bindgen-futures = "0.4" js-sys = "0.3" rand = { version = "0.7.3", features = ["wasm-bindgen"] } @@ -32,7 +32,7 @@ credentials = { path = "../../common/credentials", optional = true } crypto = { path = "../../common/crypto" } nymsphinx = { path = "../../common/nymsphinx" } topology = { path = "../../common/topology" } -gateway-client = { path = "../../common/client-libs/gateway-client" } +gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] } validator-client = { path = "../../common/client-libs/validator-client", default-features = false } wasm-utils = { path = "../../common/wasm-utils" } diff --git a/clients/webassembly/src/client/mod.rs b/clients/webassembly/src/client/mod.rs index c68c2f4d60..4bb0e4263b 100644 --- a/clients/webassembly/src/client/mod.rs +++ b/clients/webassembly/src/client/mod.rs @@ -3,7 +3,6 @@ use crypto::asymmetric::{encryption, identity}; use futures::channel::mpsc; -use gateway_client::bandwidth::BandwidthController; use gateway_client::GatewayClient; use nymsphinx::acknowledgements::AckKey; use nymsphinx::addressing::clients::Recipient; @@ -111,7 +110,7 @@ impl NymClient { let testnet_mode = self.testnet_mode; #[cfg(feature = "coconut")] - let bandwidth_controller = Some(BandwidthController::new( + let bandwidth_controller = Some(gateway_client::bandwidth::BandwidthController::new( vec![self.validator_server.clone()], *self.identity.public_key(), )); diff --git a/common/bandwidth-claim-contract/Cargo.toml b/common/bandwidth-claim-contract/Cargo.toml index 6366405f6a..e63e100d7a 100644 --- a/common/bandwidth-claim-contract/Cargo.toml +++ b/common/bandwidth-claim-contract/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bandwidth-claim-contract" version = "0.1.0" -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/client-libs/gateway-client/Cargo.toml b/common/client-libs/gateway-client/Cargo.toml index 844191d8b6..5f0b0730c2 100644 --- a/common/client-libs/gateway-client/Cargo.toml +++ b/common/client-libs/gateway-client/Cargo.toml @@ -2,7 +2,7 @@ name = "gateway-client" version = "0.1.0" authors = ["Jędrzej Stuczyński "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -15,6 +15,8 @@ log = "0.4" thiserror = "1.0" url = "2.2" rand = { version = "0.7.3", features = ["wasm-bindgen"] } +secp256k1 = "0.20.3" +web3 = { version = "0.17.0", default-features = false } # internal credentials = { path = "../../credentials" } @@ -36,12 +38,6 @@ features = ["macros", "rt", "net", "sync", "time"] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite] version = "0.14" -[target."cfg(not(target_arch = \"wasm32\"))".dependencies.secp256k1] -version = "0.20.3" - -[target."cfg(not(target_arch = \"wasm32\"))".dependencies.web3] -version = "0.17.0" - # wasm-only dependencies [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen] version = "0.2" @@ -70,4 +66,6 @@ features = ["js"] #url = "2.1" [features] -coconut = ["gateway-requests/coconut", "coconut-interface"] \ No newline at end of file +coconut = ["gateway-requests/coconut", "coconut-interface"] +wasm = ["web3/wasm", "web3/http", "web3/signing"] +default = ["web3/default"] \ No newline at end of file diff --git a/common/client-libs/gateway-client/src/bandwidth.rs b/common/client-libs/gateway-client/src/bandwidth.rs index a32a0091ae..5248b1de33 100644 --- a/common/client-libs/gateway-client/src/bandwidth.rs +++ b/common/client-libs/gateway-client/src/bandwidth.rs @@ -203,7 +203,7 @@ impl BandwidthController { &self.eth_private_key, ) .await?; - if Some(U64::from(0)) == recipt.status { + if Some(U64::from(0u64)) == recipt.status { Err(GatewayClientError::BurnTokenError( web3::Error::InvalidResponse(format!( "Transaction status is 0 (failure): {:?}", diff --git a/common/client-libs/mixnet-client/Cargo.toml b/common/client-libs/mixnet-client/Cargo.toml index 1d32991f1f..2585440542 100644 --- a/common/client-libs/mixnet-client/Cargo.toml +++ b/common/client-libs/mixnet-client/Cargo.toml @@ -2,7 +2,7 @@ name = "mixnet-client" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/client-libs/mixnet-client/src/client.rs b/common/client-libs/mixnet-client/src/client.rs index 8d3d66820c..13fcfb595a 100644 --- a/common/client-libs/mixnet-client/src/client.rs +++ b/common/client-libs/mixnet-client/src/client.rs @@ -41,6 +41,17 @@ impl Config { } } +pub trait SendWithoutResponse { + // Without response in this context means we will not listen for anything we might get back (not + // that we should get anything), including any possible io errors + fn send_without_response( + &mut self, + address: NymNodeRoutingAddress, + packet: SphinxPacket, + packet_mode: PacketMode, + ) -> io::Result<()>; +} + pub struct Client { conn_new: HashMap, config: Config, @@ -186,10 +197,10 @@ impl Client { .await }); } +} - // without response in this context means we will not listen for anything we might get back - // (not that we should get anything), including any possible io errors - pub fn send_without_response( +impl SendWithoutResponse for Client { + fn send_without_response( &mut self, address: NymNodeRoutingAddress, packet: SphinxPacket, diff --git a/common/client-libs/mixnet-client/src/forwarder.rs b/common/client-libs/mixnet-client/src/forwarder.rs index 350af150b5..ead3b25a41 100644 --- a/common/client-libs/mixnet-client/src/forwarder.rs +++ b/common/client-libs/mixnet-client/src/forwarder.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::client::{Client, Config}; +use crate::client::{Client, Config, SendWithoutResponse}; use futures::channel::mpsc; use futures::StreamExt; use log::*; diff --git a/common/client-libs/mixnet-client/src/lib.rs b/common/client-libs/mixnet-client/src/lib.rs index 6836a46f12..a63eb5ca03 100644 --- a/common/client-libs/mixnet-client/src/lib.rs +++ b/common/client-libs/mixnet-client/src/lib.rs @@ -4,4 +4,4 @@ pub mod client; pub mod forwarder; -pub use client::{Client, Config}; +pub use client::{Client, Config, SendWithoutResponse}; diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index e5f831efbe..24a0753d6e 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -2,7 +2,7 @@ name = "validator-client" version = "0.1.0" authors = ["Jędrzej Stuczyński "] -edition = "2018" +edition = "2021" rust-version = "1.56" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,10 +10,11 @@ rust-version = "1.56" [dependencies] base64 = "0.13" mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" } -vesting-contract = { path="../../../contracts/vesting" } -serde = { version="1", features=["derive"] } +vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" } +vesting-contract = { path = "../../../contracts/vesting" } +serde = { version = "1", features = ["derive"] } serde_json = "1" -reqwest = { version="0.11", features=["json"] } +reqwest = { version = "0.11", features = ["json"] } thiserror = "1" log = "0.4" url = { version = "2.2", features = ["serde"] } @@ -28,14 +29,28 @@ validator-api-requests = { path = "../../../validator-api/validator-api-requests async-trait = { version = "0.1.51", optional = true } bip39 = { version = "1", features = ["rand"], optional = true } config = { path = "../../config", optional = true } -cosmrs = { version = "0.4.1", features = ["rpc", "bip32", "cosmwasm"], optional = true } +cosmrs = { version = "0.4.1", features = [ + "rpc", + "bip32", + "cosmwasm", +], optional = true } prost = { version = "0.9", default-features = false, optional = true } flate2 = { version = "1.0.20", optional = true } sha2 = { version = "0.9.5", optional = true } itertools = { version = "0.10", optional = true } cosmwasm-std = { version = "1.0.0-beta3", optional = true } -ts-rs = {version = "5.1", optional = true} +ts-rs = { version = "5.1", optional = true } [features] -nymd-client = ["async-trait", "bip39", "config", "cosmrs", "prost", "flate2", "sha2", "itertools", "cosmwasm-std"] +nymd-client = [ + "async-trait", + "bip39", + "config", + "cosmrs", + "prost", + "flate2", + "sha2", + "itertools", + "cosmwasm-std", +] typescript-types = ["ts-rs", "validator-api-requests/ts-rs"] diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 4a3653fb21..c291f22eb1 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -1,6 +1,15 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::{validator_api, ValidatorClientError}; +use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse}; +use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond}; +use url::Url; +use validator_api_requests::models::{ + CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, + StakeSaturationResponse, +}; + #[cfg(feature = "nymd-client")] use crate::nymd::{ error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient, @@ -8,24 +17,20 @@ use crate::nymd::{ #[cfg(feature = "nymd-client")] use mixnet_contract_common::ContractStateParams; -use crate::{validator_api, ValidatorClientError}; -use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse}; #[cfg(feature = "nymd-client")] use mixnet_contract_common::{ - Delegation, MixnetContractVersion, MixnodeRewardingStatusResponse, RewardingIntervalResponse, + Delegation, IdentityKey, Interval, MixnetContractVersion, MixnodeRewardingStatusResponse, + RewardedSetNodeStatus, RewardedSetUpdateDetails, }; -use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond}; - +#[cfg(feature = "nymd-client")] +use std::collections::{HashMap, HashSet}; #[cfg(feature = "nymd-client")] use std::str::FromStr; -use url::Url; -use validator_api_requests::models::{ - CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, - StakeSaturationResponse, -}; #[cfg(feature = "nymd-client")] +#[must_use] pub struct Config { + network: network_defaults::all::Network, api_url: Url, nymd_url: Url, mixnet_contract_address: Option, @@ -34,17 +39,20 @@ pub struct Config { mixnode_page_limit: Option, gateway_page_limit: Option, mixnode_delegations_page_limit: Option, + rewarded_set_page_limit: Option, } #[cfg(feature = "nymd-client")] impl Config { pub fn new( + network: network_defaults::all::Network, nymd_url: Url, api_url: Url, mixnet_contract_address: Option, vesting_contract_address: Option, ) -> Self { Config { + network, nymd_url, mixnet_contract_address, vesting_contract_address, @@ -52,6 +60,7 @@ impl Config { mixnode_page_limit: None, gateway_page_limit: None, mixnode_delegations_page_limit: None, + rewarded_set_page_limit: None, } } @@ -69,10 +78,16 @@ impl Config { self.mixnode_delegations_page_limit = limit; self } + + pub fn with_rewarded_set_page_limit(mut self, limit: Option) -> Config { + self.rewarded_set_page_limit = limit; + self + } } #[cfg(feature = "nymd-client")] pub struct Client { + network: network_defaults::all::Network, mixnet_contract_address: Option, vesting_contract_address: Option, mnemonic: Option, @@ -80,6 +95,7 @@ pub struct Client { mixnode_page_limit: Option, gateway_page_limit: Option, mixnode_delegations_page_limit: Option, + rewarded_set_page_limit: Option, // ideally they would have been read-only, but unfortunately rust doesn't have such features pub validator_api: validator_api::Client, @@ -94,6 +110,7 @@ impl Client { ) -> Result, ValidatorClientError> { let validator_api_client = validator_api::Client::new(config.api_url.clone()); let nymd_client = NymdClient::connect_with_mnemonic( + config.network, config.nymd_url.as_str(), config.mixnet_contract_address.clone(), config.vesting_contract_address.clone(), @@ -102,12 +119,14 @@ impl Client { )?; Ok(Client { + network: config.network, mixnet_contract_address: config.mixnet_contract_address, vesting_contract_address: config.vesting_contract_address, mnemonic: Some(mnemonic), mixnode_page_limit: config.mixnode_page_limit, gateway_page_limit: config.gateway_page_limit, mixnode_delegations_page_limit: config.mixnode_delegations_page_limit, + rewarded_set_page_limit: None, validator_api: validator_api_client, nymd: nymd_client, }) @@ -115,6 +134,7 @@ impl Client { pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { self.nymd = NymdClient::connect_with_mnemonic( + self.network, new_endpoint.as_ref(), self.mixnet_contract_address.clone(), self.vesting_contract_address.clone(), @@ -131,23 +151,25 @@ impl Client { let validator_api_client = validator_api::Client::new(config.api_url.clone()); let nymd_client = NymdClient::connect( config.nymd_url.as_str(), - config.mixnet_contract_address.clone().unwrap_or_else(|| { + Some(config.mixnet_contract_address.clone().unwrap_or_else(|| { cosmrs::AccountId::from_str(network_defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS) .unwrap() - }), - config.vesting_contract_address.clone().unwrap_or_else(|| { + })), + Some(config.vesting_contract_address.clone().unwrap_or_else(|| { cosmrs::AccountId::from_str(network_defaults::DEFAULT_VESTING_CONTRACT_ADDRESS) .unwrap() - }), + })), )?; Ok(Client { + network: config.network, mixnet_contract_address: config.mixnet_contract_address, vesting_contract_address: config.vesting_contract_address, mnemonic: None, mixnode_page_limit: config.mixnode_page_limit, gateway_page_limit: config.gateway_page_limit, mixnode_delegations_page_limit: config.mixnode_delegations_page_limit, + rewarded_set_page_limit: config.rewarded_set_page_limit, validator_api: validator_api_client, nymd: nymd_client, }) @@ -156,8 +178,8 @@ impl Client { pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { self.nymd = NymdClient::connect( new_endpoint.as_ref(), - self.mixnet_contract_address.clone().unwrap(), - self.vesting_contract_address.clone().unwrap(), + self.mixnet_contract_address.clone(), + self.vesting_contract_address.clone(), )?; Ok(()) } @@ -183,6 +205,18 @@ impl Client { Ok(self.validator_api.get_mixnodes().await?) } + pub async fn get_cached_rewarded_mixnodes( + &self, + ) -> Result, ValidatorClientError> { + Ok(self.validator_api.get_rewarded_mixnodes().await?) + } + + pub async fn get_cached_active_mixnodes( + &self, + ) -> Result, ValidatorClientError> { + Ok(self.validator_api.get_active_mixnodes().await?) + } + pub async fn get_cached_gateways(&self) -> Result, ValidatorClientError> { Ok(self.validator_api.get_gateways().await?) } @@ -201,15 +235,6 @@ impl Client { Ok(self.nymd.get_mixnet_contract_version().await?) } - pub async fn get_current_rewarding_interval( - &self, - ) -> Result - where - C: CosmWasmClient + Sync, - { - Ok(self.nymd.get_current_rewarding_interval().await?) - } - pub async fn get_rewarding_status( &self, mix_identity: mixnet_contract_common::IdentityKey, @@ -231,6 +256,13 @@ impl Client { Ok(self.nymd.get_reward_pool().await?.u128()) } + pub async fn get_current_interval(&self) -> Result + where + C: CosmWasmClient + Sync, + { + Ok(self.nymd.get_current_interval().await?) + } + pub async fn get_circulating_supply(&self) -> Result where C: CosmWasmClient + Sync, @@ -245,14 +277,136 @@ impl Client { Ok(self.nymd.get_sybil_resistance_percent().await?) } - pub async fn get_epoch_reward_percent(&self) -> Result + pub async fn get_active_set_work_factor(&self) -> Result where C: CosmWasmClient + Sync, { - Ok(self.nymd.get_epoch_reward_percent().await?) + Ok(self.nymd.get_active_set_work_factor().await?) + } + + pub async fn get_interval_reward_percent(&self) -> Result + where + C: CosmWasmClient + Sync, + { + Ok(self.nymd.get_interval_reward_percent().await?) + } + + pub async fn get_current_rewarded_set_update_details( + &self, + ) -> Result + where + C: CosmWasmClient + Sync, + { + Ok(self + .nymd + .query_current_rewarded_set_update_details() + .await?) } // basically handles paging for us + pub async fn get_all_nymd_rewarded_set_mixnode_identities( + &self, + ) -> Result, ValidatorClientError> + where + C: CosmWasmClient + Sync, + { + let mut identities = Vec::new(); + let mut start_after = None; + let mut height = None; + + loop { + let mut paged_response = self + .nymd + .get_rewarded_set_identities_paged( + start_after.take(), + self.rewarded_set_page_limit, + height, + ) + .await?; + identities.append(&mut paged_response.identities); + + if height.is_none() { + // keep using the same height (the first query happened at the most recent height) + height = Some(paged_response.at_height) + } + + if let Some(start_after_res) = paged_response.start_next_after { + start_after = Some(start_after_res) + } else { + break; + } + } + + Ok(identities) + } + + pub async fn get_nymd_rewarded_and_active_sets( + &self, + ) -> Result, ValidatorClientError> + where + C: CosmWasmClient + Sync, + { + let all_mixnodes = self.get_all_nymd_mixnodes().await?; + let rewarded_set_identities = self + .get_all_nymd_rewarded_set_mixnode_identities() + .await? + .into_iter() + .collect::>(); + + Ok(all_mixnodes + .into_iter() + .filter_map(|node| { + rewarded_set_identities + .get(node.identity()) + .map(|status| (node, *status)) + }) + .collect()) + } + + /// If you need both rewarded and the active set, consider using [Self::get_nymd_rewarded_and_active_sets] instead + pub async fn get_nymd_rewarded_set(&self) -> Result, ValidatorClientError> + where + C: CosmWasmClient + Sync, + { + let all_mixnodes = self.get_all_nymd_mixnodes().await?; + let rewarded_set_identities = self + .get_all_nymd_rewarded_set_mixnode_identities() + .await? + .into_iter() + .map(|(identity, _status)| identity) + .collect::>(); + + Ok(all_mixnodes + .into_iter() + .filter(|node| rewarded_set_identities.contains(node.identity())) + .collect()) + } + + /// If you need both rewarded and the active set, consider using [Self::get_nymd_rewarded_and_active_sets] instead + pub async fn get_nymd_active_set(&self) -> Result, ValidatorClientError> + where + C: CosmWasmClient + Sync, + { + let all_mixnodes = self.get_all_nymd_mixnodes().await?; + let active_set_identities = self + .get_all_nymd_rewarded_set_mixnode_identities() + .await? + .into_iter() + .filter_map(|(identity, status)| { + if status.is_active() { + Some(identity) + } else { + None + } + }) + .collect::>(); + + Ok(all_mixnodes + .into_iter() + .filter(|node| active_set_identities.contains(node.identity())) + .collect()) + } + pub async fn get_all_nymd_mixnodes(&self) -> Result, ValidatorClientError> where C: CosmWasmClient + Sync, @@ -301,8 +455,8 @@ impl Client { pub async fn get_all_nymd_single_mixnode_delegations( &self, - identity: mixnet_contract_common::IdentityKey, - ) -> Result, ValidatorClientError> + identity: IdentityKey, + ) -> Result, ValidatorClientError> where C: CosmWasmClient + Sync, { diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs b/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs index 1d07729ce7..eb7ccc3f05 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs +++ b/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs @@ -13,6 +13,7 @@ use cosmrs::proto::cosmos::auth::v1beta1::{ }; use cosmrs::proto::cosmos::bank::v1beta1::{ QueryAllBalancesRequest, QueryAllBalancesResponse, QueryBalanceRequest, QueryBalanceResponse, + QueryTotalSupplyRequest, QueryTotalSupplyResponse, }; use cosmrs::proto::cosmos::tx::v1beta1::{ SimulateRequest, SimulateResponse as ProtoSimulateResponse, @@ -27,6 +28,7 @@ use cosmrs::tendermint::abci::Code as AbciCode; use cosmrs::tendermint::abci::Transaction; use cosmrs::tendermint::{abci, block, chain}; use cosmrs::{tx, AccountId, Coin, Denom, Tx}; +use cosmwasm_std::Coin as CosmWasmCoin; use prost::Message; use serde::{Deserialize, Serialize}; use std::convert::{TryFrom, TryInto}; @@ -162,6 +164,43 @@ pub trait CosmWasmClient: rpc::Client { .map_err(|_| NymdError::SerializationError("Coins".to_owned())) } + // this is annoyingly and inconsistently returning `Vec` rather than + // Vec, since cosmrs::Coin can't deal with IBC denoms. + // Presumably after https://github.com/cosmos/cosmos-rust/issues/173 is resolved, + // the code could be adjusted + async fn get_total_supply(&self) -> Result, NymdError> { + let path = Some("/cosmos.bank.v1beta1.Query/TotalSupply".parse().unwrap()); + + let mut supply = Vec::new(); + let mut pagination = None; + + loop { + let req = QueryTotalSupplyRequest { pagination }; + + let mut res = self + .make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req) + .await?; + + supply.append(&mut res.supply); + if let Some(pagination_info) = res.pagination { + pagination = Some(create_pagination(pagination_info.next_key)) + } else { + break; + } + } + + supply + .into_iter() + .map(|coin| { + coin.amount.parse().map(|amount| CosmWasmCoin { + denom: coin.denom, + amount, + }) + }) + .collect::>() + .map_err(|_| NymdError::SerializationError("Coins".to_owned())) + } + async fn get_tx(&self, id: tx::Hash) -> Result { Ok(self.tx(id, false).await?) } diff --git a/common/client-libs/validator-client/src/nymd/fee/helpers.rs b/common/client-libs/validator-client/src/nymd/fee/helpers.rs index 0ddbf15356..f069047120 100644 --- a/common/client-libs/validator-client/src/nymd/fee/helpers.rs +++ b/common/client-libs/validator-client/src/nymd/fee/helpers.rs @@ -41,6 +41,11 @@ pub enum Operation { WithdrawVestedCoins, TrackUndelegation, CreatePeriodicVestingAccount, + + AdvanceCurrentInterval, + WriteRewardedSet, + ClearRewardedSet, + UpdateMixnetAddress, } pub(crate) fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> Coin { @@ -78,6 +83,10 @@ impl fmt::Display for Operation { Operation::WithdrawVestedCoins => f.write_str("WithdrawVestedCoins"), Operation::TrackUndelegation => f.write_str("TrackUndelegation"), Operation::CreatePeriodicVestingAccount => f.write_str("CreatePeriodicVestingAccount"), + Operation::AdvanceCurrentInterval => f.write_str("AdvanceCurrentInterval"), + Operation::WriteRewardedSet => f.write_str("WriteRewardedSet"), + Operation::ClearRewardedSet => f.write_str("ClearRewardedSet"), + Operation::UpdateMixnetAddress => f.write_str("UpdateMixnetAddress"), } } } @@ -115,6 +124,10 @@ impl Operation { Operation::WithdrawVestedCoins => 175_000u64.into(), Operation::TrackUndelegation => 175_000u64.into(), Operation::CreatePeriodicVestingAccount => 175_000u64.into(), + Operation::AdvanceCurrentInterval => 175_000u64.into(), + Operation::WriteRewardedSet => 175_000u64.into(), + Operation::ClearRewardedSet => 175_000u64.into(), + Operation::UpdateMixnetAddress => 80_000u64.into(), } } diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nymd/mod.rs index dfaa1abbb8..dec4edcbc7 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nymd/mod.rs @@ -15,10 +15,10 @@ pub use fee::gas_price::GasPrice; use fee::helpers::Operation; use mixnet_contract_common::{ ContractStateParams, Delegation, ExecuteMsg, Gateway, GatewayBond, GatewayOwnershipResponse, - IdentityKey, LayerDistribution, MixNode, MixNodeBond, MixOwnershipResponse, + IdentityKey, Interval, LayerDistribution, MixNode, MixNodeBond, MixOwnershipResponse, MixnetContractVersion, MixnodeRewardingStatusResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedGatewayResponse, PagedMixDelegationsResponse, - PagedMixnodeResponse, QueryMsg, RewardingIntervalResponse, + PagedMixnodeResponse, PagedRewardedSetResponse, QueryMsg, RewardedSetUpdateDetails, }; use serde::Serialize; use std::convert::TryInto; @@ -27,9 +27,12 @@ pub use crate::nymd::cosmwasm_client::client::CosmWasmClient; pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient; pub use crate::nymd::fee::Fee; use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER; +pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse; pub use cosmrs::rpc::HttpClient as QueryNymdClient; +pub use cosmrs::rpc::Paging; pub use cosmrs::tendermint::block::Height; pub use cosmrs::tendermint::hash; +pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo; pub use cosmrs::tendermint::Time as TendermintTime; pub use cosmrs::tx::{self, Gas}; pub use cosmrs::Coin as CosmosCoin; @@ -57,16 +60,16 @@ pub struct NymdClient { impl NymdClient { pub fn connect( endpoint: U, - mixnet_contract_address: AccountId, - vesting_contract_address: AccountId, + mixnet_contract_address: Option, + vesting_contract_address: Option, ) -> Result, NymdError> where U: TryInto, { Ok(NymdClient { client: QueryNymdClient::new(endpoint)?, - mixnet_contract_address: Some(mixnet_contract_address), - vesting_contract_address: Some(vesting_contract_address), + mixnet_contract_address, + vesting_contract_address, client_address: None, custom_gas_limits: Default::default(), simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, @@ -103,6 +106,7 @@ impl NymdClient { } pub fn connect_with_mnemonic( + network: config::defaults::all::Network, endpoint: U, mixnet_contract_address: Option, vesting_contract_address: Option, @@ -112,7 +116,8 @@ impl NymdClient { where U: TryInto, { - let wallet = DirectSecp256k1HdWallet::from_mnemonic(mnemonic)?; + let prefix = network.bech32_prefix(); + let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic)?; let client_address = wallet .try_derive_accounts()? .into_iter() @@ -234,6 +239,17 @@ impl NymdClient { .map(|block| block.block_id.hash) } + pub async fn get_validators( + &self, + height: u64, + paging: Paging, + ) -> Result + where + C: CosmWasmClient + Sync, + { + Ok(self.client.validators(height as u32, paging).await?) + } + pub async fn get_balance( &self, address: &AccountId, @@ -245,14 +261,11 @@ impl NymdClient { self.client.get_balance(address, denom).await } - pub async fn get_mixnet_balance( - &self, - address: &AccountId, - ) -> Result, NymdError> + pub async fn get_total_supply(&self) -> Result, NymdError> where C: CosmWasmClient + Sync, { - self.get_balance(address, self.denom()?).await + self.client.get_total_supply().await } pub async fn get_contract_settings(&self) -> Result @@ -275,35 +288,65 @@ impl NymdClient { .await } - pub async fn get_current_rewarding_interval( - &self, - ) -> Result - where - C: CosmWasmClient + Sync, - { - let request = QueryMsg::CurrentRewardingInterval {}; - self.client - .query_contract_smart(self.mixnet_contract_address()?, &request) - .await - } - pub async fn get_rewarding_status( &self, mix_identity: mixnet_contract_common::IdentityKey, - rewarding_interval_nonce: u32, + interval_id: u32, ) -> Result where C: CosmWasmClient + Sync, { let request = QueryMsg::GetRewardingStatus { mix_identity, - rewarding_interval_nonce, + interval_id, }; self.client .query_contract_smart(self.mixnet_contract_address()?, &request) .await } + pub async fn query_current_rewarded_set_height(&self) -> Result + where + C: CosmWasmClient + Sync, + { + let request = QueryMsg::GetCurrentRewardedSetHeight {}; + self.client + .query_contract_smart(self.mixnet_contract_address()?, &request) + .await + } + + pub async fn query_current_rewarded_set_update_details( + &self, + ) -> Result + where + C: CosmWasmClient + Sync, + { + let request = QueryMsg::GetRewardedSetUpdateDetails {}; + self.client + .query_contract_smart(self.mixnet_contract_address()?, &request) + .await + } + + pub async fn get_rewarded_set_identities_paged( + &self, + start_after: Option, + page_limit: Option, + height: Option, + ) -> Result + where + C: CosmWasmClient + Sync, + { + let request = QueryMsg::GetRewardedSet { + height, + start_after, + limit: page_limit, + }; + + self.client + .query_contract_smart(self.mixnet_contract_address()?, &request) + .await + } + pub async fn get_layer_distribution(&self) -> Result where C: CosmWasmClient + Sync, @@ -314,6 +357,16 @@ impl NymdClient { .await } + pub async fn get_current_interval(&self) -> Result + where + C: CosmWasmClient + Sync, + { + let request = QueryMsg::GetCurrentInterval {}; + self.client + .query_contract_smart(self.mixnet_contract_address()?, &request) + .await + } + pub async fn get_reward_pool(&self) -> Result where C: CosmWasmClient + Sync, @@ -344,11 +397,21 @@ impl NymdClient { .await } - pub async fn get_epoch_reward_percent(&self) -> Result + pub async fn get_active_set_work_factor(&self) -> Result where C: CosmWasmClient + Sync, { - let request = QueryMsg::GetEpochRewardPercent {}; + let request = QueryMsg::GetActiveSetWorkFactor {}; + self.client + .query_contract_smart(self.mixnet_contract_address()?, &request) + .await + } + + pub async fn get_interval_reward_percent(&self) -> Result + where + C: CosmWasmClient + Sync, + { + let request = QueryMsg::GetIntervalRewardPercent {}; self.client .query_contract_smart(self.mixnet_contract_address()?, &request) .await @@ -1101,41 +1164,38 @@ impl NymdClient { .await } - pub async fn begin_mixnode_rewarding( - &self, - rewarding_interval_nonce: u32, - ) -> Result + pub async fn advance_current_interval(&self) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::BeginMixnodeRewarding); + let fee = self.operation_fee(Operation::AdvanceCurrentInterval); - let req = ExecuteMsg::BeginMixnodeRewarding { - rewarding_interval_nonce, - }; + let req = ExecuteMsg::AdvanceCurrentInterval {}; self.client .execute( self.address(), self.mixnet_contract_address()?, &req, fee, - "Beginning mixnode rewarding procedure", + "Advancing current interval", Vec::new(), ) .await } - pub async fn finish_mixnode_rewarding( + pub async fn write_rewarded_set( &self, - rewarding_interval_nonce: u32, + rewarded_set: Vec, + expected_active_set_size: u32, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::FinishMixnodeRewarding); + let fee = self.operation_fee(Operation::WriteRewardedSet); - let req = ExecuteMsg::FinishMixnodeRewarding { - rewarding_interval_nonce, + let req = ExecuteMsg::WriteRewardedSet { + rewarded_set, + expected_active_set_size, }; self.client .execute( @@ -1143,7 +1203,7 @@ impl NymdClient { self.mixnet_contract_address()?, &req, fee, - "Finishing mixnode rewarding procedure", + "Writing rewarded set", Vec::new(), ) .await diff --git a/common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs b/common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs index c2d91a25ce..b6bd534ccb 100644 --- a/common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs +++ b/common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs @@ -6,7 +6,8 @@ use crate::nymd::error::NymdError; use crate::nymd::NymdClient; use async_trait::async_trait; use cosmwasm_std::{Coin, Timestamp}; -use vesting_contract::messages::QueryMsg as VestingQueryMsg; +use vesting_contract::vesting::{Account, PledgeData}; +use vesting_contract_common::messages::QueryMsg as VestingQueryMsg; #[async_trait] pub trait VestingQueryClient { @@ -55,6 +56,10 @@ pub trait VestingQueryClient { vesting_account_address: &str, block_time: Option, ) -> Result; + + async fn get_account(&self, address: &str) -> Result; + async fn get_mixnode_pledge(&self, address: &str) -> Result, NymdError>; + async fn get_gateway_pledge(&self, address: &str) -> Result, NymdError>; } #[async_trait] @@ -173,4 +178,29 @@ impl VestingQueryClient for NymdClient { .query_contract_smart(self.vesting_contract_address()?, &request) .await } + + async fn get_account(&self, address: &str) -> Result { + let request = VestingQueryMsg::GetAccount { + address: address.to_string(), + }; + self.client + .query_contract_smart(self.vesting_contract_address()?, &request) + .await + } + async fn get_mixnode_pledge(&self, address: &str) -> Result, NymdError> { + let request = VestingQueryMsg::GetMixnode { + address: address.to_string(), + }; + self.client + .query_contract_smart(self.vesting_contract_address()?, &request) + .await + } + async fn get_gateway_pledge(&self, address: &str) -> Result, NymdError> { + let request = VestingQueryMsg::GetGateway { + address: address.to_string(), + }; + self.client + .query_contract_smart(self.vesting_contract_address()?, &request) + .await + } } diff --git a/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs b/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs index 8c862f0851..229e38752a 100644 --- a/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs +++ b/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs @@ -9,10 +9,12 @@ use crate::nymd::{cosmwasm_coin_to_cosmos_coin, NymdClient}; use async_trait::async_trait; use cosmwasm_std::Coin; use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode}; -use vesting_contract::messages::ExecuteMsg as VestingExecuteMsg; +use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, VestingSpecification}; #[async_trait] pub trait VestingSigningClient { + async fn update_mixnet_address(&self, address: &str) -> Result; + async fn vesting_bond_gateway( &self, gateway: Gateway, @@ -66,7 +68,7 @@ pub trait VestingSigningClient { &self, owner_address: &str, staking_address: Option, - start_time: Option, + vesting_spec: Option, amount: Coin, ) -> Result; } @@ -83,6 +85,7 @@ impl VestingSigningClient for NymdClient let req = VestingExecuteMsg::BondGateway { gateway, owner_signature: owner_signature.to_string(), + amount: pledge, }; self.client .execute( @@ -91,7 +94,7 @@ impl VestingSigningClient for NymdClient &req, fee, "VestingContract::BondGateway", - vec![cosmwasm_coin_to_cosmos_coin(pledge)], + vec![], ) .await } @@ -143,6 +146,7 @@ impl VestingSigningClient for NymdClient let req = VestingExecuteMsg::BondMixnode { mix_node, owner_signature: owner_signature.to_string(), + amount: pledge, }; self.client .execute( @@ -151,7 +155,7 @@ impl VestingSigningClient for NymdClient &req, fee, "VestingContract::BondMixnode", - vec![cosmwasm_coin_to_cosmos_coin(pledge)], + vec![], ) .await } @@ -239,6 +243,7 @@ impl VestingSigningClient for NymdClient let fee = self.operation_fee(Operation::DelegateToMixnode); let req = VestingExecuteMsg::DelegateToMixnode { mix_identity: mix_identity.into(), + amount: amount.clone(), }; self.client .execute( @@ -247,7 +252,7 @@ impl VestingSigningClient for NymdClient &req, fee, "VestingContract::DeledateToMixnode", - vec![cosmwasm_coin_to_cosmos_coin(amount.to_owned())], + vec![], ) .await } @@ -274,14 +279,14 @@ impl VestingSigningClient for NymdClient &self, owner_address: &str, staking_address: Option, - start_time: Option, + vesting_spec: Option, amount: Coin, ) -> Result { let fee = self.operation_fee(Operation::CreatePeriodicVestingAccount); let req = VestingExecuteMsg::CreateAccount { owner_address: owner_address.to_string(), staking_address, - start_time, + vesting_spec, }; self.client .execute( @@ -294,4 +299,21 @@ impl VestingSigningClient for NymdClient ) .await } + + async fn update_mixnet_address(&self, address: &str) -> Result { + let fee = self.operation_fee(Operation::UpdateMixnetAddress); + let req = VestingExecuteMsg::UpdateMixnetAddress { + address: address.to_string(), + }; + self.client + .execute( + self.address(), + self.vesting_contract_address()?, + &req, + fee, + "VestingContract::UpdateMixnetAddress", + vec![], + ) + .await + } } diff --git a/common/client-libs/validator-client/src/nymd/wallet/mod.rs b/common/client-libs/validator-client/src/nymd/wallet/mod.rs index 195ebc9239..1cadbde59c 100644 --- a/common/client-libs/validator-client/src/nymd/wallet/mod.rs +++ b/common/client-libs/validator-client/src/nymd/wallet/mod.rs @@ -62,13 +62,15 @@ impl DirectSecp256k1HdWallet { } /// Restores a wallet from the given BIP39 mnemonic using default options. - pub fn from_mnemonic(mnemonic: bip39::Mnemonic) -> Result { - DirectSecp256k1HdWalletBuilder::new().build(mnemonic) + pub fn from_mnemonic(prefix: String, mnemonic: bip39::Mnemonic) -> Result { + DirectSecp256k1HdWalletBuilder::new() + .with_prefix(prefix) + .build(mnemonic) } - pub fn generate(word_count: usize) -> Result { + pub fn generate(prefix: String, word_count: usize) -> Result { let mneomonic = bip39::Mnemonic::generate(word_count)?; - Self::from_mnemonic(mneomonic) + Self::from_mnemonic(prefix, mneomonic) } fn derive_keypair(&self, hd_path: &DerivationPath) -> Result { @@ -133,6 +135,7 @@ impl DirectSecp256k1HdWallet { } } +#[must_use] pub struct DirectSecp256k1HdWalletBuilder { /// The password to use when deriving a BIP39 seed from a mnemonic. bip39_password: String, @@ -202,7 +205,7 @@ impl DirectSecp256k1HdWalletBuilder { #[cfg(test)] mod tests { use super::*; - use network_defaults::BECH32_PREFIX; + use network_defaults::{default_network, BECH32_PREFIX}; #[test] fn generating_account_addresses() { @@ -227,7 +230,9 @@ mod tests { ]; for (mnemonic, address) in mnemonic_address.into_iter() { - let wallet = DirectSecp256k1HdWallet::from_mnemonic(mnemonic.parse().unwrap()).unwrap(); + let prefix = default_network().bech32_prefix(); + let wallet = + DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic.parse().unwrap()).unwrap(); assert_eq!( wallet.try_derive_accounts().unwrap()[0].address, address.parse().unwrap() diff --git a/common/client-libs/validator-client/src/validator_api/mod.rs b/common/client-libs/validator-client/src/validator_api/mod.rs index 528f3b9939..ba737a3dfc 100644 --- a/common/client-libs/validator-client/src/validator_api/mod.rs +++ b/common/client-libs/validator-client/src/validator_api/mod.rs @@ -9,8 +9,8 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use url::Url; use validator_api_requests::models::{ - CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, - StakeSaturationResponse, + CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse, + RewardEstimationResponse, StakeSaturationResponse, }; pub mod error; @@ -112,7 +112,7 @@ impl Client { routes::MIXNODES, routes::REWARDED, routes::INCLUSION_CHANCE, - &mixnode_id.to_string(), + mixnode_id, ], NO_PARAMS, ) @@ -232,6 +232,23 @@ impl Client { .await } + pub async fn get_mixnode_inclusion_probability( + &self, + identity: IdentityKeyRef<'_>, + ) -> Result { + self.query_validator_api( + &[ + routes::API_VERSION, + routes::STATUS_ROUTES, + routes::MIXNODE, + identity, + routes::INCLUSION_CHANCE, + ], + NO_PARAMS, + ) + .await + } + pub async fn blind_sign( &self, request_body: &BlindSignRequestBody, diff --git a/common/coconut-interface/Cargo.toml b/common/coconut-interface/Cargo.toml index 9061280c79..d2c891845d 100644 --- a/common/coconut-interface/Cargo.toml +++ b/common/coconut-interface/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "coconut-interface" version = "0.1.0" -edition = "2018" +edition = "2021" description = "Crutch library until there is proper SerDe support for coconut structs" [dependencies] diff --git a/common/config/Cargo.toml b/common/config/Cargo.toml index 7a18a25cd8..2455b32f8a 100644 --- a/common/config/Cargo.toml +++ b/common/config/Cargo.toml @@ -2,7 +2,7 @@ name = "config" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml b/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml index ba0edd57b1..248036d804 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml @@ -2,7 +2,7 @@ name = "mixnet-contract-common" version = "0.1.0" authors = ["Jędrzej Stuczyński "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -18,8 +18,13 @@ network-defaults = { path = "../../network-defaults" } fixed = { version = "1.1", features = ["serde"] } az = "1.1" log = "0.4.14" +time = { version = "0.3.6", features = ["parsing", "formatting"] } contracts-common = { path = "../contracts-common" } +[dev-dependencies] +time = { version = "0.3.5", features = ["serde", "macros"] } + + [features] default = [] diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs index a9aa1ea759..cfd618c902 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs @@ -66,7 +66,7 @@ impl Delegation { } impl Display for Delegation { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{} delegated towards {} by {} at block {}", diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs index d464e96acf..f6034e801c 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::mixnode::NodeRewardResult; -use crate::{ContractStateParams, Delegation, IdentityKeyRef, Layer}; +use crate::{ContractStateParams, Delegation, IdentityKeyRef, Interval, Layer}; use cosmwasm_std::{Addr, Coin, Event, Uint128}; pub use contracts_common::events::*; @@ -15,10 +15,10 @@ pub const GATEWAY_UNBONDING_EVENT_TYPE: &str = "gateway_unbonding"; pub const MIXNODE_BONDING_EVENT_TYPE: &str = "mixnode_bonding"; pub const MIXNODE_UNBONDING_EVENT_TYPE: &str = "mixnode_unbonding"; pub const SETTINGS_UPDATE_EVENT_TYPE: &str = "settings_update"; -pub const BEGIN_REWARDING_EVENT_TYPE: &str = "begin_rewarding"; pub const OPERATOR_REWARDING_EVENT_TYPE: &str = "mix_rewarding"; pub const MIX_DELEGATORS_REWARDING_EVENT_TYPE: &str = "mix_delegators_rewarding"; -pub const FINISH_REWARDING_EVENT_TYPE: &str = "finish_rewarding"; +pub const CHANGE_REWARDED_SET_EVENT_TYPE: &str = "change_rewarded_set"; +pub const ADVANCE_INTERVAL_EVENT_TYPE: &str = "advance_interval"; // attributes that are used in multiple places pub const OWNER_KEY: &str = "owner"; @@ -47,10 +47,9 @@ pub const NEW_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "new_minimum_mixnode_pledge"; pub const NEW_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "new_minimum_gateway_pledge"; pub const NEW_MIXNODE_REWARDED_SET_SIZE_KEY: &str = "new_mixnode_rewarded_set_size"; pub const NEW_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "new_mixnode_active_set_size"; -pub const NEW_ACTIVE_SET_WORK_FACTOR_KEY: &str = "new_active_set_work_factor"; // rewarding -pub const REWARDING_INTERVAL_NONCE_KEY: &str = "rewarding_interval_nonce"; +pub const INTERVAL_ID_KEY: &str = "interval_id"; pub const TOTAL_MIXNODE_REWARD_KEY: &str = "total_node_reward"; pub const OPERATOR_REWARD_KEY: &str = "operator_reward"; pub const LAMBDA_KEY: &str = "lambda"; @@ -62,11 +61,19 @@ pub const BOND_NOT_FOUND_VALUE: &str = "bond_not_found"; pub const BOND_TOO_FRESH_VALUE: &str = "bond_too_fresh"; pub const ZERO_UPTIME_VALUE: &str = "zero_uptime"; +// rewarded set update +pub const ACTIVE_SET_SIZE_KEY: &str = "active_set_size"; +pub const REWARDED_SET_SIZE_KEY: &str = "rewarded_set_size"; +pub const NODES_IN_REWARDED_SET_KEY: &str = "nodes_in_rewarded_set"; +pub const CURRENT_INTERVAL_ID_KEY: &str = "current_interval"; + +pub const NEW_CURRENT_INTERVAL_KEY: &str = "new_current_interval"; + pub fn new_delegation_event( delegator: &Addr, proxy: &Option, amount: &Coin, - mix_identity: IdentityKeyRef, + mix_identity: IdentityKeyRef<'_>, ) -> Event { let mut event = Event::new(DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator); @@ -84,7 +91,7 @@ pub fn new_undelegation_event( delegator: &Addr, proxy: &Option, old_delegation: &Delegation, - mix_identity: IdentityKeyRef, + mix_identity: IdentityKeyRef<'_>, ) -> Event { let mut event = Event::new(UNDELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator); @@ -106,7 +113,7 @@ pub fn new_gateway_bonding_event( owner: &Addr, proxy: &Option, amount: &Coin, - identity: IdentityKeyRef, + identity: IdentityKeyRef<'_>, ) -> Event { let mut event = Event::new(GATEWAY_BONDING_EVENT_TYPE) .add_attribute(OWNER_KEY, owner) @@ -124,7 +131,7 @@ pub fn new_gateway_unbonding_event( owner: &Addr, proxy: &Option, amount: &Coin, - identity: IdentityKeyRef, + identity: IdentityKeyRef<'_>, ) -> Event { let mut event = Event::new(GATEWAY_UNBONDING_EVENT_TYPE) .add_attribute(OWNER_KEY, owner) @@ -142,7 +149,7 @@ pub fn new_mixnode_bonding_event( owner: &Addr, proxy: &Option, amount: &Coin, - identity: IdentityKeyRef, + identity: IdentityKeyRef<'_>, assigned_layer: Layer, ) -> Event { let mut event = Event::new(MIXNODE_BONDING_EVENT_TYPE) @@ -163,7 +170,7 @@ pub fn new_mixnode_unbonding_event( owner: &Addr, proxy: &Option, amount: &Coin, - identity: IdentityKeyRef, + identity: IdentityKeyRef<'_>, ) -> Event { let mut event = Event::new(MIXNODE_UNBONDING_EVENT_TYPE) .add_attribute(OWNER_KEY, owner) @@ -231,87 +238,49 @@ pub fn new_settings_update_event( ) } - if old_params.active_set_work_factor != new_params.active_set_work_factor { - event = event - .add_attribute( - OLD_ACTIVE_SET_WORK_FACTOR_KEY, - old_params.active_set_work_factor.to_string(), - ) - .add_attribute( - NEW_ACTIVE_SET_WORK_FACTOR_KEY, - new_params.active_set_work_factor.to_string(), - ) - } - event } -pub fn new_begin_rewarding_event(rewarding_interval_nonce: u32) -> Event { - Event::new(BEGIN_REWARDING_EVENT_TYPE).add_attribute( - REWARDING_INTERVAL_NONCE_KEY, - rewarding_interval_nonce.to_string(), - ) -} - -pub fn new_finish_rewarding_event(rewarding_interval_nonce: u32) -> Event { - Event::new(FINISH_REWARDING_EVENT_TYPE).add_attribute( - REWARDING_INTERVAL_NONCE_KEY, - rewarding_interval_nonce.to_string(), - ) -} - pub fn new_not_found_mix_operator_rewarding_event( - rewarding_interval_nonce: u32, - identity: IdentityKeyRef, + interval_id: u32, + identity: IdentityKeyRef<'_>, ) -> Event { Event::new(OPERATOR_REWARDING_EVENT_TYPE) - .add_attribute( - REWARDING_INTERVAL_NONCE_KEY, - rewarding_interval_nonce.to_string(), - ) + .add_attribute(INTERVAL_ID_KEY, interval_id.to_string()) .add_attribute(NODE_IDENTITY_KEY, identity) .add_attribute(NO_REWARD_REASON_KEY, BOND_NOT_FOUND_VALUE) } pub fn new_too_fresh_bond_mix_operator_rewarding_event( - rewarding_interval_nonce: u32, - identity: IdentityKeyRef, + interval_id: u32, + identity: IdentityKeyRef<'_>, ) -> Event { Event::new(OPERATOR_REWARDING_EVENT_TYPE) - .add_attribute( - REWARDING_INTERVAL_NONCE_KEY, - rewarding_interval_nonce.to_string(), - ) + .add_attribute(INTERVAL_ID_KEY, interval_id.to_string()) .add_attribute(NODE_IDENTITY_KEY, identity) .add_attribute(NO_REWARD_REASON_KEY, BOND_TOO_FRESH_VALUE) } pub fn new_zero_uptime_mix_operator_rewarding_event( - rewarding_interval_nonce: u32, - identity: IdentityKeyRef, + interval_id: u32, + identity: IdentityKeyRef<'_>, ) -> Event { Event::new(OPERATOR_REWARDING_EVENT_TYPE) - .add_attribute( - REWARDING_INTERVAL_NONCE_KEY, - rewarding_interval_nonce.to_string(), - ) + .add_attribute(INTERVAL_ID_KEY, interval_id.to_string()) .add_attribute(NODE_IDENTITY_KEY, identity) .add_attribute(NO_REWARD_REASON_KEY, ZERO_UPTIME_VALUE) } pub fn new_mix_operator_rewarding_event( - rewarding_interval_nonce: u32, - identity: IdentityKeyRef, + interval_id: u32, + identity: IdentityKeyRef<'_>, node_reward_result: NodeRewardResult, operator_reward: Uint128, delegation_rewards_distributed: Uint128, further_delegations: bool, ) -> Event { Event::new(OPERATOR_REWARDING_EVENT_TYPE) - .add_attribute( - REWARDING_INTERVAL_NONCE_KEY, - rewarding_interval_nonce.to_string(), - ) + .add_attribute(INTERVAL_ID_KEY, interval_id.to_string()) .add_attribute(NODE_IDENTITY_KEY, identity) .add_attribute( TOTAL_MIXNODE_REWARD_KEY, @@ -331,16 +300,13 @@ pub fn new_mix_operator_rewarding_event( } pub fn new_mix_delegators_rewarding_event( - rewarding_interval_nonce: u32, - identity: IdentityKeyRef, + interval_id: u32, + identity: IdentityKeyRef<'_>, delegation_rewards_distributed: Uint128, further_delegations: bool, ) -> Event { Event::new(MIX_DELEGATORS_REWARDING_EVENT_TYPE) - .add_attribute( - REWARDING_INTERVAL_NONCE_KEY, - rewarding_interval_nonce.to_string(), - ) + .add_attribute(INTERVAL_ID_KEY, interval_id.to_string()) .add_attribute(NODE_IDENTITY_KEY, identity) .add_attribute( DISTRIBUTED_DELEGATION_REWARDS_KEY, @@ -351,3 +317,22 @@ pub fn new_mix_delegators_rewarding_event( further_delegations.to_string(), ) } + +// note that when this event is emitted, we'll know the current block height +pub fn new_change_rewarded_set_event( + active_set_size: u32, + rewarded_set_size: u32, + nodes_in_rewarded_set: u32, + current_interval_id: u32, +) -> Event { + Event::new(CHANGE_REWARDED_SET_EVENT_TYPE) + .add_attribute(ACTIVE_SET_SIZE_KEY, active_set_size.to_string()) + .add_attribute(REWARDED_SET_SIZE_KEY, rewarded_set_size.to_string()) + .add_attribute(NODES_IN_REWARDED_SET_KEY, nodes_in_rewarded_set.to_string()) + .add_attribute(CURRENT_INTERVAL_ID_KEY, current_interval_id.to_string()) +} + +pub fn new_advance_interval_event(interval: Interval) -> Event { + Event::new(ADVANCE_INTERVAL_EVENT_TYPE) + .add_attribute(NEW_CURRENT_INTERVAL_KEY, interval.to_string()) +} diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs index 217b480fa7..ff7821b150 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs @@ -98,7 +98,7 @@ impl PartialOrd for GatewayBond { } impl Display for GatewayBond { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "amount: {} {}, owner: {}, identity: {}", diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs new file mode 100644 index 0000000000..ae2e75e7ff --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs @@ -0,0 +1,365 @@ +// Copyright 2021 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; +use std::convert::TryInto; +use std::fmt::{Display, Formatter}; +use std::time::Duration; +use time::OffsetDateTime; + +// internally, since version 0.3.6, time uses deserialize_any for deserialization, which can't be handled +// by serde wasm. We could just downgrade to 0.3.5 and call it a day, but then it would break +// when we decided to upgrade it at some point in the future. And then it would have been more problematic +// to fix it, since the data would have already been stored inside the contract. +// Hence, an explicit workaround to use string representation of Rfc3339-formatted datetime. +pub(crate) mod string_rfc3339_offset_date_time { + use serde::de::Visitor; + use serde::ser::Error; + use serde::{Deserializer, Serialize, Serializer}; + use std::fmt::Formatter; + use time::format_description::well_known::Rfc3339; + use time::OffsetDateTime; + + struct Rfc3339OffsetDateTimeVisitor; + + impl<'de> Visitor<'de> for Rfc3339OffsetDateTimeVisitor { + type Value = OffsetDateTime; + + fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { + formatter.write_str("an rfc3339 `OffsetDateTime`") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + OffsetDateTime::parse(value, &Rfc3339).map_err(E::custom) + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Rfc3339OffsetDateTimeVisitor) + } + + pub(crate) fn serialize(datetime: &OffsetDateTime, serializer: S) -> Result + where + S: Serializer, + { + datetime + .format(&Rfc3339) + .map_err(S::Error::custom)? + .serialize(serializer) + } +} + +/// Representation of rewarding interval. +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +pub struct Interval { + id: u32, + #[serde(with = "string_rfc3339_offset_date_time")] + start: OffsetDateTime, + length: Duration, +} + +impl Interval { + /// Creates new interval instance. + pub const fn new(id: u32, start: OffsetDateTime, length: Duration) -> Self { + Interval { id, start, length } + } + + /// Returns the next interval. + #[must_use] + pub fn next_interval(&self) -> Self { + Interval { + id: self.id + 1, + start: self.end(), + length: self.length, + } + } + + /// Returns the last interval. + pub fn previous_interval(&self) -> Option { + if self.id > 0 { + Some(Interval { + id: self.id - 1, + start: self.start - self.length, + length: self.length, + }) + } else { + None + } + } + + /// Determines whether the provided datetime is contained within the interval + /// + /// # Arguments + /// + /// * `datetime`: specified datetime + pub fn contains(&self, datetime: OffsetDateTime) -> bool { + self.start <= datetime && datetime <= self.end() + } + + /// Determines whether the provided unix timestamp is contained within the interval + /// + /// # Arguments + /// + /// * `timestamp`: specified timestamp + pub fn contains_timestamp(&self, timestamp: i64) -> bool { + self.start_unix_timestamp() <= timestamp && timestamp <= self.end_unix_timestamp() + } + + /// Returns new instance of [Interval] such that the provided datetime would be within + /// its duration. + /// + /// # Arguments + /// + /// * `now`: current datetime + pub fn current(&self, now: OffsetDateTime) -> Option { + let mut candidate = *self; + + if now > self.start { + loop { + if candidate.contains(now) { + return Some(candidate); + } + candidate = candidate.next_interval(); + } + } else { + loop { + if candidate.contains(now) { + return Some(candidate); + } + candidate = candidate.previous_interval()?; + } + } + } + + /// Returns new instance of [Interval] such that the provided unix timestamp would be within + /// its duration. + /// + /// # Arguments + /// + /// * `now_unix`: current unix time + pub fn current_with_timestamp(&self, now_unix: i64) -> Option { + let mut candidate = *self; + + if now_unix > self.start_unix_timestamp() { + loop { + if candidate.contains_timestamp(now_unix) { + return Some(candidate); + } + candidate = candidate.next_interval(); + } + } else { + loop { + if candidate.contains_timestamp(now_unix) { + return Some(candidate); + } + candidate = candidate.previous_interval()?; + } + } + } + + /// Checks whether this interval has already finished + /// + /// # Arguments + /// + /// * `now`: current datetime + pub fn has_elapsed(&self, now: OffsetDateTime) -> bool { + self.end() < now + } + + /// Returns id of this interval + pub const fn id(&self) -> u32 { + self.id + } + + /// Determines amount of time left until this interval finishes. + /// + /// # Arguments + /// + /// * `now`: current datetime + pub fn until_end(&self, now: OffsetDateTime) -> Option { + let remaining = self.end() - now; + if remaining.is_negative() { + None + } else { + remaining.try_into().ok() + } + } + + /// Returns the starting datetime of this interval. + pub const fn start(&self) -> OffsetDateTime { + self.start + } + + /// Returns the length of this interval. + pub const fn length(&self) -> Duration { + self.length + } + + /// Returns the ending datetime of this interval. + pub fn end(&self) -> OffsetDateTime { + self.start + self.length + } + + /// Returns the unix timestamp of the start of this interval. + pub const fn start_unix_timestamp(&self) -> i64 { + self.start().unix_timestamp() + } + + /// Returns the unix timestamp of the end of this interval. + pub fn end_unix_timestamp(&self) -> i64 { + self.end().unix_timestamp() + } +} + +impl Display for Interval { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let length = self.length().as_secs(); + let full_hours = length / 3600; + let rem = length % 3600; + write!( + f, + "Interval {}: {} - {} ({}h {}s)", + self.id, + self.start(), + self.end(), + full_hours, + rem + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn previous_interval() { + let interval = Interval { + id: 1, + start: time::macros::datetime!(2021-08-23 12:00 UTC), + length: Duration::from_secs(24 * 60 * 60), + }; + let expected = Interval { + id: 0, + start: time::macros::datetime!(2021-08-22 12:00 UTC), + length: Duration::from_secs(24 * 60 * 60), + }; + assert_eq!(expected, interval.previous_interval().unwrap()); + + let genesis_interval = Interval { + id: 0, + start: time::macros::datetime!(2021-08-23 12:00 UTC), + length: Duration::from_secs(24 * 60 * 60), + }; + assert!(genesis_interval.previous_interval().is_none()); + } + + #[test] + fn next_interval() { + let interval = Interval { + id: 0, + start: time::macros::datetime!(2021-08-23 12:00 UTC), + length: Duration::from_secs(24 * 60 * 60), + }; + let expected = Interval { + id: 1, + start: time::macros::datetime!(2021-08-24 12:00 UTC), + length: Duration::from_secs(24 * 60 * 60), + }; + + assert_eq!(expected, interval.next_interval()) + } + + #[test] + fn checking_for_datetime_inclusion() { + let interval = Interval { + id: 100, + start: time::macros::datetime!(2021-08-23 12:00 UTC), + length: Duration::from_secs(24 * 60 * 60), + }; + + // it must contain its own boundaries + assert!(interval.contains(interval.start)); + assert!(interval.contains(interval.end())); + + let in_the_midle = interval.start + Duration::from_secs(interval.length.as_secs() / 2); + assert!(interval.contains(in_the_midle)); + + assert!(!interval.contains(interval.next_interval().end())); + assert!(!interval.contains(interval.previous_interval().unwrap().start())); + } + + #[test] + fn determining_current_interval() { + let first_interval = Interval { + id: 100, + start: time::macros::datetime!(2021-08-23 12:00 UTC), + length: Duration::from_secs(24 * 60 * 60), + }; + + // interval just before + let fake_now = first_interval.start - Duration::from_secs(123); + assert_eq!( + first_interval.previous_interval(), + first_interval.current(fake_now) + ); + + // this interval (start boundary) + assert_eq!( + first_interval, + first_interval.current(first_interval.start).unwrap() + ); + + // this interval (in the middle) + let fake_now = first_interval.start + Duration::from_secs(123); + assert_eq!(first_interval, first_interval.current(fake_now).unwrap()); + + // this interval (end boundary) + assert_eq!( + first_interval, + first_interval.current(first_interval.end()).unwrap() + ); + + // next interval + let fake_now = first_interval.end() + Duration::from_secs(123); + assert_eq!( + first_interval.next_interval(), + first_interval.current(fake_now).unwrap() + ); + + // few intervals in the past + let fake_now = first_interval.start() + - first_interval.length + - first_interval.length + - first_interval.length; + assert_eq!( + first_interval + .previous_interval() + .unwrap() + .previous_interval() + .unwrap() + .previous_interval() + .unwrap(), + first_interval.current(fake_now).unwrap() + ); + + // few intervals in the future + let fake_now = first_interval.end() + + first_interval.length + + first_interval.length + + first_interval.length; + assert_eq!( + first_interval + .next_interval() + .next_interval() + .next_interval(), + first_interval.current(fake_now).unwrap() + ); + } +} diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs index 6f1854cc12..8bb4f35f32 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs @@ -5,6 +5,7 @@ mod delegation; pub mod error; pub mod events; mod gateway; +mod interval; pub mod mixnode; mod msg; mod types; @@ -17,6 +18,9 @@ pub use delegation::{ PagedMixDelegationsResponse, }; pub use gateway::{Gateway, GatewayBond, GatewayOwnershipResponse, PagedGatewayResponse}; -pub use mixnode::{Layer, MixNode, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse}; +pub use interval::Interval; +pub use mixnode::{ + Layer, MixNode, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse, RewardedSetNodeStatus, +}; pub use msg::*; pub use types::*; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs index ff77ec628a..38a2ff9297 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs @@ -5,7 +5,7 @@ use crate::{IdentityKey, SphinxKey}; use az::CheckedCast; use cosmwasm_std::{coin, Addr, Coin, Uint128}; use log::error; -use network_defaults::DEFAULT_OPERATOR_EPOCH_COST; +use network_defaults::DEFAULT_OPERATOR_INTERVAL_COST; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -18,6 +18,19 @@ fixed::const_fixed_from_int! { const ONE: U128 = 1; } +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)] +pub enum RewardedSetNodeStatus { + Active, + Standby, +} + +impl RewardedSetNodeStatus { + pub fn is_active(&self) -> bool { + matches!(self, RewardedSetNodeStatus::Active) + } +} + #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)] pub struct MixNode { @@ -133,7 +146,7 @@ impl NodeRewardParams { } pub fn operator_cost(&self) -> U128 { - U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_EPOCH_COST as u128) + U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128) } pub fn set_reward_blockstamp(&mut self, blockstamp: u64) { @@ -325,7 +338,7 @@ impl MixNodeBond { &self.mix_node } - pub fn total_stake(&self) -> Option { + pub fn total_bond(&self) -> Option { if self.pledge_amount.denom != self.total_delegation.denom { None } else { @@ -495,7 +508,7 @@ impl PartialOrd for MixNodeBond { } impl Display for MixNodeBond { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "amount: {} {}, owner: {}, identity: {}", diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs index 8f74824ec0..500147f329 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs @@ -38,28 +38,18 @@ pub enum ExecuteMsg { mix_identity: IdentityKey, }, - BeginMixnodeRewarding { - // nonce of the current rewarding interval - rewarding_interval_nonce: u32, - }, - - FinishMixnodeRewarding { - // nonce of the current rewarding interval - rewarding_interval_nonce: u32, - }, - RewardMixnode { identity: IdentityKey, // percentage value in range 0-100 params: NodeRewardParams, - // nonce of the current rewarding interval - rewarding_interval_nonce: u32, + // id of the current rewarding interval + interval_id: u32, }, RewardNextMixDelegators { mix_identity: IdentityKey, - // nonce of the current rewarding interval - rewarding_interval_nonce: u32, + // id of the current rewarding interval + interval_id: u32, }, DelegateToMixnodeOnBehalf { mix_identity: IdentityKey, @@ -85,6 +75,11 @@ pub enum ExecuteMsg { UnbondGatewayOnBehalf { owner: String, }, + WriteRewardedSet { + rewarded_set: Vec, + expected_active_set_size: u32, + }, + AdvanceCurrentInterval {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -106,7 +101,6 @@ pub enum QueryMsg { address: String, }, StateParams {}, - CurrentRewardingInterval {}, // gets all [paged] delegations in the entire network // TODO: do we even want that? GetAllNetworkDelegations { @@ -137,12 +131,25 @@ pub enum QueryMsg { LayerDistribution {}, GetRewardPool {}, GetCirculatingSupply {}, - GetEpochRewardPercent {}, + GetIntervalRewardPercent {}, GetSybilResistancePercent {}, + GetActiveSetWorkFactor {}, GetRewardingStatus { mix_identity: IdentityKey, - rewarding_interval_nonce: u32, + interval_id: u32, }, + GetRewardedSet { + height: Option, + start_after: Option, + limit: Option, + }, + GetRewardedSetHeightsForInterval { + interval_id: u32, + }, + GetRewardedSetUpdateDetails {}, + GetCurrentRewardedSetHeight {}, + GetCurrentInterval {}, + GetRewardedSetRefreshBlocks {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs index 81db65650b..f95a6bfb05 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::mixnode::DelegatorRewardParams; -use crate::Layer; +use crate::{Layer, RewardedSetNodeStatus}; use cosmwasm_std::{Addr, Uint128}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -27,19 +27,12 @@ impl LayerDistribution { } } -#[derive(Debug, Default, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)] -pub struct RewardingIntervalResponse { - pub current_rewarding_interval_starting_block: u64, - pub current_rewarding_interval_nonce: u32, - pub rewarding_in_progress: bool, -} - #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct ContractStateParams { - // so currently epoch_length is being unused and validator API performs rewarding - // based on its own epoch length config value. I guess that's fine for time being + // so currently interval_length is being unused and validator API performs rewarding + // based on its own interval length config value. I guess that's fine for time being // however, in the future, the contract constant should be controlling it instead. - // pub epoch_length: u32, // length of a rewarding epoch/interval, expressed in hours + // pub interval_length: u32, // length of a rewarding interval/interval, expressed in hours pub minimum_mixnode_pledge: Uint128, // minimum amount a mixnode must pledge to get into the system pub minimum_gateway_pledge: Uint128, // minimum amount a gateway must pledge to get into the system @@ -50,7 +43,6 @@ pub struct ContractStateParams { // subset of rewarded mixnodes that are actively receiving mix traffic // used to handle shorter-term (e.g. hourly) fluctuations of demand pub mixnode_active_set_size: u32, - pub active_set_work_factor: u8, } impl Display for ContractStateParams { @@ -75,11 +67,6 @@ impl Display for ContractStateParams { f, "mixnode active set size: {}", self.mixnode_active_set_size - )?; - write!( - f, - "mixnode active set work factor: {}", - self.active_set_work_factor ) } } @@ -136,3 +123,23 @@ pub struct MixnetContractVersion { pub type IdentityKey = String; pub type IdentityKeyRef<'a> = &'a str; pub type SphinxKey = String; + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +pub struct PagedRewardedSetResponse { + pub identities: Vec<(IdentityKey, RewardedSetNodeStatus)>, + pub start_next_after: Option, + pub at_height: u64, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +pub struct RewardedSetUpdateDetails { + pub refresh_rate_blocks: u64, + pub last_refreshed_block: u64, + pub current_height: u64, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +pub struct IntervalRewardedSetHeightsResponse { + pub interval_id: u32, + pub heights: Vec, +} diff --git a/common/cosmwasm-smart-contracts/vesting-contract/.DS_Store b/common/cosmwasm-smart-contracts/vesting-contract/.DS_Store new file mode 100644 index 0000000000..5172429f26 Binary files /dev/null and b/common/cosmwasm-smart-contracts/vesting-contract/.DS_Store differ diff --git a/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml b/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml index 278244b8f0..c5829ec703 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml @@ -7,3 +7,8 @@ edition = "2021" [dependencies] cosmwasm-std = "1.0.0-beta3" +mixnet-contract-common = { path = "../mixnet-contract" } +serde = { version = "1.0", features = ["derive"] } +schemars = "0.8" +cw-storage-plus = "0.11.1" +config = {path = "../../config"} diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs index 002130e5fd..1230a985b1 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs @@ -1,4 +1,11 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use config::defaults::DENOM; +use cosmwasm_std::Coin; pub mod events; +pub mod messages; + +pub fn one_unym() -> Coin { + Coin::new(1, DENOM) +} diff --git a/contracts/vesting/src/messages.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs similarity index 65% rename from contracts/vesting/src/messages.rs rename to common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs index f32661818a..dbd88042f5 100644 --- a/contracts/vesting/src/messages.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs @@ -5,13 +5,56 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] -pub struct InitMsg {} +pub struct InitMsg { + pub mixnet_contract_address: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct MigrateMsg {} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)] +pub struct VestingSpecification { + start_time: Option, + period_seconds: Option, + num_periods: Option, +} + +impl VestingSpecification { + pub fn new( + start_time: Option, + period_seconds: Option, + num_periods: Option, + ) -> Self { + Self { + start_time, + period_seconds, + num_periods, + } + } + + pub fn start_time(&self) -> Option { + self.start_time + } + + pub fn period_seconds(&self) -> u64 { + self.period_seconds.unwrap_or(3 * 30 * 86400) + } + + pub fn num_periods(&self) -> u64 { + self.num_periods.unwrap_or(8) + } +} #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { + UpdateMixnetAddress { + address: String, + }, DelegateToMixnode { mix_identity: IdentityKey, + amount: Coin, }, UndelegateFromMixnode { mix_identity: IdentityKey, @@ -19,7 +62,7 @@ pub enum ExecuteMsg { CreateAccount { owner_address: String, staking_address: Option, - start_time: Option, + vesting_spec: Option, }, WithdrawVestedCoins { amount: Coin, @@ -32,6 +75,7 @@ pub enum ExecuteMsg { BondMixnode { mix_node: MixNode, owner_signature: String, + amount: Coin, }, UnbondMixnode {}, TrackUnbondMixnode { @@ -41,6 +85,7 @@ pub enum ExecuteMsg { BondGateway { gateway: Gateway, owner_signature: String, + amount: Coin, }, UnbondGateway {}, TrackUnbondGateway { @@ -91,4 +136,13 @@ pub enum QueryMsg { block_time: Option, vesting_account_address: String, }, + GetAccount { + address: String, + }, + GetMixnode { + address: String, + }, + GetGateway { + address: String, + }, } diff --git a/common/credentials/Cargo.toml b/common/credentials/Cargo.toml index 8536669e00..bbfe5a1c4e 100644 --- a/common/credentials/Cargo.toml +++ b/common/credentials/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "credentials" version = "0.1.0" -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/credentials/src/coconut/bandwidth.rs b/common/credentials/src/coconut/bandwidth.rs index ff9ec442a0..cb72374b6c 100644 --- a/common/credentials/src/coconut/bandwidth.rs +++ b/common/credentials/src/coconut/bandwidth.rs @@ -27,7 +27,7 @@ pub struct BandwidthVoucherAttributes { pub binding_number: PrivateAttribute, // the value (e.g., bandwidth) encoded in this voucher pub voucher_value: PublicAttribute, - // a field with public information, e.g., type of voucher, epoch etc. + // a field with public information, e.g., type of voucher, interval etc. pub voucher_info: PublicAttribute, } diff --git a/common/credentials/src/coconut/utils.rs b/common/credentials/src/coconut/utils.rs index e65ddf8d38..c1f9897baf 100644 --- a/common/credentials/src/coconut/utils.rs +++ b/common/credentials/src/coconut/utils.rs @@ -28,7 +28,7 @@ use crate::error::Error; /// use credentials::obtain_aggregate_verification_key; /// /// async fn example() -> Result<(), ParseError> { -/// let validators = vec!["https://testnet-milhon-validator1.nymtech.net/api".parse()?, "https://testnet-milhon-validator2.nymtech.net/api".parse()?]; +/// let validators = vec!["https://sandbox-validator1.nymtech.net/api".parse()?, "https://sandbox-validator2.nymtech.net/api".parse()?]; /// let aggregated_key = obtain_aggregate_verification_key(&validators).await; /// // deal with the obtained Result /// Ok(()) diff --git a/common/crypto/Cargo.toml b/common/crypto/Cargo.toml index f55a364a68..a638b089dc 100644 --- a/common/crypto/Cargo.toml +++ b/common/crypto/Cargo.toml @@ -2,7 +2,7 @@ name = "crypto" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/mixnode-common/src/verloc/mod.rs b/common/mixnode-common/src/verloc/mod.rs index 52060cb804..623bdc9e18 100644 --- a/common/mixnode-common/src/verloc/mod.rs +++ b/common/mixnode-common/src/verloc/mod.rs @@ -77,6 +77,7 @@ impl Config { } } +#[must_use] pub struct ConfigBuilder(Config); impl ConfigBuilder { diff --git a/common/network-defaults/Cargo.toml b/common/network-defaults/Cargo.toml index dfd640b08a..ec1ee84178 100644 --- a/common/network-defaults/Cargo.toml +++ b/common/network-defaults/Cargo.toml @@ -2,7 +2,7 @@ name = "network-defaults" version = "0.1.0" authors = ["Jędrzej Stuczyński "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,4 +11,3 @@ cfg-if = "1.0.0" hex-literal = "0.3.3" serde = {version = "1.0", features = ["derive"]} url = "2.2" -time = { version = "0.3", features = ["macros"] } diff --git a/common/network-defaults/build.rs b/common/network-defaults/build.rs index 6f24be9d3d..6d97abcfe6 100644 --- a/common/network-defaults/build.rs +++ b/common/network-defaults/build.rs @@ -3,7 +3,7 @@ fn main() { match option_env!("NETWORK") { - Some("milhon") => println!("cargo:rustc-cfg=network=\"milhon\"",), + Some("mainnet") => println!("cargo:rustc-cfg=network=\"mainnet\"",), None | Some("sandbox") => println!("cargo:rustc-cfg=network=\"sandbox\"",), Some("qa") => println!("cargo:rustc-cfg=network=\"qa\""), _ => panic!("No such network"), diff --git a/common/network-defaults/src/all.rs b/common/network-defaults/src/all.rs index e0b5f11127..f3e42442b2 100644 --- a/common/network-defaults/src/all.rs +++ b/common/network-defaults/src/all.rs @@ -4,13 +4,23 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use crate::{milhon, qa, sandbox, ValidatorDetails}; +use crate::{mainnet, qa, sandbox, ValidatorDetails}; -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum Network { - MILHON, QA, SANDBOX, + MAINNET, +} + +impl Network { + pub fn bech32_prefix(&self) -> String { + match self { + Self::QA => String::from(qa::BECH32_PREFIX), + Self::SANDBOX => String::from(sandbox::BECH32_PREFIX), + Self::MAINNET => String::from(mainnet::BECH32_PREFIX), + } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -35,36 +45,23 @@ impl SupportedNetworks { for network in support { match network { - Network::MILHON => networks.insert( - Network::MILHON, + Network::MAINNET => networks.insert( + Network::MAINNET, NetworkDetails { - bech32_prefix: String::from(milhon::BECH32_PREFIX), - denom: String::from(milhon::DENOM), - mixnet_contract_address: String::from(milhon::MIXNET_CONTRACT_ADDRESS), - vesting_contract_address: String::from(milhon::VESTING_CONTRACT_ADDRESS), + bech32_prefix: String::from(mainnet::BECH32_PREFIX), + denom: String::from(mainnet::DENOM), + mixnet_contract_address: String::from(mainnet::MIXNET_CONTRACT_ADDRESS), + vesting_contract_address: String::from(mainnet::VESTING_CONTRACT_ADDRESS), bandwidth_claim_contract_address: String::from( - milhon::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, + mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, ), rewarding_validator_address: String::from( - milhon::REWARDING_VALIDATOR_ADDRESS, + mainnet::REWARDING_VALIDATOR_ADDRESS, ), - validators: milhon::validators(), - }, - ), - Network::QA => networks.insert( - Network::QA, - NetworkDetails { - bech32_prefix: String::from(qa::BECH32_PREFIX), - denom: String::from(qa::DENOM), - mixnet_contract_address: String::from(qa::MIXNET_CONTRACT_ADDRESS), - vesting_contract_address: String::from(qa::VESTING_CONTRACT_ADDRESS), - bandwidth_claim_contract_address: String::from( - qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, - ), - rewarding_validator_address: String::from(qa::REWARDING_VALIDATOR_ADDRESS), - validators: qa::validators(), + validators: mainnet::validators(), }, ), + Network::SANDBOX => networks.insert( Network::SANDBOX, NetworkDetails { @@ -81,6 +78,20 @@ impl SupportedNetworks { validators: sandbox::validators(), }, ), + Network::QA => networks.insert( + Network::QA, + NetworkDetails { + bech32_prefix: String::from(qa::BECH32_PREFIX), + denom: String::from(qa::DENOM), + mixnet_contract_address: String::from(qa::MIXNET_CONTRACT_ADDRESS), + vesting_contract_address: String::from(qa::VESTING_CONTRACT_ADDRESS), + bandwidth_claim_contract_address: String::from( + qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, + ), + rewarding_validator_address: String::from(qa::REWARDING_VALIDATOR_ADDRESS), + validators: qa::validators(), + }, + ), }; } SupportedNetworks { networks } diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index c3f1638b24..b2e5c3a514 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -1,32 +1,30 @@ // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use serde::{Deserialize, Serialize}; -use std::time::Duration; -use time::OffsetDateTime; use url::Url; pub mod all; pub mod eth_contract; -mod milhon; -mod qa; -mod sandbox; +pub mod mainnet; +pub mod qa; +pub mod sandbox; cfg_if::cfg_if! { - if #[cfg(network = "milhon")] { - pub const BECH32_PREFIX: &str = milhon::BECH32_PREFIX; - pub const DENOM: &str = milhon::DENOM; + if #[cfg(network = "mainnet")] { + pub const BECH32_PREFIX: &str = mainnet::BECH32_PREFIX; + pub const DENOM: &str = mainnet::DENOM; - pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = milhon::MIXNET_CONTRACT_ADDRESS; - pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = milhon::VESTING_CONTRACT_ADDRESS; - pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = milhon::BANDWIDTH_CLAIM_CONTRACT_ADDRESS; - pub const DEFAULT_REWARDING_VALIDATOR_ADDRESS: &str = milhon::REWARDING_VALIDATOR_ADDRESS; + pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = mainnet::MIXNET_CONTRACT_ADDRESS; + pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = mainnet::VESTING_CONTRACT_ADDRESS; + pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS; + pub const DEFAULT_REWARDING_VALIDATOR_ADDRESS: &str = mainnet::REWARDING_VALIDATOR_ADDRESS; pub fn default_validators() -> Vec { - milhon::validators() + mainnet::validators() } pub fn default_network() -> all::Network { - all::Network::MILHON + all::Network::MAINNET } } else if #[cfg(network = "qa")] { pub const BECH32_PREFIX: &str = qa::BECH32_PREFIX; @@ -152,10 +150,9 @@ pub const DEFAULT_VALIDATOR_API_PORT: u16 = 8080; pub const VALIDATOR_API_VERSION: &str = "v1"; // REWARDING -pub const DEFAULT_FIRST_EPOCH_START: OffsetDateTime = time::macros::datetime!(2021-08-23 12:00 UTC); -pub const DEFAULT_EPOCH_LENGTH: Duration = Duration::from_secs(24 * 60 * 60 * 30); // 30 days -/// We'll be assuming a few more things, profit margin and cost function. Since we don't have relialable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate epoch costs to Nyms. We'll also assume a cost of 40$ per epoch(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms -pub const DEFAULT_OPERATOR_EPOCH_COST: u64 = 40_000_000; // 40$/(30 days) at 1 Nym == 1$ + +/// We'll be assuming a few more things, profit margin and cost function. Since we don't have relialable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms +pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 40_000_000; // 40$/(30 days) at 1 Nym == 1$ // TODO: is there a way to get this from the chain pub const TOTAL_SUPPLY: u128 = 1_000_000_000_000_000; diff --git a/common/network-defaults/src/mainnet.rs b/common/network-defaults/src/mainnet.rs new file mode 100644 index 0000000000..ebb30bc082 --- /dev/null +++ b/common/network-defaults/src/mainnet.rs @@ -0,0 +1,20 @@ +// Copyright 2021 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::ValidatorDetails; + +pub(crate) const BECH32_PREFIX: &str = "n"; +pub const DENOM: &str = "unym"; + +pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; +pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; +pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = + "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; +pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e"; + +pub(crate) fn validators() -> Vec { + vec![ValidatorDetails::new( + "https://rpc.nyx.nodes.guru/", + Some("https://api.nyx.nodes.guru/"), + )] +} diff --git a/common/network-defaults/src/milhon.rs b/common/network-defaults/src/milhon.rs deleted file mode 100644 index a4e4f3e640..0000000000 --- a/common/network-defaults/src/milhon.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::ValidatorDetails; - -pub(crate) const BECH32_PREFIX: &str = "punk"; -pub(crate) const DENOM: &str = "upunk"; - -pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen"; -pub(crate) const VESTING_CONTRACT_ADDRESS: &str = ""; -pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = - "punk1jld76tqw4wnpfenmay2xkv86nr3j0w426eka82"; -pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfdy7hgk3"; - -pub(crate) fn validators() -> Vec { - vec![ - ValidatorDetails::new( - "https://testnet-milhon-validator1.nymtech.net", - Some("https://testnet-milhon-validator1.nymtech.net/api"), - ), - ValidatorDetails::new("https://testnet-milhon-validator2.nymtech.net", None), - ] -} diff --git a/common/network-defaults/src/qa.rs b/common/network-defaults/src/qa.rs index 1e5fd029ae..206971b6a1 100644 --- a/common/network-defaults/src/qa.rs +++ b/common/network-defaults/src/qa.rs @@ -4,7 +4,7 @@ use crate::ValidatorDetails; pub(crate) const BECH32_PREFIX: &str = "nymt"; -pub(crate) const DENOM: &str = "unymt"; +pub const DENOM: &str = "unymt"; pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt14hj2tavq8fpesdwxxcu44rty3hh90vhuysqrsr"; pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv"; diff --git a/common/network-defaults/src/sandbox.rs b/common/network-defaults/src/sandbox.rs index 81b766882e..f010b9ac14 100644 --- a/common/network-defaults/src/sandbox.rs +++ b/common/network-defaults/src/sandbox.rs @@ -4,7 +4,7 @@ use crate::ValidatorDetails; pub(crate) const BECH32_PREFIX: &str = "nymt"; -pub(crate) const DENOM: &str = "unymt"; +pub const DENOM: &str = "unymt"; pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j"; pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s"; diff --git a/common/nonexhaustive-delayqueue/Cargo.toml b/common/nonexhaustive-delayqueue/Cargo.toml index 7dfd0318d2..ea64ddf6c6 100644 --- a/common/nonexhaustive-delayqueue/Cargo.toml +++ b/common/nonexhaustive-delayqueue/Cargo.toml @@ -2,7 +2,7 @@ name = "nonexhaustive-delayqueue" version = "0.1.0" authors = ["Jędrzej Stuczyński "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymcoconut/Cargo.toml b/common/nymcoconut/Cargo.toml index db1c3dc236..bc15c04621 100644 --- a/common/nymcoconut/Cargo.toml +++ b/common/nymcoconut/Cargo.toml @@ -2,7 +2,7 @@ name = "nymcoconut" version = "0.5.0" authors = ["Jedrzej Stuczynski ", "Ania Piotrowska "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymcoconut/src/impls/serde.rs b/common/nymcoconut/src/impls/serde.rs index bf6300527b..91dbf41f46 100644 --- a/common/nymcoconut/src/impls/serde.rs +++ b/common/nymcoconut/src/impls/serde.rs @@ -23,7 +23,7 @@ macro_rules! impl_serde { impl<'de> Visitor<'de> for $visitor { type Value = $struct; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!(formatter, "A base58 encoded struct") } diff --git a/common/nymsphinx/Cargo.toml b/common/nymsphinx/Cargo.toml index ce94c101b0..460fd11390 100644 --- a/common/nymsphinx/Cargo.toml +++ b/common/nymsphinx/Cargo.toml @@ -2,7 +2,7 @@ name = "nymsphinx" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymsphinx/acknowledgements/Cargo.toml b/common/nymsphinx/acknowledgements/Cargo.toml index 3e6f725490..fa706ec804 100644 --- a/common/nymsphinx/acknowledgements/Cargo.toml +++ b/common/nymsphinx/acknowledgements/Cargo.toml @@ -2,7 +2,7 @@ name = "nymsphinx-acknowledgements" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymsphinx/addressing/Cargo.toml b/common/nymsphinx/addressing/Cargo.toml index b1c04c452b..f65c011e16 100644 --- a/common/nymsphinx/addressing/Cargo.toml +++ b/common/nymsphinx/addressing/Cargo.toml @@ -2,7 +2,7 @@ name = "nymsphinx-addressing" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymsphinx/anonymous-replies/Cargo.toml b/common/nymsphinx/anonymous-replies/Cargo.toml index be41fef7f2..246f6de509 100644 --- a/common/nymsphinx/anonymous-replies/Cargo.toml +++ b/common/nymsphinx/anonymous-replies/Cargo.toml @@ -2,7 +2,7 @@ name = "nymsphinx-anonymous-replies" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymsphinx/chunking/Cargo.toml b/common/nymsphinx/chunking/Cargo.toml index 0b7d19050d..5f47ab5358 100644 --- a/common/nymsphinx/chunking/Cargo.toml +++ b/common/nymsphinx/chunking/Cargo.toml @@ -2,7 +2,7 @@ name = "nymsphinx-chunking" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymsphinx/cover/Cargo.toml b/common/nymsphinx/cover/Cargo.toml index 49d58431c6..bb58af09b7 100644 --- a/common/nymsphinx/cover/Cargo.toml +++ b/common/nymsphinx/cover/Cargo.toml @@ -2,7 +2,7 @@ name = "nymsphinx-cover" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymsphinx/forwarding/Cargo.toml b/common/nymsphinx/forwarding/Cargo.toml index 80ab053665..6b29d9f9e5 100644 --- a/common/nymsphinx/forwarding/Cargo.toml +++ b/common/nymsphinx/forwarding/Cargo.toml @@ -2,7 +2,7 @@ name = "nymsphinx-forwarding" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymsphinx/framing/Cargo.toml b/common/nymsphinx/framing/Cargo.toml index 7e73173606..0cddb5bf22 100644 --- a/common/nymsphinx/framing/Cargo.toml +++ b/common/nymsphinx/framing/Cargo.toml @@ -2,7 +2,7 @@ name = "nymsphinx-framing" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymsphinx/params/Cargo.toml b/common/nymsphinx/params/Cargo.toml index 3e5fbf56b3..409dc38d58 100644 --- a/common/nymsphinx/params/Cargo.toml +++ b/common/nymsphinx/params/Cargo.toml @@ -2,7 +2,7 @@ name = "nymsphinx-params" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/nymsphinx/src/preparer/mod.rs b/common/nymsphinx/src/preparer/mod.rs index 63d2f67559..3f56f9bada 100644 --- a/common/nymsphinx/src/preparer/mod.rs +++ b/common/nymsphinx/src/preparer/mod.rs @@ -54,6 +54,7 @@ impl From for PreparationError { /// an optional reply-SURB, padding it to appropriate length, encrypting its content, /// and chunking into appropriate size [`Fragment`]s. #[cfg_attr(not(target_arch = "wasm32"), derive(Clone))] +#[must_use] pub struct MessagePreparer { /// Instance of a cryptographically secure random number generator. rng: R, diff --git a/common/nymsphinx/src/receiver.rs b/common/nymsphinx/src/receiver.rs index d06afa9a91..1707983ca3 100644 --- a/common/nymsphinx/src/receiver.rs +++ b/common/nymsphinx/src/receiver.rs @@ -58,6 +58,7 @@ impl MessageReceiver { } /// Allows setting non-default number of expected mix hops in the network. + #[must_use] pub fn with_mix_hops(mut self, hops: u8) -> Self { self.num_mix_hops = hops; self diff --git a/common/nymsphinx/types/Cargo.toml b/common/nymsphinx/types/Cargo.toml index 4eca2b7df7..34a916bbf5 100644 --- a/common/nymsphinx/types/Cargo.toml +++ b/common/nymsphinx/types/Cargo.toml @@ -2,7 +2,7 @@ name = "nymsphinx-types" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/pemstore/Cargo.toml b/common/pemstore/Cargo.toml index 9c5b4f5c1e..db4b331327 100644 --- a/common/pemstore/Cargo.toml +++ b/common/pemstore/Cargo.toml @@ -2,7 +2,7 @@ name = "pemstore" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/socks5/ordered-buffer/Cargo.toml b/common/socks5/ordered-buffer/Cargo.toml index 1a47058ef5..f5ea5545d8 100644 --- a/common/socks5/ordered-buffer/Cargo.toml +++ b/common/socks5/ordered-buffer/Cargo.toml @@ -2,7 +2,7 @@ name = "ordered-buffer" version = "0.1.0" authors = ["Dave Hrycyszyn "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/socks5/proxy-helpers/Cargo.toml b/common/socks5/proxy-helpers/Cargo.toml index 9d9b42d627..e3e6470a03 100644 --- a/common/socks5/proxy-helpers/Cargo.toml +++ b/common/socks5/proxy-helpers/Cargo.toml @@ -2,7 +2,7 @@ name = "proxy-helpers" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/socks5/requests/Cargo.toml b/common/socks5/requests/Cargo.toml index 2a900389ca..39ccacb4b1 100644 --- a/common/socks5/requests/Cargo.toml +++ b/common/socks5/requests/Cargo.toml @@ -2,7 +2,7 @@ name = "socks5-requests" version = "0.1.0" authors = ["Dave Hrycyszyn "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index 7d7bb7ecc3..f2a8a899fd 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -2,7 +2,7 @@ name = "topology" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/topology/src/filter.rs b/common/topology/src/filter.rs index 652b5f92e9..6c1a3c2fe1 100644 --- a/common/topology/src/filter.rs +++ b/common/topology/src/filter.rs @@ -9,6 +9,7 @@ pub trait Versioned: Clone { } pub trait VersionFilterable { + #[must_use] fn filter_by_version(&self, expected_version: &str) -> Self; } diff --git a/common/topology/src/gateway.rs b/common/topology/src/gateway.rs index 70284f1c3e..bc9a569df8 100644 --- a/common/topology/src/gateway.rs +++ b/common/topology/src/gateway.rs @@ -33,7 +33,7 @@ impl From for GatewayConversionError { } impl Display for GatewayConversionError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { GatewayConversionError::InvalidIdentityKey(err) => write!( f, diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index c14256512a..7d5b9843d4 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -206,10 +206,12 @@ impl NymTopology { true } + #[must_use] pub fn filter_system_version(&self, expected_version: &str) -> Self { self.filter_node_versions(expected_version, expected_version) } + #[must_use] pub fn filter_node_versions( &self, expected_mix_version: &str, diff --git a/common/topology/src/mix.rs b/common/topology/src/mix.rs index f28c9175b1..9d3eaf3b65 100644 --- a/common/topology/src/mix.rs +++ b/common/topology/src/mix.rs @@ -33,7 +33,7 @@ impl From for MixnodeConversionError { } impl Display for MixnodeConversionError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { MixnodeConversionError::InvalidIdentityKey(err) => write!( f, diff --git a/common/version-checker/Cargo.toml b/common/version-checker/Cargo.toml index 6cc9b02bb8..e6ccdbf241 100644 --- a/common/version-checker/Cargo.toml +++ b/common/version-checker/Cargo.toml @@ -2,7 +2,7 @@ name = "version-checker" version = "0.1.0" authors = ["Dave Hrycyszyn "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/common/wasm-utils/Cargo.toml b/common/wasm-utils/Cargo.toml index 01b6689524..30f61734b2 100644 --- a/common/wasm-utils/Cargo.toml +++ b/common/wasm-utils/Cargo.toml @@ -2,14 +2,14 @@ name = "wasm-utils" version = "0.1.0" authors = ["Jedrzej Stuczynski "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] futures = "0.3" js-sys = "^0.3.51" -wasm-bindgen = "0.2" +wasm-bindgen = "=0.2.78" wasm-bindgen-futures = "0.4" # we don't want entire tokio-tungstenite, tungstenite itself is just fine - we just want message and error enums diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index e0ad7ddf60..864a01cdab 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -17,9 +17,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.49" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a03e93e97a28fbc9f42fbc5ba0886a3c67eb637b476dbee711f80a6ffe8223d" +checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" [[package]] name = "arrayref" @@ -41,9 +41,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "az" -version = "1.1.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6dff4a1892b54d70af377bf7a17064192e822865791d812957f21e3108c325" +checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4" [[package]] name = "bandwidth-claim" @@ -123,7 +123,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", ] [[package]] @@ -143,9 +143,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bumpalo" -version = "3.8.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" [[package]] name = "byte-tools" @@ -155,9 +155,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.7.2" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" +checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" [[package]] name = "byteorder" @@ -209,7 +209,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", ] [[package]] @@ -293,9 +293,9 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.0.0-beta4" +version = "1.0.0-beta3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f472c0bfd584a4510702537d0b65c5331095e76099586a12f749fd69afda9c5e" +checksum = "b4a4e55f0d64fed54cd2202301b8d466af8de044589247dabd77a4222f52f749" dependencies = [ "cosmwasm-std", "serde", @@ -327,7 +327,7 @@ dependencies = [ "config", "digest 0.9.0", "ed25519-dalek", - "generic-array 0.14.4", + "generic-array 0.14.5", "hkdf", "hmac", "log", @@ -344,7 +344,7 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", "rand_core 0.6.3", "subtle 2.4.1", "zeroize", @@ -366,7 +366,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", "subtle 2.4.1", ] @@ -405,9 +405,9 @@ dependencies = [ [[package]] name = "der" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2" +checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" dependencies = [ "const-oid", ] @@ -427,7 +427,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", ] [[package]] @@ -493,7 +493,7 @@ checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b" dependencies = [ "crypto-bigint", "ff", - "generic-array 0.14.4", + "generic-array 0.14.5", "group", "pkcs8", "rand_core 0.6.3", @@ -539,9 +539,9 @@ dependencies = [ [[package]] name = "fixed" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d333a26ec13a023c6dff4b7584de4d323cfee2e508f5dd2bbee6669e4f7efdf" +checksum = "80a9a8cb2e34880a498f09367089339bda5e12d6f871640f947850f7113058c0" dependencies = [ "az", "bytemuck", @@ -571,9 +571,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" dependencies = [ "typenum", "version_check", @@ -605,9 +605,9 @@ dependencies = [ [[package]] name = "getset" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504" +checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" dependencies = [ "proc-macro-error", "proc-macro2", @@ -617,9 +617,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.13.24" +version = "0.13.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "845e007a28f1fcac035715988a234e8ec5458fd825b20a20c7dec74237ef341f" +checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6" dependencies = [ "bitflags", "libc", @@ -720,9 +720,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.8" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "jobserver" @@ -768,15 +768,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.107" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" [[package]] name = "libgit2-sys" -version = "0.12.25+1.3.0" +version = "0.12.26+1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68169ef08d6519b2fe133ecc637408d933c0174b23b80bb2f79828966fbaab" +checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494" dependencies = [ "cc", "libc", @@ -853,8 +853,9 @@ dependencies = [ "schemars", "serde", "thiserror", + "time 0.3.6", "vergen", - "vesting-contract", + "vesting-contract-common", ] [[package]] @@ -871,6 +872,7 @@ dependencies = [ "serde", "serde_repr", "thiserror", + "time 0.3.6", ] [[package]] @@ -880,7 +882,6 @@ dependencies = [ "cfg-if", "hex-literal", "serde", - "time 0.3.5", "url", ] @@ -904,6 +905,15 @@ dependencies = [ "libm", ] +[[package]] +name = "num_threads" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71a1eb3a36534514077c1e079ada2fb170ef30c47d203aa6916138cf882ecd52" +dependencies = [ + "libc", +] + [[package]] name = "nymsphinx-types" version = "0.1.0" @@ -913,9 +923,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" [[package]] name = "opaque-debug" @@ -1008,15 +1018,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" [[package]] name = "ppv-lite86" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro-error" @@ -1044,9 +1054,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] @@ -1059,9 +1069,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.10" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" dependencies = [ "proc-macro2", ] @@ -1152,21 +1162,21 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "schemars" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271ac0c667b8229adf70f0f957697c96fafd7486ab7481e15dc5e45e3e6a4368" +checksum = "c6b5a3c80cea1ab61f4260238409510e814e38b4b563c06044edf91e7dc070e3" dependencies = [ "dyn-clone", "schemars_derive", @@ -1176,9 +1186,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ebda811090b257411540779860bc09bf321bc587f58d2c5864309d1566214e7" +checksum = "41ae4dce13e8614c46ac3c38ef1c0d668b101df6ac39817aebdaa26642ddae9b" dependencies = [ "proc-macro2", "quote", @@ -1194,9 +1204,9 @@ checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" [[package]] name = "serde" -version = "1.0.130" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" dependencies = [ "serde_derive", ] @@ -1212,9 +1222,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.130" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" dependencies = [ "proc-macro2", "quote", @@ -1234,9 +1244,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.70" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e277c495ac6cd1a01a58d0a0c574568b4d1ddf14f59965c6a58b8d96400b54f3" +checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" dependencies = [ "itoa", "ryu", @@ -1268,9 +1278,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if", @@ -1350,9 +1360,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.81" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" dependencies = [ "proc-macro2", "quote", @@ -1403,11 +1413,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" +checksum = "c8d54b9298e05179c335de2b9645d061255bcd5155f843b3e328d2cfe0a5b413" dependencies = [ + "itoa", "libc", + "num_threads", "time-macros", ] @@ -1443,9 +1455,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" @@ -1506,9 +1518,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "5.1.18" +version = "5.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d48696c0fbbdafd9553e14c4584b4b9583931e9474a3ae506f1872b890d0b47" +checksum = "6cf88d94e969e7956d924ba70741316796177fa0c79a2c9f4ab04998d96e966e" dependencies = [ "anyhow", "cfg-if", @@ -1523,9 +1535,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vesting-contract" @@ -1545,7 +1557,12 @@ dependencies = [ name = "vesting-contract-common" version = "0.1.0" dependencies = [ + "config", "cosmwasm-std", + "cw-storage-plus", + "mixnet-contract-common", + "schemars", + "serde", ] [[package]] diff --git a/contracts/bandwidth-claim/Cargo.toml b/contracts/bandwidth-claim/Cargo.toml index 3480a608f1..390baf1ab7 100644 --- a/contracts/bandwidth-claim/Cargo.toml +++ b/contracts/bandwidth-claim/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bandwidth-claim" version = "0.1.0" -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/contracts/bandwidth-claim/src/lib.rs b/contracts/bandwidth-claim/src/lib.rs index be64c829b2..53be5bfb7e 100644 --- a/contracts/bandwidth-claim/src/lib.rs +++ b/contracts/bandwidth-claim/src/lib.rs @@ -21,7 +21,7 @@ use bandwidth_claim_contract::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, Quer /// `msg` is the contract initialization message, sort of like a constructor call. #[entry_point] pub fn instantiate( - _deps: DepsMut, + _deps: DepsMut<'_>, _env: Env, _info: MessageInfo, _msg: InstantiateMsg, @@ -32,7 +32,7 @@ pub fn instantiate( /// Handle an incoming message #[entry_point] pub fn execute( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, msg: ExecuteMsg, @@ -43,7 +43,7 @@ pub fn execute( } #[entry_point] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result { +pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result { let query_res = match msg { QueryMsg::GetPayments { start_after, limit } => { to_binary(&queries::query_payments_paged(deps, start_after, limit)?) @@ -54,7 +54,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result Result { +pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { Ok(Default::default()) } diff --git a/contracts/bandwidth-claim/src/queries.rs b/contracts/bandwidth-claim/src/queries.rs index 7992ba52ee..1a4e61ca1e 100644 --- a/contracts/bandwidth-claim/src/queries.rs +++ b/contracts/bandwidth-claim/src/queries.rs @@ -24,7 +24,7 @@ fn calculate_start_value>(start_after: Option) -> Option, start_after: Option, limit: Option, ) -> StdResult { diff --git a/contracts/bandwidth-claim/src/storage.rs b/contracts/bandwidth-claim/src/storage.rs index d99531d7db..b49db84d83 100644 --- a/contracts/bandwidth-claim/src/storage.rs +++ b/contracts/bandwidth-claim/src/storage.rs @@ -19,15 +19,15 @@ pub enum Status { Spent, } -pub fn payments(storage: &mut dyn Storage) -> Bucket { +pub fn payments(storage: &mut dyn Storage) -> Bucket<'_, Payment> { bucket(storage, PREFIX_PAYMENTS) } -pub fn payments_read(storage: &dyn Storage) -> ReadonlyBucket { +pub fn payments_read(storage: &dyn Storage) -> ReadonlyBucket<'_, Payment> { bucket_read(storage, PREFIX_PAYMENTS) } -pub fn status(storage: &mut dyn Storage) -> Bucket { +pub fn status(storage: &mut dyn Storage) -> Bucket<'_, Status> { bucket(storage, PREFIX_STATUS) } diff --git a/contracts/bandwidth-claim/src/transactions.rs b/contracts/bandwidth-claim/src/transactions.rs index 8884b5abef..9677204942 100644 --- a/contracts/bandwidth-claim/src/transactions.rs +++ b/contracts/bandwidth-claim/src/transactions.rs @@ -8,7 +8,7 @@ use crate::storage::{payments, status, Status}; use bandwidth_claim_contract::payment::{LinkPaymentData, Payment}; pub(crate) fn link_payment( - deps: DepsMut, + deps: DepsMut<'_>, _env: Env, _info: MessageInfo, data: LinkPaymentData, diff --git a/contracts/basic-bandwidth-generation/.gitignore b/contracts/basic-bandwidth-generation/.gitignore index c071436eee..b76c36af73 100644 --- a/contracts/basic-bandwidth-generation/.gitignore +++ b/contracts/basic-bandwidth-generation/.gitignore @@ -1,5 +1,6 @@ node_modules ../.env +.to_do.md #Hardhat files cache diff --git a/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.dbg.json b/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.dbg.json index 8a9759bdf8..86d5f44dff 100644 --- a/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.dbg.json +++ b/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.dbg.json @@ -1,4 +1,4 @@ { "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../../build-info/6dfbc7f87ae3c2727d63a3fba8d02a41.json" + "buildInfo": "../../../../../build-info/1f95016aa87f8376998977fedfcae017.json" } diff --git a/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.json b/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.json index 9d6ac8dd00..8fd7d531d1 100644 --- a/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.json +++ b/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.json @@ -290,8 +290,8 @@ "type": "function" } ], - "bytecode": "0x60806040523480156200001157600080fd5b506040516200171b3803806200171b833981810160405281019062000037919062000193565b81600390805190602001906200004f92919062000071565b5080600490805190602001906200006892919062000071565b50505062000376565b8280546200007f906200029b565b90600052602060002090601f016020900481019282620000a35760008555620000ef565b82601f10620000be57805160ff1916838001178555620000ef565b82800160010185558215620000ef579182015b82811115620000ee578251825591602001919060010190620000d1565b5b509050620000fe919062000102565b5090565b5b808211156200011d57600081600090555060010162000103565b5090565b60006200013862000132846200022f565b62000206565b9050828152602081018484840111156200015157600080fd5b6200015e84828562000265565b509392505050565b600082601f8301126200017857600080fd5b81516200018a84826020860162000121565b91505092915050565b60008060408385031215620001a757600080fd5b600083015167ffffffffffffffff811115620001c257600080fd5b620001d08582860162000166565b925050602083015167ffffffffffffffff811115620001ee57600080fd5b620001fc8582860162000166565b9150509250929050565b60006200021262000225565b9050620002208282620002d1565b919050565b6000604051905090565b600067ffffffffffffffff8211156200024d576200024c62000336565b5b620002588262000365565b9050602081019050919050565b60005b838110156200028557808201518184015260208101905062000268565b8381111562000295576000848401525b50505050565b60006002820490506001821680620002b457607f821691505b60208210811415620002cb57620002ca62000307565b5b50919050565b620002dc8262000365565b810181811067ffffffffffffffff82111715620002fe57620002fd62000336565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b61139580620003866000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610e35565b60405180910390f35b6100e660048036038101906100e19190610c83565b610308565b6040516100f39190610e1a565b60405180910390f35b610104610326565b6040516101119190610f37565b60405180910390f35b610134600480360381019061012f9190610c34565b610330565b6040516101419190610e1a565b60405180910390f35b610152610428565b60405161015f9190610f52565b60405180910390f35b610182600480360381019061017d9190610c83565b610431565b60405161018f9190610e1a565b60405180910390f35b6101b260048036038101906101ad9190610bcf565b6104dd565b6040516101bf9190610f37565b60405180910390f35b6101d0610525565b6040516101dd9190610e35565b60405180910390f35b61020060048036038101906101fb9190610c83565b6105b7565b60405161020d9190610e1a565b60405180910390f35b610230600480360381019061022b9190610c83565b6106a2565b60405161023d9190610e1a565b60405180910390f35b610260600480360381019061025b9190610bf8565b6106c0565b60405161026d9190610f37565b60405180910390f35b60606003805461028590611067565b80601f01602080910402602001604051908101604052809291908181526020018280546102b190611067565b80156102fe5780601f106102d3576101008083540402835291602001916102fe565b820191906000526020600020905b8154815290600101906020018083116102e157829003601f168201915b5050505050905090565b600061031c610315610747565b848461074f565b6001905092915050565b6000600254905090565b600061033d84848461091a565b6000600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610388610747565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610408576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103ff90610eb7565b60405180910390fd5b61041c85610414610747565b85840361074f565b60019150509392505050565b60006012905090565b60006104d361043e610747565b84846001600061044c610747565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546104ce9190610f89565b61074f565b6001905092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60606004805461053490611067565b80601f016020809104026020016040519081016040528092919081815260200182805461056090611067565b80156105ad5780601f10610582576101008083540402835291602001916105ad565b820191906000526020600020905b81548152906001019060200180831161059057829003601f168201915b5050505050905090565b600080600160006105c6610747565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610683576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067a90610f17565b60405180910390fd5b61069761068e610747565b8585840361074f565b600191505092915050565b60006106b66106af610747565b848461091a565b6001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156107bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b690610ef7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561082f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161082690610e77565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258360405161090d9190610f37565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561098a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161098190610ed7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156109fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109f190610e57565b60405180910390fd5b610a05838383610b9b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610a8b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a8290610e97565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610b1e9190610f89565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610b829190610f37565b60405180910390a3610b95848484610ba0565b50505050565b505050565b505050565b600081359050610bb481611331565b92915050565b600081359050610bc981611348565b92915050565b600060208284031215610be157600080fd5b6000610bef84828501610ba5565b91505092915050565b60008060408385031215610c0b57600080fd5b6000610c1985828601610ba5565b9250506020610c2a85828601610ba5565b9150509250929050565b600080600060608486031215610c4957600080fd5b6000610c5786828701610ba5565b9350506020610c6886828701610ba5565b9250506040610c7986828701610bba565b9150509250925092565b60008060408385031215610c9657600080fd5b6000610ca485828601610ba5565b9250506020610cb585828601610bba565b9150509250929050565b610cc881610ff1565b82525050565b6000610cd982610f6d565b610ce38185610f78565b9350610cf3818560208601611034565b610cfc816110f7565b840191505092915050565b6000610d14602383610f78565b9150610d1f82611108565b604082019050919050565b6000610d37602283610f78565b9150610d4282611157565b604082019050919050565b6000610d5a602683610f78565b9150610d65826111a6565b604082019050919050565b6000610d7d602883610f78565b9150610d88826111f5565b604082019050919050565b6000610da0602583610f78565b9150610dab82611244565b604082019050919050565b6000610dc3602483610f78565b9150610dce82611293565b604082019050919050565b6000610de6602583610f78565b9150610df1826112e2565b604082019050919050565b610e058161101d565b82525050565b610e1481611027565b82525050565b6000602082019050610e2f6000830184610cbf565b92915050565b60006020820190508181036000830152610e4f8184610cce565b905092915050565b60006020820190508181036000830152610e7081610d07565b9050919050565b60006020820190508181036000830152610e9081610d2a565b9050919050565b60006020820190508181036000830152610eb081610d4d565b9050919050565b60006020820190508181036000830152610ed081610d70565b9050919050565b60006020820190508181036000830152610ef081610d93565b9050919050565b60006020820190508181036000830152610f1081610db6565b9050919050565b60006020820190508181036000830152610f3081610dd9565b9050919050565b6000602082019050610f4c6000830184610dfc565b92915050565b6000602082019050610f676000830184610e0b565b92915050565b600081519050919050565b600082825260208201905092915050565b6000610f948261101d565b9150610f9f8361101d565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610fd457610fd3611099565b5b828201905092915050565b6000610fea82610ffd565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b60005b83811015611052578082015181840152602081019050611037565b83811115611061576000848401525b50505050565b6000600282049050600182168061107f57607f821691505b60208210811415611093576110926110c8565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000601f19601f8301169050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206160008201527f6c6c6f77616e6365000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b61133a81610fdf565b811461134557600080fd5b50565b6113518161101d565b811461135c57600080fd5b5056fea2646970667358221220b3c129c93663de4578dfe5a3ceef494143e28bb9ac4461738559f65db42d2db064736f6c63430008040033", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610e35565b60405180910390f35b6100e660048036038101906100e19190610c83565b610308565b6040516100f39190610e1a565b60405180910390f35b610104610326565b6040516101119190610f37565b60405180910390f35b610134600480360381019061012f9190610c34565b610330565b6040516101419190610e1a565b60405180910390f35b610152610428565b60405161015f9190610f52565b60405180910390f35b610182600480360381019061017d9190610c83565b610431565b60405161018f9190610e1a565b60405180910390f35b6101b260048036038101906101ad9190610bcf565b6104dd565b6040516101bf9190610f37565b60405180910390f35b6101d0610525565b6040516101dd9190610e35565b60405180910390f35b61020060048036038101906101fb9190610c83565b6105b7565b60405161020d9190610e1a565b60405180910390f35b610230600480360381019061022b9190610c83565b6106a2565b60405161023d9190610e1a565b60405180910390f35b610260600480360381019061025b9190610bf8565b6106c0565b60405161026d9190610f37565b60405180910390f35b60606003805461028590611067565b80601f01602080910402602001604051908101604052809291908181526020018280546102b190611067565b80156102fe5780601f106102d3576101008083540402835291602001916102fe565b820191906000526020600020905b8154815290600101906020018083116102e157829003601f168201915b5050505050905090565b600061031c610315610747565b848461074f565b6001905092915050565b6000600254905090565b600061033d84848461091a565b6000600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610388610747565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610408576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103ff90610eb7565b60405180910390fd5b61041c85610414610747565b85840361074f565b60019150509392505050565b60006012905090565b60006104d361043e610747565b84846001600061044c610747565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546104ce9190610f89565b61074f565b6001905092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60606004805461053490611067565b80601f016020809104026020016040519081016040528092919081815260200182805461056090611067565b80156105ad5780601f10610582576101008083540402835291602001916105ad565b820191906000526020600020905b81548152906001019060200180831161059057829003601f168201915b5050505050905090565b600080600160006105c6610747565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610683576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067a90610f17565b60405180910390fd5b61069761068e610747565b8585840361074f565b600191505092915050565b60006106b66106af610747565b848461091a565b6001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156107bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b690610ef7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561082f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161082690610e77565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258360405161090d9190610f37565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561098a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161098190610ed7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156109fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109f190610e57565b60405180910390fd5b610a05838383610b9b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610a8b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a8290610e97565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610b1e9190610f89565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610b829190610f37565b60405180910390a3610b95848484610ba0565b50505050565b505050565b505050565b600081359050610bb481611331565b92915050565b600081359050610bc981611348565b92915050565b600060208284031215610be157600080fd5b6000610bef84828501610ba5565b91505092915050565b60008060408385031215610c0b57600080fd5b6000610c1985828601610ba5565b9250506020610c2a85828601610ba5565b9150509250929050565b600080600060608486031215610c4957600080fd5b6000610c5786828701610ba5565b9350506020610c6886828701610ba5565b9250506040610c7986828701610bba565b9150509250925092565b60008060408385031215610c9657600080fd5b6000610ca485828601610ba5565b9250506020610cb585828601610bba565b9150509250929050565b610cc881610ff1565b82525050565b6000610cd982610f6d565b610ce38185610f78565b9350610cf3818560208601611034565b610cfc816110f7565b840191505092915050565b6000610d14602383610f78565b9150610d1f82611108565b604082019050919050565b6000610d37602283610f78565b9150610d4282611157565b604082019050919050565b6000610d5a602683610f78565b9150610d65826111a6565b604082019050919050565b6000610d7d602883610f78565b9150610d88826111f5565b604082019050919050565b6000610da0602583610f78565b9150610dab82611244565b604082019050919050565b6000610dc3602483610f78565b9150610dce82611293565b604082019050919050565b6000610de6602583610f78565b9150610df1826112e2565b604082019050919050565b610e058161101d565b82525050565b610e1481611027565b82525050565b6000602082019050610e2f6000830184610cbf565b92915050565b60006020820190508181036000830152610e4f8184610cce565b905092915050565b60006020820190508181036000830152610e7081610d07565b9050919050565b60006020820190508181036000830152610e9081610d2a565b9050919050565b60006020820190508181036000830152610eb081610d4d565b9050919050565b60006020820190508181036000830152610ed081610d70565b9050919050565b60006020820190508181036000830152610ef081610d93565b9050919050565b60006020820190508181036000830152610f1081610db6565b9050919050565b60006020820190508181036000830152610f3081610dd9565b9050919050565b6000602082019050610f4c6000830184610dfc565b92915050565b6000602082019050610f676000830184610e0b565b92915050565b600081519050919050565b600082825260208201905092915050565b6000610f948261101d565b9150610f9f8361101d565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610fd457610fd3611099565b5b828201905092915050565b6000610fea82610ffd565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b60005b83811015611052578082015181840152602081019050611037565b83811115611061576000848401525b50505050565b6000600282049050600182168061107f57607f821691505b60208210811415611093576110926110c8565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000601f19601f8301169050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206160008201527f6c6c6f77616e6365000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b61133a81610fdf565b811461134557600080fd5b50565b6113518161101d565b811461135c57600080fd5b5056fea2646970667358221220b3c129c93663de4578dfe5a3ceef494143e28bb9ac4461738559f65db42d2db064736f6c63430008040033", + "bytecode": "0x60806040523480156200001157600080fd5b5060405162000b5638038062000b568339810160408190526200003491620001db565b81516200004990600390602085019062000068565b5080516200005f90600490602084019062000068565b50505062000282565b828054620000769062000245565b90600052602060002090601f0160209004810192826200009a5760008555620000e5565b82601f10620000b557805160ff1916838001178555620000e5565b82800160010185558215620000e5579182015b82811115620000e5578251825591602001919060010190620000c8565b50620000f3929150620000f7565b5090565b5b80821115620000f35760008155600101620000f8565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200013657600080fd5b81516001600160401b03808211156200015357620001536200010e565b604051601f8301601f19908116603f011681019082821181831017156200017e576200017e6200010e565b816040528381526020925086838588010111156200019b57600080fd5b600091505b83821015620001bf5785820183015181830184015290820190620001a0565b83821115620001d15760008385830101525b9695505050505050565b60008060408385031215620001ef57600080fd5b82516001600160401b03808211156200020757600080fd5b620002158683870162000124565b935060208501519150808211156200022c57600080fd5b506200023b8582860162000124565b9150509250929050565b600181811c908216806200025a57607f821691505b602082108114156200027c57634e487b7160e01b600052602260045260246000fd5b50919050565b6108c480620002926000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461012357806370a082311461013657806395d89b411461015f578063a457c2d714610167578063a9059cbb1461017a578063dd62ed3e1461018d57600080fd5b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100ef57806323b872dd14610101578063313ce56714610114575b600080fd5b6100b66101c6565b6040516100c39190610701565b60405180910390f35b6100df6100da366004610772565b610258565b60405190151581526020016100c3565b6002545b6040519081526020016100c3565b6100df61010f36600461079c565b61026e565b604051601281526020016100c3565b6100df610131366004610772565b61031d565b6100f36101443660046107d8565b6001600160a01b031660009081526020819052604090205490565b6100b6610359565b6100df610175366004610772565b610368565b6100df610188366004610772565b610401565b6100f361019b3660046107fa565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6060600380546101d59061082d565b80601f01602080910402602001604051908101604052809291908181526020018280546102019061082d565b801561024e5780601f106102235761010080835404028352916020019161024e565b820191906000526020600020905b81548152906001019060200180831161023157829003601f168201915b5050505050905090565b600061026533848461040e565b50600192915050565b600061027b848484610532565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156103055760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b610312853385840361040e565b506001949350505050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610265918590610354908690610868565b61040e565b6060600480546101d59061082d565b3360009081526001602090815260408083206001600160a01b0386168452909152812054828110156103ea5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016102fc565b6103f7338585840361040e565b5060019392505050565b6000610265338484610532565b6001600160a01b0383166104705760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016102fc565b6001600160a01b0382166104d15760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016102fc565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166105965760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016102fc565b6001600160a01b0382166105f85760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016102fc565b6001600160a01b038316600090815260208190526040902054818110156106705760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016102fc565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906106a7908490610868565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516106f391815260200190565b60405180910390a350505050565b600060208083528351808285015260005b8181101561072e57858101830151858201604001528201610712565b81811115610740576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b038116811461076d57600080fd5b919050565b6000806040838503121561078557600080fd5b61078e83610756565b946020939093013593505050565b6000806000606084860312156107b157600080fd5b6107ba84610756565b92506107c860208501610756565b9150604084013590509250925092565b6000602082840312156107ea57600080fd5b6107f382610756565b9392505050565b6000806040838503121561080d57600080fd5b61081683610756565b915061082460208401610756565b90509250929050565b600181811c9082168061084157607f821691505b6020821081141561086257634e487b7160e01b600052602260045260246000fd5b50919050565b6000821982111561088957634e487b7160e01b600052601160045260246000fd5b50019056fea2646970667358221220200d63d7227620e0df6481e76a67dffb28ef35c0c3c55e1ee631b46bcb5e66c264736f6c634300080a0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461012357806370a082311461013657806395d89b411461015f578063a457c2d714610167578063a9059cbb1461017a578063dd62ed3e1461018d57600080fd5b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100ef57806323b872dd14610101578063313ce56714610114575b600080fd5b6100b66101c6565b6040516100c39190610701565b60405180910390f35b6100df6100da366004610772565b610258565b60405190151581526020016100c3565b6002545b6040519081526020016100c3565b6100df61010f36600461079c565b61026e565b604051601281526020016100c3565b6100df610131366004610772565b61031d565b6100f36101443660046107d8565b6001600160a01b031660009081526020819052604090205490565b6100b6610359565b6100df610175366004610772565b610368565b6100df610188366004610772565b610401565b6100f361019b3660046107fa565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6060600380546101d59061082d565b80601f01602080910402602001604051908101604052809291908181526020018280546102019061082d565b801561024e5780601f106102235761010080835404028352916020019161024e565b820191906000526020600020905b81548152906001019060200180831161023157829003601f168201915b5050505050905090565b600061026533848461040e565b50600192915050565b600061027b848484610532565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156103055760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b610312853385840361040e565b506001949350505050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610265918590610354908690610868565b61040e565b6060600480546101d59061082d565b3360009081526001602090815260408083206001600160a01b0386168452909152812054828110156103ea5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016102fc565b6103f7338585840361040e565b5060019392505050565b6000610265338484610532565b6001600160a01b0383166104705760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016102fc565b6001600160a01b0382166104d15760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016102fc565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166105965760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016102fc565b6001600160a01b0382166105f85760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016102fc565b6001600160a01b038316600090815260208190526040902054818110156106705760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016102fc565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906106a7908490610868565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516106f391815260200190565b60405180910390a350505050565b600060208083528351808285015260005b8181101561072e57858101830151858201604001528201610712565b81811115610740576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b038116811461076d57600080fd5b919050565b6000806040838503121561078557600080fd5b61078e83610756565b946020939093013593505050565b6000806000606084860312156107b157600080fd5b6107ba84610756565b92506107c860208501610756565b9150604084013590509250925092565b6000602082840312156107ea57600080fd5b6107f382610756565b9392505050565b6000806040838503121561080d57600080fd5b61081683610756565b915061082460208401610756565b90509250929050565b600181811c9082168061084157607f821691505b6020821081141561086257634e487b7160e01b600052602260045260246000fd5b50919050565b6000821982111561088957634e487b7160e01b600052601160045260246000fd5b50019056fea2646970667358221220200d63d7227620e0df6481e76a67dffb28ef35c0c3c55e1ee631b46bcb5e66c264736f6c634300080a0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.dbg.json b/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.dbg.json index 8a9759bdf8..86d5f44dff 100644 --- a/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.dbg.json +++ b/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.dbg.json @@ -1,4 +1,4 @@ { "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../../build-info/6dfbc7f87ae3c2727d63a3fba8d02a41.json" + "buildInfo": "../../../../../build-info/1f95016aa87f8376998977fedfcae017.json" } diff --git a/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol/IERC20Metadata.dbg.json b/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol/IERC20Metadata.dbg.json index 5f2b42b50a..5118accb6b 100644 --- a/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol/IERC20Metadata.dbg.json +++ b/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol/IERC20Metadata.dbg.json @@ -1,4 +1,4 @@ { "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../../../build-info/6dfbc7f87ae3c2727d63a3fba8d02a41.json" + "buildInfo": "../../../../../../build-info/1f95016aa87f8376998977fedfcae017.json" } diff --git a/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/utils/Context.sol/Context.dbg.json b/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/utils/Context.sol/Context.dbg.json index 7bbf60451f..16b4690e2a 100644 --- a/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/utils/Context.sol/Context.dbg.json +++ b/contracts/basic-bandwidth-generation/artifacts/@openzeppelin/contracts/utils/Context.sol/Context.dbg.json @@ -1,4 +1,4 @@ { "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../build-info/6dfbc7f87ae3c2727d63a3fba8d02a41.json" + "buildInfo": "../../../../build-info/1f95016aa87f8376998977fedfcae017.json" } diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/math/SafeMath.sol/SafeMath.dbg.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/math/SafeMath.sol/SafeMath.dbg.json deleted file mode 100644 index bea78f6d8d..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/math/SafeMath.sol/SafeMath.dbg.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../build-info/10e5405221d21329b74f12bf00d0cd2d.json" -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/math/SafeMath.sol/SafeMath.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/math/SafeMath.sol/SafeMath.json deleted file mode 100644 index e1a95dfe90..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/math/SafeMath.sol/SafeMath.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "SafeMath", - "sourceName": "@openzeppelin/contracts/math/SafeMath.sol", - "abi": [], - "bytecode": "0x60566023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212207ceb5b9e8bb631a9723802d1487d75b0f9db1e57e569e37561e9d35cd45a718c64736f6c63430006060033", - "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212207ceb5b9e8bb631a9723802d1487d75b0f9db1e57e569e37561e9d35cd45a718c64736f6c63430006060033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.dbg.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.dbg.json deleted file mode 100644 index e548d120de..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.dbg.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../../build-info/10e5405221d21329b74f12bf00d0cd2d.json" -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.json deleted file mode 100644 index f6b18c4e98..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.json +++ /dev/null @@ -1,297 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "ERC20", - "sourceName": "@openzeppelin/contracts/token/ERC20/ERC20.sol", - "abi": [ - { - "inputs": [ - { - "internalType": "string", - "name": "name_", - "type": "string" - }, - { - "internalType": "string", - "name": "symbol_", - "type": "string" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "subtractedValue", - "type": "uint256" - } - ], - "name": "decreaseAllowance", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "addedValue", - "type": "uint256" - } - ], - "name": "increaseAllowance", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "0x60806040523480156200001157600080fd5b5060405162000ca538038062000ca5833981810160405260408110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b9083019060208201858111156200006e57600080fd5b82516401000000008111828201881017156200008957600080fd5b82525081516020918201929091019080838360005b83811015620000b85781810151838201526020016200009e565b50505050905090810190601f168015620000e65780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200010a57600080fd5b9083019060208201858111156200012057600080fd5b82516401000000008111828201881017156200013b57600080fd5b82525081516020918201929091019080838360005b838110156200016a57818101518382015260200162000150565b50505050905090810190601f168015620001985780820380516001836020036101000a031916815260200191505b5060405250508251620001b491506003906020850190620001e0565b508051620001ca906004906020840190620001e0565b50506005805460ff191660121790555062000285565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022357805160ff191683800117855562000253565b8280016001018555821562000253579182015b828111156200025357825182559160200191906001019062000236565b506200026192915062000265565b5090565b6200028291905b808211156200026157600081556001016200026c565b90565b610a1080620002956000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063395093511161007157806339509351146101d957806370a082311461020557806395d89b411461022b578063a457c2d714610233578063a9059cbb1461025f578063dd62ed3e1461028b576100a9565b806306fdde03146100ae578063095ea7b31461012b57806318160ddd1461016b57806323b872dd14610185578063313ce567146101bb575b600080fd5b6100b66102b9565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100f05781810151838201526020016100d8565b50505050905090810190601f16801561011d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101576004803603604081101561014157600080fd5b506001600160a01b03813516906020013561034f565b604080519115158252519081900360200190f35b61017361036c565b60408051918252519081900360200190f35b6101576004803603606081101561019b57600080fd5b506001600160a01b03813581169160208101359091169060400135610372565b6101c36103ff565b6040805160ff9092168252519081900360200190f35b610157600480360360408110156101ef57600080fd5b506001600160a01b038135169060200135610408565b6101736004803603602081101561021b57600080fd5b50356001600160a01b031661045c565b6100b6610477565b6101576004803603604081101561024957600080fd5b506001600160a01b0381351690602001356104d8565b6101576004803603604081101561027557600080fd5b506001600160a01b038135169060200135610546565b610173600480360360408110156102a157600080fd5b506001600160a01b038135811691602001351661055a565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b820191906000526020600020905b81548152906001019060200180831161032857829003601f168201915b5050505050905090565b600061036361035c610585565b8484610589565b50600192915050565b60025490565b600061037f848484610675565b6103f58461038b610585565b6103f085604051806060016040528060288152602001610945602891396001600160a01b038a166000908152600160205260408120906103c9610585565b6001600160a01b03168152602081019190915260400160002054919063ffffffff6107dc16565b610589565b5060019392505050565b60055460ff1690565b6000610363610415610585565b846103f08560016000610426610585565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff61087316565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b60006103636104e5610585565b846103f0856040518060600160405280602581526020016109b6602591396001600061050f610585565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff6107dc16565b6000610363610553610585565b8484610675565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3390565b6001600160a01b0383166105ce5760405162461bcd60e51b81526004018080602001828103825260248152602001806109926024913960400191505060405180910390fd5b6001600160a01b0382166106135760405162461bcd60e51b81526004018080602001828103825260228152602001806108fd6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166106ba5760405162461bcd60e51b815260040180806020018281038252602581526020018061096d6025913960400191505060405180910390fd5b6001600160a01b0382166106ff5760405162461bcd60e51b81526004018080602001828103825260238152602001806108da6023913960400191505060405180910390fd5b61070a8383836108d4565b61074d8160405180606001604052806026815260200161091f602691396001600160a01b038616600090815260208190526040902054919063ffffffff6107dc16565b6001600160a01b038085166000908152602081905260408082209390935590841681522054610782908263ffffffff61087316565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b6000818484111561086b5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610830578181015183820152602001610818565b50505050905090810190601f16801561085d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000828201838110156108cd576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122007c47290196c2ddfd0852f1dfc62deceb75163eca44542513eab3af1a7a838f964736f6c63430006060033", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c8063395093511161007157806339509351146101d957806370a082311461020557806395d89b411461022b578063a457c2d714610233578063a9059cbb1461025f578063dd62ed3e1461028b576100a9565b806306fdde03146100ae578063095ea7b31461012b57806318160ddd1461016b57806323b872dd14610185578063313ce567146101bb575b600080fd5b6100b66102b9565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100f05781810151838201526020016100d8565b50505050905090810190601f16801561011d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101576004803603604081101561014157600080fd5b506001600160a01b03813516906020013561034f565b604080519115158252519081900360200190f35b61017361036c565b60408051918252519081900360200190f35b6101576004803603606081101561019b57600080fd5b506001600160a01b03813581169160208101359091169060400135610372565b6101c36103ff565b6040805160ff9092168252519081900360200190f35b610157600480360360408110156101ef57600080fd5b506001600160a01b038135169060200135610408565b6101736004803603602081101561021b57600080fd5b50356001600160a01b031661045c565b6100b6610477565b6101576004803603604081101561024957600080fd5b506001600160a01b0381351690602001356104d8565b6101576004803603604081101561027557600080fd5b506001600160a01b038135169060200135610546565b610173600480360360408110156102a157600080fd5b506001600160a01b038135811691602001351661055a565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b820191906000526020600020905b81548152906001019060200180831161032857829003601f168201915b5050505050905090565b600061036361035c610585565b8484610589565b50600192915050565b60025490565b600061037f848484610675565b6103f58461038b610585565b6103f085604051806060016040528060288152602001610945602891396001600160a01b038a166000908152600160205260408120906103c9610585565b6001600160a01b03168152602081019190915260400160002054919063ffffffff6107dc16565b610589565b5060019392505050565b60055460ff1690565b6000610363610415610585565b846103f08560016000610426610585565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff61087316565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156103455780601f1061031a57610100808354040283529160200191610345565b60006103636104e5610585565b846103f0856040518060600160405280602581526020016109b6602591396001600061050f610585565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff6107dc16565b6000610363610553610585565b8484610675565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3390565b6001600160a01b0383166105ce5760405162461bcd60e51b81526004018080602001828103825260248152602001806109926024913960400191505060405180910390fd5b6001600160a01b0382166106135760405162461bcd60e51b81526004018080602001828103825260228152602001806108fd6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166106ba5760405162461bcd60e51b815260040180806020018281038252602581526020018061096d6025913960400191505060405180910390fd5b6001600160a01b0382166106ff5760405162461bcd60e51b81526004018080602001828103825260238152602001806108da6023913960400191505060405180910390fd5b61070a8383836108d4565b61074d8160405180606001604052806026815260200161091f602691396001600160a01b038616600090815260208190526040902054919063ffffffff6107dc16565b6001600160a01b038085166000908152602081905260408082209390935590841681522054610782908263ffffffff61087316565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b6000818484111561086b5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610830578181015183820152602001610818565b50505050905090810190601f16801561085d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000828201838110156108cd576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122007c47290196c2ddfd0852f1dfc62deceb75163eca44542513eab3af1a7a838f964736f6c63430006060033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.dbg.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.dbg.json deleted file mode 100644 index e548d120de..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.dbg.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../../build-info/10e5405221d21329b74f12bf00d0cd2d.json" -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.json deleted file mode 100644 index 663a02d9d8..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.json +++ /dev/null @@ -1,194 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "IERC20", - "sourceName": "@openzeppelin/contracts/token/ERC20/IERC20.sol", - "abi": [ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "0x", - "deployedBytecode": "0x", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/SafeERC20.sol/SafeERC20.dbg.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/SafeERC20.sol/SafeERC20.dbg.json deleted file mode 100644 index e548d120de..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/SafeERC20.sol/SafeERC20.dbg.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../../build-info/10e5405221d21329b74f12bf00d0cd2d.json" -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/SafeERC20.sol/SafeERC20.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/SafeERC20.sol/SafeERC20.json deleted file mode 100644 index 32820d7a1f..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/token/ERC20/SafeERC20.sol/SafeERC20.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "SafeERC20", - "sourceName": "@openzeppelin/contracts/token/ERC20/SafeERC20.sol", - "abi": [], - "bytecode": "0x60566023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220a10ad72c41cead301751dbbfdbfa8dc0de387237baaadf00c593410d921ba53164736f6c63430006060033", - "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220a10ad72c41cead301751dbbfdbfa8dc0de387237baaadf00c593410d921ba53164736f6c63430006060033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Address.sol/Address.dbg.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Address.sol/Address.dbg.json deleted file mode 100644 index bea78f6d8d..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Address.sol/Address.dbg.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../build-info/10e5405221d21329b74f12bf00d0cd2d.json" -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Address.sol/Address.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Address.sol/Address.json deleted file mode 100644 index 3113f68c6d..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Address.sol/Address.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "Address", - "sourceName": "@openzeppelin/contracts/utils/Address.sol", - "abi": [], - "bytecode": "0x60566023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220bf51151e2a4e160194baa49fc39a27bad1253743e398fefe6fb6935ec7d37a5e64736f6c63430006060033", - "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220bf51151e2a4e160194baa49fc39a27bad1253743e398fefe6fb6935ec7d37a5e64736f6c63430006060033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Context.sol/Context.dbg.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Context.sol/Context.dbg.json deleted file mode 100644 index bea78f6d8d..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Context.sol/Context.dbg.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../build-info/10e5405221d21329b74f12bf00d0cd2d.json" -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Context.sol/Context.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Context.sol/Context.json deleted file mode 100644 index 8fe86fc78f..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/Context.sol/Context.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "Context", - "sourceName": "@openzeppelin/contracts/utils/Context.sol", - "abi": [], - "bytecode": "0x", - "deployedBytecode": "0x", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/ReentrancyGuard.sol/ReentrancyGuard.dbg.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/ReentrancyGuard.sol/ReentrancyGuard.dbg.json deleted file mode 100644 index bea78f6d8d..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/ReentrancyGuard.sol/ReentrancyGuard.dbg.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "_format": "hh-sol-dbg-1", - "buildInfo": "../../../../build-info/10e5405221d21329b74f12bf00d0cd2d.json" -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/ReentrancyGuard.sol/ReentrancyGuard.json b/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/ReentrancyGuard.sol/ReentrancyGuard.json deleted file mode 100644 index 528717537e..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/@openzeppelin/contracts/utils/ReentrancyGuard.sol/ReentrancyGuard.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "ReentrancyGuard", - "sourceName": "@openzeppelin/contracts/utils/ReentrancyGuard.sol", - "abi": [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - } - ], - "bytecode": "0x", - "deployedBytecode": "0x", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/BandwidthGenerator.sol/BandwidthGenerator.json b/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/BandwidthGenerator.sol/BandwidthGenerator.json deleted file mode 100644 index 5dcfb9588c..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/BandwidthGenerator.sol/BandwidthGenerator.json +++ /dev/null @@ -1,222 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "BandwidthGenerator", - "sourceName": "contracts/BandwidthGenerator.sol", - "abi": [ - { - "inputs": [ - { - "internalType": "contract CosmosERC20", - "name": "_erc20", - "type": "address" - }, - { - "internalType": "contract Gravity", - "name": "_gravityBridge", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "Bandwidth", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "VerificationKey", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "SignedVerificationKey", - "type": "bytes" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "CosmosRecipient", - "type": "bytes32" - } - ], - "name": "BBCredentialPurchased", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "NewBytesPerToken", - "type": "uint256" - } - ], - "name": "RatioChanged", - "type": "event" - }, - { - "inputs": [], - "name": "BytesPerToken", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_amount", - "type": "uint256" - } - ], - "name": "bandwidthFromToken", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_newBytesPerTokenAmount", - "type": "uint256" - } - ], - "name": "changeRatio", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "erc20", - "outputs": [ - { - "internalType": "contract CosmosERC20", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_verificationKey", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_signedVerificationKey", - "type": "bytes" - }, - { - "internalType": "bytes32", - "name": "_cosmosRecipient", - "type": "bytes32" - } - ], - "name": "generateBasicBandwidthCredential", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "gravityBridge", - "outputs": [ - { - "internalType": "contract Gravity", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "0x608060405234801561001057600080fd5b50604051610b18380380610b188339818101604052604081101561003357600080fd5b508051602090910151600061004f6001600160e01b0361015c16565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001600160a01b0382166100de5760405162461bcd60e51b8152600401808060200182810382526030815260200180610ae86030913960400191505060405180910390fd5b6001600160a01b0381166101235760405162461bcd60e51b8152600401808060200182810382526039815260200180610aaf6039913960400191505060405180910390fd5b600180546001600160a01b039384166001600160a01b031991821617909155600280549290931691161790556340000000600355610160565b3390565b6109408061016f6000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c80639e0dda8b116100665780639e0dda8b146101825780639fbb65e9146101b1578063a4da2d02146101b9578063f2fde38b146101c1578063f6a33253146101e757610093565b8063715018a61461009857806373f48677146100a2578063785e9e86146101565780638da5cb5b1461017a575b600080fd5b6100a0610204565b005b6100a0600480360360808110156100b857600080fd5b8135916020810135918101906060810160408201356401000000008111156100df57600080fd5b8201836020820111156100f157600080fd5b8035906020019184600183028401116401000000008311171561011357600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955050913592506102c2915050565b61015e61053b565b604080516001600160a01b039092168252519081900360200190f35b61015e61054a565b61019f6004803603602081101561019857600080fd5b5035610559565b60408051918252519081900360200190f35b61019f610592565b61015e610598565b6100a0600480360360208110156101d757600080fd5b50356001600160a01b03166105a7565b6100a0600480360360208110156101fd57600080fd5b50356106bb565b61020c61079e565b6001600160a01b031661021d61054a565b6001600160a01b031614610278576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b81516040146103025760405162461bcd60e51b81526004018080602001828103825260338152602001806108d86033913960400191505060405180910390fd5b600154604080516323b872dd60e01b81523360048201523060248201526044810187905290516001600160a01b03909216916323b872dd916064808201926020929091908290030181600087803b15801561035c57600080fd5b505af1158015610370573d6000803e3d6000fd5b505050506040513d602081101561038657600080fd5b50506001546002546040805163095ea7b360e01b81526001600160a01b039283166004820152602481018890529051919092169163095ea7b39160448083019260209291908290030181600087803b1580156103e157600080fd5b505af11580156103f5573d6000803e3d6000fd5b505050506040513d602081101561040b57600080fd5b505060025460015460408051631ffbe7f960e01b81526001600160a01b039283166004820152602481018590526044810188905290519190921691631ffbe7f991606480830192600092919082900301818387803b15801561046c57600080fd5b505af1158015610480573d6000803e3d6000fd5b50505050600061048f85610559565b905081847fc300d88f6eea8461741d440d04aa6147e081770af25e30862f6cc38d1033b9d183866040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156104f95781810151838201526020016104e1565b50505050905090810190601f1680156105265780820380516001836020036101000a031916815260200191505b50935050505060405180910390a35050505050565b6001546001600160a01b031681565b6000546001600160a01b031690565b600080610571600354846107a290919063ffffffff16565b905061058b81670de0b6b3a764000063ffffffff61080416565b9392505050565b60035481565b6002546001600160a01b031681565b6105af61079e565b6001600160a01b03166105c061054a565b6001600160a01b03161461061b576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166106605760405162461bcd60e51b81526004018080602001828103825260268152602001806108916026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6106c361079e565b6001600160a01b03166106d461054a565b6001600160a01b03161461072f576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b8061076b5760405162461bcd60e51b815260040180806020018281038252602581526020018061086c6025913960400191505060405180910390fd5b600381905560405181907fe200219383a2dbe79fad4e1549a81b6057429299f33583641c5fdddcdc0b379790600090a250565b3390565b6000826107b1575060006107fe565b828202828482816107be57fe5b04146107fb5760405162461bcd60e51b81526004018080602001828103825260218152602001806108b76021913960400191505060405180910390fd5b90505b92915050565b600080821161085a576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161086357fe5b04939250505056fe42616e64776964746847656e657261746f723a2070726963652063616e6e6f7420626520304f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7742616e64776964746847656e657261746f723a205369676e617475726520646f65736e27742068617665203634206279746573a2646970667358221220f91ec99d2617d86852e5a7b9e83177368e551623bc5a0faa1daa4f51dfd32a2764736f6c6343000606003342616e64776964746847656e657261746f723a20677261766974792062726964676520616464726573732063616e6e6f74206265206e756c6c42616e64776964746847656e657261746f723a20657263323020616464726573732063616e6e6f74206265206e756c6c", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100935760003560e01c80639e0dda8b116100665780639e0dda8b146101825780639fbb65e9146101b1578063a4da2d02146101b9578063f2fde38b146101c1578063f6a33253146101e757610093565b8063715018a61461009857806373f48677146100a2578063785e9e86146101565780638da5cb5b1461017a575b600080fd5b6100a0610204565b005b6100a0600480360360808110156100b857600080fd5b8135916020810135918101906060810160408201356401000000008111156100df57600080fd5b8201836020820111156100f157600080fd5b8035906020019184600183028401116401000000008311171561011357600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955050913592506102c2915050565b61015e61053b565b604080516001600160a01b039092168252519081900360200190f35b61015e61054a565b61019f6004803603602081101561019857600080fd5b5035610559565b60408051918252519081900360200190f35b61019f610592565b61015e610598565b6100a0600480360360208110156101d757600080fd5b50356001600160a01b03166105a7565b6100a0600480360360208110156101fd57600080fd5b50356106bb565b61020c61079e565b6001600160a01b031661021d61054a565b6001600160a01b031614610278576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b81516040146103025760405162461bcd60e51b81526004018080602001828103825260338152602001806108d86033913960400191505060405180910390fd5b600154604080516323b872dd60e01b81523360048201523060248201526044810187905290516001600160a01b03909216916323b872dd916064808201926020929091908290030181600087803b15801561035c57600080fd5b505af1158015610370573d6000803e3d6000fd5b505050506040513d602081101561038657600080fd5b50506001546002546040805163095ea7b360e01b81526001600160a01b039283166004820152602481018890529051919092169163095ea7b39160448083019260209291908290030181600087803b1580156103e157600080fd5b505af11580156103f5573d6000803e3d6000fd5b505050506040513d602081101561040b57600080fd5b505060025460015460408051631ffbe7f960e01b81526001600160a01b039283166004820152602481018590526044810188905290519190921691631ffbe7f991606480830192600092919082900301818387803b15801561046c57600080fd5b505af1158015610480573d6000803e3d6000fd5b50505050600061048f85610559565b905081847fc300d88f6eea8461741d440d04aa6147e081770af25e30862f6cc38d1033b9d183866040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156104f95781810151838201526020016104e1565b50505050905090810190601f1680156105265780820380516001836020036101000a031916815260200191505b50935050505060405180910390a35050505050565b6001546001600160a01b031681565b6000546001600160a01b031690565b600080610571600354846107a290919063ffffffff16565b905061058b81670de0b6b3a764000063ffffffff61080416565b9392505050565b60035481565b6002546001600160a01b031681565b6105af61079e565b6001600160a01b03166105c061054a565b6001600160a01b03161461061b576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166106605760405162461bcd60e51b81526004018080602001828103825260268152602001806108916026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6106c361079e565b6001600160a01b03166106d461054a565b6001600160a01b03161461072f576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b8061076b5760405162461bcd60e51b815260040180806020018281038252602581526020018061086c6025913960400191505060405180910390fd5b600381905560405181907fe200219383a2dbe79fad4e1549a81b6057429299f33583641c5fdddcdc0b379790600090a250565b3390565b6000826107b1575060006107fe565b828202828482816107be57fe5b04146107fb5760405162461bcd60e51b81526004018080602001828103825260218152602001806108b76021913960400191505060405180910390fd5b90505b92915050565b600080821161085a576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161086357fe5b04939250505056fe42616e64776964746847656e657261746f723a2070726963652063616e6e6f7420626520304f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7742616e64776964746847656e657261746f723a205369676e617475726520646f65736e27742068617665203634206279746573a2646970667358221220f91ec99d2617d86852e5a7b9e83177368e551623bc5a0faa1daa4f51dfd32a2764736f6c63430006060033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/CosmosToken.sol/CosmosERC20.dbg.json b/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/CosmosToken.sol/CosmosERC20.dbg.json deleted file mode 100644 index 24a82a1032..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/CosmosToken.sol/CosmosERC20.dbg.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "_format": "hh-sol-dbg-1", - "buildInfo": "../../build-info/10e5405221d21329b74f12bf00d0cd2d.json" -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/CosmosToken.sol/CosmosERC20.json b/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/CosmosToken.sol/CosmosERC20.json deleted file mode 100644 index fa39aa04ea..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/CosmosToken.sol/CosmosERC20.json +++ /dev/null @@ -1,325 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "CosmosERC20", - "sourceName": "contracts/CosmosToken.sol", - "abi": [ - { - "inputs": [ - { - "internalType": "address", - "name": "_gravityAddress", - "type": "address" - }, - { - "internalType": "string", - "name": "_name", - "type": "string" - }, - { - "internalType": "string", - "name": "_symbol", - "type": "string" - }, - { - "internalType": "uint8", - "name": "_decimals", - "type": "uint8" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "subtractedValue", - "type": "uint256" - } - ], - "name": "decreaseAllowance", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "addedValue", - "type": "uint256" - } - ], - "name": "increaseAllowance", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_amount", - "type": "uint256" - } - ], - "name": "mintForUnitTesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "0x6080604052600160ff1b6006553480156200001957600080fd5b5060405162000fc138038062000fc1833981810160405260808110156200003f57600080fd5b8151602083018051604051929492938301929190846401000000008211156200006757600080fd5b9083019060208201858111156200007d57600080fd5b82516401000000008111828201881017156200009857600080fd5b82525081516020918201929091019080838360005b83811015620000c7578181015183820152602001620000ad565b50505050905090810190601f168015620000f55780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200011957600080fd5b9083019060208201858111156200012f57600080fd5b82516401000000008111828201881017156200014a57600080fd5b82525081516020918201929091019080838360005b83811015620001795781810151838201526020016200015f565b50505050905090810190601f168015620001a75780820380516001836020036101000a031916815260200191505b5060405260209081015185519093508592508491620001cc91600391850190620003b9565b508051620001e2906004906020840190620003b9565b50506005805460ff191660121790555062000206816001600160e01b036200022416565b6200021a846006546200023a60201b60201c565b505050506200045e565b6005805460ff191660ff92909216919091179055565b6001600160a01b03821662000296576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b620002ad600083836001600160e01b036200035216565b620002c9816002546200035760201b620009b61790919060201c565b6002556001600160a01b03821660009081526020818152604090912054620002fc918390620009b662000357821b17901c565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b505050565b600082820183811015620003b2576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620003fc57805160ff19168380011785556200042c565b828001600101855582156200042c579182015b828111156200042c5782518255916020019190600101906200040f565b506200043a9291506200043e565b5090565b6200045b91905b808211156200043a576000815560010162000445565b90565b610b53806200046e6000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c80633950935111610071578063395093511461021257806370a082311461023e57806395d89b4114610264578063a457c2d71461026c578063a9059cbb14610298578063dd62ed3e146102c4576100b4565b806306fdde03146100b9578063095ea7b31461013657806318160ddd1461017657806323b872dd146101905780632ebdf54d146101c6578063313ce567146101f4575b600080fd5b6100c16102f2565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fb5781810151838201526020016100e3565b50505050905090810190601f1680156101285780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101626004803603604081101561014c57600080fd5b506001600160a01b038135169060200135610388565b604080519115158252519081900360200190f35b61017e6103a5565b60408051918252519081900360200190f35b610162600480360360608110156101a657600080fd5b506001600160a01b038135811691602081013590911690604001356103ab565b6101f2600480360360408110156101dc57600080fd5b506001600160a01b038135169060200135610438565b005b6101fc610446565b6040805160ff9092168252519081900360200190f35b6101626004803603604081101561022857600080fd5b506001600160a01b03813516906020013561044f565b61017e6004803603602081101561025457600080fd5b50356001600160a01b03166104a3565b6100c16104be565b6101626004803603604081101561028257600080fd5b506001600160a01b03813516906020013561051f565b610162600480360360408110156102ae57600080fd5b506001600160a01b03813516906020013561058d565b61017e600480360360408110156102da57600080fd5b506001600160a01b03813581169160200135166105a1565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b820191906000526020600020905b81548152906001019060200180831161036157829003601f168201915b5050505050905090565b600061039c6103956105cc565b84846105d0565b50600192915050565b60025490565b60006103b88484846106bc565b61042e846103c46105cc565b61042985604051806060016040528060288152602001610a88602891396001600160a01b038a166000908152600160205260408120906104026105cc565b6001600160a01b03168152602081019190915260400160002054919063ffffffff61082316565b6105d0565b5060019392505050565b61044282826108ba565b5050565b60055460ff1690565b600061039c61045c6105cc565b84610429856001600061046d6105cc565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff6109b616565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b600061039c61052c6105cc565b8461042985604051806060016040528060258152602001610af960259139600160006105566105cc565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff61082316565b600061039c61059a6105cc565b84846106bc565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3390565b6001600160a01b0383166106155760405162461bcd60e51b8152600401808060200182810382526024815260200180610ad56024913960400191505060405180910390fd5b6001600160a01b03821661065a5760405162461bcd60e51b8152600401808060200182810382526022815260200180610a406022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107015760405162461bcd60e51b8152600401808060200182810382526025815260200180610ab06025913960400191505060405180910390fd5b6001600160a01b0382166107465760405162461bcd60e51b8152600401808060200182810382526023815260200180610a1d6023913960400191505060405180910390fd5b610751838383610a17565b61079481604051806060016040528060268152602001610a62602691396001600160a01b038616600090815260208190526040902054919063ffffffff61082316565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546107c9908263ffffffff6109b616565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108b25760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561087757818101518382015260200161085f565b50505050905090810190601f1680156108a45780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6001600160a01b038216610915576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b61092160008383610a17565b600254610934908263ffffffff6109b616565b6002556001600160a01b038216600090815260208190526040902054610960908263ffffffff6109b616565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b600082820183811015610a10576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220465410ed025b5712ee2eaddad7a20856f64710d3b57e1ff91396130750dfabd564736f6c63430006060033", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100b45760003560e01c80633950935111610071578063395093511461021257806370a082311461023e57806395d89b4114610264578063a457c2d71461026c578063a9059cbb14610298578063dd62ed3e146102c4576100b4565b806306fdde03146100b9578063095ea7b31461013657806318160ddd1461017657806323b872dd146101905780632ebdf54d146101c6578063313ce567146101f4575b600080fd5b6100c16102f2565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fb5781810151838201526020016100e3565b50505050905090810190601f1680156101285780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101626004803603604081101561014c57600080fd5b506001600160a01b038135169060200135610388565b604080519115158252519081900360200190f35b61017e6103a5565b60408051918252519081900360200190f35b610162600480360360608110156101a657600080fd5b506001600160a01b038135811691602081013590911690604001356103ab565b6101f2600480360360408110156101dc57600080fd5b506001600160a01b038135169060200135610438565b005b6101fc610446565b6040805160ff9092168252519081900360200190f35b6101626004803603604081101561022857600080fd5b506001600160a01b03813516906020013561044f565b61017e6004803603602081101561025457600080fd5b50356001600160a01b03166104a3565b6100c16104be565b6101626004803603604081101561028257600080fd5b506001600160a01b03813516906020013561051f565b610162600480360360408110156102ae57600080fd5b506001600160a01b03813516906020013561058d565b61017e600480360360408110156102da57600080fd5b506001600160a01b03813581169160200135166105a1565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b820191906000526020600020905b81548152906001019060200180831161036157829003601f168201915b5050505050905090565b600061039c6103956105cc565b84846105d0565b50600192915050565b60025490565b60006103b88484846106bc565b61042e846103c46105cc565b61042985604051806060016040528060288152602001610a88602891396001600160a01b038a166000908152600160205260408120906104026105cc565b6001600160a01b03168152602081019190915260400160002054919063ffffffff61082316565b6105d0565b5060019392505050565b61044282826108ba565b5050565b60055460ff1690565b600061039c61045c6105cc565b84610429856001600061046d6105cc565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff6109b616565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b600061039c61052c6105cc565b8461042985604051806060016040528060258152602001610af960259139600160006105566105cc565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff61082316565b600061039c61059a6105cc565b84846106bc565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3390565b6001600160a01b0383166106155760405162461bcd60e51b8152600401808060200182810382526024815260200180610ad56024913960400191505060405180910390fd5b6001600160a01b03821661065a5760405162461bcd60e51b8152600401808060200182810382526022815260200180610a406022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107015760405162461bcd60e51b8152600401808060200182810382526025815260200180610ab06025913960400191505060405180910390fd5b6001600160a01b0382166107465760405162461bcd60e51b8152600401808060200182810382526023815260200180610a1d6023913960400191505060405180910390fd5b610751838383610a17565b61079481604051806060016040528060268152602001610a62602691396001600160a01b038616600090815260208190526040902054919063ffffffff61082316565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546107c9908263ffffffff6109b616565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108b25760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561087757818101518382015260200161085f565b50505050905090810190601f1680156108a45780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6001600160a01b038216610915576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b61092160008383610a17565b600254610934908263ffffffff6109b616565b6002556001600160a01b038216600090815260208190526040902054610960908263ffffffff6109b616565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b600082820183811015610a10576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220465410ed025b5712ee2eaddad7a20856f64710d3b57e1ff91396130750dfabd564736f6c63430006060033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/Gravity.sol/Gravity.dbg.json b/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/Gravity.sol/Gravity.dbg.json deleted file mode 100644 index 24a82a1032..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/Gravity.sol/Gravity.dbg.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "_format": "hh-sol-dbg-1", - "buildInfo": "../../build-info/10e5405221d21329b74f12bf00d0cd2d.json" -} diff --git a/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/Gravity.sol/Gravity.json b/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/Gravity.sol/Gravity.json deleted file mode 100644 index 109851c28c..0000000000 --- a/contracts/basic-bandwidth-generation/artifacts/contracts/contracts/Gravity.sol/Gravity.json +++ /dev/null @@ -1,678 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "Gravity", - "sourceName": "contracts/Gravity.sol", - "abi": [ - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_gravityId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_powerThreshold", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "_validators", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "_powers", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "string", - "name": "_cosmosDenom", - "type": "string" - }, - { - "indexed": true, - "internalType": "address", - "name": "_tokenContract", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "_name", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "_symbol", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "_decimals", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "_eventNonce", - "type": "uint256" - } - ], - "name": "ERC20DeployedEvent", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32", - "name": "_invalidationId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "_invalidationNonce", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "_returnData", - "type": "bytes" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "_eventNonce", - "type": "uint256" - } - ], - "name": "LogicCallEvent", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "_tokenContract", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "_sender", - "type": "address" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "_destination", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "_amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "_eventNonce", - "type": "uint256" - } - ], - "name": "SendToCosmosEvent", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "_batchNonce", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "_token", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "_eventNonce", - "type": "uint256" - } - ], - "name": "TransactionBatchExecutedEvent", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "_newValsetNonce", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "_eventNonce", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address[]", - "name": "_validators", - "type": "address[]" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "_powers", - "type": "uint256[]" - } - ], - "name": "ValsetUpdatedEvent", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "_cosmosDenom", - "type": "string" - }, - { - "internalType": "string", - "name": "_name", - "type": "string" - }, - { - "internalType": "string", - "name": "_symbol", - "type": "string" - }, - { - "internalType": "uint8", - "name": "_decimals", - "type": "uint8" - } - ], - "name": "deployERC20", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_erc20Address", - "type": "address" - } - ], - "name": "lastBatchNonce", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_invalidation_id", - "type": "bytes32" - } - ], - "name": "lastLogicCallNonce", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_tokenContract", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "_destination", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_amount", - "type": "uint256" - } - ], - "name": "sendToCosmos", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "state_gravityId", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "state_invalidationMapping", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "state_lastBatchNonces", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "state_lastEventNonce", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "state_lastValsetCheckpoint", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "state_lastValsetNonce", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "state_powerThreshold", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "_currentValidators", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "_currentPowers", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "_currentValsetNonce", - "type": "uint256" - }, - { - "internalType": "uint8[]", - "name": "_v", - "type": "uint8[]" - }, - { - "internalType": "bytes32[]", - "name": "_r", - "type": "bytes32[]" - }, - { - "internalType": "bytes32[]", - "name": "_s", - "type": "bytes32[]" - }, - { - "internalType": "uint256[]", - "name": "_amounts", - "type": "uint256[]" - }, - { - "internalType": "address[]", - "name": "_destinations", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "_fees", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "_batchNonce", - "type": "uint256" - }, - { - "internalType": "address", - "name": "_tokenContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_batchTimeout", - "type": "uint256" - } - ], - "name": "submitBatch", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "_currentValidators", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "_currentPowers", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "_currentValsetNonce", - "type": "uint256" - }, - { - "internalType": "uint8[]", - "name": "_v", - "type": "uint8[]" - }, - { - "internalType": "bytes32[]", - "name": "_r", - "type": "bytes32[]" - }, - { - "internalType": "bytes32[]", - "name": "_s", - "type": "bytes32[]" - }, - { - "components": [ - { - "internalType": "uint256[]", - "name": "transferAmounts", - "type": "uint256[]" - }, - { - "internalType": "address[]", - "name": "transferTokenContracts", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "feeAmounts", - "type": "uint256[]" - }, - { - "internalType": "address[]", - "name": "feeTokenContracts", - "type": "address[]" - }, - { - "internalType": "address", - "name": "logicContractAddress", - "type": "address" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "timeOut", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "invalidationId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "invalidationNonce", - "type": "uint256" - } - ], - "internalType": "struct LogicCallArgs", - "name": "_args", - "type": "tuple" - } - ], - "name": "submitLogicCall", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "_currentValidators", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "_currentPowers", - "type": "uint256[]" - }, - { - "internalType": "uint8[]", - "name": "_v", - "type": "uint8[]" - }, - { - "internalType": "bytes32[]", - "name": "_r", - "type": "bytes32[]" - }, - { - "internalType": "bytes32[]", - "name": "_s", - "type": "bytes32[]" - }, - { - "internalType": "bytes32", - "name": "_theHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_powerThreshold", - "type": "uint256" - } - ], - "name": "testCheckValidatorSignatures", - "outputs": [], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "_validators", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "_powers", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "_valsetNonce", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_gravityId", - "type": "bytes32" - } - ], - "name": "testMakeCheckpoint", - "outputs": [], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "_newValidators", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "_newPowers", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "_newValsetNonce", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "_currentValidators", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "_currentPowers", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "_currentValsetNonce", - "type": "uint256" - }, - { - "internalType": "uint8[]", - "name": "_v", - "type": "uint8[]" - }, - { - "internalType": "bytes32[]", - "name": "_r", - "type": "bytes32[]" - }, - { - "internalType": "bytes32[]", - "name": "_s", - "type": "bytes32[]" - } - ], - "name": "updateValset", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "0x6080604052600060045560016005553480156200001b57600080fd5b50604051620038c2380380620038c28339810160408190526200003e9162000233565b60016000558051825114620000705760405162461bcd60e51b8152600401620000679062000431565b60405180910390fd5b6000805b8251811015620000af578281815181106200008b57fe5b60200260200101518201915084821115620000a657620000af565b60010162000074565b50838111620000d25760405162461bcd60e51b81526004016200006790620003d4565b6000620000eb848483896001600160e01b036200014d16565b60068790556007869055600181905560045460055460405192935090917fb119f1f36224601586b5037da909ecf37e83864dddea5d32ad4e32ac1d97e62b9162000139918890889062000468565b60405180910390a2505050505050620004e8565b6040516000906918da1958dadc1bda5b9d60b21b9082906200017c908590849088908b908b906020016200038d565b60408051808303601f190181529190528051602090910120979650505050505050565b80516001600160a01b0381168114620001b757600080fd5b92915050565b600082601f830112620001ce578081fd5b8151620001e5620001df82620004c8565b620004a1565b8181529150602080830190848101818402860182018710156200020757600080fd5b60005b8481101562000228578151845292820192908201906001016200020a565b505050505092915050565b6000806000806080858703121562000249578384fd5b845160208087015160408801519296509450906001600160401b038082111562000271578485fd5b81880189601f82011262000283578586fd5b8051925062000296620001df84620004c8565b83815284810190828601868602840187018d1015620002b3578889fd5b8893505b85841015620002e157620002cc8d826200019f565b835260019390930192918601918601620002b7565b5060608b01519097509450505080831115620002fb578384fd5b50506200030b87828801620001bd565b91505092959194509250565b6000815180845260208085019450808401835b83811015620003515781516001600160a01b0316875295820195908201906001016200032a565b509495945050505050565b6000815180845260208085019450808401835b8381101562000351578151875295820195908201906001016200036f565b600086825285602083015284604083015260a06060830152620003b460a083018562000317565b8281036080840152620003c881856200035c565b98975050505050505050565b6020808252603c908201527f5375626d69747465642076616c696461746f7220736574207369676e6174757260408201527f657320646f206e6f74206861766520656e6f75676820706f7765722e00000000606082015260800190565b6020808252601f908201527f4d616c666f726d65642063757272656e742076616c696461746f722073657400604082015260600190565b60008482526060602083015262000483606083018562000317565b82810360408401526200049781856200035c565b9695505050505050565b6040518181016001600160401b0381118282101715620004c057600080fd5b604052919050565b60006001600160401b03821115620004de578081fd5b5060209081020190565b6133ca80620004f86000396000f3fe60806040523480156200001157600080fd5b5060043610620001185760003560e01c8063c227c30b11620000a5578063e3cb9f62116200006f578063e3cb9f621462000224578063e5a2b5d2146200023b578063f2b533071462000245578063f7955637146200024f5762000118565b8063c227c30b14620001c8578063c9d194d514620001df578063db7c4e5714620001f6578063df97174b146200020d5762000118565b80637dfb6f8611620000e75780637dfb6f86146200018657806383b435db146200019d578063b56561fe14620001b4578063bdda81d414620001be5762000118565b8063011b2174146200011d5780630c246c82146200014c5780631ffbe7f9146200016557806373b20547146200017c575b600080fd5b620001346200012e366004620013ef565b62000266565b60405162000143919062001c0d565b60405180910390f35b620001636200015d36600462001830565b62000281565b005b62000163620001763660046200140d565b620005ce565b6200013462000684565b6200013462000197366004620019d4565b6200068a565b62000163620001ae36600462001678565b6200069c565b6200013462000968565b620001346200096e565b62000163620001d93660046200193b565b62000974565b62000134620001f0366004620019d4565b62000989565b62000163620002073660046200144e565b6200099b565b620001346200021e366004620013ef565b620009b5565b62000163620002353660046200153c565b620009c7565b6200013462000b53565b6200013462000b59565b6200016362000260366004620019ed565b62000b5f565b6001600160a01b031660009081526002602052604090205490565b60026000541415620002b05760405162461bcd60e51b8152600401620002a790620022df565b60405180910390fd5b600260005560c08101514310620002db5760405162461bcd60e51b8152600401620002a79062001fec565b61010081015160e082015160009081526003602052604090205410620003155760405162461bcd60e51b8152600401620002a79062001f58565b8551875114801562000328575083518751145b801562000336575082518751145b801562000344575081518751145b620003635760405162461bcd60e51b8152600401620002a790620022a8565b6001546200037688888860065462000c11565b14620003965760405162461bcd60e51b8152600401620002a79062001e6e565b60208101515181515114620003bf5760405162461bcd60e51b8152600401620002a790620020f5565b80606001515181604001515114620003eb5760405162461bcd60e51b8152600401620002a79062001e3e565b6000600654681b1bd9da58d0d85b1b60ba1b836000015184602001518560400151866060015187608001518860a001518960c001518a60e001518b6101000151604051602001620004479b9a9998979695949392919062001c5d565b6040516020818303038152906040528051906020012090506200047288888787878660075462000c63565b61010082015160e08301516000908152600360205260408120919091555b825151811015620004f857620004ef836080015184600001518381518110620004b557fe5b602002602001015185602001518481518110620004ce57fe5b60200260200101516001600160a01b031662000d6d9092919063ffffffff16565b60010162000490565b5060606200050f83608001518460a0015162000dcc565b905060005b8360400151518110156200055a576200055133856040015183815181106200053857fe5b602002602001015186606001518481518110620004ce57fe5b60010162000514565b506005546200057190600163ffffffff62000e1916565b600581905560e08401516101008501516040517f7c2bb24f8e1b3725cb613d7f11ef97d9745cc97a0e40f730621c052d684077a193620005b693929186919062001d83565b60405180910390a15050600160005550505050505050565b60026000541415620005f45760405162461bcd60e51b8152600401620002a790620022df565b6002600055620006166001600160a01b03841633308463ffffffff62000e4116565b6005546200062c90600163ffffffff62000e1916565b6005819055604051839133916001600160a01b038716917fd7767894d73c589daeca9643f445f03d7be61aad2950c117e7cbff4176fca7e491620006729187916200234f565b60405180910390a45050600160005550565b60055481565b60036020526000908152604090205481565b60026000541415620006c25760405162461bcd60e51b8152600401620002a790620022df565b600260008181556001600160a01b038416815260209190915260409020548311620007015760405162461bcd60e51b8152600401620002a79062001ecb565b804310620007235760405162461bcd60e51b8152600401620002a79062002098565b8a518c5114801562000736575088518c51145b801562000744575087518c51145b801562000752575086518c51145b620007715760405162461bcd60e51b8152600401620002a790620022a8565b600154620007848d8d8d60065462000c11565b14620007a45760405162461bcd60e51b8152600401620002a79062001e6e565b84518651148015620007b7575083518651145b620007d65760405162461bcd60e51b8152600401620002a79062001f21565b620008348c8c8b8b8b6006546f0e8e4c2dce6c2c6e8d2dedc84c2e8c6d60831b8d8d8d8d8d8d6040516020016200081598979695949392919062001d0c565b6040516020818303038152906040528051906020012060075462000c63565b6001600160a01b0382166000908152600260205260408120849055805b8751811015620008d757620008a38782815181106200086c57fe5b60200260200101518983815181106200088157fe5b6020026020010151866001600160a01b031662000d6d9092919063ffffffff16565b620008cc868281518110620008b457fe5b60200260200101518362000e1990919063ffffffff16565b915060010162000851565b50620008f46001600160a01b038416338363ffffffff62000d6d16565b506005546200090b90600163ffffffff62000e1916565b60058190556040516001600160a01b0384169185917f02c7e81975f8edb86e2a0c038b7b86a49c744236abf0f6177ff5afc6986ab708916200094d9162001c0d565b60405180910390a35050600160005550505050505050505050565b60045481565b60065481565b620009828484848462000c11565b5050505050565b60009081526003602052604090205490565b620009ac8787878787878762000c63565b50505050505050565b60026020526000908152604090205481565b60026000541415620009ed5760405162461bcd60e51b8152600401620002a790620022df565b600260005583871162000a145760405162461bcd60e51b8152600401620002a790620021ca565b875189511462000a385760405162461bcd60e51b8152600401620002a79062002136565b8451865114801562000a4b575082518651145b801562000a59575081518651145b801562000a67575080518651145b62000a865760405162461bcd60e51b8152600401620002a790620022a8565b60015462000a9987878760065462000c11565b1462000ab95760405162461bcd60e51b8152600401620002a79062001e6e565b600062000acb8a8a8a60065462000c11565b905062000ae087878686868660075462000c63565b6001818155600489905560055462000afe9163ffffffff62000e1916565b600581905560405189917fb119f1f36224601586b5037da909ecf37e83864dddea5d32ad4e32ac1d97e62b9162000b3a91908e908e9062002316565b60405180910390a2505060016000555050505050505050565b60075481565b60015481565b60003084848460405162000b7390620010d0565b62000b82949392919062001ba5565b604051809103906000f08015801562000b9f573d6000803e3d6000fd5b5060055490915062000bb990600163ffffffff62000e1916565b60058190556040516001600160a01b038316917f82fe3a4fa49c6382d0c085746698ddbbafe6c2bf61285b19410644b5b26287c79162000c029189918991899189919062001de8565b60405180910390a25050505050565b6040516000906918da1958dadc1bda5b9d60b21b90829062000c40908590849088908b908b9060200162001c16565b60408051808303601f190181529190528051602090910120979650505050505050565b6000805b885181101562000d405786818151811062000c7e57fe5b602002602001015160ff1660001462000d375762000cf089828151811062000ca257fe5b60200260200101518589848151811062000cb857fe5b602002602001015189858151811062000ccd57fe5b602002602001015189868151811062000ce257fe5b602002602001015162000e6b565b62000d0f5760405162461bcd60e51b8152600401620002a7906200200f565b87818151811062000d1c57fe5b6020026020010151820191508282111562000d375762000d40565b60010162000c67565b5081811162000d635760405162461bcd60e51b8152600401620002a7906200216d565b5050505050505050565b62000dc78363a9059cbb60e01b848460405160240162000d8f92919062001bf4565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915262000f0b565b505050565b606062000e1083836040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c6564000081525062000fa2565b90505b92915050565b60008282018381101562000e105760405162461bcd60e51b8152600401620002a79062001fb5565b62000e65846323b872dd60e01b85858560405160240162000d8f9392919062001b81565b50505050565b6000808560405160200162000e81919062001b50565b6040516020818303038152906040528051906020012090506001818686866040516000815260200160405260405162000ebe949392919062001db5565b6020604051602081039080840390855afa15801562000ee1573d6000803e3d6000fd5b505050602060405103516001600160a01b0316876001600160a01b03161491505095945050505050565b606062000f62826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031662000fa29092919063ffffffff16565b80519091501562000dc7578080602001905181019062000f839190620019b2565b62000dc75760405162461bcd60e51b8152600401620002a7906200225e565b606062000fb3848460008562000fbd565b90505b9392505050565b60608247101562000fe25760405162461bcd60e51b8152600401620002a79062002052565b62000fed856200108c565b6200100c5760405162461bcd60e51b8152600401620002a79062002227565b60006060866001600160a01b031685876040516200102b919062001b32565b60006040518083038185875af1925050503d80600081146200106a576040519150601f19603f3d011682016040523d82523d6000602084013e6200106f565b606091505b50915091506200108182828662001092565b979650505050505050565b3b151590565b60608315620010a357508162000fb6565b825115620010b45782518084602001fd5b8160405162461bcd60e51b8152600401620002a7919062001dd3565b610fc180620023d483390190565b80356001600160a01b038116811462000e1357600080fd5b600082601f83011262001107578081fd5b81356200111e620011188262002384565b6200235d565b8181529150602080830190848101818402860182018710156200114057600080fd5b60005b848110156200116b57620011588883620010de565b8452928201929082019060010162001143565b505050505092915050565b600082601f83011262001187578081fd5b813562001198620011188262002384565b818152915060208083019084810181840286018201871015620011ba57600080fd5b60005b848110156200116b57813584529282019290820190600101620011bd565b600082601f830112620011ec578081fd5b8135620011fd620011188262002384565b8181529150602080830190848101818402860182018710156200121f57600080fd5b60005b848110156200116b57620012378883620013dd565b8452928201929082019060010162001222565b600082601f8301126200125b578081fd5b81356001600160401b0381111562001271578182fd5b62001286601f8201601f19166020016200235d565b91508082528360208285010111156200129e57600080fd5b8060208401602084013760009082016020015292915050565b6000610120808385031215620012cb578182fd5b620012d6816200235d565b91505081356001600160401b0380821115620012f157600080fd5b620012ff8583860162001176565b835260208401359150808211156200131657600080fd5b6200132485838601620010f6565b602084015260408401359150808211156200133e57600080fd5b6200134c8583860162001176565b604084015260608401359150808211156200136657600080fd5b6200137485838601620010f6565b6060840152620013888560808601620010de565b608084015260a0840135915080821115620013a257600080fd5b50620013b1848285016200124a565b60a08301525060c082013560c082015260e082013560e082015261010080830135818301525092915050565b803560ff8116811462000e1357600080fd5b60006020828403121562001401578081fd5b62000e108383620010de565b60008060006060848603121562001422578182fd5b83356001600160a01b038116811462001439578283fd5b95602085013595506040909401359392505050565b600080600080600080600060e0888a03121562001469578283fd5b87356001600160401b038082111562001480578485fd5b6200148e8b838c01620010f6565b985060208a0135915080821115620014a4578485fd5b620014b28b838c0162001176565b975060408a0135915080821115620014c8578485fd5b620014d68b838c01620011db565b965060608a0135915080821115620014ec578485fd5b620014fa8b838c0162001176565b955060808a013591508082111562001510578485fd5b506200151f8a828b0162001176565b93505060a0880135915060c0880135905092959891949750929550565b60008060008060008060008060006101208a8c0312156200155b578182fd5b89356001600160401b038082111562001572578384fd5b620015808d838e01620010f6565b9a5060208c013591508082111562001596578384fd5b620015a48d838e0162001176565b995060408c0135985060608c0135915080821115620015c1578384fd5b620015cf8d838e01620010f6565b975060808c0135915080821115620015e5578384fd5b620015f38d838e0162001176565b965060a08c0135955060c08c013591508082111562001610578384fd5b6200161e8d838e01620011db565b945060e08c013591508082111562001634578384fd5b620016428d838e0162001176565b93506101008c013591508082111562001659578283fd5b50620016688c828d0162001176565b9150509295985092959850929598565b6000806000806000806000806000806000806101808d8f0312156200169b578586fd5b6001600160401b038d351115620016b0578586fd5b620016bf8e8e358f01620010f6565b9b506001600160401b0360208e01351115620016d9578586fd5b620016eb8e60208f01358f0162001176565b9a5060408d013599506001600160401b0360608e013511156200170c578586fd5b6200171e8e60608f01358f01620011db565b98506001600160401b0360808e0135111562001738578586fd5b6200174a8e60808f01358f0162001176565b97506001600160401b0360a08e0135111562001764578586fd5b620017768e60a08f01358f0162001176565b96506001600160401b0360c08e0135111562001790578586fd5b620017a28e60c08f01358f0162001176565b95506001600160401b0360e08e01351115620017bc578283fd5b620017ce8e60e08f01358f01620010f6565b94506001600160401b036101008e01351115620017e9578283fd5b620017fc8e6101008f01358f0162001176565b93506101208d01359250620018168e6101408f01620010de565b91506101608d013590509295989b509295989b509295989b565b600080600080600080600060e0888a0312156200184b578081fd5b87356001600160401b038082111562001862578283fd5b620018708b838c01620010f6565b985060208a013591508082111562001886578283fd5b620018948b838c0162001176565b975060408a0135965060608a0135915080821115620018b1578283fd5b620018bf8b838c01620011db565b955060808a0135915080821115620018d5578283fd5b620018e38b838c0162001176565b945060a08a0135915080821115620018f9578283fd5b620019078b838c0162001176565b935060c08a01359150808211156200191d578283fd5b506200192c8a828b01620012b7565b91505092959891949750929550565b6000806000806080858703121562001951578182fd5b84356001600160401b038082111562001968578384fd5b6200197688838901620010f6565b955060208701359150808211156200198c578384fd5b506200199b8782880162001176565b949794965050505060408301359260600135919050565b600060208284031215620019c4578081fd5b8151801515811462000e10578182fd5b600060208284031215620019e6578081fd5b5035919050565b6000806000806080858703121562001a03578182fd5b84356001600160401b038082111562001a1a578384fd5b62001a28888389016200124a565b9550602087013591508082111562001a3e578384fd5b62001a4c888389016200124a565b9450604087013591508082111562001a62578384fd5b5062001a71878288016200124a565b92505062001a838660608701620013dd565b905092959194509250565b6000815180845260208085019450808401835b8381101562001ac85781516001600160a01b03168752958201959082019060010162001aa1565b509495945050505050565b6000815180845260208085019450808401835b8381101562001ac85781518752958201959082019060010162001ae6565b6000815180845262001b1e816020860160208601620023a4565b601f01601f19169290920160200192915050565b6000825162001b46818460208701620023a4565b9190910192915050565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b038516815260806020820181905260009062001bcb9083018662001b04565b828103604084015262001bdf818662001b04565b91505060ff8316606083015295945050505050565b6001600160a01b03929092168252602082015260400190565b90815260200190565b600086825285602083015284604083015260a0606083015262001c3d60a083018562001a8e565b828103608084015262001c51818562001ad3565b98975050505050505050565b60006101608d83528c602084015280604084015262001c7f8184018d62001ad3565b838103606085015262001c93818d62001a8e565b915050828103608084015262001caa818b62001ad3565b83810360a085015262001cbe818b62001a8e565b6001600160a01b038a1660c086015284810360e0860152915062001ce59050818862001b04565b61010084019690965250506101208101929092526101409091015298975050505050505050565b60006101008a835289602084015280604084015262001d2e8184018a62001ad3565b838103606085015262001d42818a62001a8e565b915050828103608084015262001d59818862001ad3565b60a084019690965250506001600160a01b039290921660c083015260e09091015295945050505050565b60008582528460208301526080604083015262001da4608083018562001b04565b905082606083015295945050505050565b93845260ff9290921660208401526040830152606082015260800190565b60006020825262000e10602083018462001b04565b600060a0825262001dfd60a083018862001b04565b828103602084015262001e11818862001b04565b838103604085015262001e25818862001b04565b60ff969096166060850152505050608001529392505050565b6020808252601690820152754d616c666f726d6564206c697374206f66206665657360501b604082015260600190565b6020808252603f908201527f537570706c6965642063757272656e742076616c696461746f727320616e642060408201527f706f7765727320646f206e6f74206d6174636820636865636b706f696e742e00606082015260800190565b60208082526036908201527f4e6577206261746368206e6f6e6365206d7573742062652067726561746572206040820152757468616e207468652063757272656e74206e6f6e636560501b606082015260800190565b6020808252601f908201527f4d616c666f726d6564206261746368206f66207472616e73616374696f6e7300604082015260600190565b6020808252603d908201527f4e657720696e76616c69646174696f6e206e6f6e6365206d757374206265206760408201527f726561746572207468616e207468652063757272656e74206e6f6e6365000000606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b602080825260099082015268151a5b5959081bdd5d60ba1b604082015260600190565b60208082526023908201527f56616c696461746f72207369676e617475726520646f6573206e6f74206d617460408201526231b41760e91b606082015260800190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b6020808252603b908201527f42617463682074696d656f7574206d757374206265206772656174657220746860408201527f616e207468652063757272656e7420626c6f636b206865696768740000000000606082015260800190565b60208082526021908201527f4d616c666f726d6564206c697374206f6620746f6b656e207472616e736665726040820152607360f81b606082015260800190565b6020808252601b908201527f4d616c666f726d6564206e65772076616c696461746f72207365740000000000604082015260600190565b6020808252603c908201527f5375626d69747465642076616c696461746f7220736574207369676e6174757260408201527f657320646f206e6f74206861766520656e6f75676820706f7765722e00000000606082015260800190565b60208082526037908201527f4e65772076616c736574206e6f6e6365206d757374206265206772656174657260408201527f207468616e207468652063757272656e74206e6f6e6365000000000000000000606082015260800190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f4d616c666f726d65642063757272656e742076616c696461746f722073657400604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60008482526060602083015262002331606083018562001a8e565b828103604084015262002345818562001ad3565b9695505050505050565b918252602082015260400190565b6040518181016001600160401b03811182821017156200237c57600080fd5b604052919050565b60006001600160401b038211156200239a578081fd5b5060209081020190565b60005b83811015620023c1578181015183820152602001620023a7565b8381111562000e65575050600091015256fe6080604052600160ff1b6006553480156200001957600080fd5b5060405162000fc138038062000fc1833981810160405260808110156200003f57600080fd5b8151602083018051604051929492938301929190846401000000008211156200006757600080fd5b9083019060208201858111156200007d57600080fd5b82516401000000008111828201881017156200009857600080fd5b82525081516020918201929091019080838360005b83811015620000c7578181015183820152602001620000ad565b50505050905090810190601f168015620000f55780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200011957600080fd5b9083019060208201858111156200012f57600080fd5b82516401000000008111828201881017156200014a57600080fd5b82525081516020918201929091019080838360005b83811015620001795781810151838201526020016200015f565b50505050905090810190601f168015620001a75780820380516001836020036101000a031916815260200191505b5060405260209081015185519093508592508491620001cc91600391850190620003b9565b508051620001e2906004906020840190620003b9565b50506005805460ff191660121790555062000206816001600160e01b036200022416565b6200021a846006546200023a60201b60201c565b505050506200045e565b6005805460ff191660ff92909216919091179055565b6001600160a01b03821662000296576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b620002ad600083836001600160e01b036200035216565b620002c9816002546200035760201b620009b61790919060201c565b6002556001600160a01b03821660009081526020818152604090912054620002fc918390620009b662000357821b17901c565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b505050565b600082820183811015620003b2576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620003fc57805160ff19168380011785556200042c565b828001600101855582156200042c579182015b828111156200042c5782518255916020019190600101906200040f565b506200043a9291506200043e565b5090565b6200045b91905b808211156200043a576000815560010162000445565b90565b610b53806200046e6000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c80633950935111610071578063395093511461021257806370a082311461023e57806395d89b4114610264578063a457c2d71461026c578063a9059cbb14610298578063dd62ed3e146102c4576100b4565b806306fdde03146100b9578063095ea7b31461013657806318160ddd1461017657806323b872dd146101905780632ebdf54d146101c6578063313ce567146101f4575b600080fd5b6100c16102f2565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fb5781810151838201526020016100e3565b50505050905090810190601f1680156101285780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101626004803603604081101561014c57600080fd5b506001600160a01b038135169060200135610388565b604080519115158252519081900360200190f35b61017e6103a5565b60408051918252519081900360200190f35b610162600480360360608110156101a657600080fd5b506001600160a01b038135811691602081013590911690604001356103ab565b6101f2600480360360408110156101dc57600080fd5b506001600160a01b038135169060200135610438565b005b6101fc610446565b6040805160ff9092168252519081900360200190f35b6101626004803603604081101561022857600080fd5b506001600160a01b03813516906020013561044f565b61017e6004803603602081101561025457600080fd5b50356001600160a01b03166104a3565b6100c16104be565b6101626004803603604081101561028257600080fd5b506001600160a01b03813516906020013561051f565b610162600480360360408110156102ae57600080fd5b506001600160a01b03813516906020013561058d565b61017e600480360360408110156102da57600080fd5b506001600160a01b03813581169160200135166105a1565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b820191906000526020600020905b81548152906001019060200180831161036157829003601f168201915b5050505050905090565b600061039c6103956105cc565b84846105d0565b50600192915050565b60025490565b60006103b88484846106bc565b61042e846103c46105cc565b61042985604051806060016040528060288152602001610a88602891396001600160a01b038a166000908152600160205260408120906104026105cc565b6001600160a01b03168152602081019190915260400160002054919063ffffffff61082316565b6105d0565b5060019392505050565b61044282826108ba565b5050565b60055460ff1690565b600061039c61045c6105cc565b84610429856001600061046d6105cc565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff6109b616565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b600061039c61052c6105cc565b8461042985604051806060016040528060258152602001610af960259139600160006105566105cc565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff61082316565b600061039c61059a6105cc565b84846106bc565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3390565b6001600160a01b0383166106155760405162461bcd60e51b8152600401808060200182810382526024815260200180610ad56024913960400191505060405180910390fd5b6001600160a01b03821661065a5760405162461bcd60e51b8152600401808060200182810382526022815260200180610a406022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107015760405162461bcd60e51b8152600401808060200182810382526025815260200180610ab06025913960400191505060405180910390fd5b6001600160a01b0382166107465760405162461bcd60e51b8152600401808060200182810382526023815260200180610a1d6023913960400191505060405180910390fd5b610751838383610a17565b61079481604051806060016040528060268152602001610a62602691396001600160a01b038616600090815260208190526040902054919063ffffffff61082316565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546107c9908263ffffffff6109b616565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108b25760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561087757818101518382015260200161085f565b50505050905090810190601f1680156108a45780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6001600160a01b038216610915576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b61092160008383610a17565b600254610934908263ffffffff6109b616565b6002556001600160a01b038216600090815260208190526040902054610960908263ffffffff6109b616565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b600082820183811015610a10576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220465410ed025b5712ee2eaddad7a20856f64710d3b57e1ff91396130750dfabd564736f6c63430006060033a264697066735822122018de9c84330245743d440a39eb3a986a4b6ba93368acb80508a288ff06657a3d64736f6c63430006060033", - "deployedBytecode": "0x60806040523480156200001157600080fd5b5060043610620001185760003560e01c8063c227c30b11620000a5578063e3cb9f62116200006f578063e3cb9f621462000224578063e5a2b5d2146200023b578063f2b533071462000245578063f7955637146200024f5762000118565b8063c227c30b14620001c8578063c9d194d514620001df578063db7c4e5714620001f6578063df97174b146200020d5762000118565b80637dfb6f8611620000e75780637dfb6f86146200018657806383b435db146200019d578063b56561fe14620001b4578063bdda81d414620001be5762000118565b8063011b2174146200011d5780630c246c82146200014c5780631ffbe7f9146200016557806373b20547146200017c575b600080fd5b620001346200012e366004620013ef565b62000266565b60405162000143919062001c0d565b60405180910390f35b620001636200015d36600462001830565b62000281565b005b62000163620001763660046200140d565b620005ce565b6200013462000684565b6200013462000197366004620019d4565b6200068a565b62000163620001ae36600462001678565b6200069c565b6200013462000968565b620001346200096e565b62000163620001d93660046200193b565b62000974565b62000134620001f0366004620019d4565b62000989565b62000163620002073660046200144e565b6200099b565b620001346200021e366004620013ef565b620009b5565b62000163620002353660046200153c565b620009c7565b6200013462000b53565b6200013462000b59565b6200016362000260366004620019ed565b62000b5f565b6001600160a01b031660009081526002602052604090205490565b60026000541415620002b05760405162461bcd60e51b8152600401620002a790620022df565b60405180910390fd5b600260005560c08101514310620002db5760405162461bcd60e51b8152600401620002a79062001fec565b61010081015160e082015160009081526003602052604090205410620003155760405162461bcd60e51b8152600401620002a79062001f58565b8551875114801562000328575083518751145b801562000336575082518751145b801562000344575081518751145b620003635760405162461bcd60e51b8152600401620002a790620022a8565b6001546200037688888860065462000c11565b14620003965760405162461bcd60e51b8152600401620002a79062001e6e565b60208101515181515114620003bf5760405162461bcd60e51b8152600401620002a790620020f5565b80606001515181604001515114620003eb5760405162461bcd60e51b8152600401620002a79062001e3e565b6000600654681b1bd9da58d0d85b1b60ba1b836000015184602001518560400151866060015187608001518860a001518960c001518a60e001518b6101000151604051602001620004479b9a9998979695949392919062001c5d565b6040516020818303038152906040528051906020012090506200047288888787878660075462000c63565b61010082015160e08301516000908152600360205260408120919091555b825151811015620004f857620004ef836080015184600001518381518110620004b557fe5b602002602001015185602001518481518110620004ce57fe5b60200260200101516001600160a01b031662000d6d9092919063ffffffff16565b60010162000490565b5060606200050f83608001518460a0015162000dcc565b905060005b8360400151518110156200055a576200055133856040015183815181106200053857fe5b602002602001015186606001518481518110620004ce57fe5b60010162000514565b506005546200057190600163ffffffff62000e1916565b600581905560e08401516101008501516040517f7c2bb24f8e1b3725cb613d7f11ef97d9745cc97a0e40f730621c052d684077a193620005b693929186919062001d83565b60405180910390a15050600160005550505050505050565b60026000541415620005f45760405162461bcd60e51b8152600401620002a790620022df565b6002600055620006166001600160a01b03841633308463ffffffff62000e4116565b6005546200062c90600163ffffffff62000e1916565b6005819055604051839133916001600160a01b038716917fd7767894d73c589daeca9643f445f03d7be61aad2950c117e7cbff4176fca7e491620006729187916200234f565b60405180910390a45050600160005550565b60055481565b60036020526000908152604090205481565b60026000541415620006c25760405162461bcd60e51b8152600401620002a790620022df565b600260008181556001600160a01b038416815260209190915260409020548311620007015760405162461bcd60e51b8152600401620002a79062001ecb565b804310620007235760405162461bcd60e51b8152600401620002a79062002098565b8a518c5114801562000736575088518c51145b801562000744575087518c51145b801562000752575086518c51145b620007715760405162461bcd60e51b8152600401620002a790620022a8565b600154620007848d8d8d60065462000c11565b14620007a45760405162461bcd60e51b8152600401620002a79062001e6e565b84518651148015620007b7575083518651145b620007d65760405162461bcd60e51b8152600401620002a79062001f21565b620008348c8c8b8b8b6006546f0e8e4c2dce6c2c6e8d2dedc84c2e8c6d60831b8d8d8d8d8d8d6040516020016200081598979695949392919062001d0c565b6040516020818303038152906040528051906020012060075462000c63565b6001600160a01b0382166000908152600260205260408120849055805b8751811015620008d757620008a38782815181106200086c57fe5b60200260200101518983815181106200088157fe5b6020026020010151866001600160a01b031662000d6d9092919063ffffffff16565b620008cc868281518110620008b457fe5b60200260200101518362000e1990919063ffffffff16565b915060010162000851565b50620008f46001600160a01b038416338363ffffffff62000d6d16565b506005546200090b90600163ffffffff62000e1916565b60058190556040516001600160a01b0384169185917f02c7e81975f8edb86e2a0c038b7b86a49c744236abf0f6177ff5afc6986ab708916200094d9162001c0d565b60405180910390a35050600160005550505050505050505050565b60045481565b60065481565b620009828484848462000c11565b5050505050565b60009081526003602052604090205490565b620009ac8787878787878762000c63565b50505050505050565b60026020526000908152604090205481565b60026000541415620009ed5760405162461bcd60e51b8152600401620002a790620022df565b600260005583871162000a145760405162461bcd60e51b8152600401620002a790620021ca565b875189511462000a385760405162461bcd60e51b8152600401620002a79062002136565b8451865114801562000a4b575082518651145b801562000a59575081518651145b801562000a67575080518651145b62000a865760405162461bcd60e51b8152600401620002a790620022a8565b60015462000a9987878760065462000c11565b1462000ab95760405162461bcd60e51b8152600401620002a79062001e6e565b600062000acb8a8a8a60065462000c11565b905062000ae087878686868660075462000c63565b6001818155600489905560055462000afe9163ffffffff62000e1916565b600581905560405189917fb119f1f36224601586b5037da909ecf37e83864dddea5d32ad4e32ac1d97e62b9162000b3a91908e908e9062002316565b60405180910390a2505060016000555050505050505050565b60075481565b60015481565b60003084848460405162000b7390620010d0565b62000b82949392919062001ba5565b604051809103906000f08015801562000b9f573d6000803e3d6000fd5b5060055490915062000bb990600163ffffffff62000e1916565b60058190556040516001600160a01b038316917f82fe3a4fa49c6382d0c085746698ddbbafe6c2bf61285b19410644b5b26287c79162000c029189918991899189919062001de8565b60405180910390a25050505050565b6040516000906918da1958dadc1bda5b9d60b21b90829062000c40908590849088908b908b9060200162001c16565b60408051808303601f190181529190528051602090910120979650505050505050565b6000805b885181101562000d405786818151811062000c7e57fe5b602002602001015160ff1660001462000d375762000cf089828151811062000ca257fe5b60200260200101518589848151811062000cb857fe5b602002602001015189858151811062000ccd57fe5b602002602001015189868151811062000ce257fe5b602002602001015162000e6b565b62000d0f5760405162461bcd60e51b8152600401620002a7906200200f565b87818151811062000d1c57fe5b6020026020010151820191508282111562000d375762000d40565b60010162000c67565b5081811162000d635760405162461bcd60e51b8152600401620002a7906200216d565b5050505050505050565b62000dc78363a9059cbb60e01b848460405160240162000d8f92919062001bf4565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915262000f0b565b505050565b606062000e1083836040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c6564000081525062000fa2565b90505b92915050565b60008282018381101562000e105760405162461bcd60e51b8152600401620002a79062001fb5565b62000e65846323b872dd60e01b85858560405160240162000d8f9392919062001b81565b50505050565b6000808560405160200162000e81919062001b50565b6040516020818303038152906040528051906020012090506001818686866040516000815260200160405260405162000ebe949392919062001db5565b6020604051602081039080840390855afa15801562000ee1573d6000803e3d6000fd5b505050602060405103516001600160a01b0316876001600160a01b03161491505095945050505050565b606062000f62826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031662000fa29092919063ffffffff16565b80519091501562000dc7578080602001905181019062000f839190620019b2565b62000dc75760405162461bcd60e51b8152600401620002a7906200225e565b606062000fb3848460008562000fbd565b90505b9392505050565b60608247101562000fe25760405162461bcd60e51b8152600401620002a79062002052565b62000fed856200108c565b6200100c5760405162461bcd60e51b8152600401620002a79062002227565b60006060866001600160a01b031685876040516200102b919062001b32565b60006040518083038185875af1925050503d80600081146200106a576040519150601f19603f3d011682016040523d82523d6000602084013e6200106f565b606091505b50915091506200108182828662001092565b979650505050505050565b3b151590565b60608315620010a357508162000fb6565b825115620010b45782518084602001fd5b8160405162461bcd60e51b8152600401620002a7919062001dd3565b610fc180620023d483390190565b80356001600160a01b038116811462000e1357600080fd5b600082601f83011262001107578081fd5b81356200111e620011188262002384565b6200235d565b8181529150602080830190848101818402860182018710156200114057600080fd5b60005b848110156200116b57620011588883620010de565b8452928201929082019060010162001143565b505050505092915050565b600082601f83011262001187578081fd5b813562001198620011188262002384565b818152915060208083019084810181840286018201871015620011ba57600080fd5b60005b848110156200116b57813584529282019290820190600101620011bd565b600082601f830112620011ec578081fd5b8135620011fd620011188262002384565b8181529150602080830190848101818402860182018710156200121f57600080fd5b60005b848110156200116b57620012378883620013dd565b8452928201929082019060010162001222565b600082601f8301126200125b578081fd5b81356001600160401b0381111562001271578182fd5b62001286601f8201601f19166020016200235d565b91508082528360208285010111156200129e57600080fd5b8060208401602084013760009082016020015292915050565b6000610120808385031215620012cb578182fd5b620012d6816200235d565b91505081356001600160401b0380821115620012f157600080fd5b620012ff8583860162001176565b835260208401359150808211156200131657600080fd5b6200132485838601620010f6565b602084015260408401359150808211156200133e57600080fd5b6200134c8583860162001176565b604084015260608401359150808211156200136657600080fd5b6200137485838601620010f6565b6060840152620013888560808601620010de565b608084015260a0840135915080821115620013a257600080fd5b50620013b1848285016200124a565b60a08301525060c082013560c082015260e082013560e082015261010080830135818301525092915050565b803560ff8116811462000e1357600080fd5b60006020828403121562001401578081fd5b62000e108383620010de565b60008060006060848603121562001422578182fd5b83356001600160a01b038116811462001439578283fd5b95602085013595506040909401359392505050565b600080600080600080600060e0888a03121562001469578283fd5b87356001600160401b038082111562001480578485fd5b6200148e8b838c01620010f6565b985060208a0135915080821115620014a4578485fd5b620014b28b838c0162001176565b975060408a0135915080821115620014c8578485fd5b620014d68b838c01620011db565b965060608a0135915080821115620014ec578485fd5b620014fa8b838c0162001176565b955060808a013591508082111562001510578485fd5b506200151f8a828b0162001176565b93505060a0880135915060c0880135905092959891949750929550565b60008060008060008060008060006101208a8c0312156200155b578182fd5b89356001600160401b038082111562001572578384fd5b620015808d838e01620010f6565b9a5060208c013591508082111562001596578384fd5b620015a48d838e0162001176565b995060408c0135985060608c0135915080821115620015c1578384fd5b620015cf8d838e01620010f6565b975060808c0135915080821115620015e5578384fd5b620015f38d838e0162001176565b965060a08c0135955060c08c013591508082111562001610578384fd5b6200161e8d838e01620011db565b945060e08c013591508082111562001634578384fd5b620016428d838e0162001176565b93506101008c013591508082111562001659578283fd5b50620016688c828d0162001176565b9150509295985092959850929598565b6000806000806000806000806000806000806101808d8f0312156200169b578586fd5b6001600160401b038d351115620016b0578586fd5b620016bf8e8e358f01620010f6565b9b506001600160401b0360208e01351115620016d9578586fd5b620016eb8e60208f01358f0162001176565b9a5060408d013599506001600160401b0360608e013511156200170c578586fd5b6200171e8e60608f01358f01620011db565b98506001600160401b0360808e0135111562001738578586fd5b6200174a8e60808f01358f0162001176565b97506001600160401b0360a08e0135111562001764578586fd5b620017768e60a08f01358f0162001176565b96506001600160401b0360c08e0135111562001790578586fd5b620017a28e60c08f01358f0162001176565b95506001600160401b0360e08e01351115620017bc578283fd5b620017ce8e60e08f01358f01620010f6565b94506001600160401b036101008e01351115620017e9578283fd5b620017fc8e6101008f01358f0162001176565b93506101208d01359250620018168e6101408f01620010de565b91506101608d013590509295989b509295989b509295989b565b600080600080600080600060e0888a0312156200184b578081fd5b87356001600160401b038082111562001862578283fd5b620018708b838c01620010f6565b985060208a013591508082111562001886578283fd5b620018948b838c0162001176565b975060408a0135965060608a0135915080821115620018b1578283fd5b620018bf8b838c01620011db565b955060808a0135915080821115620018d5578283fd5b620018e38b838c0162001176565b945060a08a0135915080821115620018f9578283fd5b620019078b838c0162001176565b935060c08a01359150808211156200191d578283fd5b506200192c8a828b01620012b7565b91505092959891949750929550565b6000806000806080858703121562001951578182fd5b84356001600160401b038082111562001968578384fd5b6200197688838901620010f6565b955060208701359150808211156200198c578384fd5b506200199b8782880162001176565b949794965050505060408301359260600135919050565b600060208284031215620019c4578081fd5b8151801515811462000e10578182fd5b600060208284031215620019e6578081fd5b5035919050565b6000806000806080858703121562001a03578182fd5b84356001600160401b038082111562001a1a578384fd5b62001a28888389016200124a565b9550602087013591508082111562001a3e578384fd5b62001a4c888389016200124a565b9450604087013591508082111562001a62578384fd5b5062001a71878288016200124a565b92505062001a838660608701620013dd565b905092959194509250565b6000815180845260208085019450808401835b8381101562001ac85781516001600160a01b03168752958201959082019060010162001aa1565b509495945050505050565b6000815180845260208085019450808401835b8381101562001ac85781518752958201959082019060010162001ae6565b6000815180845262001b1e816020860160208601620023a4565b601f01601f19169290920160200192915050565b6000825162001b46818460208701620023a4565b9190910192915050565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b038516815260806020820181905260009062001bcb9083018662001b04565b828103604084015262001bdf818662001b04565b91505060ff8316606083015295945050505050565b6001600160a01b03929092168252602082015260400190565b90815260200190565b600086825285602083015284604083015260a0606083015262001c3d60a083018562001a8e565b828103608084015262001c51818562001ad3565b98975050505050505050565b60006101608d83528c602084015280604084015262001c7f8184018d62001ad3565b838103606085015262001c93818d62001a8e565b915050828103608084015262001caa818b62001ad3565b83810360a085015262001cbe818b62001a8e565b6001600160a01b038a1660c086015284810360e0860152915062001ce59050818862001b04565b61010084019690965250506101208101929092526101409091015298975050505050505050565b60006101008a835289602084015280604084015262001d2e8184018a62001ad3565b838103606085015262001d42818a62001a8e565b915050828103608084015262001d59818862001ad3565b60a084019690965250506001600160a01b039290921660c083015260e09091015295945050505050565b60008582528460208301526080604083015262001da4608083018562001b04565b905082606083015295945050505050565b93845260ff9290921660208401526040830152606082015260800190565b60006020825262000e10602083018462001b04565b600060a0825262001dfd60a083018862001b04565b828103602084015262001e11818862001b04565b838103604085015262001e25818862001b04565b60ff969096166060850152505050608001529392505050565b6020808252601690820152754d616c666f726d6564206c697374206f66206665657360501b604082015260600190565b6020808252603f908201527f537570706c6965642063757272656e742076616c696461746f727320616e642060408201527f706f7765727320646f206e6f74206d6174636820636865636b706f696e742e00606082015260800190565b60208082526036908201527f4e6577206261746368206e6f6e6365206d7573742062652067726561746572206040820152757468616e207468652063757272656e74206e6f6e636560501b606082015260800190565b6020808252601f908201527f4d616c666f726d6564206261746368206f66207472616e73616374696f6e7300604082015260600190565b6020808252603d908201527f4e657720696e76616c69646174696f6e206e6f6e6365206d757374206265206760408201527f726561746572207468616e207468652063757272656e74206e6f6e6365000000606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b602080825260099082015268151a5b5959081bdd5d60ba1b604082015260600190565b60208082526023908201527f56616c696461746f72207369676e617475726520646f6573206e6f74206d617460408201526231b41760e91b606082015260800190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b6020808252603b908201527f42617463682074696d656f7574206d757374206265206772656174657220746860408201527f616e207468652063757272656e7420626c6f636b206865696768740000000000606082015260800190565b60208082526021908201527f4d616c666f726d6564206c697374206f6620746f6b656e207472616e736665726040820152607360f81b606082015260800190565b6020808252601b908201527f4d616c666f726d6564206e65772076616c696461746f72207365740000000000604082015260600190565b6020808252603c908201527f5375626d69747465642076616c696461746f7220736574207369676e6174757260408201527f657320646f206e6f74206861766520656e6f75676820706f7765722e00000000606082015260800190565b60208082526037908201527f4e65772076616c736574206e6f6e6365206d757374206265206772656174657260408201527f207468616e207468652063757272656e74206e6f6e6365000000000000000000606082015260800190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f4d616c666f726d65642063757272656e742076616c696461746f722073657400604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60008482526060602083015262002331606083018562001a8e565b828103604084015262002345818562001ad3565b9695505050505050565b918252602082015260400190565b6040518181016001600160401b03811182821017156200237c57600080fd5b604052919050565b60006001600160401b038211156200239a578081fd5b5060209081020190565b60005b83811015620023c1578181015183820152602001620023a7565b8381111562000e65575050600091015256fe6080604052600160ff1b6006553480156200001957600080fd5b5060405162000fc138038062000fc1833981810160405260808110156200003f57600080fd5b8151602083018051604051929492938301929190846401000000008211156200006757600080fd5b9083019060208201858111156200007d57600080fd5b82516401000000008111828201881017156200009857600080fd5b82525081516020918201929091019080838360005b83811015620000c7578181015183820152602001620000ad565b50505050905090810190601f168015620000f55780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200011957600080fd5b9083019060208201858111156200012f57600080fd5b82516401000000008111828201881017156200014a57600080fd5b82525081516020918201929091019080838360005b83811015620001795781810151838201526020016200015f565b50505050905090810190601f168015620001a75780820380516001836020036101000a031916815260200191505b5060405260209081015185519093508592508491620001cc91600391850190620003b9565b508051620001e2906004906020840190620003b9565b50506005805460ff191660121790555062000206816001600160e01b036200022416565b6200021a846006546200023a60201b60201c565b505050506200045e565b6005805460ff191660ff92909216919091179055565b6001600160a01b03821662000296576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b620002ad600083836001600160e01b036200035216565b620002c9816002546200035760201b620009b61790919060201c565b6002556001600160a01b03821660009081526020818152604090912054620002fc918390620009b662000357821b17901c565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b505050565b600082820183811015620003b2576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620003fc57805160ff19168380011785556200042c565b828001600101855582156200042c579182015b828111156200042c5782518255916020019190600101906200040f565b506200043a9291506200043e565b5090565b6200045b91905b808211156200043a576000815560010162000445565b90565b610b53806200046e6000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c80633950935111610071578063395093511461021257806370a082311461023e57806395d89b4114610264578063a457c2d71461026c578063a9059cbb14610298578063dd62ed3e146102c4576100b4565b806306fdde03146100b9578063095ea7b31461013657806318160ddd1461017657806323b872dd146101905780632ebdf54d146101c6578063313ce567146101f4575b600080fd5b6100c16102f2565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fb5781810151838201526020016100e3565b50505050905090810190601f1680156101285780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101626004803603604081101561014c57600080fd5b506001600160a01b038135169060200135610388565b604080519115158252519081900360200190f35b61017e6103a5565b60408051918252519081900360200190f35b610162600480360360608110156101a657600080fd5b506001600160a01b038135811691602081013590911690604001356103ab565b6101f2600480360360408110156101dc57600080fd5b506001600160a01b038135169060200135610438565b005b6101fc610446565b6040805160ff9092168252519081900360200190f35b6101626004803603604081101561022857600080fd5b506001600160a01b03813516906020013561044f565b61017e6004803603602081101561025457600080fd5b50356001600160a01b03166104a3565b6100c16104be565b6101626004803603604081101561028257600080fd5b506001600160a01b03813516906020013561051f565b610162600480360360408110156102ae57600080fd5b506001600160a01b03813516906020013561058d565b61017e600480360360408110156102da57600080fd5b506001600160a01b03813581169160200135166105a1565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b820191906000526020600020905b81548152906001019060200180831161036157829003601f168201915b5050505050905090565b600061039c6103956105cc565b84846105d0565b50600192915050565b60025490565b60006103b88484846106bc565b61042e846103c46105cc565b61042985604051806060016040528060288152602001610a88602891396001600160a01b038a166000908152600160205260408120906104026105cc565b6001600160a01b03168152602081019190915260400160002054919063ffffffff61082316565b6105d0565b5060019392505050565b61044282826108ba565b5050565b60055460ff1690565b600061039c61045c6105cc565b84610429856001600061046d6105cc565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff6109b616565b6001600160a01b031660009081526020819052604090205490565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561037e5780601f106103535761010080835404028352916020019161037e565b600061039c61052c6105cc565b8461042985604051806060016040528060258152602001610af960259139600160006105566105cc565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff61082316565b600061039c61059a6105cc565b84846106bc565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3390565b6001600160a01b0383166106155760405162461bcd60e51b8152600401808060200182810382526024815260200180610ad56024913960400191505060405180910390fd5b6001600160a01b03821661065a5760405162461bcd60e51b8152600401808060200182810382526022815260200180610a406022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b0383166107015760405162461bcd60e51b8152600401808060200182810382526025815260200180610ab06025913960400191505060405180910390fd5b6001600160a01b0382166107465760405162461bcd60e51b8152600401808060200182810382526023815260200180610a1d6023913960400191505060405180910390fd5b610751838383610a17565b61079481604051806060016040528060268152602001610a62602691396001600160a01b038616600090815260208190526040902054919063ffffffff61082316565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546107c9908263ffffffff6109b616565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156108b25760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561087757818101518382015260200161085f565b50505050905090810190601f1680156108a45780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6001600160a01b038216610915576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b61092160008383610a17565b600254610934908263ffffffff6109b616565b6002556001600160a01b038216600090815260208190526040902054610960908263ffffffff6109b616565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b600082820183811015610a10576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b50505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220465410ed025b5712ee2eaddad7a20856f64710d3b57e1ff91396130750dfabd564736f6c63430006060033a264697066735822122018de9c84330245743d440a39eb3a986a4b6ba93368acb80508a288ff06657a3d64736f6c63430006060033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/contracts/basic-bandwidth-generation/cache/solidity-files-cache.json b/contracts/basic-bandwidth-generation/cache/solidity-files-cache.json index 39dba74c8f..e842d693af 100644 --- a/contracts/basic-bandwidth-generation/cache/solidity-files-cache.json +++ b/contracts/basic-bandwidth-generation/cache/solidity-files-cache.json @@ -2,11 +2,11 @@ "_format": "hh-sol-cache-2", "files": { "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/BandwidthGenerator.sol": { - "lastModificationDate": 1639657373736, - "contentHash": "ac31a05f19ad88d4c28011a830d29b56", + "lastModificationDate": 1643129770695, + "contentHash": "642b72e2d50d565db7bc994cace3f5a0", "sourceName": "contracts/BandwidthGenerator.sol", "solcConfig": { - "version": "0.6.6", + "version": "0.8.10", "settings": { "optimizer": { "enabled": true, @@ -31,21 +31,21 @@ "./CosmosToken.sol", "./Gravity.sol", "@openzeppelin/contracts/access/Ownable.sol", - "@openzeppelin/contracts/math/SafeMath.sol" + "@openzeppelin/contracts/utils/math/SafeMath.sol" ], "versionPragmas": [ - "0.6.6" + "0.8.10" ], "artifacts": [ "BandwidthGenerator" ] }, "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/CosmosToken.sol": { - "lastModificationDate": 1639657191231, - "contentHash": "0f05f96ee3c1151b6cd4b699aa3167e9", + "lastModificationDate": 1642773899716, + "contentHash": "fc5dd09fe73bc6cfece970f702a3aba1", "sourceName": "contracts/CosmosToken.sol", "solcConfig": { - "version": "0.6.6", + "version": "0.8.10", "settings": { "optimizer": { "enabled": true, @@ -70,18 +70,18 @@ "@openzeppelin/contracts/token/ERC20/ERC20.sol" ], "versionPragmas": [ - "^0.6.6" + "0.8.10" ], "artifacts": [ "CosmosERC20" ] }, "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/Gravity.sol": { - "lastModificationDate": 1639657191231, - "contentHash": "eaa1cd71cea24d419ef5f67a66dc672e", + "lastModificationDate": 1642773899716, + "contentHash": "0d6dae561f7b541bafb892b8593a08ac", "sourceName": "contracts/Gravity.sol", "solcConfig": { - "version": "0.6.6", + "version": "0.8.10", "settings": { "optimizer": { "enabled": true, @@ -103,26 +103,26 @@ } }, "imports": [ - "@openzeppelin/contracts/math/SafeMath.sol", "@openzeppelin/contracts/token/ERC20/IERC20.sol", - "@openzeppelin/contracts/token/ERC20/SafeERC20.sol", + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol", + "@openzeppelin/contracts/security/ReentrancyGuard.sol", "@openzeppelin/contracts/utils/Address.sol", - "@openzeppelin/contracts/utils/ReentrancyGuard.sol", + "@openzeppelin/contracts/utils/cryptography/ECDSA.sol", "./CosmosToken.sol" ], "versionPragmas": [ - "^0.6.6" + "0.8.10" ], "artifacts": [ "Gravity" ] }, "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/access/Ownable.sol": { - "lastModificationDate": 1639657303711, - "contentHash": "6748815a5b45c4aeeda56819f41190e0", + "lastModificationDate": 1641812554274, + "contentHash": "4fe56b59ced59d87df6b796758f62895", "sourceName": "@openzeppelin/contracts/access/Ownable.sol", "solcConfig": { - "version": "0.6.6", + "version": "0.8.10", "settings": { "optimizer": { "enabled": true, @@ -147,18 +147,18 @@ "../utils/Context.sol" ], "versionPragmas": [ - ">=0.6.0 <0.8.0" + "^0.8.0" ], "artifacts": [ "Ownable" ] }, - "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/math/SafeMath.sol": { - "lastModificationDate": 1639657303763, - "contentHash": "e03e12206057e809eb76c5f681170c32", - "sourceName": "@openzeppelin/contracts/math/SafeMath.sol", + "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol": { + "lastModificationDate": 1641812554378, + "contentHash": "5365090efc586b728719e562ebfed0d6", + "sourceName": "@openzeppelin/contracts/utils/math/SafeMath.sol", "solcConfig": { - "version": "0.6.6", + "version": "0.8.10", "settings": { "optimizer": { "enabled": true, @@ -181,18 +181,18 @@ }, "imports": [], "versionPragmas": [ - ">=0.6.0 <0.8.0" + "^0.8.0" ], "artifacts": [ "SafeMath" ] }, "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": { - "lastModificationDate": 1639657303507, - "contentHash": "8065b340476f61365c076897199425f1", + "lastModificationDate": 1641812553614, + "contentHash": "2cd550cedf51b8d294607dad5023d717", "sourceName": "@openzeppelin/contracts/token/ERC20/ERC20.sol", "solcConfig": { - "version": "0.6.6", + "version": "0.8.10", "settings": { "optimizer": { "enabled": true, @@ -214,57 +214,23 @@ } }, "imports": [ - "../../utils/Context.sol", "./IERC20.sol", - "../../math/SafeMath.sol" + "./extensions/IERC20Metadata.sol", + "../../utils/Context.sol" ], "versionPragmas": [ - ">=0.6.0 <0.8.0" + "^0.8.0" ], "artifacts": [ "ERC20" ] }, - "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Context.sol": { - "lastModificationDate": 1639657303395, - "contentHash": "2adbd82f6d055a4751566d4671512b03", - "sourceName": "@openzeppelin/contracts/utils/Context.sol", - "solcConfig": { - "version": "0.6.6", - "settings": { - "optimizer": { - "enabled": true, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [], - "versionPragmas": [ - ">=0.6.0 <0.8.0" - ], - "artifacts": [ - "Context" - ] - }, "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": { - "lastModificationDate": 1639657303639, - "contentHash": "e0a41531d159d3a32f84b7a3ecf9fabb", + "lastModificationDate": 1641812554110, + "contentHash": "0eac3e1a83ee62326ca007811285b274", "sourceName": "@openzeppelin/contracts/token/ERC20/IERC20.sol", "solcConfig": { - "version": "0.6.6", + "version": "0.8.10", "settings": { "optimizer": { "enabled": true, @@ -287,18 +253,18 @@ }, "imports": [], "versionPragmas": [ - ">=0.6.0 <0.8.0" + "^0.8.0" ], "artifacts": [ "IERC20" ] }, - "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/SafeERC20.sol": { - "lastModificationDate": 1639657303759, - "contentHash": "33e22842646d746e5c4124c2fdc051aa", - "sourceName": "@openzeppelin/contracts/token/ERC20/SafeERC20.sol", + "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "lastModificationDate": 1641812554118, + "contentHash": "aa1be06992a99bb7393b26a6af3c4dfb", + "sourceName": "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol", "solcConfig": { - "version": "0.6.6", + "version": "0.8.10", "settings": { "optimizer": { "enabled": true, @@ -320,23 +286,92 @@ } }, "imports": [ - "./IERC20.sol", - "../../math/SafeMath.sol", - "../../utils/Address.sol" + "../IERC20.sol" ], "versionPragmas": [ - ">=0.6.0 <0.8.0" + "^0.8.0" + ], + "artifacts": [ + "IERC20Metadata" + ] + }, + "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Context.sol": { + "lastModificationDate": 1641812553346, + "contentHash": "851485d5b925529b1a2f34a0be077891", + "sourceName": "@openzeppelin/contracts/utils/Context.sol", + "solcConfig": { + "version": "0.8.10", + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers" + ], + "": [ + "ast" + ] + } + } + } + }, + "imports": [], + "versionPragmas": [ + "^0.8.0" + ], + "artifacts": [ + "Context" + ] + }, + "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "lastModificationDate": 1641812554370, + "contentHash": "d37406082a74a9b6b114de522fcb6349", + "sourceName": "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol", + "solcConfig": { + "version": "0.8.10", + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers" + ], + "": [ + "ast" + ] + } + } + } + }, + "imports": [ + "../IERC20.sol", + "../../../utils/Address.sol" + ], + "versionPragmas": [ + "^0.8.0" ], "artifacts": [ "SafeERC20" ] }, - "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Address.sol": { - "lastModificationDate": 1639657303363, - "contentHash": "7aa46886ff5abe7515496208a5e2ce5a", - "sourceName": "@openzeppelin/contracts/utils/Address.sol", + "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol": { + "lastModificationDate": 1641812554322, + "contentHash": "53fbff678f378956efcb207fa748eaa6", + "sourceName": "@openzeppelin/contracts/security/ReentrancyGuard.sol", "solcConfig": { - "version": "0.6.6", + "version": "0.8.10", "settings": { "optimizer": { "enabled": true, @@ -359,18 +394,52 @@ }, "imports": [], "versionPragmas": [ - ">=0.6.2 <0.8.0" + "^0.8.0" + ], + "artifacts": [ + "ReentrancyGuard" + ] + }, + "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Address.sol": { + "lastModificationDate": 1641812553270, + "contentHash": "c5f6c4e4df069c789e7d84b4c3011913", + "sourceName": "@openzeppelin/contracts/utils/Address.sol", + "solcConfig": { + "version": "0.8.10", + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers" + ], + "": [ + "ast" + ] + } + } + } + }, + "imports": [], + "versionPragmas": [ + "^0.8.0" ], "artifacts": [ "Address" ] }, - "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/ReentrancyGuard.sol": { - "lastModificationDate": 1639657303747, - "contentHash": "1c60f58cee45c61469e1aea31e4dd879", - "sourceName": "@openzeppelin/contracts/utils/ReentrancyGuard.sol", + "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol": { + "lastModificationDate": 1641812553414, + "contentHash": "197dbfaf7146845fa76331a4c9980e9f", + "sourceName": "@openzeppelin/contracts/utils/cryptography/ECDSA.sol", "solcConfig": { - "version": "0.6.6", + "version": "0.8.10", "settings": { "optimizer": { "enabled": true, @@ -393,10 +462,87 @@ }, "imports": [], "versionPragmas": [ - ">=0.6.0 <0.8.0" + "^0.8.0" ], "artifacts": [ - "ReentrancyGuard" + "ECDSA" + ] + }, + "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/test-contracts/TestCosmosToken.sol": { + "lastModificationDate": 1642773899716, + "contentHash": "957b6079ee0d5a6e048fce8555c14dee", + "sourceName": "contracts/test-contracts/TestCosmosToken.sol", + "solcConfig": { + "version": "0.8.10", + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers" + ], + "": [ + "ast" + ] + } + } + } + }, + "imports": [ + "@openzeppelin/contracts/token/ERC20/ERC20.sol" + ], + "versionPragmas": [ + "0.8.10" + ], + "artifacts": [ + "TestCosmosERC20" + ] + }, + "/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/test-contracts/TestGravity.sol": { + "lastModificationDate": 1642773899716, + "contentHash": "f24299c3acb20aff23914e16b7ba95e1", + "sourceName": "contracts/test-contracts/TestGravity.sol", + "solcConfig": { + "version": "0.8.10", + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers" + ], + "": [ + "ast" + ] + } + } + } + }, + "imports": [ + "@openzeppelin/contracts/token/ERC20/IERC20.sol", + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol", + "@openzeppelin/contracts/security/ReentrancyGuard.sol", + "@openzeppelin/contracts/utils/Address.sol", + "@openzeppelin/contracts/utils/cryptography/ECDSA.sol", + "./TestCosmosToken.sol" + ], + "versionPragmas": [ + "0.8.10" + ], + "artifacts": [ + "TestGravity" ] } } diff --git a/contracts/basic-bandwidth-generation/contractAddresses.json b/contracts/basic-bandwidth-generation/contractAddresses.json new file mode 100644 index 0000000000..1c5f0b6952 --- /dev/null +++ b/contracts/basic-bandwidth-generation/contractAddresses.json @@ -0,0 +1,14 @@ +{ + "rinkeby":{ + "NYM_ERC20":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B", + "BANDWIDTH_GENERATOR":"0x5FbDB2315678afecb367f032d93F642f64180aa3", + "GRAVITY":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B" + }, + "mainnet":{ + "NYM_ERC20":"0xCf6DeE9947fdDc958985E5283e63d41CBC83Ff61", + "NYMT": "0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F", + "BANDWIDTH_GENERATOR":"", + "BANDWIDTH_GENERATOR_NYMT":"0xB3BF30DD53044c9050B7309031Bbf26b2cecF3be", + "GRAVITY":"0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906" + } +} \ No newline at end of file diff --git a/contracts/basic-bandwidth-generation/contracts/BandwidthGenerator.sol b/contracts/basic-bandwidth-generation/contracts/BandwidthGenerator.sol index 2438476670..933a47974d 100644 --- a/contracts/basic-bandwidth-generation/contracts/BandwidthGenerator.sol +++ b/contracts/basic-bandwidth-generation/contracts/BandwidthGenerator.sol @@ -1,18 +1,20 @@ -pragma solidity 0.6.6; +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.10; import "./CosmosToken.sol"; import "./Gravity.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; /** * @title BandwidthGenerator * @dev Contract for generating Basic Bandwidth Credentials (BBCs) on the Nym cosmos blockchain, * using ERC20 representations of NYM as payment. Utilises the Gravity Bridge for cross-chain payment. + * + * Credential generation can be switched on/off by the contract owner. * * Credentials represent a certain amount of bandwidth which can be sent through the Nym Mixnet. * By default 1 NYM = 1 GB of bandwidth. The `BytesPerToken` amount can be adjusted by the contract owner. - * * The amount of bandwidth bought is calculated according to the following formula: * `(Token amount in 'wei' / 10**18) * BytesPerToken` */ @@ -23,28 +25,39 @@ contract BandwidthGenerator is Ownable { CosmosERC20 public erc20; Gravity public gravityBridge; uint256 public BytesPerToken; + bool public credentialGenerationEnabled; event BBCredentialPurchased( uint256 Bandwidth, uint256 indexed VerificationKey, bytes SignedVerificationKey, - bytes32 indexed CosmosRecipient + string CosmosRecipient ); event RatioChanged( uint256 indexed NewBytesPerToken ); - + + event CredentialGenerationSwitch( + bool Enabled + ); + + modifier checkEnabled() { + require(credentialGenerationEnabled, "BandwidthGenerator: credential generation isn't currently enabled"); + _; + } + /** * @param _erc20 Address of the erc20NYM deployed through the Gravity Bridge. * @param _gravityBridge Address of the deployed Gravity Bridge. */ - constructor(CosmosERC20 _erc20, Gravity _gravityBridge) public { + constructor(CosmosERC20 _erc20, Gravity _gravityBridge) { require(address(_erc20) != address(0), "BandwidthGenerator: erc20 address cannot be null"); require(address(_gravityBridge) != address(0), "BandwidthGenerator: gravity bridge address cannot be null"); erc20 = _erc20; gravityBridge = _gravityBridge; BytesPerToken = 1073741824; // default amount set at deployment: 1 erc20NYM = 1073741824 Bytes = 1GB + credentialGenerationEnabled = true; } /** @@ -56,6 +69,15 @@ contract BandwidthGenerator is Ownable { BytesPerToken = _newBytesPerTokenAmount; emit RatioChanged(_newBytesPerTokenAmount); } + + /** + * @dev Switches credential generation on/off. Can only be called by Owner. + * @param _generation Whether credential generation is turned on/off. + */ + function credentialGenerationSwitch(bool _generation) public onlyOwner { + credentialGenerationEnabled = _generation; + emit CredentialGenerationSwitch(_generation); + } /** * @dev Function to create a BBC for account owning the verification key on the Nym Cosmos Blockchain @@ -65,7 +87,7 @@ contract BandwidthGenerator is Ownable { * @param _signedVerificationKey Number of erc20NYMs to spend signed by _verificationKey for auth on Cosmos Blockchain. * @param _cosmosRecipient Address of the recipient of payment on Nym Cosmos Blockchain. */ - function generateBasicBandwidthCredential(uint256 _amount, uint256 _verificationKey, bytes memory _signedVerificationKey, bytes32 _cosmosRecipient) public { + function generateBasicBandwidthCredential(uint256 _amount, uint256 _verificationKey, bytes memory _signedVerificationKey, string calldata _cosmosRecipient) public checkEnabled { require(_signedVerificationKey.length == 64, "BandwidthGenerator: Signature doesn't have 64 bytes"); erc20.transferFrom(msg.sender, address(this), _amount); erc20.approve(address(gravityBridge), _amount); diff --git a/contracts/basic-bandwidth-generation/contracts/CosmosToken.sol b/contracts/basic-bandwidth-generation/contracts/CosmosToken.sol index a2714161b5..9c5556cd79 100644 --- a/contracts/basic-bandwidth-generation/contracts/CosmosToken.sol +++ b/contracts/basic-bandwidth-generation/contracts/CosmosToken.sol @@ -1,33 +1,35 @@ -pragma solidity ^0.6.6; +//SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.10; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -/** -* This is a slightly modified version of the cosmos erc20 contract -* which I have done for unit testing. -* -* All that has been changed is the MAX_UINT variable to allow -* me to mint some tokens more easily in unit tests, and the -* addition of the public mint() function. - */ - contract CosmosERC20 is ERC20 { - /* canonical amount */ - // uint256 MAX_UINT = 2**256 - 1; + uint256 MAX_UINT = 2**256 - 1; + uint8 private cosmosDecimals; + address private gravityAddress; - /* unit testing amount */ - uint256 HALF_MAX_UINT = 2**256 / 2; + // This override ensures we return the proper number of decimals + // for the cosmos token + function decimals() public view virtual override returns (uint8) { + return cosmosDecimals; + } + + // This is not an accurate total supply. Instead this is the total supply + // of the given cosmos asset on Ethereum at this moment in time. Keeping + // a totally accurate supply would require constant updates from the Cosmos + // side, while in theory this could be piggy-backed on some existing bridge + // operation it's a lot of complextiy to add so we chose to forgoe it. + function totalSupply() public view virtual override returns (uint256) { + return MAX_UINT - balanceOf(gravityAddress); + } constructor( address _gravityAddress, string memory _name, string memory _symbol, uint8 _decimals - ) public ERC20(_name, _symbol) { - _setupDecimals(_decimals); - _mint(_gravityAddress, HALF_MAX_UINT); + ) ERC20(_name, _symbol) { + cosmosDecimals = _decimals; + gravityAddress = _gravityAddress; + _mint(_gravityAddress, MAX_UINT); } - - function mintForUnitTesting(address _to, uint _amount) public { - _mint(_to, _amount); - } -} +} \ No newline at end of file diff --git a/contracts/basic-bandwidth-generation/contracts/Gravity.sol b/contracts/basic-bandwidth-generation/contracts/Gravity.sol index 15bca4aab8..0b541280e1 100644 --- a/contracts/basic-bandwidth-generation/contracts/Gravity.sol +++ b/contracts/basic-bandwidth-generation/contracts/Gravity.sol @@ -1,13 +1,27 @@ -pragma solidity ^0.6.6; +//SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.10; -import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/Address.sol"; -import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "./CosmosToken.sol"; -pragma experimental ABIEncoderV2; +error InvalidSignature(); +error InvalidValsetNonce(uint256 newNonce, uint256 currentNonce); +error InvalidBatchNonce(uint256 newNonce, uint256 currentNonce); +error InvalidLogicCallNonce(uint256 newNonce, uint256 currentNonce); +error InvalidLogicCallTransfers(); +error InvalidLogicCallFees(); +error InvalidSendToCosmos(); +error IncorrectCheckpoint(); +error MalformedNewValidatorSet(); +error MalformedCurrentValidatorSet(); +error MalformedBatch(); +error InsufficientPower(uint256 cumulativePower, uint256 powerThreshold); +error BatchTimedOut(); +error LogicCallTimedOut(); // This is being used purely to avoid stack too deep errors struct LogicCallArgs { @@ -26,10 +40,36 @@ struct LogicCallArgs { uint256 invalidationNonce; } +// This is used purely to avoid stack too deep errors +// represents everything about a given validator set +struct ValsetArgs { + // the validators in this set, represented by an Ethereum address + address[] validators; + // the powers of the given validators in the same order as above + uint256[] powers; + // the nonce of this validator set + uint256 valsetNonce; + // the reward amount denominated in the below reward token, can be + // set to zero + uint256 rewardAmount; + // the reward token, should be set to the zero address if not being used + address rewardToken; +} + +// This represents a validator signature +struct Signature { + uint8 v; + bytes32 r; + bytes32 s; +} + contract Gravity is ReentrancyGuard { - using SafeMath for uint256; using SafeERC20 for IERC20; + // The number of 'votes' required to execute a valset + // update or batch execution, set to 2/3 of 2^32 + uint256 constant constant_powerThreshold = 2863311530; + // These are updated often bytes32 public state_lastValsetCheckpoint; mapping(address => uint256) public state_lastBatchNonces; @@ -39,9 +79,8 @@ contract Gravity is ReentrancyGuard { // value indicating that no events have yet been submitted uint256 public state_lastEventNonce = 1; - // These are set once at initialization - bytes32 public state_gravityId; - uint256 public state_powerThreshold; + // This is set once at initialization + bytes32 public immutable state_gravityId; // TransactionBatchExecutedEvent and SendToCosmosEvent both include the field _eventNonce. // This is incremented every time one of these events is emitted. It is checked by the @@ -57,7 +96,7 @@ contract Gravity is ReentrancyGuard { event SendToCosmosEvent( address indexed _tokenContract, address indexed _sender, - bytes32 indexed _destination, + string _destination, uint256 _amount, uint256 _eventNonce ); @@ -73,6 +112,8 @@ contract Gravity is ReentrancyGuard { event ValsetUpdatedEvent( uint256 indexed _newValsetNonce, uint256 _eventNonce, + uint256 _rewardAmount, + address _rewardToken, address[] _validators, uint256[] _powers ); @@ -85,42 +126,26 @@ contract Gravity is ReentrancyGuard { // TEST FIXTURES // These are here to make it easier to measure gas usage. They should be removed before production - function testMakeCheckpoint( - address[] memory _validators, - uint256[] memory _powers, - uint256 _valsetNonce, - bytes32 _gravityId - ) public pure { - makeCheckpoint(_validators, _powers, _valsetNonce, _gravityId); + function testMakeCheckpoint(ValsetArgs calldata _valsetArgs, bytes32 _gravityId) external pure { + makeCheckpoint(_valsetArgs, _gravityId); } function testCheckValidatorSignatures( - address[] memory _currentValidators, - uint256[] memory _currentPowers, - uint8[] memory _v, - bytes32[] memory _r, - bytes32[] memory _s, + ValsetArgs calldata _currentValset, + Signature[] calldata _sigs, bytes32 _theHash, uint256 _powerThreshold - ) public pure { - checkValidatorSignatures( - _currentValidators, - _currentPowers, - _v, - _r, - _s, - _theHash, - _powerThreshold - ); + ) external pure { + checkValidatorSignatures(_currentValset, _sigs, _theHash, _powerThreshold); } // END TEST FIXTURES - function lastBatchNonce(address _erc20Address) public view returns (uint256) { + function lastBatchNonce(address _erc20Address) external view returns (uint256) { return state_lastBatchNonces[_erc20Address]; } - function lastLogicCallNonce(bytes32 _invalidation_id) public view returns (uint256) { + function lastLogicCallNonce(bytes32 _invalidation_id) external view returns (uint256) { return state_invalidationMapping[_invalidation_id]; } @@ -128,13 +153,23 @@ contract Gravity is ReentrancyGuard { function verifySig( address _signer, bytes32 _theHash, - uint8 _v, - bytes32 _r, - bytes32 _s + Signature calldata _sig ) private pure returns (bool) { - bytes32 messageDigest = - keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _theHash)); - return _signer == ecrecover(messageDigest, _v, _r, _s); + bytes32 messageDigest = keccak256( + abi.encodePacked("\x19Ethereum Signed Message:\n32", _theHash) + ); + return _signer == ECDSA.recover(messageDigest, _sig.v, _sig.r, _sig.s); + } + + // Utility function to determine that a validator set and signatures are well formed + function validateValset(ValsetArgs calldata _valset, Signature[] calldata _sigs) private pure { + // Check that current validators, powers, and signatures (v,r,s) set is well-formed + if ( + _valset.validators.length != _valset.powers.length || + _valset.validators.length != _sigs.length + ) { + revert MalformedCurrentValidatorSet(); + } } // Make a new checkpoint from the supplied validator set @@ -145,47 +180,51 @@ contract Gravity is ReentrancyGuard { // Where h is the keccak256 hash function. // The validator powers must be decreasing or equal. This is important for checking the signatures on the // next valset, since it allows the caller to stop verifying signatures once a quorum of signatures have been verified. - function makeCheckpoint( - address[] memory _validators, - uint256[] memory _powers, - uint256 _valsetNonce, - bytes32 _gravityId - ) private pure returns (bytes32) { + function makeCheckpoint(ValsetArgs memory _valsetArgs, bytes32 _gravityId) + private + pure + returns (bytes32) + { // bytes32 encoding of the string "checkpoint" bytes32 methodName = 0x636865636b706f696e7400000000000000000000000000000000000000000000; - bytes32 checkpoint = - keccak256(abi.encode(_gravityId, methodName, _valsetNonce, _validators, _powers)); + bytes32 checkpoint = keccak256( + abi.encode( + _gravityId, + methodName, + _valsetArgs.valsetNonce, + _valsetArgs.validators, + _valsetArgs.powers, + _valsetArgs.rewardAmount, + _valsetArgs.rewardToken + ) + ); return checkpoint; } function checkValidatorSignatures( // The current validator set and their powers - address[] memory _currentValidators, - uint256[] memory _currentPowers, + ValsetArgs calldata _currentValset, // The current validator's signatures - uint8[] memory _v, - bytes32[] memory _r, - bytes32[] memory _s, + Signature[] calldata _sigs, // This is what we are checking they have signed bytes32 _theHash, uint256 _powerThreshold ) private pure { uint256 cumulativePower = 0; - for (uint256 i = 0; i < _currentValidators.length; i++) { + for (uint256 i = 0; i < _currentValset.validators.length; i++) { // If v is set to 0, this signifies that it was not possible to get a signature from this validator and we skip evaluation // (In a valid signature, it is either 27 or 28) - if (_v[i] != 0) { + if (_sigs[i].v != 0) { // Check that the current validator has signed off on the hash - require( - verifySig(_currentValidators[i], _theHash, _v[i], _r[i], _s[i]), - "Validator signature does not match." - ); + if (!verifySig(_currentValset.validators[i], _theHash, _sigs[i])) { + revert InvalidSignature(); + } // Sum up cumulative power - cumulativePower = cumulativePower + _currentPowers[i]; + cumulativePower = cumulativePower + _currentValset.powers[i]; // Break early to avoid wasting gas if (cumulativePower > _powerThreshold) { @@ -195,76 +234,81 @@ contract Gravity is ReentrancyGuard { } // Check that there was enough power - require( - cumulativePower > _powerThreshold, - "Submitted validator set signatures do not have enough power." - ); + if (cumulativePower <= _powerThreshold) { + revert InsufficientPower(cumulativePower, _powerThreshold); + } // Success } // This updates the valset by checking that the validators in the current valset have signed off on the // new valset. The signatures supplied are the signatures of the current valset over the checkpoint hash // generated from the new valset. - // Anyone can call this function, but they must supply valid signatures of state_powerThreshold of the current valset over + // Anyone can call this function, but they must supply valid signatures of constant_powerThreshold of the current valset over // the new valset. function updateValset( // The new version of the validator set - address[] memory _newValidators, - uint256[] memory _newPowers, - uint256 _newValsetNonce, + ValsetArgs calldata _newValset, // The current validators that approve the change - address[] memory _currentValidators, - uint256[] memory _currentPowers, - uint256 _currentValsetNonce, + ValsetArgs calldata _currentValset, // These are arrays of the parts of the current validator's signatures - uint8[] memory _v, - bytes32[] memory _r, - bytes32[] memory _s - ) public nonReentrant { + Signature[] calldata _sigs + ) external { // CHECKS // Check that the valset nonce is greater than the old one - require( - _newValsetNonce > _currentValsetNonce, - "New valset nonce must be greater than the current nonce" - ); + if (_newValset.valsetNonce <= _currentValset.valsetNonce) { + revert InvalidValsetNonce({ + newNonce: _newValset.valsetNonce, + currentNonce: _currentValset.valsetNonce + }); + } + + // Check that the valset nonce is less than a million nonces forward from the old one + // this makes it difficult for an attacker to lock out the contract by getting a single + // bad validator set through with uint256 max nonce + if (_newValset.valsetNonce > _currentValset.valsetNonce + 1000000) { + revert InvalidValsetNonce({ + newNonce: _newValset.valsetNonce, + currentNonce: _currentValset.valsetNonce + }); + } // Check that new validators and powers set is well-formed - require(_newValidators.length == _newPowers.length, "Malformed new validator set"); + if ( + _newValset.validators.length != _newValset.powers.length || + _newValset.validators.length == 0 + ) { + revert MalformedNewValidatorSet(); + } // Check that current validators, powers, and signatures (v,r,s) set is well-formed - require( - _currentValidators.length == _currentPowers.length && - _currentValidators.length == _v.length && - _currentValidators.length == _r.length && - _currentValidators.length == _s.length, - "Malformed current validator set" - ); + validateValset(_currentValset, _sigs); + + // Check cumulative power to ensure the contract has sufficient power to actually + // pass a vote + uint256 cumulativePower = 0; + for (uint256 i = 0; i < _newValset.powers.length; i++) { + cumulativePower = cumulativePower + _newValset.powers[i]; + if (cumulativePower > constant_powerThreshold) { + break; + } + } + if (cumulativePower <= constant_powerThreshold) { + revert InsufficientPower({ + cumulativePower: cumulativePower, + powerThreshold: constant_powerThreshold + }); + } // Check that the supplied current validator set matches the saved checkpoint - require( - makeCheckpoint( - _currentValidators, - _currentPowers, - _currentValsetNonce, - state_gravityId - ) == state_lastValsetCheckpoint, - "Supplied current validators and powers do not match checkpoint." - ); + if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) { + revert IncorrectCheckpoint(); + } // Check that enough current validators have signed off on the new validator set - bytes32 newCheckpoint = - makeCheckpoint(_newValidators, _newPowers, _newValsetNonce, state_gravityId); + bytes32 newCheckpoint = makeCheckpoint(_newValset, state_gravityId); - checkValidatorSignatures( - _currentValidators, - _currentPowers, - _v, - _r, - _s, - newCheckpoint, - state_powerThreshold - ); + checkValidatorSignatures(_currentValset, _sigs, newCheckpoint, constant_powerThreshold); // ACTIONS @@ -273,83 +317,87 @@ contract Gravity is ReentrancyGuard { state_lastValsetCheckpoint = newCheckpoint; // Store new nonce - state_lastValsetNonce = _newValsetNonce; + state_lastValsetNonce = _newValset.valsetNonce; + + // Send submission reward to msg.sender if reward token is a valid value + if (_newValset.rewardToken != address(0) && _newValset.rewardAmount != 0) { + IERC20(_newValset.rewardToken).safeTransfer(msg.sender, _newValset.rewardAmount); + } // LOGS - state_lastEventNonce = state_lastEventNonce.add(1); - emit ValsetUpdatedEvent(_newValsetNonce, state_lastEventNonce, _newValidators, _newPowers); + + state_lastEventNonce = state_lastEventNonce + 1; + emit ValsetUpdatedEvent( + _newValset.valsetNonce, + state_lastEventNonce, + _newValset.rewardAmount, + _newValset.rewardToken, + _newValset.validators, + _newValset.powers + ); } // submitBatch processes a batch of Cosmos -> Ethereum transactions by sending the tokens in the transactions // to the destination addresses. It is approved by the current Cosmos validator set. - // Anyone can call this function, but they must supply valid signatures of state_powerThreshold of the current valset over + // Anyone can call this function, but they must supply valid signatures of constant_powerThreshold of the current valset over // the batch. function submitBatch( // The validators that approve the batch - address[] memory _currentValidators, - uint256[] memory _currentPowers, - uint256 _currentValsetNonce, + ValsetArgs calldata _currentValset, // These are arrays of the parts of the validators signatures - uint8[] memory _v, - bytes32[] memory _r, - bytes32[] memory _s, + Signature[] calldata _sigs, // The batch of transactions - uint256[] memory _amounts, - address[] memory _destinations, - uint256[] memory _fees, + uint256[] calldata _amounts, + address[] calldata _destinations, + uint256[] calldata _fees, uint256 _batchNonce, address _tokenContract, // a block height beyond which this batch is not valid // used to provide a fee-free timeout uint256 _batchTimeout - ) public nonReentrant { + ) external nonReentrant { // CHECKS scoped to reduce stack depth { // Check that the batch nonce is higher than the last nonce for this token - require( - state_lastBatchNonces[_tokenContract] < _batchNonce, - "New batch nonce must be greater than the current nonce" - ); + if (_batchNonce <= state_lastBatchNonces[_tokenContract]) { + revert InvalidBatchNonce({ + newNonce: _batchNonce, + currentNonce: state_lastBatchNonces[_tokenContract] + }); + } + + // Check that the batch nonce is less than one million nonces forward from the old one + // this makes it difficult for an attacker to lock out the contract by getting a single + // bad batch through with uint256 max nonce + if (_batchNonce > state_lastBatchNonces[_tokenContract] + 1000000) { + revert InvalidBatchNonce({ + newNonce: _batchNonce, + currentNonce: state_lastBatchNonces[_tokenContract] + }); + } // Check that the block height is less than the timeout height - require( - block.number < _batchTimeout, - "Batch timeout must be greater than the current block height" - ); + if (block.number >= _batchTimeout) { + revert BatchTimedOut(); + } // Check that current validators, powers, and signatures (v,r,s) set is well-formed - require( - _currentValidators.length == _currentPowers.length && - _currentValidators.length == _v.length && - _currentValidators.length == _r.length && - _currentValidators.length == _s.length, - "Malformed current validator set" - ); + validateValset(_currentValset, _sigs); // Check that the supplied current validator set matches the saved checkpoint - require( - makeCheckpoint( - _currentValidators, - _currentPowers, - _currentValsetNonce, - state_gravityId - ) == state_lastValsetCheckpoint, - "Supplied current validators and powers do not match checkpoint." - ); + if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) { + revert IncorrectCheckpoint(); + } // Check that the transaction batch is well-formed - require( - _amounts.length == _destinations.length && _amounts.length == _fees.length, - "Malformed batch of transactions" - ); + if (_amounts.length != _destinations.length || _amounts.length != _fees.length) { + revert MalformedBatch(); + } // Check that enough current validators have signed off on the transaction batch and valset checkValidatorSignatures( - _currentValidators, - _currentPowers, - _v, - _r, - _s, + _currentValset, + _sigs, // Get hash of the transaction batch and checkpoint keccak256( abi.encode( @@ -364,7 +412,7 @@ contract Gravity is ReentrancyGuard { _batchTimeout ) ), - state_powerThreshold + constant_powerThreshold ); // ACTIONS @@ -377,7 +425,7 @@ contract Gravity is ReentrancyGuard { uint256 totalFee; for (uint256 i = 0; i < _amounts.length; i++) { IERC20(_tokenContract).safeTransfer(_destinations[i], _amounts[i]); - totalFee = totalFee.add(_fees[i]); + totalFee = totalFee + _fees[i]; } // Send transaction fees to msg.sender @@ -387,7 +435,7 @@ contract Gravity is ReentrancyGuard { // LOGS scoped to reduce stack depth { - state_lastEventNonce = state_lastEventNonce.add(1); + state_lastEventNonce = state_lastEventNonce + 1; emit TransactionBatchExecutedEvent(_batchNonce, _tokenContract, state_lastEventNonce); } } @@ -403,61 +451,48 @@ contract Gravity is ReentrancyGuard { // for each call. function submitLogicCall( // The validators that approve the call - address[] memory _currentValidators, - uint256[] memory _currentPowers, - uint256 _currentValsetNonce, + ValsetArgs calldata _currentValset, // These are arrays of the parts of the validators signatures - uint8[] memory _v, - bytes32[] memory _r, - bytes32[] memory _s, + Signature[] calldata _sigs, LogicCallArgs memory _args - ) public nonReentrant { + ) external nonReentrant { // CHECKS scoped to reduce stack depth { // Check that the call has not timed out - require(block.number < _args.timeOut, "Timed out"); + if (block.number >= _args.timeOut) { + revert LogicCallTimedOut(); + } // Check that the invalidation nonce is higher than the last nonce for this invalidation Id - require( - state_invalidationMapping[_args.invalidationId] < _args.invalidationNonce, - "New invalidation nonce must be greater than the current nonce" - ); + if (state_invalidationMapping[_args.invalidationId] >= _args.invalidationNonce) { + revert InvalidLogicCallNonce({ + newNonce: _args.invalidationNonce, + currentNonce: state_invalidationMapping[_args.invalidationId] + }); + } + + // note the lack of nonce skipping check, it's not needed here since an attacker + // will never be able to fill the invalidationId space, therefore a nonce lockout + // is simply not possible // Check that current validators, powers, and signatures (v,r,s) set is well-formed - require( - _currentValidators.length == _currentPowers.length && - _currentValidators.length == _v.length && - _currentValidators.length == _r.length && - _currentValidators.length == _s.length, - "Malformed current validator set" - ); + validateValset(_currentValset, _sigs); // Check that the supplied current validator set matches the saved checkpoint - require( - makeCheckpoint( - _currentValidators, - _currentPowers, - _currentValsetNonce, - state_gravityId - ) == state_lastValsetCheckpoint, - "Supplied current validators and powers do not match checkpoint." - ); + if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) { + revert IncorrectCheckpoint(); + } - // Check that the token transfer list is well-formed - require( - _args.transferAmounts.length == _args.transferTokenContracts.length, - "Malformed list of token transfers" - ); + if (_args.transferAmounts.length != _args.transferTokenContracts.length) { + revert InvalidLogicCallTransfers(); + } - // Check that the fee list is well-formed - require( - _args.feeAmounts.length == _args.feeTokenContracts.length, - "Malformed list of fees" - ); + if (_args.feeAmounts.length != _args.feeTokenContracts.length) { + revert InvalidLogicCallFees(); + } } - - bytes32 argsHash = - keccak256( + { + bytes32 argsHash = keccak256( abi.encode( state_gravityId, // bytes32 encoding of "logicCall" @@ -474,17 +509,13 @@ contract Gravity is ReentrancyGuard { ) ); - { // Check that enough current validators have signed off on the transaction batch and valset checkValidatorSignatures( - _currentValidators, - _currentPowers, - _v, - _r, - _s, + _currentValset, + _sigs, // Get hash of the transaction batch and checkpoint argsHash, - state_powerThreshold + constant_powerThreshold ); } @@ -511,7 +542,7 @@ contract Gravity is ReentrancyGuard { // LOGS scoped to reduce stack depth { - state_lastEventNonce = state_lastEventNonce.add(1); + state_lastEventNonce = state_lastEventNonce + 1; emit LogicCallEvent( _args.invalidationId, _args.invalidationNonce, @@ -523,31 +554,50 @@ contract Gravity is ReentrancyGuard { function sendToCosmos( address _tokenContract, - bytes32 _destination, + string calldata _destination, uint256 _amount - ) public nonReentrant { + ) external nonReentrant { + // we snapshot our current balance of this token + uint256 ourStartingBalance = IERC20(_tokenContract).balanceOf(address(this)); + + // attempt to transfer the user specified amount IERC20(_tokenContract).safeTransferFrom(msg.sender, address(this), _amount); - state_lastEventNonce = state_lastEventNonce.add(1); + + // check what this particular ERC20 implementation actually gave us, since it doesn't + // have to be at all related to the _amount + uint256 ourEndingBalance = IERC20(_tokenContract).balanceOf(address(this)); + + // a very strange ERC20 may trigger this condition, if we didn't have this we would + // underflow, so it's mostly just an error message printer + if (ourEndingBalance <= ourStartingBalance) { + revert InvalidSendToCosmos(); + } + + state_lastEventNonce = state_lastEventNonce + 1; + + // emit to Cosmos the actual amount our balance has changed, rather than the user + // provided amount. This protects against a small set of wonky ERC20 behavior, like + // burning on send but not tokens that for example change every users balance every day. emit SendToCosmosEvent( _tokenContract, msg.sender, _destination, - _amount, + ourEndingBalance - ourStartingBalance, state_lastEventNonce ); } function deployERC20( - string memory _cosmosDenom, - string memory _name, - string memory _symbol, + string calldata _cosmosDenom, + string calldata _name, + string calldata _symbol, uint8 _decimals - ) public { + ) external { // Deploy an ERC20 with entire supply granted to Gravity.sol CosmosERC20 erc20 = new CosmosERC20(address(this), _name, _symbol, _decimals); // Fire an event to let the Cosmos module know - state_lastEventNonce = state_lastEventNonce.add(1); + state_lastEventNonce = state_lastEventNonce + 1; emit ERC20DeployedEvent( _cosmosDenom, address(erc20), @@ -561,41 +611,53 @@ contract Gravity is ReentrancyGuard { constructor( // A unique identifier for this gravity instance to use in signatures bytes32 _gravityId, - // How much voting power is needed to approve operations - uint256 _powerThreshold, - // The validator set + // The validator set, not in valset args format since many of it's + // arguments would never be used in this case address[] memory _validators, uint256[] memory _powers - ) public { + ) { // CHECKS // Check that validators, powers, and signatures (v,r,s) set is well-formed - require(_validators.length == _powers.length, "Malformed current validator set"); + if (_validators.length != _powers.length || _validators.length == 0) { + revert MalformedCurrentValidatorSet(); + } // Check cumulative power to ensure the contract has sufficient power to actually // pass a vote uint256 cumulativePower = 0; for (uint256 i = 0; i < _powers.length; i++) { cumulativePower = cumulativePower + _powers[i]; - if (cumulativePower > _powerThreshold) { + if (cumulativePower > constant_powerThreshold) { break; } } - require( - cumulativePower > _powerThreshold, - "Submitted validator set signatures do not have enough power." - ); + if (cumulativePower <= constant_powerThreshold) { + revert InsufficientPower({ + cumulativePower: cumulativePower, + powerThreshold: constant_powerThreshold + }); + } - bytes32 newCheckpoint = makeCheckpoint(_validators, _powers, 0, _gravityId); + ValsetArgs memory _valset; + _valset = ValsetArgs(_validators, _powers, 0, 0, address(0)); + + bytes32 newCheckpoint = makeCheckpoint(_valset, _gravityId); // ACTIONS state_gravityId = _gravityId; - state_powerThreshold = _powerThreshold; state_lastValsetCheckpoint = newCheckpoint; // LOGS - emit ValsetUpdatedEvent(state_lastValsetNonce, state_lastEventNonce, _validators, _powers); + emit ValsetUpdatedEvent( + state_lastValsetNonce, + state_lastEventNonce, + 0, + address(0), + _validators, + _powers + ); } -} +} \ No newline at end of file diff --git a/contracts/basic-bandwidth-generation/contracts/test-contracts/TestCosmosToken.sol b/contracts/basic-bandwidth-generation/contracts/test-contracts/TestCosmosToken.sol new file mode 100644 index 0000000000..9c88d8defd --- /dev/null +++ b/contracts/basic-bandwidth-generation/contracts/test-contracts/TestCosmosToken.sol @@ -0,0 +1,55 @@ +//SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.10; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +/** +* This is a slightly modified version of the cosmos erc20 contract +* which I have done for unit testing. +* +* All that has been changed is the MAX_UINT variable to allow +* me to mint some tokens more easily in unit tests, and the +* addition of the public mint() function. +*/ + + +contract TestCosmosERC20 is ERC20 { + /* canonical amount */ + // uint256 MAX_UINT = 2**256 - 1; + + /* unit testing amount */ + uint256 HALF_MAX_UINT = 2**256 / 2; + + uint8 private cosmosDecimals; + address private gravityAddress; + + // This override ensures we return the proper number of decimals + // for the cosmos token + function decimals() public view virtual override returns (uint8) { + return cosmosDecimals; + } + + // This is not an accurate total supply. Instead this is the total supply + // of the given cosmos asset on Ethereum at this moment in time. Keeping + // a totally accurate supply would require constant updates from the Cosmos + // side, while in theory this could be piggy-backed on some existing bridge + // operation it's a lot of complextiy to add so we chose to forgoe it. + function totalSupply() public view virtual override returns (uint256) { + return HALF_MAX_UINT - balanceOf(gravityAddress); + } + + constructor( + address _gravityAddress, + string memory _name, + string memory _symbol, + uint8 _decimals + ) ERC20(_name, _symbol) { + cosmosDecimals = _decimals; + gravityAddress = _gravityAddress; + _mint(_gravityAddress, HALF_MAX_UINT); + } + + // Additional function for our (nym repo) unit tests with bridge + function mintForUnitTesting(address _to, uint _amount) public { + _mint(_to, _amount); + } +} \ No newline at end of file diff --git a/contracts/basic-bandwidth-generation/contracts/test-contracts/TestGravity.sol b/contracts/basic-bandwidth-generation/contracts/test-contracts/TestGravity.sol new file mode 100644 index 0000000000..3ac41eac39 --- /dev/null +++ b/contracts/basic-bandwidth-generation/contracts/test-contracts/TestGravity.sol @@ -0,0 +1,671 @@ +//SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.10; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import "./TestCosmosToken.sol"; + +/** +* This is a slightly modified version of the gravity bridge contract +* which I have done for unit testing. +* +* All that has been changed is ___ +*/ + +error InvalidSignature(); +error InvalidValsetNonce(uint256 newNonce, uint256 currentNonce); +error InvalidBatchNonce(uint256 newNonce, uint256 currentNonce); +error InvalidLogicCallNonce(uint256 newNonce, uint256 currentNonce); +error InvalidLogicCallTransfers(); +error InvalidLogicCallFees(); +error InvalidSendToCosmos(); +error IncorrectCheckpoint(); +error MalformedNewValidatorSet(); +error MalformedCurrentValidatorSet(); +error MalformedBatch(); +error InsufficientPower(uint256 cumulativePower, uint256 powerThreshold); +error BatchTimedOut(); +error LogicCallTimedOut(); + +// This is being used purely to avoid stack too deep errors +struct LogicCallArgs { + // Transfers out to the logic contract + uint256[] transferAmounts; + address[] transferTokenContracts; + // The fees (transferred to msg.sender) + uint256[] feeAmounts; + address[] feeTokenContracts; + // The arbitrary logic call + address logicContractAddress; + bytes payload; + // Invalidation metadata + uint256 timeOut; + bytes32 invalidationId; + uint256 invalidationNonce; +} + +// This is used purely to avoid stack too deep errors +// represents everything about a given validator set +struct ValsetArgs { + // the validators in this set, represented by an Ethereum address + address[] validators; + // the powers of the given validators in the same order as above + uint256[] powers; + // the nonce of this validator set + uint256 valsetNonce; + // the reward amount denominated in the below reward token, can be + // set to zero + uint256 rewardAmount; + // the reward token, should be set to the zero address if not being used + address rewardToken; +} + +// This represents a validator signature +struct Signature { + uint8 v; + bytes32 r; + bytes32 s; +} + +contract TestGravity is ReentrancyGuard { + using SafeERC20 for IERC20; + + // The number of 'votes' required to execute a valset + // update or batch execution, set to 2/3 of 2^32 + uint256 constant constant_powerThreshold = 2863311530; + + // These are updated often + bytes32 public state_lastValsetCheckpoint; + mapping(address => uint256) public state_lastBatchNonces; + mapping(bytes32 => uint256) public state_invalidationMapping; + uint256 public state_lastValsetNonce = 0; + // event nonce zero is reserved by the Cosmos module as a special + // value indicating that no events have yet been submitted + uint256 public state_lastEventNonce = 1; + + // This is set once at initialization + bytes32 public immutable state_gravityId; + + // TransactionBatchExecutedEvent and SendToCosmosEvent both include the field _eventNonce. + // This is incremented every time one of these events is emitted. It is checked by the + // Cosmos module to ensure that all events are received in order, and that none are lost. + // + // ValsetUpdatedEvent does not include the field _eventNonce because it is never submitted to the Cosmos + // module. It is purely for the use of relayers to allow them to successfully submit batches. + event TransactionBatchExecutedEvent( + uint256 indexed _batchNonce, + address indexed _token, + uint256 _eventNonce + ); + event SendToCosmosEvent( + address indexed _tokenContract, + address indexed _sender, + string _destination, + uint256 _amount, + uint256 _eventNonce + ); + event ERC20DeployedEvent( + // FYI: Can't index on a string without doing a bunch of weird stuff + string _cosmosDenom, + address indexed _tokenContract, + string _name, + string _symbol, + uint8 _decimals, + uint256 _eventNonce + ); + event ValsetUpdatedEvent( + uint256 indexed _newValsetNonce, + uint256 _eventNonce, + uint256 _rewardAmount, + address _rewardToken, + address[] _validators, + uint256[] _powers + ); + event LogicCallEvent( + bytes32 _invalidationId, + uint256 _invalidationNonce, + bytes _returnData, + uint256 _eventNonce + ); + + // TEST FIXTURES + // These are here to make it easier to measure gas usage. They should be removed before production + function testMakeCheckpoint(ValsetArgs calldata _valsetArgs, bytes32 _gravityId) external pure { + makeCheckpoint(_valsetArgs, _gravityId); + } + + function testCheckValidatorSignatures( + ValsetArgs calldata _currentValset, + Signature[] calldata _sigs, + bytes32 _theHash, + uint256 _powerThreshold + ) external pure { + checkValidatorSignatures(_currentValset, _sigs, _theHash, _powerThreshold); + } + + // END TEST FIXTURES + + function lastBatchNonce(address _erc20Address) external view returns (uint256) { + return state_lastBatchNonces[_erc20Address]; + } + + function lastLogicCallNonce(bytes32 _invalidation_id) external view returns (uint256) { + return state_invalidationMapping[_invalidation_id]; + } + + // Utility function to verify geth style signatures + function verifySig( + address _signer, + bytes32 _theHash, + Signature calldata _sig + ) private pure returns (bool) { + bytes32 messageDigest = keccak256( + abi.encodePacked("\x19Ethereum Signed Message:\n32", _theHash) + ); + return _signer == ECDSA.recover(messageDigest, _sig.v, _sig.r, _sig.s); + } + + // Utility function to determine that a validator set and signatures are well formed + function validateValset(ValsetArgs calldata _valset, Signature[] calldata _sigs) private pure { + // Check that current validators, powers, and signatures (v,r,s) set is well-formed + if ( + _valset.validators.length != _valset.powers.length || + _valset.validators.length != _sigs.length + ) { + revert MalformedCurrentValidatorSet(); + } + } + + // Make a new checkpoint from the supplied validator set + // A checkpoint is a hash of all relevant information about the valset. This is stored by the contract, + // instead of storing the information directly. This saves on storage and gas. + // The format of the checkpoint is: + // h(gravityId, "checkpoint", valsetNonce, validators[], powers[]) + // Where h is the keccak256 hash function. + // The validator powers must be decreasing or equal. This is important for checking the signatures on the + // next valset, since it allows the caller to stop verifying signatures once a quorum of signatures have been verified. + function makeCheckpoint(ValsetArgs memory _valsetArgs, bytes32 _gravityId) + private + pure + returns (bytes32) + { + // bytes32 encoding of the string "checkpoint" + bytes32 methodName = 0x636865636b706f696e7400000000000000000000000000000000000000000000; + + bytes32 checkpoint = keccak256( + abi.encode( + _gravityId, + methodName, + _valsetArgs.valsetNonce, + _valsetArgs.validators, + _valsetArgs.powers, + _valsetArgs.rewardAmount, + _valsetArgs.rewardToken + ) + ); + + return checkpoint; + } + + function checkValidatorSignatures( + // The current validator set and their powers + ValsetArgs calldata _currentValset, + // The current validator's signatures + Signature[] calldata _sigs, + // This is what we are checking they have signed + bytes32 _theHash, + uint256 _powerThreshold + ) private pure { + uint256 cumulativePower = 0; + + for (uint256 i = 0; i < _currentValset.validators.length; i++) { + // If v is set to 0, this signifies that it was not possible to get a signature from this validator and we skip evaluation + // (In a valid signature, it is either 27 or 28) + if (_sigs[i].v != 0) { + // Check that the current validator has signed off on the hash + if (!verifySig(_currentValset.validators[i], _theHash, _sigs[i])) { + revert InvalidSignature(); + } + + // Sum up cumulative power + cumulativePower = cumulativePower + _currentValset.powers[i]; + + // Break early to avoid wasting gas + if (cumulativePower > _powerThreshold) { + break; + } + } + } + + // Check that there was enough power + if (cumulativePower <= _powerThreshold) { + revert InsufficientPower(cumulativePower, _powerThreshold); + } + // Success + } + + // This updates the valset by checking that the validators in the current valset have signed off on the + // new valset. The signatures supplied are the signatures of the current valset over the checkpoint hash + // generated from the new valset. + // Anyone can call this function, but they must supply valid signatures of constant_powerThreshold of the current valset over + // the new valset. + function updateValset( + // The new version of the validator set + ValsetArgs calldata _newValset, + // The current validators that approve the change + ValsetArgs calldata _currentValset, + // These are arrays of the parts of the current validator's signatures + Signature[] calldata _sigs + ) external { + // CHECKS + + // Check that the valset nonce is greater than the old one + if (_newValset.valsetNonce <= _currentValset.valsetNonce) { + revert InvalidValsetNonce({ + newNonce: _newValset.valsetNonce, + currentNonce: _currentValset.valsetNonce + }); + } + + // Check that the valset nonce is less than a million nonces forward from the old one + // this makes it difficult for an attacker to lock out the contract by getting a single + // bad validator set through with uint256 max nonce + if (_newValset.valsetNonce > _currentValset.valsetNonce + 1000000) { + revert InvalidValsetNonce({ + newNonce: _newValset.valsetNonce, + currentNonce: _currentValset.valsetNonce + }); + } + + // Check that new validators and powers set is well-formed + if ( + _newValset.validators.length != _newValset.powers.length || + _newValset.validators.length == 0 + ) { + revert MalformedNewValidatorSet(); + } + + // Check that current validators, powers, and signatures (v,r,s) set is well-formed + validateValset(_currentValset, _sigs); + + // Check cumulative power to ensure the contract has sufficient power to actually + // pass a vote + uint256 cumulativePower = 0; + for (uint256 i = 0; i < _newValset.powers.length; i++) { + cumulativePower = cumulativePower + _newValset.powers[i]; + if (cumulativePower > constant_powerThreshold) { + break; + } + } + if (cumulativePower <= constant_powerThreshold) { + revert InsufficientPower({ + cumulativePower: cumulativePower, + powerThreshold: constant_powerThreshold + }); + } + + // Check that the supplied current validator set matches the saved checkpoint + if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) { + revert IncorrectCheckpoint(); + } + + // Check that enough current validators have signed off on the new validator set + bytes32 newCheckpoint = makeCheckpoint(_newValset, state_gravityId); + + checkValidatorSignatures(_currentValset, _sigs, newCheckpoint, constant_powerThreshold); + + // ACTIONS + + // Stored to be used next time to validate that the valset + // supplied by the caller is correct. + state_lastValsetCheckpoint = newCheckpoint; + + // Store new nonce + state_lastValsetNonce = _newValset.valsetNonce; + + // Send submission reward to msg.sender if reward token is a valid value + if (_newValset.rewardToken != address(0) && _newValset.rewardAmount != 0) { + IERC20(_newValset.rewardToken).safeTransfer(msg.sender, _newValset.rewardAmount); + } + + // LOGS + + state_lastEventNonce = state_lastEventNonce + 1; + emit ValsetUpdatedEvent( + _newValset.valsetNonce, + state_lastEventNonce, + _newValset.rewardAmount, + _newValset.rewardToken, + _newValset.validators, + _newValset.powers + ); + } + + // submitBatch processes a batch of Cosmos -> Ethereum transactions by sending the tokens in the transactions + // to the destination addresses. It is approved by the current Cosmos validator set. + // Anyone can call this function, but they must supply valid signatures of constant_powerThreshold of the current valset over + // the batch. + function submitBatch( + // The validators that approve the batch + ValsetArgs calldata _currentValset, + // These are arrays of the parts of the validators signatures + Signature[] calldata _sigs, + // The batch of transactions + uint256[] calldata _amounts, + address[] calldata _destinations, + uint256[] calldata _fees, + uint256 _batchNonce, + address _tokenContract, + // a block height beyond which this batch is not valid + // used to provide a fee-free timeout + uint256 _batchTimeout + ) external nonReentrant { + // CHECKS scoped to reduce stack depth + { + // Check that the batch nonce is higher than the last nonce for this token + if (_batchNonce <= state_lastBatchNonces[_tokenContract]) { + revert InvalidBatchNonce({ + newNonce: _batchNonce, + currentNonce: state_lastBatchNonces[_tokenContract] + }); + } + + // Check that the batch nonce is less than one million nonces forward from the old one + // this makes it difficult for an attacker to lock out the contract by getting a single + // bad batch through with uint256 max nonce + if (_batchNonce > state_lastBatchNonces[_tokenContract] + 1000000) { + revert InvalidBatchNonce({ + newNonce: _batchNonce, + currentNonce: state_lastBatchNonces[_tokenContract] + }); + } + + // Check that the block height is less than the timeout height + if (block.number >= _batchTimeout) { + revert BatchTimedOut(); + } + + // Check that current validators, powers, and signatures (v,r,s) set is well-formed + validateValset(_currentValset, _sigs); + + // Check that the supplied current validator set matches the saved checkpoint + if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) { + revert IncorrectCheckpoint(); + } + + // Check that the transaction batch is well-formed + if (_amounts.length != _destinations.length || _amounts.length != _fees.length) { + revert MalformedBatch(); + } + + // Check that enough current validators have signed off on the transaction batch and valset + checkValidatorSignatures( + _currentValset, + _sigs, + // Get hash of the transaction batch and checkpoint + keccak256( + abi.encode( + state_gravityId, + // bytes32 encoding of "transactionBatch" + 0x7472616e73616374696f6e426174636800000000000000000000000000000000, + _amounts, + _destinations, + _fees, + _batchNonce, + _tokenContract, + _batchTimeout + ) + ), + constant_powerThreshold + ); + + // ACTIONS + + // Store batch nonce + state_lastBatchNonces[_tokenContract] = _batchNonce; + + { + // Send transaction amounts to destinations + uint256 totalFee; + for (uint256 i = 0; i < _amounts.length; i++) { + IERC20(_tokenContract).safeTransfer(_destinations[i], _amounts[i]); + totalFee = totalFee + _fees[i]; + } + + // Send transaction fees to msg.sender + IERC20(_tokenContract).safeTransfer(msg.sender, totalFee); + } + } + + // LOGS scoped to reduce stack depth + { + state_lastEventNonce = state_lastEventNonce + 1; + emit TransactionBatchExecutedEvent(_batchNonce, _tokenContract, state_lastEventNonce); + } + } + + // This makes calls to contracts that execute arbitrary logic + // First, it gives the logic contract some tokens + // Then, it gives msg.senders tokens for fees + // Then, it calls an arbitrary function on the logic contract + // invalidationId and invalidationNonce are used for replay prevention. + // They can be used to implement a per-token nonce by setting the token + // address as the invalidationId and incrementing the nonce each call. + // They can be used for nonce-free replay prevention by using a different invalidationId + // for each call. + function submitLogicCall( + // The validators that approve the call + ValsetArgs calldata _currentValset, + // These are arrays of the parts of the validators signatures + Signature[] calldata _sigs, + LogicCallArgs memory _args + ) external nonReentrant { + // CHECKS scoped to reduce stack depth + { + // Check that the call has not timed out + if (block.number >= _args.timeOut) { + revert LogicCallTimedOut(); + } + + // Check that the invalidation nonce is higher than the last nonce for this invalidation Id + if (state_invalidationMapping[_args.invalidationId] >= _args.invalidationNonce) { + revert InvalidLogicCallNonce({ + newNonce: _args.invalidationNonce, + currentNonce: state_invalidationMapping[_args.invalidationId] + }); + } + + // note the lack of nonce skipping check, it's not needed here since an attacker + // will never be able to fill the invalidationId space, therefore a nonce lockout + // is simply not possible + + // Check that current validators, powers, and signatures (v,r,s) set is well-formed + validateValset(_currentValset, _sigs); + + // Check that the supplied current validator set matches the saved checkpoint + if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) { + revert IncorrectCheckpoint(); + } + + if (_args.transferAmounts.length != _args.transferTokenContracts.length) { + revert InvalidLogicCallTransfers(); + } + + if (_args.feeAmounts.length != _args.feeTokenContracts.length) { + revert InvalidLogicCallFees(); + } + } + { + bytes32 argsHash = keccak256( + abi.encode( + state_gravityId, + // bytes32 encoding of "logicCall" + 0x6c6f67696343616c6c0000000000000000000000000000000000000000000000, + _args.transferAmounts, + _args.transferTokenContracts, + _args.feeAmounts, + _args.feeTokenContracts, + _args.logicContractAddress, + _args.payload, + _args.timeOut, + _args.invalidationId, + _args.invalidationNonce + ) + ); + + // Check that enough current validators have signed off on the transaction batch and valset + checkValidatorSignatures( + _currentValset, + _sigs, + // Get hash of the transaction batch and checkpoint + argsHash, + constant_powerThreshold + ); + } + + // ACTIONS + + // Update invaldiation nonce + state_invalidationMapping[_args.invalidationId] = _args.invalidationNonce; + + // Send tokens to the logic contract + for (uint256 i = 0; i < _args.transferAmounts.length; i++) { + IERC20(_args.transferTokenContracts[i]).safeTransfer( + _args.logicContractAddress, + _args.transferAmounts[i] + ); + } + + // Make call to logic contract + bytes memory returnData = Address.functionCall(_args.logicContractAddress, _args.payload); + + // Send fees to msg.sender + for (uint256 i = 0; i < _args.feeAmounts.length; i++) { + IERC20(_args.feeTokenContracts[i]).safeTransfer(msg.sender, _args.feeAmounts[i]); + } + + // LOGS scoped to reduce stack depth + { + state_lastEventNonce = state_lastEventNonce + 1; + emit LogicCallEvent( + _args.invalidationId, + _args.invalidationNonce, + returnData, + state_lastEventNonce + ); + } + } + + function sendToCosmos( + address _tokenContract, + string calldata _destination, + uint256 _amount + ) external nonReentrant { + // we snapshot our current balance of this token + uint256 ourStartingBalance = IERC20(_tokenContract).balanceOf(address(this)); + + // attempt to transfer the user specified amount + IERC20(_tokenContract).safeTransferFrom(msg.sender, address(this), _amount); + + // check what this particular ERC20 implementation actually gave us, since it doesn't + // have to be at all related to the _amount + uint256 ourEndingBalance = IERC20(_tokenContract).balanceOf(address(this)); + + // a very strange ERC20 may trigger this condition, if we didn't have this we would + // underflow, so it's mostly just an error message printer + if (ourEndingBalance <= ourStartingBalance) { + revert InvalidSendToCosmos(); + } + + state_lastEventNonce = state_lastEventNonce + 1; + + // emit to Cosmos the actual amount our balance has changed, rather than the user + // provided amount. This protects against a small set of wonky ERC20 behavior, like + // burning on send but not tokens that for example change every users balance every day. + emit SendToCosmosEvent( + _tokenContract, + msg.sender, + _destination, + ourEndingBalance - ourStartingBalance, + state_lastEventNonce + ); + } + + function deployERC20( + string calldata _cosmosDenom, + string calldata _name, + string calldata _symbol, + uint8 _decimals + ) external { + // NOTE this is an edit made for the nym codebase unit tests - in the canonical bridge it is the entire token supply + // Deploy an ERC20 with half of entire supply granted to Gravity.sol + TestCosmosERC20 erc20 = new TestCosmosERC20(address(this), _name, _symbol, _decimals); + + // Fire an event to let the Cosmos module know + state_lastEventNonce = state_lastEventNonce + 1; + emit ERC20DeployedEvent( + _cosmosDenom, + address(erc20), + _name, + _symbol, + _decimals, + state_lastEventNonce + ); + } + + constructor( + // A unique identifier for this gravity instance to use in signatures + bytes32 _gravityId, + // The validator set, not in valset args format since many of it's + // arguments would never be used in this case + address[] memory _validators, + uint256[] memory _powers + ) { + // CHECKS + + // Check that validators, powers, and signatures (v,r,s) set is well-formed + if (_validators.length != _powers.length || _validators.length == 0) { + revert MalformedCurrentValidatorSet(); + } + + // Check cumulative power to ensure the contract has sufficient power to actually + // pass a vote + uint256 cumulativePower = 0; + for (uint256 i = 0; i < _powers.length; i++) { + cumulativePower = cumulativePower + _powers[i]; + if (cumulativePower > constant_powerThreshold) { + break; + } + } + if (cumulativePower <= constant_powerThreshold) { + revert InsufficientPower({ + cumulativePower: cumulativePower, + powerThreshold: constant_powerThreshold + }); + } + + ValsetArgs memory _valset; + _valset = ValsetArgs(_validators, _powers, 0, 0, address(0)); + + bytes32 newCheckpoint = makeCheckpoint(_valset, _gravityId); + + // ACTIONS + + state_gravityId = _gravityId; + state_lastValsetCheckpoint = newCheckpoint; + + // LOGS + + emit ValsetUpdatedEvent( + state_lastValsetNonce, + state_lastEventNonce, + 0, + address(0), + _validators, + _powers + ); + } +} \ No newline at end of file diff --git a/contracts/basic-bandwidth-generation/hardhat.config.js b/contracts/basic-bandwidth-generation/hardhat.config.js index f3e7fbb0cb..a11fc74668 100644 --- a/contracts/basic-bandwidth-generation/hardhat.config.js +++ b/contracts/basic-bandwidth-generation/hardhat.config.js @@ -1,6 +1,7 @@ require("@nomiclabs/hardhat-etherscan"); require("@nomiclabs/hardhat-truffle5"); require("@nomiclabs/hardhat-web3"); +require("@nomiclabs/hardhat-ethers"); require('dotenv').config({ path: require('find-config')('.env') }); /** @@ -8,22 +9,31 @@ require('dotenv').config({ path: require('find-config')('.env') }); */ module.exports = { solidity: { - version: "0.6.6", + version: "0.8.10", settings: { optimizer: { enabled: true } } }, - paths: { - artifacts: "./artifacts/contracts" - }, + // paths: { + // artifacts: "./artifacts/contracts" + // }, networks: { - // rinkeby: { - // url: process.env.RINKEBY_URL, //Infura url with projectId - // accounts: [process.env.PRIV_KEY], // private key of account used for contract interaction - // gas: "auto", - // gasPrice: "auto" - // }, + localhost: { + url: "http://127.0.0.1:8545" + }, + rinkeby: { + url: process.env.RINKEBY_URL, //Infura url with projectId + accounts: [process.env.PRIV_KEY], // private key of account used for contract interaction + gas: "auto", + gasPrice: "auto" + }, + mainnet: { + url: process.env.MAINNET_URL, //Infura url with projectId + accounts: [process.env.PRIV_KEY], // private key of account used for contract interaction + gas: "auto", + gasPrice: "auto" + } }, etherscan: { // Your API key for Etherscan diff --git a/contracts/basic-bandwidth-generation/package-lock.json b/contracts/basic-bandwidth-generation/package-lock.json index eba9f64f04..caa5fad537 100644 --- a/contracts/basic-bandwidth-generation/package-lock.json +++ b/contracts/basic-bandwidth-generation/package-lock.json @@ -625,6 +625,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.4.1.tgz", "integrity": "sha512-9mhbjUk76BiSluiiW4BaYyI58KSbDMMQpCLdsAR+RsT2GyATiNYxVv+pGWRrekmsIdY3I+hOqsYQSTkc8L/mcg==", + "dev": true, "requires": { "@ethersproject/address": "^5.4.0", "@ethersproject/bignumber": "^5.4.0", @@ -684,12 +685,35 @@ } }, "@ethersproject/basex": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.4.0.tgz", - "integrity": "sha512-J07+QCVJ7np2bcpxydFVf/CuYo9mZ7T73Pe7KQY4c1lRlrixMeblauMxHXD0MPwFmUHZIILDNViVkykFBZylbg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.5.0.tgz", + "integrity": "sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ==", "requires": { - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/properties": "^5.4.0" + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + }, + "dependencies": { + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + } } }, "@ethersproject/bignumber": { @@ -719,20 +743,220 @@ } }, "@ethersproject/contracts": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.4.1.tgz", - "integrity": "sha512-m+z2ZgPy4pyR15Je//dUaymRUZq5MtDajF6GwFbGAVmKz/RF+DNIPwF0k5qEcL3wPGVqUjFg2/krlCRVTU4T5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.5.0.tgz", + "integrity": "sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg==", "requires": { - "@ethersproject/abi": "^5.4.0", - "@ethersproject/abstract-provider": "^5.4.0", - "@ethersproject/abstract-signer": "^5.4.0", - "@ethersproject/address": "^5.4.0", - "@ethersproject/bignumber": "^5.4.0", - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/constants": "^5.4.0", - "@ethersproject/logger": "^5.4.0", - "@ethersproject/properties": "^5.4.0", - "@ethersproject/transactions": "^5.4.0" + "@ethersproject/abi": "^5.5.0", + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0" + }, + "dependencies": { + "@ethersproject/abi": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.5.0.tgz", + "integrity": "sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", + "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", + "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/address": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", + "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/rlp": "^5.5.0" + } + }, + "@ethersproject/base64": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", + "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", + "requires": { + "@ethersproject/bytes": "^5.5.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/hash": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", + "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/networks": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", + "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/rlp": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", + "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/signing-key": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", + "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/transactions": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", + "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0" + } + }, + "@ethersproject/web": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", + "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", + "requires": { + "@ethersproject/base64": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + } } }, "@ethersproject/hash": { @@ -751,42 +975,380 @@ } }, "@ethersproject/hdnode": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.4.0.tgz", - "integrity": "sha512-pKxdS0KAaeVGfZPp1KOiDLB0jba11tG6OP1u11QnYfb7pXn6IZx0xceqWRr6ygke8+Kw74IpOoSi7/DwANhy8Q==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.5.0.tgz", + "integrity": "sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q==", "requires": { - "@ethersproject/abstract-signer": "^5.4.0", - "@ethersproject/basex": "^5.4.0", - "@ethersproject/bignumber": "^5.4.0", - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/logger": "^5.4.0", - "@ethersproject/pbkdf2": "^5.4.0", - "@ethersproject/properties": "^5.4.0", - "@ethersproject/sha2": "^5.4.0", - "@ethersproject/signing-key": "^5.4.0", - "@ethersproject/strings": "^5.4.0", - "@ethersproject/transactions": "^5.4.0", - "@ethersproject/wordlists": "^5.4.0" + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/basex": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/pbkdf2": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/wordlists": "^5.5.0" + }, + "dependencies": { + "@ethersproject/abstract-provider": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", + "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", + "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/address": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", + "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/rlp": "^5.5.0" + } + }, + "@ethersproject/base64": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", + "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", + "requires": { + "@ethersproject/bytes": "^5.5.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/networks": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", + "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/rlp": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", + "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/signing-key": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", + "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/transactions": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", + "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0" + } + }, + "@ethersproject/web": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", + "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", + "requires": { + "@ethersproject/base64": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + } } }, "@ethersproject/json-wallets": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.4.0.tgz", - "integrity": "sha512-igWcu3fx4aiczrzEHwG1xJZo9l1cFfQOWzTqwRw/xcvxTk58q4f9M7cjh51EKphMHvrJtcezJ1gf1q1AUOfEQQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz", + "integrity": "sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ==", "requires": { - "@ethersproject/abstract-signer": "^5.4.0", - "@ethersproject/address": "^5.4.0", - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/hdnode": "^5.4.0", - "@ethersproject/keccak256": "^5.4.0", - "@ethersproject/logger": "^5.4.0", - "@ethersproject/pbkdf2": "^5.4.0", - "@ethersproject/properties": "^5.4.0", - "@ethersproject/random": "^5.4.0", - "@ethersproject/strings": "^5.4.0", - "@ethersproject/transactions": "^5.4.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hdnode": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/pbkdf2": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", "aes-js": "3.0.0", "scrypt-js": "3.0.1" + }, + "dependencies": { + "@ethersproject/abstract-provider": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", + "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", + "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/address": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", + "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/rlp": "^5.5.0" + } + }, + "@ethersproject/base64": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", + "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", + "requires": { + "@ethersproject/bytes": "^5.5.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/networks": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", + "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/rlp": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", + "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/signing-key": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", + "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/transactions": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", + "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0" + } + }, + "@ethersproject/web": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", + "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", + "requires": { + "@ethersproject/base64": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + } } }, "@ethersproject/keccak256": { @@ -812,12 +1374,27 @@ } }, "@ethersproject/pbkdf2": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.4.0.tgz", - "integrity": "sha512-x94aIv6tiA04g6BnazZSLoRXqyusawRyZWlUhKip2jvoLpzJuLb//KtMM6PEovE47pMbW+Qe1uw+68ameJjB7g==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz", + "integrity": "sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg==", "requires": { - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/sha2": "^5.4.0" + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/sha2": "^5.5.0" + }, + "dependencies": { + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + } } }, "@ethersproject/properties": { @@ -829,31 +1406,213 @@ } }, "@ethersproject/providers": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.4.5.tgz", - "integrity": "sha512-1GkrvkiAw3Fj28cwi1Sqm8ED1RtERtpdXmRfwIBGmqBSN5MoeRUHuwHPppMtbPayPgpFcvD7/Gdc9doO5fGYgw==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.5.2.tgz", + "integrity": "sha512-hkbx7x/MKcRjyrO4StKXCzCpWer6s97xnm34xkfPiarhtEUVAN4TBBpamM+z66WcTt7H5B53YwbRj1n7i8pZoQ==", "requires": { - "@ethersproject/abstract-provider": "^5.4.0", - "@ethersproject/abstract-signer": "^5.4.0", - "@ethersproject/address": "^5.4.0", - "@ethersproject/basex": "^5.4.0", - "@ethersproject/bignumber": "^5.4.0", - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/constants": "^5.4.0", - "@ethersproject/hash": "^5.4.0", - "@ethersproject/logger": "^5.4.0", - "@ethersproject/networks": "^5.4.0", - "@ethersproject/properties": "^5.4.0", - "@ethersproject/random": "^5.4.0", - "@ethersproject/rlp": "^5.4.0", - "@ethersproject/sha2": "^5.4.0", - "@ethersproject/strings": "^5.4.0", - "@ethersproject/transactions": "^5.4.0", - "@ethersproject/web": "^5.4.0", + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/basex": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0", "bech32": "1.1.4", "ws": "7.4.6" }, "dependencies": { + "@ethersproject/abstract-provider": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", + "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", + "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/address": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", + "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/rlp": "^5.5.0" + } + }, + "@ethersproject/base64": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", + "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", + "requires": { + "@ethersproject/bytes": "^5.5.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/hash": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", + "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/networks": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", + "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/rlp": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", + "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/signing-key": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", + "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/transactions": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", + "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0" + } + }, + "@ethersproject/web": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", + "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", + "requires": { + "@ethersproject/base64": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, "ws": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", @@ -862,12 +1621,27 @@ } }, "@ethersproject/random": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.4.0.tgz", - "integrity": "sha512-pnpWNQlf0VAZDEOVp1rsYQosmv2o0ITS/PecNw+mS2/btF8eYdspkN0vIXrCMtkX09EAh9bdk8GoXmFXM1eAKw==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.5.1.tgz", + "integrity": "sha512-YaU2dQ7DuhL5Au7KbcQLHxcRHfgyNgvFV4sQOo0HrtW3Zkrc9ctWNz8wXQ4uCSfSDsqX2vcjhroxU5RQRV0nqA==", "requires": { - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/logger": "^5.4.0" + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + }, + "dependencies": { + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + } } }, "@ethersproject/rlp": { @@ -880,13 +1654,28 @@ } }, "@ethersproject/sha2": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.4.0.tgz", - "integrity": "sha512-siheo36r1WD7Cy+bDdE1BJ8y0bDtqXCOxRMzPa4bV1TGt/eTUUt03BHoJNB6reWJD8A30E/pdJ8WFkq+/uz4Gg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.5.0.tgz", + "integrity": "sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==", "requires": { - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/logger": "^5.4.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", "hash.js": "1.1.7" + }, + "dependencies": { + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + } } }, "@ethersproject/signing-key": { @@ -903,15 +1692,73 @@ } }, "@ethersproject/solidity": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.4.0.tgz", - "integrity": "sha512-XFQTZ7wFSHOhHcV1DpcWj7VXECEiSrBuv7JErJvB9Uo+KfCdc3QtUZV+Vjh/AAaYgezUEKbCtE6Khjm44seevQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.5.0.tgz", + "integrity": "sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw==", "requires": { - "@ethersproject/bignumber": "^5.4.0", - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/keccak256": "^5.4.0", - "@ethersproject/sha2": "^5.4.0", - "@ethersproject/strings": "^5.4.0" + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + }, + "dependencies": { + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + } } }, "@ethersproject/strings": { @@ -941,35 +1788,252 @@ } }, "@ethersproject/units": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.4.0.tgz", - "integrity": "sha512-Z88krX40KCp+JqPCP5oPv5p750g+uU6gopDYRTBGcDvOASh6qhiEYCRatuM/suC4S2XW9Zz90QI35MfSrTIaFg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.5.0.tgz", + "integrity": "sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag==", "requires": { - "@ethersproject/bignumber": "^5.4.0", - "@ethersproject/constants": "^5.4.0", - "@ethersproject/logger": "^5.4.0" + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + }, + "dependencies": { + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + } } }, "@ethersproject/wallet": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.4.0.tgz", - "integrity": "sha512-wU29majLjM6AjCjpat21mPPviG+EpK7wY1+jzKD0fg3ui5fgedf2zEu1RDgpfIMsfn8fJHJuzM4zXZ2+hSHaSQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.5.0.tgz", + "integrity": "sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q==", "requires": { - "@ethersproject/abstract-provider": "^5.4.0", - "@ethersproject/abstract-signer": "^5.4.0", - "@ethersproject/address": "^5.4.0", - "@ethersproject/bignumber": "^5.4.0", - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/hash": "^5.4.0", - "@ethersproject/hdnode": "^5.4.0", - "@ethersproject/json-wallets": "^5.4.0", - "@ethersproject/keccak256": "^5.4.0", - "@ethersproject/logger": "^5.4.0", - "@ethersproject/properties": "^5.4.0", - "@ethersproject/random": "^5.4.0", - "@ethersproject/signing-key": "^5.4.0", - "@ethersproject/transactions": "^5.4.0", - "@ethersproject/wordlists": "^5.4.0" + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/hdnode": "^5.5.0", + "@ethersproject/json-wallets": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/wordlists": "^5.5.0" + }, + "dependencies": { + "@ethersproject/abstract-provider": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", + "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", + "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/address": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", + "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/rlp": "^5.5.0" + } + }, + "@ethersproject/base64": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", + "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", + "requires": { + "@ethersproject/bytes": "^5.5.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/hash": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", + "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/networks": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", + "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/rlp": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", + "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/signing-key": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", + "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/transactions": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", + "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0" + } + }, + "@ethersproject/web": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", + "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", + "requires": { + "@ethersproject/base64": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + } } }, "@ethersproject/web": { @@ -985,15 +2049,199 @@ } }, "@ethersproject/wordlists": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.4.0.tgz", - "integrity": "sha512-FemEkf6a+EBKEPxlzeVgUaVSodU7G0Na89jqKjmWMlDB0tomoU8RlEMgUvXyqtrg8N4cwpLh8nyRnm1Nay1isA==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.5.0.tgz", + "integrity": "sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q==", "requires": { - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/hash": "^5.4.0", - "@ethersproject/logger": "^5.4.0", - "@ethersproject/properties": "^5.4.0", - "@ethersproject/strings": "^5.4.0" + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + }, + "dependencies": { + "@ethersproject/abstract-provider": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", + "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", + "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/address": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", + "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/rlp": "^5.5.0" + } + }, + "@ethersproject/base64": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", + "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", + "requires": { + "@ethersproject/bytes": "^5.5.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/hash": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", + "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/networks": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", + "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/rlp": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", + "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/signing-key": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", + "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/transactions": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", + "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0" + } + }, + "@ethersproject/web": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", + "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", + "requires": { + "@ethersproject/base64": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + } } }, "@nodelib/fs.scandir": { @@ -1023,9 +2271,9 @@ } }, "@nomiclabs/hardhat-ethers": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.2.tgz", - "integrity": "sha512-6quxWe8wwS4X5v3Au8q1jOvXYEPkS1Fh+cME5u6AwNdnI4uERvPlVjlgRWzpnb+Rrt1l/cEqiNRH9GlsBMSDQg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.4.tgz", + "integrity": "sha512-7LMR344TkdCYkMVF9LuC9VU2NBIi84akQiwqm7OufpWaDgHbWhuanY53rk3SVAW0E4HBk5xn5wl5+bN5f+Mq5w==", "dev": true }, "@nomiclabs/hardhat-etherscan": { @@ -4520,40 +5768,240 @@ } }, "ethers": { - "version": "5.4.7", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.4.7.tgz", - "integrity": "sha512-iZc5p2nqfWK1sj8RabwsPM28cr37Bpq7ehTQ5rWExBr2Y09Sn1lDKZOED26n+TsZMye7Y6mIgQ/1cwpSD8XZew==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.5.3.tgz", + "integrity": "sha512-fTT4WT8/hTe/BLwRUtl7I5zlpF3XC3P/Xwqxc5AIP2HGlH15qpmjs0Ou78az93b1rLITzXLFxoNX63B8ZbUd7g==", "requires": { - "@ethersproject/abi": "5.4.1", - "@ethersproject/abstract-provider": "5.4.1", - "@ethersproject/abstract-signer": "5.4.1", - "@ethersproject/address": "5.4.0", - "@ethersproject/base64": "5.4.0", - "@ethersproject/basex": "5.4.0", - "@ethersproject/bignumber": "5.4.2", - "@ethersproject/bytes": "5.4.0", - "@ethersproject/constants": "5.4.0", - "@ethersproject/contracts": "5.4.1", - "@ethersproject/hash": "5.4.0", - "@ethersproject/hdnode": "5.4.0", - "@ethersproject/json-wallets": "5.4.0", - "@ethersproject/keccak256": "5.4.0", - "@ethersproject/logger": "5.4.1", - "@ethersproject/networks": "5.4.2", - "@ethersproject/pbkdf2": "5.4.0", - "@ethersproject/properties": "5.4.1", - "@ethersproject/providers": "5.4.5", - "@ethersproject/random": "5.4.0", - "@ethersproject/rlp": "5.4.0", - "@ethersproject/sha2": "5.4.0", - "@ethersproject/signing-key": "5.4.0", - "@ethersproject/solidity": "5.4.0", - "@ethersproject/strings": "5.4.0", - "@ethersproject/transactions": "5.4.0", - "@ethersproject/units": "5.4.0", - "@ethersproject/wallet": "5.4.0", - "@ethersproject/web": "5.4.0", - "@ethersproject/wordlists": "5.4.0" + "@ethersproject/abi": "5.5.0", + "@ethersproject/abstract-provider": "5.5.1", + "@ethersproject/abstract-signer": "5.5.0", + "@ethersproject/address": "5.5.0", + "@ethersproject/base64": "5.5.0", + "@ethersproject/basex": "5.5.0", + "@ethersproject/bignumber": "5.5.0", + "@ethersproject/bytes": "5.5.0", + "@ethersproject/constants": "5.5.0", + "@ethersproject/contracts": "5.5.0", + "@ethersproject/hash": "5.5.0", + "@ethersproject/hdnode": "5.5.0", + "@ethersproject/json-wallets": "5.5.0", + "@ethersproject/keccak256": "5.5.0", + "@ethersproject/logger": "5.5.0", + "@ethersproject/networks": "5.5.2", + "@ethersproject/pbkdf2": "5.5.0", + "@ethersproject/properties": "5.5.0", + "@ethersproject/providers": "5.5.2", + "@ethersproject/random": "5.5.1", + "@ethersproject/rlp": "5.5.0", + "@ethersproject/sha2": "5.5.0", + "@ethersproject/signing-key": "5.5.0", + "@ethersproject/solidity": "5.5.0", + "@ethersproject/strings": "5.5.0", + "@ethersproject/transactions": "5.5.0", + "@ethersproject/units": "5.5.0", + "@ethersproject/wallet": "5.5.0", + "@ethersproject/web": "5.5.1", + "@ethersproject/wordlists": "5.5.0" + }, + "dependencies": { + "@ethersproject/abi": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.5.0.tgz", + "integrity": "sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", + "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", + "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/address": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", + "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/rlp": "^5.5.0" + } + }, + "@ethersproject/base64": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", + "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", + "requires": { + "@ethersproject/bytes": "^5.5.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/hash": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", + "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/networks": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", + "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/rlp": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", + "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/signing-key": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", + "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/transactions": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", + "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0" + } + }, + "@ethersproject/web": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", + "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", + "requires": { + "@ethersproject/base64": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + } } }, "ethjs-abi": { @@ -4966,6 +6414,23 @@ "reusify": "^1.0.4" } }, + "file-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/file-match/-/file-match-1.0.2.tgz", + "integrity": "sha1-ycrSZdLIrfOoFHWw30dYWQafrvc=", + "requires": { + "utils-extend": "^1.0.6" + } + }, + "file-system": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/file-system/-/file-system-2.2.2.tgz", + "integrity": "sha1-fWWDPjojR9zZVqgTxncVPtPt2Yc=", + "requires": { + "file-match": "^1.0.1", + "utils-extend": "^1.0.4" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -5059,9 +6524,9 @@ } }, "follow-redirects": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", - "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "dev": true }, "for-each": { @@ -17880,9 +19345,9 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, "requires": { "glob": "^7.0.0", @@ -19216,6 +20681,11 @@ "object.getownpropertydescriptors": "^2.1.1" } }, + "utils-extend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/utils-extend/-/utils-extend-1.0.8.tgz", + "integrity": "sha1-zP17ZFQPjpDuIe7Fd2nQZRyril8=" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/contracts/basic-bandwidth-generation/package.json b/contracts/basic-bandwidth-generation/package.json index 3f98a678a2..da712a61ad 100644 --- a/contracts/basic-bandwidth-generation/package.json +++ b/contracts/basic-bandwidth-generation/package.json @@ -1,20 +1,21 @@ { "devDependencies": { - "@nomiclabs/hardhat-ethers": "^2.0.2", + "@nomiclabs/hardhat-ethers": "^2.0.4", "@nomiclabs/hardhat-etherscan": "^2.1.6", "@nomiclabs/hardhat-waffle": "^2.0.1", "chai": "^4.3.4", "ethereum-waffle": "^3.4.0", - "ethers": "^5.4.7", + "ethers": "^5.5.3", "hardhat": "^2.6.4", "solidity-coverage": "^0.7.17" }, "dependencies": { "@nomiclabs/hardhat-truffle5": "^2.0.2", "@nomiclabs/hardhat-web3": "^2.0.0", - "@openzeppelin/contracts": "^4.4.2", + "@openzeppelin/contracts": "4.4.2", "@openzeppelin/test-helpers": "^0.5.15", "dotenv": "^10.0.0", + "file-system": "^2.2.2", "find-config": "^1.0.0" } } diff --git a/contracts/basic-bandwidth-generation/scripts/mainnet/deploy-bandwidth-generator.js b/contracts/basic-bandwidth-generation/scripts/mainnet/deploy-bandwidth-generator.js new file mode 100644 index 0000000000..5346c8cfee --- /dev/null +++ b/contracts/basic-bandwidth-generation/scripts/mainnet/deploy-bandwidth-generator.js @@ -0,0 +1,34 @@ +const { ethers } = require('hardhat'); +const { constants } = require('@openzeppelin/test-helpers'); +const contracts = require("../../contractAddresses.json"); +const fs = require('file-system'); + +async function main() { + + const BandwidthGenerator = await ethers.getContractFactory("BandwidthGenerator"); + + console.log('preparing to deploy contract...') + + // if this is failing, check whether the ERC20 address has been manually added to the contract addresses json file + const bandwidthGenerator = await BandwidthGenerator.deploy( + contracts.mainnet.NYM_ERC20, + contracts.mainnet.GRAVITY + ); + + console.log('...contract successfully deployed...'); + + contracts.mainnet.BANDWIDTH_GENERATOR = bandwidthGenerator.address; + // the location of the json file is relative to where you are running the script from - run from root of directory + fs.writeFileSync('./contractAddresses.json', JSON.stringify(contracts), (err) => { + if (err) throw err; + }); + + console.log(`...bandwidthGenerator.sol deployed at ${bandwidthGenerator.address}`); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/contracts/basic-bandwidth-generation/scripts/mainnet/mainnet-nymt-bandwidth-args.js b/contracts/basic-bandwidth-generation/scripts/mainnet/mainnet-nymt-bandwidth-args.js new file mode 100644 index 0000000000..5ba86557f7 --- /dev/null +++ b/contracts/basic-bandwidth-generation/scripts/mainnet/mainnet-nymt-bandwidth-args.js @@ -0,0 +1,6 @@ +// arguments for verification of bandwidth generator constructed with NYMT via hardhat-etherscan plugin +// npx hardhat verify --constructor-args ./scripts/mainnet/mainnet-nymt-bandwidth-args.js --network mainnet 0xB3BF30DD53044c9050B7309031Bbf26b2cecF3be +module.exports = [ + "0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F", + "0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906" +]; \ No newline at end of file diff --git a/contracts/basic-bandwidth-generation/scripts/mainnet/mainnet-token-args.js b/contracts/basic-bandwidth-generation/scripts/mainnet/mainnet-token-args.js new file mode 100644 index 0000000000..25b602ce77 --- /dev/null +++ b/contracts/basic-bandwidth-generation/scripts/mainnet/mainnet-token-args.js @@ -0,0 +1,8 @@ +// arguments for verification of gravity contract via hardhat-etherscan plugin +// npx hardhat verify --constructor-args ./scripts/mainnet/mainnet-token-args.js --network mainnet 0xCf6DeE9947fdDc958985E5283e63d41CBC83Ff61 +module.exports = [ + "0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906", + "nym", + "nym", + 6 +]; \ No newline at end of file diff --git a/contracts/basic-bandwidth-generation/scripts/rinkeby/deploy-bandwidth-generator.js b/contracts/basic-bandwidth-generation/scripts/rinkeby/deploy-bandwidth-generator.js new file mode 100644 index 0000000000..c9afa21a5d --- /dev/null +++ b/contracts/basic-bandwidth-generation/scripts/rinkeby/deploy-bandwidth-generator.js @@ -0,0 +1,30 @@ +const { ethers } = require('hardhat'); +const { constants } = require('@openzeppelin/test-helpers'); +const contracts = require("../../contractAddresses.json"); +const fs = require('file-system'); + +async function main() { + const BandwidthGenerator = await ethers.getContractFactory("BandwidthGenerator"); + // if this is failing, check whether the ERC20 address has been manually added to the contract addresses json file + const bandwidthGenerator = await BandwidthGenerator.deploy( + contracts.rinkeby.NYM_ERC20, + contracts.rinkeby.GRAVITY + ); + + console.log("deploying..."); + + contracts.rinkeby.BANDWIDTH_GENERATOR = bandwidthGenerator.address; + // the location of the json file is relative to where you are running the script from - run from root of directory + fs.writeFileSync('./contractAddresses.json', JSON.stringify(contracts), (err) => { + if (err) throw err; + }); + + console.log(`...bandwidthGenerator.sol deployed at ${bandwidthGenerator.address}`); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/contracts/basic-bandwidth-generation/scripts/rinkeby/deploy-gravity.js b/contracts/basic-bandwidth-generation/scripts/rinkeby/deploy-gravity.js new file mode 100644 index 0000000000..44b269bbeb --- /dev/null +++ b/contracts/basic-bandwidth-generation/scripts/rinkeby/deploy-gravity.js @@ -0,0 +1,32 @@ +const { ethers } = require('hardhat'); +const { constants } = require('@openzeppelin/test-helpers'); +const contracts = require("../../contractAddresses.json"); +const fs = require('file-system'); + +async function main() { + const [deployer] = await ethers.getSigners(); + console.log(deployer.address); + console.log(constants.ZERO_BYTES32); + const Gravity = await ethers.getContractFactory("Gravity"); + // deploy with args from unit tests + const gravity = await Gravity.deploy( + constants.ZERO_BYTES32, + [deployer.address], + [2863311531] + ); + + contracts.rinkeby.GRAVITY = gravity.address; + // the location of the json file is relative to where you are running the script from - run from root of directory + fs.writeFileSync('./contractAddresses.json', JSON.stringify(contracts), (err) => { + if (err) throw err; + }); + + console.log(`gravity.sol deployed at ${gravity.address}`); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/contracts/basic-bandwidth-generation/scripts/rinkeby/gravity-args.js b/contracts/basic-bandwidth-generation/scripts/rinkeby/gravity-args.js new file mode 100644 index 0000000000..4fcb240804 --- /dev/null +++ b/contracts/basic-bandwidth-generation/scripts/rinkeby/gravity-args.js @@ -0,0 +1,6 @@ +// arguments for verification of gravity contract via hardhat-etherscan plugin +module.exports = [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + ["0xf5b1B5C9b89906219Ba29a6cb12F0528c4C25D18"], + [2863311531] +]; \ No newline at end of file diff --git a/contracts/basic-bandwidth-generation/scripts/rinkeby/test-token-args.js b/contracts/basic-bandwidth-generation/scripts/rinkeby/test-token-args.js new file mode 100644 index 0000000000..31b58bd7e3 --- /dev/null +++ b/contracts/basic-bandwidth-generation/scripts/rinkeby/test-token-args.js @@ -0,0 +1,7 @@ +// arguments for verification of gravity contract via hardhat-etherscan plugin +module.exports = [ + "0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B", + "test", + "tst", + 6 +]; \ No newline at end of file diff --git a/contracts/basic-bandwidth-generation/test/BandwidthGenerator.js b/contracts/basic-bandwidth-generation/test/BandwidthGenerator.js index 22851bc480..86863b3345 100644 --- a/contracts/basic-bandwidth-generation/test/BandwidthGenerator.js +++ b/contracts/basic-bandwidth-generation/test/BandwidthGenerator.js @@ -3,8 +3,8 @@ const { constants, expectRevert, expectEvent } = require('@openzeppelin/test-hel const { artifacts, web3 } = require("hardhat"); const BN = require('bn.js'); const BandwidthGenerator = artifacts.require('BandwidthGenerator'); -const Gravity = artifacts.require('Gravity'); -const CosmosToken = artifacts.require('CosmosERC20'); +const Gravity = artifacts.require('test-contracts/TestGravity'); +const CosmosToken = artifacts.require('TestCosmosERC20'); contract('BandwidthGenerator', (accounts) => { @@ -13,6 +13,7 @@ contract('BandwidthGenerator', (accounts) => { let erc20token; let owner = accounts[0]; let user = accounts[1]; + let cosmosRecipient = "nymt1f06hzmwf9chqewkpv93ajk6tayzp4784m2da9x"; // random sandbox testnet address let initialRatio = 1073741824; // 1073741824 bytes = 1GB let newRatio; let tokenAmount = web3.utils.toWei('100'); // this is converting 100 tokens to their representation in wei: 100000000000000000000 @@ -25,17 +26,16 @@ contract('BandwidthGenerator', (accounts) => { // deploy gravity bridge with test data gravity = await Gravity.new( constants.ZERO_BYTES32, - 1, [owner], - [10] + [2863311531] ); // deploy erc20 NYM from bridge await gravity.deployERC20( - 'eNYM', + 'cosmosNYMDenomination', 'NYMERC20', 'NYM', - 18 + 6 ); // grab event args for getting token address @@ -49,6 +49,7 @@ contract('BandwidthGenerator', (accounts) => { // deploy bandwidthGenerator contract with contract address of erc20NYM & address of gravity bridge bandwidthGenerator = await BandwidthGenerator.new(erc20token.address, gravity.address); + }); context(">> deployment parameters are valid", () => { @@ -64,6 +65,9 @@ contract('BandwidthGenerator', (accounts) => { it("returns the correct contract admin", async () => { expect((await bandwidthGenerator.owner()).toString()).to.equal((owner).toString()); }); + it("returns the correct default generation state: true", async () => { + expect((await bandwidthGenerator.credentialGenerationEnabled())).to.equal(true); + }); }); context(">> deployment parameters are invalid", () => { @@ -82,7 +86,7 @@ contract('BandwidthGenerator', (accounts) => { }); context(">> generateBasicBandwidthCredential()", () => { - before("", async () => { + before("mint tokens & approve", async () => { // transfer tokens to account which will create a BBCredential await erc20token.mintForUnitTesting(user, tokenAmount); // approve transfer to contract @@ -95,7 +99,7 @@ contract('BandwidthGenerator', (accounts) => { 15, [0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba, 0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba], - constants.ZERO_BYTES32, + cosmosRecipient, { from: user } ); @@ -105,7 +109,7 @@ contract('BandwidthGenerator', (accounts) => { Bandwidth: expectedBandwidthInMB.toString(), VerificationKey: '15', SignedVerificationKey: '0x39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba', - CosmosRecipient: constants.ZERO_BYTES32 + CosmosRecipient: cosmosRecipient }); await expectEvent.inTransaction(tx.tx, erc20token, 'Transfer', { @@ -116,7 +120,7 @@ contract('BandwidthGenerator', (accounts) => { await expectEvent.inTransaction(tx.tx, gravity, 'SendToCosmosEvent', { _tokenContract: erc20token.address, _sender: bandwidthGenerator.address, - _destination: constants.ZERO_BYTES32, + _destination: cosmosRecipient, _amount: halfTokenAmount }); @@ -133,7 +137,7 @@ contract('BandwidthGenerator', (accounts) => { 15, [0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba, 0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba], - constants.ZERO_BYTES32, + cosmosRecipient, { from: user } ); @@ -143,7 +147,7 @@ contract('BandwidthGenerator', (accounts) => { Bandwidth: newexpectedBandwidthInMB.toString(), VerificationKey: '15', SignedVerificationKey: '0x39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba', - CosmosRecipient: constants.ZERO_BYTES32 + CosmosRecipient: cosmosRecipient }); await expectEvent.inTransaction(tx.tx, erc20token, 'Transfer', { @@ -154,7 +158,7 @@ contract('BandwidthGenerator', (accounts) => { await expectEvent.inTransaction(tx.tx, gravity, 'SendToCosmosEvent', { _tokenContract: erc20token.address, _sender: bandwidthGenerator.address, - _destination: constants.ZERO_BYTES32, + _destination: cosmosRecipient, _amount: unevenTokenAmount }); }); @@ -168,7 +172,7 @@ contract('BandwidthGenerator', (accounts) => { 16, [0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba, 0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba], - constants.ZERO_BYTES32, + cosmosRecipient, { from: user } ), "BandwidthGenerator: Signature doesn't have 64 bytes" ); @@ -196,7 +200,7 @@ contract('BandwidthGenerator', (accounts) => { 15, [0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba, 0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba], - constants.ZERO_BYTES32, + cosmosRecipient, { from: user } ); @@ -206,7 +210,7 @@ contract('BandwidthGenerator', (accounts) => { Bandwidth: expectedBandwidthInMB.toString(), VerificationKey: '15', SignedVerificationKey: '0x39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba', - CosmosRecipient: constants.ZERO_BYTES32 + CosmosRecipient: cosmosRecipient }); await expectEvent.inTransaction(tx.tx, erc20token, 'Transfer', { @@ -217,11 +221,46 @@ contract('BandwidthGenerator', (accounts) => { await expectEvent.inTransaction(tx.tx, gravity, 'SendToCosmosEvent', { _tokenContract: erc20token.address, _sender: bandwidthGenerator.address, - _destination: constants.ZERO_BYTES32, + _destination: cosmosRecipient, _amount: oneToken }); }); }); + context(">>credential generation admin switch", () => { + it("only admin can switch credential generation off", async () => { + await expectRevert( + bandwidthGenerator.credentialGenerationSwitch(false, { from: user }), + "Ownable: caller is not the owner" + ) + }); + it("admin can switch credential generation on/off & switch generates an event", async () => { + let tx = await bandwidthGenerator.credentialGenerationSwitch(false); + expect((await bandwidthGenerator.credentialGenerationEnabled())).to.equal(false); + await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'CredentialGenerationSwitch', { + Enabled: false, + }); + + tx = await bandwidthGenerator.credentialGenerationSwitch(true); + expect((await bandwidthGenerator.credentialGenerationEnabled())).to.equal(true); + + await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'CredentialGenerationSwitch', { + Enabled: true, + }); + }); + it("cannot generate credentials if switch = false", async () => { + await bandwidthGenerator.credentialGenerationSwitch(false); + await expectRevert( + bandwidthGenerator.generateBasicBandwidthCredential( + oneToken, + 15, + [0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba, + 0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba], + cosmosRecipient, + { from: user } + ), "BandwidthGenerator: credential generation isn't currently enabled" + ); + }); + }); }); diff --git a/contracts/mixnet/Cargo.toml b/contracts/mixnet/Cargo.toml index d1ca3066fe..ad4a677d14 100644 --- a/contracts/mixnet/Cargo.toml +++ b/contracts/mixnet/Cargo.toml @@ -2,7 +2,7 @@ name = "mixnet-contract" version = "0.1.0" authors = ["Dave Hrycyszyn "] -edition = "2018" +edition = "2021" exclude = [ # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. @@ -17,17 +17,18 @@ crate-type = ["cdylib", "rlib"] [dependencies] mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } +vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } config = { path = "../../common/config"} -vesting-contract = { path = "../vesting" } -cosmwasm-std = "1.0.0-beta4" -cosmwasm-storage = "1.0.0-beta4" +cosmwasm-std = "1.0.0-beta3" +cosmwasm-storage = "1.0.0-beta3" cw-storage-plus = "0.11.1" bs58 = "0.4.0" schemars = "0.8" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { version = "1.0.23" } +time = { version = "0.3", features = ["macros"] } [dev-dependencies] cosmwasm-schema = "1.0.0-beta3" diff --git a/contracts/mixnet/examples/schema.rs b/contracts/mixnet/examples/schema.rs index 8f390777ca..0571f23914 100644 --- a/contracts/mixnet/examples/schema.rs +++ b/contracts/mixnet/examples/schema.rs @@ -1,8 +1,6 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -extern crate mixnet_contract; - use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; use mixnet_contract_common::{ExecuteMsg, InstantiateMsg, MixNodeBond, QueryMsg}; use std::env::current_dir; diff --git a/contracts/mixnet/src/constants.rs b/contracts/mixnet/src/constants.rs new file mode 100644 index 0000000000..14a850e4dd --- /dev/null +++ b/contracts/mixnet/src/constants.rs @@ -0,0 +1,20 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::time::Duration; + +// approximately 1 week (assuming 5s per block) +// i.e. approximately quarter of the interval (there are 3600 * 60 * 7 = 604800 seconds in a week, i.e. ~604800 / 5 = 120960 blocks) +pub const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 120960; + +pub const INTERVAL_REWARD_PERCENT: u8 = 2; // Used to calculate interval reward pool +pub const SYBIL_RESISTANCE_PERCENT: u8 = 30; +pub const ACTIVE_SET_WORK_FACTOR: u8 = 10; + +// TODO: this, in theory, represents "epoch" length. +// However, since the blocktime is not EXACTLY 5s, we can't really guarantee 720 epochs in interval +// and we can't change this easily to `Duration`, because then the entire rewarded set storage +// would be messed up... (as we look up stuff "by blocks") +pub const REWARDED_SET_REFRESH_BLOCKS: u64 = 720; // with blocktime being approximately 5s, it should be roughly 1h + +pub const REWARDING_INTERVAL_LENGTH: Duration = Duration::from_secs(60 * 60 * 720); // 720h, i.e. 30 days diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index 420a011ae0..8b7ec320f2 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -1,6 +1,10 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::constants::{ + ACTIVE_SET_WORK_FACTOR, INTERVAL_REWARD_PERCENT, REWARDING_INTERVAL_LENGTH, + SYBIL_RESISTANCE_PERCENT, +}; use crate::delegations::queries::query_all_network_delegations_paged; use crate::delegations::queries::query_delegator_delegations_paged; use crate::delegations::queries::query_mixnode_delegation; @@ -8,8 +12,13 @@ use crate::delegations::queries::query_mixnode_delegations_paged; use crate::error::ContractError; use crate::gateways::queries::query_gateways_paged; use crate::gateways::queries::query_owns_gateway; +use crate::interval::queries::{ + query_current_interval, query_current_rewarded_set_height, query_rewarded_set, + query_rewarded_set_heights_for_interval, query_rewarded_set_refresh_minimum_blocks, + query_rewarded_set_update_details, +}; +use crate::interval::storage as interval_storage; use crate::mixnet_contract_settings::models::ContractState; -use crate::mixnet_contract_settings::queries::query_rewarding_interval; use crate::mixnet_contract_settings::queries::{ query_contract_settings_params, query_contract_version, }; @@ -17,15 +26,17 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::mixnodes::bonding_queries as mixnode_queries; use crate::mixnodes::bonding_queries::query_mixnodes_paged; use crate::mixnodes::layer_queries::query_layer_distribution; -use crate::rewards::queries::query_reward_pool; -use crate::rewards::queries::{query_circulating_supply, query_rewarding_status}; +use crate::rewards::queries::{ + query_circulating_supply, query_reward_pool, query_rewarding_status, +}; use crate::rewards::storage as rewards_storage; use cosmwasm_std::{ entry_point, to_binary, Addr, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Uint128, }; use mixnet_contract_common::{ - ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, + ContractStateParams, ExecuteMsg, InstantiateMsg, Interval, MigrateMsg, QueryMsg, }; +use time::OffsetDateTime; /// Constant specifying minimum of coin required to bond a gateway pub const INITIAL_GATEWAY_PLEDGE: Uint128 = Uint128::new(100_000_000); @@ -37,15 +48,12 @@ pub const INITIAL_MIXNODE_REWARDED_SET_SIZE: u32 = 200; pub const INITIAL_MIXNODE_ACTIVE_SET_SIZE: u32 = 100; pub const INITIAL_REWARD_POOL: u128 = 250_000_000_000_000; -pub const EPOCH_REWARD_PERCENT: u8 = 2; // Used to calculate epoch reward pool -pub const DEFAULT_SYBIL_RESISTANCE_PERCENT: u8 = 30; -pub const DEFAULT_ACTIVE_SET_WORK_FACTOR: u8 = 10; +pub const INITIAL_ACTIVE_SET_WORK_FACTOR: u8 = 10; -fn default_initial_state( - owner: Addr, - rewarding_validator_address: Addr, - env: Env, -) -> ContractState { +pub const DEFAULT_FIRST_INTERVAL_START: OffsetDateTime = + time::macros::datetime!(2022-01-01 12:00 UTC); + +fn default_initial_state(owner: Addr, rewarding_validator_address: Addr) -> ContractState { ContractState { owner, rewarding_validator_address, @@ -54,11 +62,7 @@ fn default_initial_state( minimum_gateway_pledge: INITIAL_GATEWAY_PLEDGE, mixnode_rewarded_set_size: INITIAL_MIXNODE_REWARDED_SET_SIZE, mixnode_active_set_size: INITIAL_MIXNODE_ACTIVE_SET_SIZE, - active_set_work_factor: DEFAULT_ACTIVE_SET_WORK_FACTOR, }, - rewarding_interval_starting_block: env.block.height, - latest_rewarding_interval_nonce: 0, - rewarding_in_progress: false, } } @@ -69,17 +73,21 @@ fn default_initial_state( /// `msg` is the contract initialization message, sort of like a constructor call. #[entry_point] pub fn instantiate( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, msg: InstantiateMsg, ) -> Result { let rewarding_validator_address = deps.api.addr_validate(&msg.rewarding_validator_address)?; - let state = default_initial_state(info.sender, rewarding_validator_address, env); + let state = default_initial_state(info.sender, rewarding_validator_address); + let rewarding_interval = + Interval::new(0, DEFAULT_FIRST_INTERVAL_START, REWARDING_INTERVAL_LENGTH); mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &state)?; mixnet_params_storage::LAYERS.save(deps.storage, &Default::default())?; rewards_storage::REWARD_POOL.save(deps.storage, &Uint128::new(INITIAL_REWARD_POOL))?; + interval_storage::CURRENT_INTERVAL.save(deps.storage, &rewarding_interval)?; + interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?; Ok(Response::default()) } @@ -87,7 +95,7 @@ pub fn instantiate( /// Handle an incoming message #[entry_point] pub fn execute( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, msg: ExecuteMsg, @@ -135,14 +143,14 @@ pub fn execute( ExecuteMsg::RewardMixnode { identity, params, - rewarding_interval_nonce, + interval_id, } => crate::rewards::transactions::try_reward_mixnode( deps, env, info, identity, params, - rewarding_interval_nonce, + interval_id, ), ExecuteMsg::DelegateToMixnode { mix_identity } => { crate::delegations::transactions::try_delegate_to_mixnode(deps, env, info, mix_identity) @@ -154,29 +162,14 @@ pub fn execute( mix_identity, ) } - ExecuteMsg::BeginMixnodeRewarding { - rewarding_interval_nonce, - } => crate::rewards::transactions::try_begin_mixnode_rewarding( - deps, - env, - info, - rewarding_interval_nonce, - ), - ExecuteMsg::FinishMixnodeRewarding { - rewarding_interval_nonce, - } => crate::rewards::transactions::try_finish_mixnode_rewarding( - deps, - info, - rewarding_interval_nonce, - ), ExecuteMsg::RewardNextMixDelegators { mix_identity, - rewarding_interval_nonce, + interval_id, } => crate::rewards::transactions::try_reward_next_mixnode_delegators( deps, info, mix_identity, - rewarding_interval_nonce, + interval_id, ), ExecuteMsg::DelegateToMixnodeOnBehalf { mix_identity, @@ -227,11 +220,24 @@ pub fn execute( ExecuteMsg::UnbondGatewayOnBehalf { owner } => { crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner) } + ExecuteMsg::WriteRewardedSet { + rewarded_set, + expected_active_set_size, + } => crate::interval::transactions::try_write_rewarded_set( + deps, + env, + info, + rewarded_set, + expected_active_set_size, + ), + ExecuteMsg::AdvanceCurrentInterval {} => { + crate::interval::transactions::try_advance_interval(env, deps.storage) + } } } #[entry_point] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result { +pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result { let query_res = match msg { QueryMsg::GetContractVersion {} => to_binary(&query_contract_version()), QueryMsg::GetMixNodes { start_after, limit } => { @@ -245,7 +251,6 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result to_binary(&query_owns_gateway(deps, address)?), QueryMsg::StateParams {} => to_binary(&query_contract_settings_params(deps)?), - QueryMsg::CurrentRewardingInterval {} => to_binary(&query_rewarding_interval(deps)?), QueryMsg::LayerDistribution {} => to_binary(&query_layer_distribution(deps)?), QueryMsg::GetMixnodeDelegations { mix_identity, @@ -276,22 +281,98 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result to_binary(&query_mixnode_delegation(deps, mix_identity, delegator)?), QueryMsg::GetRewardPool {} => to_binary(&query_reward_pool(deps)?), QueryMsg::GetCirculatingSupply {} => to_binary(&query_circulating_supply(deps)?), - QueryMsg::GetEpochRewardPercent {} => to_binary(&EPOCH_REWARD_PERCENT), - QueryMsg::GetSybilResistancePercent {} => to_binary(&DEFAULT_SYBIL_RESISTANCE_PERCENT), + QueryMsg::GetIntervalRewardPercent {} => to_binary(&INTERVAL_REWARD_PERCENT), + QueryMsg::GetSybilResistancePercent {} => to_binary(&SYBIL_RESISTANCE_PERCENT), + QueryMsg::GetActiveSetWorkFactor {} => to_binary(&ACTIVE_SET_WORK_FACTOR), QueryMsg::GetRewardingStatus { mix_identity, - rewarding_interval_nonce, - } => to_binary(&query_rewarding_status( - deps, - mix_identity, - rewarding_interval_nonce, + interval_id, + } => to_binary(&query_rewarding_status(deps, mix_identity, interval_id)?), + QueryMsg::GetRewardedSet { + height, + start_after, + limit, + } => to_binary(&query_rewarded_set( + deps.storage, + height, + start_after, + limit, )?), + QueryMsg::GetRewardedSetHeightsForInterval { interval_id } => to_binary( + &query_rewarded_set_heights_for_interval(deps.storage, interval_id)?, + ), + QueryMsg::GetRewardedSetUpdateDetails {} => { + to_binary(&query_rewarded_set_update_details(env, deps.storage)?) + } + QueryMsg::GetCurrentRewardedSetHeight {} => { + to_binary(&query_current_rewarded_set_height(deps.storage)?) + } + QueryMsg::GetCurrentInterval {} => to_binary(&query_current_interval(deps.storage)?), + QueryMsg::GetRewardedSetRefreshBlocks {} => { + to_binary(&query_rewarded_set_refresh_minimum_blocks()) + } }; Ok(query_res?) } #[entry_point] -pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result { +pub fn migrate(deps: DepsMut<'_>, env: Env, _msg: MigrateMsg) -> Result { + use cw_storage_plus::Item; + use serde::{Deserialize, Serialize}; + + // needed migration: + /* + 1. removal of rewarding_interval_starting_block field from ContractState + 2. removal of latest_rewarding_interval_nonce field from ContractState + 3. removal of rewarding_in_progress field from ContractState + 4. interval_storage::CURRENT_INTERVAL.save(deps.storage, &Interval::default())?; + 5. interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?; + 6. removal of active_set_work_factor fields from ContractStateParams + */ + + #[derive(Serialize, Deserialize)] + pub struct OldContractStateParams { + pub minimum_mixnode_pledge: Uint128, + pub minimum_gateway_pledge: Uint128, + pub mixnode_rewarded_set_size: u32, + pub mixnode_active_set_size: u32, + pub active_set_work_factor: u8, + } + + #[derive(Serialize, Deserialize)] + struct OldContractState { + pub owner: Addr, // only the owner account can update state + pub rewarding_validator_address: Addr, + pub params: OldContractStateParams, + pub rewarding_interval_starting_block: u64, + pub latest_rewarding_interval_nonce: u32, + pub rewarding_in_progress: bool, + } + + let old_contract_state: Item<'_, OldContractState> = Item::new("config"); + + let old_state = old_contract_state.load(deps.storage)?; + + let new_params = mixnet_contract_common::ContractStateParams { + minimum_mixnode_pledge: old_state.params.minimum_mixnode_pledge, + minimum_gateway_pledge: old_state.params.minimum_mixnode_pledge, + mixnode_rewarded_set_size: old_state.params.mixnode_rewarded_set_size, + mixnode_active_set_size: old_state.params.mixnode_active_set_size, + }; + + let new_state = crate::mixnet_contract_settings::models::ContractState { + owner: old_state.owner, + rewarding_validator_address: old_state.rewarding_validator_address, + params: new_params, + }; + let rewarding_interval = + Interval::new(0, DEFAULT_FIRST_INTERVAL_START, REWARDING_INTERVAL_LENGTH); + + mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &new_state)?; + + interval_storage::CURRENT_INTERVAL.save(deps.storage, &rewarding_interval)?; + interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?; + Ok(Default::default()) } diff --git a/contracts/mixnet/src/delegations/queries.rs b/contracts/mixnet/src/delegations/queries.rs index 258f1d548d..45656a90a1 100644 --- a/contracts/mixnet/src/delegations/queries.rs +++ b/contracts/mixnet/src/delegations/queries.rs @@ -13,7 +13,7 @@ use mixnet_contract_common::{ }; pub(crate) fn query_all_network_delegations_paged( - deps: Deps, + deps: Deps<'_>, start_after: Option<(IdentityKey, String)>, limit: Option, ) -> StdResult { @@ -42,7 +42,7 @@ pub(crate) fn query_all_network_delegations_paged( } pub(crate) fn query_delegator_delegations_paged( - deps: Deps, + deps: Deps<'_>, delegation_owner: String, start_after: Option, limit: Option, @@ -76,7 +76,7 @@ pub(crate) fn query_delegator_delegations_paged( // queries for delegation value of given address for particular node pub(crate) fn query_mixnode_delegation( - deps: Deps, + deps: Deps<'_>, mix_identity: IdentityKey, delegator: String, ) -> Result { @@ -92,7 +92,7 @@ pub(crate) fn query_mixnode_delegation( } pub(crate) fn query_mixnode_delegations_paged( - deps: Deps, + deps: Deps<'_>, mix_identity: IdentityKey, start_after: Option, limit: Option, diff --git a/contracts/mixnet/src/delegations/transactions.rs b/contracts/mixnet/src/delegations/transactions.rs index 9d060e891c..9dc8c21bc2 100644 --- a/contracts/mixnet/src/delegations/transactions.rs +++ b/contracts/mixnet/src/delegations/transactions.rs @@ -9,7 +9,8 @@ use cosmwasm_std::{coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, Messa use cw_storage_plus::PrimaryKey; use mixnet_contract_common::events::{new_delegation_event, new_undelegation_event}; use mixnet_contract_common::{Delegation, IdentityKey}; -use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::one_unym; fn validate_delegation_stake(mut delegation: Vec) -> Result { // check if anything was put as delegation @@ -35,7 +36,7 @@ fn validate_delegation_stake(mut delegation: Vec) -> Result, env: Env, info: MessageInfo, mix_identity: IdentityKey, @@ -47,7 +48,7 @@ pub(crate) fn try_delegate_to_mixnode( } pub(crate) fn try_delegate_to_mixnode_on_behalf( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, mix_identity: IdentityKey, @@ -67,7 +68,7 @@ pub(crate) fn try_delegate_to_mixnode_on_behalf( } pub(crate) fn _try_delegate_to_mixnode( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, mix_identity: IdentityKey, delegate: &str, @@ -131,7 +132,7 @@ pub(crate) fn _try_delegate_to_mixnode( } pub(crate) fn try_remove_delegation_from_mixnode( - deps: DepsMut, + deps: DepsMut<'_>, info: MessageInfo, mix_identity: IdentityKey, ) -> Result { @@ -139,7 +140,7 @@ pub(crate) fn try_remove_delegation_from_mixnode( } pub(crate) fn try_remove_delegation_from_mixnode_on_behalf( - deps: DepsMut, + deps: DepsMut<'_>, info: MessageInfo, mix_identity: IdentityKey, delegate: String, @@ -148,7 +149,7 @@ pub(crate) fn try_remove_delegation_from_mixnode_on_behalf( } pub(crate) fn _try_remove_delegation_from_mixnode( - deps: DepsMut, + deps: DepsMut<'_>, mix_identity: IdentityKey, delegate: &str, proxy: Option, @@ -212,7 +213,7 @@ pub(crate) fn _try_remove_delegation_from_mixnode( amount: old_delegation.amount.clone(), }); - let track_undelegation_msg = wasm_execute(proxy, &msg, coins(0, DENOM))?; + let track_undelegation_msg = wasm_execute(proxy, &msg, vec![one_unym()])?; response = response.add_message(track_undelegation_msg); } diff --git a/contracts/mixnet/src/error.rs b/contracts/mixnet/src/error.rs index 46e85e9de0..ff4d93bd16 100644 --- a/contracts/mixnet/src/error.rs +++ b/contracts/mixnet/src/error.rs @@ -12,99 +12,116 @@ use thiserror::Error; /// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. #[derive(Error, Debug, PartialEq)] pub enum ContractError { - #[error("{0}")] + #[error("MIXNET ({}): {0}", line!())] Std(#[from] StdError), - #[error("Not enough funds sent for mixnode bond. (received {received}, minimum {minimum})")] + #[error("MIXNET ({}): Not enough funds sent for mixnode bond. (received {received}, minimum {minimum})", line!())] InsufficientMixNodeBond { received: u128, minimum: u128 }, - #[error("Mixnode ({identity}) does not exist")] + #[error("MIXNET ({}): Mixnode ({identity}) does not exist", line!())] MixNodeBondNotFound { identity: IdentityKey }, - #[error("Not enough funds sent for gateway bond. (received {received}, minimum {minimum})")] + #[error("MIXNET ({}): Not enough funds sent for gateway bond. (received {received}, minimum {minimum})", line!())] InsufficientGatewayBond { received: u128, minimum: u128 }, - #[error("{owner} does not seem to own any mixnodes")] + #[error("MIXNET ({}): {owner} does not seem to own any mixnodes", line!())] NoAssociatedMixNodeBond { owner: Addr }, - #[error("{owner} does not seem to own any gateways")] + #[error("MIXNET ({}): {owner} does not seem to own any gateways", line!())] NoAssociatedGatewayBond { owner: Addr }, - #[error("Unauthorized")] + #[error("MIXNET ({}): Unauthorized", line!())] Unauthorized, - #[error("Wrong coin denomination, you must send {}", DENOM)] + #[error("MIXNET ({}): Wrong coin denomination, you must send {}", line!(), DENOM)] WrongDenom, - #[error("Received multiple coin types during staking")] + #[error("MIXNET ({}): Received multiple coin types during staking", line!())] MultipleDenoms, - #[error("No coin was sent for the bonding, you must send {}", DENOM)] + #[error("MIXNET ({}): No coin was sent for the bonding, you must send {}", line!(), DENOM)] NoBondFound, - #[error("Provided active set size is bigger than the demanded set")] + #[error("MIXNET ({}): Provided active set size is bigger than the rewarded set", line!())] InvalidActiveSetSize, - #[error("Provided active set size is zero")] + #[error("MIXNET ({}): Provided active set size is zero", line!())] ZeroActiveSet, - #[error("Provided rewarded set size is zero")] + #[error("MIXNET ({}): Provided rewarded set size is zero", line!())] ZeroRewardedSet, - #[error("This address has already bonded a mixnode")] + #[error("MIXNET ({}): This address has already bonded a mixnode", line!())] AlreadyOwnsMixnode, - #[error("This address has already bonded a gateway")] + #[error("MIXNET ({}): This address has already bonded a gateway", line!())] AlreadyOwnsGateway, - #[error("Mixnode with this identity already exists. Its owner is {owner}")] + #[error("MIXNET ({}): Mixnode with this identity already exists. Its owner is {owner}", line!())] DuplicateMixnode { owner: Addr }, - #[error("Gateway with this identity already exists. Its owner is {owner}")] + #[error("MIXNET ({}): Gateway with this identity already exists. Its owner is {owner}", line!())] DuplicateGateway { owner: Addr }, - #[error("No funds were provided for the delegation")] + #[error("MIXNET ({}): No funds were provided for the delegation", line!())] EmptyDelegation, - #[error("Could not find any delegation information associated with mixnode {identity} for {address}")] + #[error("MIXNET ({}): Could not find any delegation information associated with mixnode {identity} for {address}", line!())] NoMixnodeDelegationFound { identity: IdentityKey, address: Addr, }, - #[error("We tried to remove more funds then are available in the Reward pool. Wanted to remove {to_remove}, but have only {reward_pool}")] + #[error("MIXNET ({}): We tried to remove more funds then are available in the Reward pool. Wanted to remove {to_remove}, but have only {reward_pool}", line!())] OutOfFunds { to_remove: u128, reward_pool: u128 }, - #[error("Received invalid rewarding interval nonce. Expected {expected}, received {received}")] - InvalidRewardingIntervalNonce { received: u32, expected: u32 }, + #[error("MIXNET ({}): Received invalid interval id. Expected {expected}, received {received}", line!())] + InvalidIntervalId { received: u32, expected: u32 }, - #[error("Rewarding distribution is currently in progress")] - RewardingInProgress, - - #[error("Rewarding distribution is currently not in progress")] - RewardingNotInProgress, - - #[error("Mixnode {identity} has already been rewarded during the current rewarding interval")] + #[error("MIXNET ({}): Mixnode {identity} has already been rewarded during the current rewarding interval", line!())] MixnodeAlreadyRewarded { identity: IdentityKey }, - #[error("Some of mixnodes {identity} delegators are still pending reward")] + #[error("MIXNET ({}): Some of mixnodes {identity} delegators are still pending reward", line!())] DelegatorsPendingReward { identity: IdentityKey }, - #[error("Mixnode's {identity} operator has not been rewarded yet - cannot perform delegator rewarding until that happens")] + #[error("MIXNET ({}): Mixnode's {identity} operator has not been rewarded yet - cannot perform delegator rewarding until that happens", line!())] MixnodeOperatorNotRewarded { identity: IdentityKey }, - #[error("Proxy address mismatch, expected {existing}, got {incoming}")] + #[error("MIXNET ({}): Proxy address mismatch, expected {existing}, got {incoming}", line!())] ProxyMismatch { existing: String, incoming: String }, - #[error("Failed to recover ed25519 public key from its base58 representation - {0}")] + #[error("MIXNET ({}): Failed to recover ed25519 public key from its base58 representation - {0}", line!())] MalformedEd25519IdentityKey(String), - #[error("Failed to recover ed25519 signature from its base58 representation - {0}")] + #[error("MIXNET ({}): Failed to recover ed25519 signature from its base58 representation - {0}", line!())] MalformedEd25519Signature(String), - #[error("Provided ed25519 signature did not verify correctly")] + #[error("MIXNET ({}): Provided ed25519 signature did not verify correctly", line!())] InvalidEd25519Signature, - #[error("Profit margin percent needs to be an integer in range [0, 100], recieved {0}")] + #[error("MIXNET ({}): Profit margin percent needs to be an integer in range [0, 100], received {0}", line!())] InvalidProfitMarginPercent(u8), + + #[error("MIXNET ({}): Rewarded set height not set, was rewarding set determined?", line!())] + RewardSetHeightMapEmpty, + + #[error("MIXNET ({}): Received unexpected value for the active set. Got: {received}, expected: {expected}", line!())] + UnexpectedActiveSetSize { received: u32, expected: u32 }, + + #[error("MIXNET ({}): Received unexpected value for the rewarded set. Got: {received}, expected at most: {expected}", line!())] + UnexpectedRewardedSetSize { received: u32, expected: u32 }, + + #[error("MIXNET ({}): There hasn't been sufficient delay since last rewarded set update. It was last updated at height {last_update}. The delay is {minimum_delay}. The current block height is {current_height}", line!())] + TooFrequentRewardedSetUpdate { + last_update: u64, + minimum_delay: u64, + current_height: u64, + }, + + #[error("MIXNET ({}): Can't change to the desired interval as it's not in progress yet. It starts at {interval_start} and finishes at {interval_end}, while the current block time is {current_block_time}", line!())] + IntervalNotInProgress { + current_block_time: u64, + interval_start: i64, + interval_end: i64, + }, } diff --git a/contracts/mixnet/src/gateways/queries.rs b/contracts/mixnet/src/gateways/queries.rs index 4125ca2143..5a4ab5d2db 100644 --- a/contracts/mixnet/src/gateways/queries.rs +++ b/contracts/mixnet/src/gateways/queries.rs @@ -10,7 +10,7 @@ use mixnet_contract_common::{ }; pub(crate) fn query_gateways_paged( - deps: Deps, + deps: Deps<'_>, start_after: Option, limit: Option, ) -> StdResult { @@ -31,7 +31,7 @@ pub(crate) fn query_gateways_paged( } pub(crate) fn query_owns_gateway( - deps: Deps, + deps: Deps<'_>, address: String, ) -> StdResult { let validated_addr = deps.api.addr_validate(&address)?; diff --git a/contracts/mixnet/src/gateways/storage.rs b/contracts/mixnet/src/gateways/storage.rs index 723a8c6ea0..4c74067bc6 100644 --- a/contracts/mixnet/src/gateways/storage.rs +++ b/contracts/mixnet/src/gateways/storage.rs @@ -46,7 +46,7 @@ mod tests { // currently this is only used in tests but may become useful later on pub(crate) fn read_gateway_pledge_amount( storage: &dyn Storage, - identity: IdentityKeyRef, + identity: IdentityKeyRef<'_>, ) -> StdResult { let node = storage::gateways().load(storage, identity)?; Ok(node.pledge_amount.amount) diff --git a/contracts/mixnet/src/gateways/transactions.rs b/contracts/mixnet/src/gateways/transactions.rs index 6426b44357..35e42bd2ac 100644 --- a/contracts/mixnet/src/gateways/transactions.rs +++ b/contracts/mixnet/src/gateways/transactions.rs @@ -7,14 +7,15 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::support::helpers::{ensure_no_existing_bond, validate_node_identity_signature}; use config::defaults::DENOM; use cosmwasm_std::{ - coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, + wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, }; use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event}; use mixnet_contract_common::{Gateway, GatewayBond, Layer}; -use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::one_unym; pub fn try_add_gateway( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, gateway: Gateway, @@ -39,7 +40,7 @@ pub fn try_add_gateway( } pub fn try_add_gateway_on_behalf( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, gateway: Gateway, @@ -66,7 +67,7 @@ pub fn try_add_gateway_on_behalf( } pub(crate) fn _try_add_gateway( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, gateway: Gateway, pledge: Coin, @@ -119,7 +120,7 @@ pub(crate) fn _try_add_gateway( } pub fn try_remove_gateway_on_behalf( - deps: DepsMut, + deps: DepsMut<'_>, info: MessageInfo, owner: String, ) -> Result { @@ -127,12 +128,12 @@ pub fn try_remove_gateway_on_behalf( _try_remove_gateway(deps, &owner, Some(proxy)) } -pub fn try_remove_gateway(deps: DepsMut, info: MessageInfo) -> Result { +pub fn try_remove_gateway(deps: DepsMut<'_>, info: MessageInfo) -> Result { _try_remove_gateway(deps, info.sender.as_ref(), None) } pub(crate) fn _try_remove_gateway( - deps: DepsMut, + deps: DepsMut<'_>, owner: &str, proxy: Option, ) -> Result { @@ -176,7 +177,7 @@ pub(crate) fn _try_remove_gateway( amount: gateway_bond.pledge_amount(), }; - let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?; + let track_unbond_message = wasm_execute(proxy, &msg, vec![one_unym()])?; response = response.add_message(track_unbond_message); } diff --git a/contracts/mixnet/src/interval/mod.rs b/contracts/mixnet/src/interval/mod.rs new file mode 100644 index 0000000000..11f07ae776 --- /dev/null +++ b/contracts/mixnet/src/interval/mod.rs @@ -0,0 +1,6 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod queries; +pub mod storage; +pub mod transactions; diff --git a/contracts/mixnet/src/interval/queries.rs b/contracts/mixnet/src/interval/queries.rs new file mode 100644 index 0000000000..a740209ead --- /dev/null +++ b/contracts/mixnet/src/interval/queries.rs @@ -0,0 +1,425 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use super::storage; +use crate::error::ContractError; +use cosmwasm_std::{Env, Order, StdResult, Storage}; +use cw_storage_plus::Bound; +use mixnet_contract_common::{ + IdentityKey, Interval, IntervalRewardedSetHeightsResponse, PagedRewardedSetResponse, + RewardedSetNodeStatus, RewardedSetUpdateDetails, +}; + +pub fn query_current_interval(storage: &dyn Storage) -> Result { + Ok(storage::CURRENT_INTERVAL.load(storage)?) +} + +pub(crate) fn query_rewarded_set_refresh_minimum_blocks() -> u64 { + crate::constants::REWARDED_SET_REFRESH_BLOCKS +} + +pub fn query_rewarded_set_heights_for_interval( + storage: &dyn Storage, + interval_id: u32, +) -> Result { + // I don't think we have to deal with paging here as at most we're going to have 720 values here + // and I think the validators are capable of performing 720 storage reads at once if they're only + // reading u64 (+ u8) values... + let heights = storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL + .prefix(interval_id) + .range(storage, None, None, Order::Ascending) + .map(|val| val.map(|(height, _)| height)) + .collect::>>()?; + + Ok(IntervalRewardedSetHeightsResponse { + interval_id, + heights, + }) +} + +// note: I have removed the `query_rewarded_set_for_interval`, because I don't think it's appropriate +// for the contract to go through so much data (i.e. all "rewarded" sets of particular interval) in one go. +// To achieve the same result, the client would have to instead first call `query_rewarded_set_heights_for_interval` +// to learn the heights used in given interval and then for each of them `query_rewarded_set` for that particular height. + +pub fn query_current_rewarded_set_height(storage: &dyn Storage) -> Result { + Ok(storage::CURRENT_REWARDED_SET_HEIGHT.load(storage)?) +} + +fn query_rewarded_set_at_height( + storage: &dyn Storage, + height: u64, + start_after: Option, + limit: u32, +) -> Result, ContractError> { + let start = start_after.map(Bound::exclusive); + + let rewarded_set = storage::REWARDED_SET + .prefix(height) + .range(storage, start, None, Order::Ascending) + .take(limit as usize) + .collect::>()?; + Ok(rewarded_set) +} + +pub fn query_rewarded_set( + storage: &dyn Storage, + height: Option, + start_after: Option, + limit: Option, +) -> Result { + let height = match height { + Some(height) => height, + None => query_current_rewarded_set_height(storage)?, + }; + let limit = limit + .unwrap_or(storage::REWARDED_NODE_DEFAULT_PAGE_LIMIT) + .min(storage::REWARDED_NODE_MAX_PAGE_LIMIT); + + // query for an additional element to determine paging requirements + let mut paged_result = query_rewarded_set_at_height(storage, height, start_after, limit + 1)?; + + if paged_result.len() > limit as usize { + paged_result.truncate(limit as usize); + Ok(PagedRewardedSetResponse { + start_next_after: paged_result.last().map(|res| res.0.clone()), + identities: paged_result, + at_height: height, + }) + } else { + Ok(PagedRewardedSetResponse { + identities: paged_result, + start_next_after: None, + at_height: height, + }) + } +} + +// this was all put together into the same query so that all information would be synced together +pub fn query_rewarded_set_update_details( + env: Env, + storage: &dyn Storage, +) -> Result { + Ok(RewardedSetUpdateDetails { + refresh_rate_blocks: query_rewarded_set_refresh_minimum_blocks(), + last_refreshed_block: query_current_rewarded_set_height(storage)?, + current_height: env.block.height, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::interval::storage::REWARDED_NODE_MAX_PAGE_LIMIT; + use crate::support::tests::test_helpers; + use cosmwasm_std::testing::mock_env; + + fn store_rewarded_nodes( + storage: &mut dyn Storage, + height: u64, + active_set: u32, + rewarded_set: u32, + ) -> Vec { + let identities = (0..rewarded_set) + .map(|i| format!("identity{:04}", i)) + .collect::>(); + storage::save_rewarded_set(storage, height, active_set, identities.clone()).unwrap(); + identities + } + + #[test] + fn querying_for_rewarded_set_heights_for_interval() { + let mut deps = test_helpers::init_contract(); + + // no data + assert!( + query_rewarded_set_heights_for_interval(deps.as_ref().storage, 0) + .unwrap() + .heights + .is_empty() + ); + + // 100 heights + for i in 0..100 { + storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL + .save(deps.as_mut().storage, (1, i), &0u8) + .unwrap(); + } + let expected = (0..100).collect::>(); + assert_eq!( + expected, + query_rewarded_set_heights_for_interval(deps.as_ref().storage, 1) + .unwrap() + .heights + ); + + // 100 heights for different interval + for i in 200..300 { + storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL + .save(deps.as_mut().storage, (10, i), &0u8) + .unwrap(); + } + let expected = (200..300).collect::>(); + assert_eq!( + expected, + query_rewarded_set_heights_for_interval(deps.as_ref().storage, 10) + .unwrap() + .heights + ) + } + + #[test] + fn querying_for_rewarded_set_at_height() { + let mut deps = test_helpers::init_contract(); + + // store some nodes + let identities1 = store_rewarded_nodes(deps.as_mut().storage, 1, 100, 200); + let identities2 = store_rewarded_nodes(deps.as_mut().storage, 2, 50, 200); + let identities3 = store_rewarded_nodes(deps.as_mut().storage, 3, 150, 200); + let identities4 = store_rewarded_nodes(deps.as_mut().storage, 4, 300, 500); + let identities5 = store_rewarded_nodes(deps.as_mut().storage, 5, 500, 500); + + // expected2 and 3 are basically sanity checks to ensure changing active set size (increase or decrease) + // doesn't affect the ordering + + let expected1 = identities1 + .into_iter() + .enumerate() + .map(|(i, identity)| { + if i < 100 { + (identity, RewardedSetNodeStatus::Active) + } else { + (identity, RewardedSetNodeStatus::Standby) + } + }) + .collect::>(); + + assert_eq!( + expected1, + query_rewarded_set_at_height(deps.as_ref().storage, 1, None, 1000).unwrap() + ); + + let expected2 = identities2 + .into_iter() + .enumerate() + .map(|(i, identity)| { + if i < 50 { + (identity, RewardedSetNodeStatus::Active) + } else { + (identity, RewardedSetNodeStatus::Standby) + } + }) + .collect::>(); + + assert_eq!( + expected2, + query_rewarded_set_at_height(deps.as_ref().storage, 2, None, 1000).unwrap() + ); + + let expected3 = identities3 + .into_iter() + .enumerate() + .map(|(i, identity)| { + if i < 150 { + (identity, RewardedSetNodeStatus::Active) + } else { + (identity, RewardedSetNodeStatus::Standby) + } + }) + .collect::>(); + + assert_eq!( + expected3, + query_rewarded_set_at_height(deps.as_ref().storage, 3, None, 1000).unwrap() + ); + + // check limit and paging + // active: 300, rewarded: 500 + let first_100 = identities4 + .iter() + .take(100) + .map(|identity| (identity.clone(), RewardedSetNodeStatus::Active)) + .collect::>(); + assert_eq!( + first_100, + query_rewarded_set_at_height(deps.as_ref().storage, 4, None, 100).unwrap() + ); + + let expected_single1 = vec![("identity0299".to_string(), RewardedSetNodeStatus::Active)]; + let expected_single2 = vec![("identity0300".to_string(), RewardedSetNodeStatus::Standby)]; + assert_eq!( + expected_single1, + query_rewarded_set_at_height( + deps.as_ref().storage, + 4, + Some("identity0298".to_string()), + 1 + ) + .unwrap() + ); + assert_eq!( + expected_single2, + query_rewarded_set_at_height( + deps.as_ref().storage, + 4, + Some("identity0299".to_string()), + 1 + ) + .unwrap() + ); + + let last_100 = identities4 + .iter() + .skip(400) + .map(|identity| (identity.clone(), RewardedSetNodeStatus::Standby)) + .collect::>(); + assert_eq!( + last_100, + query_rewarded_set_at_height( + deps.as_ref().storage, + 4, + Some("identity0399".to_string()), + 100 + ) + .unwrap() + ); + + // all nodes are in the active set + let expected5 = identities5 + .into_iter() + .map(|identity| (identity, RewardedSetNodeStatus::Active)) + .collect::>(); + + assert_eq!( + expected5, + query_rewarded_set_at_height(deps.as_ref().storage, 5, None, 1000).unwrap() + ); + } + + #[test] + fn querying_for_rewarded_set() { + let mut deps = test_helpers::init_contract(); + + let current_height = 123; + let other_height = 456; + let different_height = 789; + + storage::CURRENT_REWARDED_SET_HEIGHT + .save(deps.as_mut().storage, ¤t_height) + .unwrap(); + + let identities1 = store_rewarded_nodes(deps.as_mut().storage, current_height, 50, 100); + let identities2 = store_rewarded_nodes(deps.as_mut().storage, other_height, 100, 200); + let identities3 = store_rewarded_nodes( + deps.as_mut().storage, + different_height, + storage::REWARDED_NODE_MAX_PAGE_LIMIT, + storage::REWARDED_NODE_MAX_PAGE_LIMIT * 2, + ); + + // if height is not set, current height is used, else it's just passed + let expected1 = PagedRewardedSetResponse { + identities: identities1 + .into_iter() + .enumerate() + .map(|(i, identity)| { + if i < 50 { + (identity, RewardedSetNodeStatus::Active) + } else { + (identity, RewardedSetNodeStatus::Standby) + } + }) + .collect::>(), + start_next_after: None, + at_height: current_height, + }; + let expected2 = PagedRewardedSetResponse { + identities: identities2 + .into_iter() + .enumerate() + .map(|(i, identity)| { + if i < 100 { + (identity, RewardedSetNodeStatus::Active) + } else { + (identity, RewardedSetNodeStatus::Standby) + } + }) + .collect::>(), + start_next_after: None, + at_height: other_height, + }; + + assert_eq!( + Ok(expected1), + query_rewarded_set(deps.as_ref().storage, None, None, None) + ); + assert_eq!( + Ok(expected2), + query_rewarded_set(deps.as_ref().storage, Some(other_height), None, None) + ); + + // if limit is not set, a default one is used instead + let expected3 = PagedRewardedSetResponse { + identities: identities3 + .iter() + .take(storage::REWARDED_NODE_DEFAULT_PAGE_LIMIT as usize) + .cloned() + .map(|identity| (identity, RewardedSetNodeStatus::Active)) + .collect::>(), + start_next_after: Some(format!( + "identity{:04}", + storage::REWARDED_NODE_DEFAULT_PAGE_LIMIT - 1 + )), + at_height: different_height, + }; + assert_eq!( + Ok(expected3), + query_rewarded_set(deps.as_ref().storage, Some(different_height), None, None) + ); + + // limit cannot be larger that pre-defined maximum + let expected4 = PagedRewardedSetResponse { + identities: identities3 + .iter() + .take(storage::REWARDED_NODE_MAX_PAGE_LIMIT as usize) + .cloned() + .map(|identity| (identity, RewardedSetNodeStatus::Active)) + .collect::>(), + start_next_after: Some(format!( + "identity{:04}", + storage::REWARDED_NODE_MAX_PAGE_LIMIT - 1 + )), + at_height: different_height, + }; + assert_eq!( + Ok(expected4), + query_rewarded_set( + deps.as_ref().storage, + Some(different_height), + None, + Some(REWARDED_NODE_MAX_PAGE_LIMIT * 100) + ) + ); + } + + #[test] + fn querying_for_rewarded_set_update_details() { + let env = mock_env(); + let mut deps = test_helpers::init_contract(); + + let current_height = 123; + storage::CURRENT_REWARDED_SET_HEIGHT + .save(deps.as_mut().storage, ¤t_height) + .unwrap(); + + // returns whatever is in the correct environment + assert_eq!( + RewardedSetUpdateDetails { + refresh_rate_blocks: crate::constants::REWARDED_SET_REFRESH_BLOCKS, + last_refreshed_block: current_height, + current_height: env.block.height + }, + query_rewarded_set_update_details(env, deps.as_ref().storage).unwrap() + ) + } +} diff --git a/contracts/mixnet/src/interval/storage.rs b/contracts/mixnet/src/interval/storage.rs new file mode 100644 index 0000000000..c877ca7e1f --- /dev/null +++ b/contracts/mixnet/src/interval/storage.rs @@ -0,0 +1,86 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_std::{StdResult, Storage}; +use cw_storage_plus::{Item, Map}; +use mixnet_contract_common::{IdentityKey, Interval, RewardedSetNodeStatus}; + +// type aliases for better reasoning for storage keys +// (I found it helpful) +type BlockHeight = u64; +type IntervalId = u32; + +// TODO: those values need to be verified +pub(crate) const REWARDED_NODE_DEFAULT_PAGE_LIMIT: u32 = 1000; +pub(crate) const REWARDED_NODE_MAX_PAGE_LIMIT: u32 = 1500; + +pub(crate) const CURRENT_INTERVAL: Item<'_, Interval> = Item::new("cep"); +pub(crate) const CURRENT_REWARDED_SET_HEIGHT: Item<'_, BlockHeight> = Item::new("crh"); + +// I've changed the `()` data to an `u8` as after serializing `()` is represented as "null", +// taking more space than a single digit u8. If we don't care about what's there, why not go with more efficient approach? : ) +pub(crate) const REWARDED_SET_HEIGHTS_FOR_INTERVAL: Map<'_, (IntervalId, BlockHeight), u8> = + Map::new("rsh"); + +// pub(crate) const REWARDED_SET: Map<(u64, IdentityKey), NodeStatus> = Map::new("rs"); +pub(crate) const REWARDED_SET: Map<'_, (BlockHeight, IdentityKey), RewardedSetNodeStatus> = + Map::new("rs"); + +pub(crate) fn save_rewarded_set( + storage: &mut dyn Storage, + height: BlockHeight, + active_set_size: u32, + entries: Vec, +) -> StdResult<()> { + for (i, identity) in entries.into_iter().enumerate() { + // first k nodes are active + let set_status = if i < active_set_size as usize { + RewardedSetNodeStatus::Active + } else { + RewardedSetNodeStatus::Standby + }; + + REWARDED_SET.save(storage, (height, identity), &set_status)?; + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::support::tests::test_helpers; + + #[test] + fn saving_rewarded_set() { + let mut deps = test_helpers::init_contract(); + + let active_set_size = 100; + let mut nodes = Vec::new(); + for i in 0..1000 { + nodes.push(format!("identity{:04}", i)) + } + + save_rewarded_set(deps.as_mut().storage, 1234, active_set_size, nodes).unwrap(); + + // first k nodes MUST BE active + for i in 0..1000 { + let identity = format!("identity{:04}", i); + if i < active_set_size { + assert_eq!( + RewardedSetNodeStatus::Active, + REWARDED_SET + .load(deps.as_ref().storage, (1234, identity)) + .unwrap() + ) + } else { + assert_eq!( + RewardedSetNodeStatus::Standby, + REWARDED_SET + .load(deps.as_ref().storage, (1234, identity)) + .unwrap() + ) + } + } + } +} diff --git a/contracts/mixnet/src/interval/transactions.rs b/contracts/mixnet/src/interval/transactions.rs new file mode 100644 index 0000000000..03334fecbc --- /dev/null +++ b/contracts/mixnet/src/interval/transactions.rs @@ -0,0 +1,308 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use super::storage; +use crate::error::ContractError; +use crate::error::ContractError::IntervalNotInProgress; +use crate::mixnet_contract_settings::storage as mixnet_params_storage; +use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, Storage}; +use mixnet_contract_common::events::{new_advance_interval_event, new_change_rewarded_set_event}; +use mixnet_contract_common::IdentityKey; + +pub fn try_write_rewarded_set( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + rewarded_set: Vec, + active_set_size: u32, +) -> Result { + let state = mixnet_params_storage::CONTRACT_STATE.load(deps.storage)?; + + // check if this is executed by the permitted validator, if not reject the transaction + if info.sender != state.rewarding_validator_address { + return Err(ContractError::Unauthorized); + } + + // sanity check to make sure the sending validator is in sync with the contract state + // (i.e. so that we'd known that top k nodes are actually expected to be active) + if active_set_size != state.params.mixnode_active_set_size { + return Err(ContractError::UnexpectedActiveSetSize { + received: active_set_size, + expected: state.params.mixnode_active_set_size, + }); + } + + if rewarded_set.len() as u32 > state.params.mixnode_rewarded_set_size { + return Err(ContractError::UnexpectedRewardedSetSize { + received: rewarded_set.len() as u32, + expected: state.params.mixnode_rewarded_set_size, + }); + } + + let last_update = storage::CURRENT_REWARDED_SET_HEIGHT.load(deps.storage)?; + let block_height = env.block.height; + + if last_update + crate::constants::REWARDED_SET_REFRESH_BLOCKS > block_height { + return Err(ContractError::TooFrequentRewardedSetUpdate { + last_update, + minimum_delay: crate::constants::REWARDED_SET_REFRESH_BLOCKS, + current_height: block_height, + }); + } + + let current_interval = storage::CURRENT_INTERVAL.load(deps.storage)?.id(); + let num_nodes = rewarded_set.len(); + + storage::save_rewarded_set(deps.storage, block_height, active_set_size, rewarded_set)?; + storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL.save( + deps.storage, + (current_interval, block_height), + &0u8, + )?; + storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &block_height)?; + + Ok(Response::new().add_event(new_change_rewarded_set_event( + state.params.mixnode_active_set_size, + state.params.mixnode_rewarded_set_size, + num_nodes as u32, + current_interval, + ))) +} + +pub fn try_advance_interval( + env: Env, + storage: &mut dyn Storage, +) -> Result { + // in theory, we could have just changed the state and relied on its reversal upon failed + // execution, but better safe than sorry and do not modify the state at all unless we know + // all checks have succeeded. + let current_interval = storage::CURRENT_INTERVAL.load(storage)?; + let next_interval = current_interval.next_interval(); + + if next_interval.start_unix_timestamp() > env.block.time.seconds() as i64 { + // the reason for this check is as follows: + // nobody, even trusted validators, should be able to continuously keep advancing intervals, + // because otherwise it would be possible for them to continuously keep rewarding nodes. + // + // Therefore, even if "trusted" validator, responsible for rewarding, is malicious, + // they can't send rewards more often than every `REWARDED_SET_REFRESH_BLOCKS` + // and changing this value requires going through governance and having agreement of + // the super-majority of the validators (by stake) + return Err(IntervalNotInProgress { + current_block_time: env.block.time.seconds(), + interval_start: next_interval.start_unix_timestamp(), + interval_end: next_interval.end_unix_timestamp(), + }); + } + + storage::CURRENT_INTERVAL.save(storage, &next_interval)?; + + Ok(Response::new().add_event(new_advance_interval_event(next_interval))) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::support::tests::test_helpers; + use cosmwasm_std::testing::{mock_env, mock_info}; + use cosmwasm_std::Timestamp; + use mixnet_contract_common::{Interval, RewardedSetNodeStatus}; + use std::time::Duration; + use time::OffsetDateTime; + + #[test] + fn writing_rewarded_set() { + let mut env = mock_env(); + let mut deps = test_helpers::init_contract(); + let current_state = mixnet_params_storage::CONTRACT_STATE + .load(deps.as_mut().storage) + .unwrap(); + let authorised_sender = mock_info(current_state.rewarding_validator_address.as_str(), &[]); + let full_rewarded_set = (0..current_state.params.mixnode_rewarded_set_size) + .map(|i| format!("identity{:04}", i)) + .collect::>(); + let last_update = 123; + storage::CURRENT_REWARDED_SET_HEIGHT + .save(deps.as_mut().storage, &last_update) + .unwrap(); + + // can only be performed by the permitted validator + let dummy_sender = mock_info("dummy_sender", &[]); + assert_eq!( + Err(ContractError::Unauthorized), + try_write_rewarded_set( + deps.as_mut(), + env.clone(), + dummy_sender, + full_rewarded_set.clone(), + current_state.params.mixnode_active_set_size + ) + ); + + // the sender must use the same active set size as the one defined in the contract + assert_eq!( + Err(ContractError::UnexpectedActiveSetSize { + received: 123, + expected: current_state.params.mixnode_active_set_size + }), + try_write_rewarded_set( + deps.as_mut(), + env.clone(), + authorised_sender.clone(), + full_rewarded_set.clone(), + 123 + ) + ); + + // the sender cannot provide more nodes than the rewarded set size + let mut bigger_set = full_rewarded_set.clone(); + bigger_set.push("another_node".to_string()); + assert_eq!( + Err(ContractError::UnexpectedRewardedSetSize { + received: current_state.params.mixnode_rewarded_set_size + 1, + expected: current_state.params.mixnode_rewarded_set_size + }), + try_write_rewarded_set( + deps.as_mut(), + env.clone(), + authorised_sender.clone(), + bigger_set, + current_state.params.mixnode_active_set_size + ) + ); + + // cannot be performed too soon after a previous update + env.block.height = last_update + 1; + assert_eq!( + Err(ContractError::TooFrequentRewardedSetUpdate { + last_update, + minimum_delay: crate::constants::REWARDED_SET_REFRESH_BLOCKS, + current_height: last_update + 1, + }), + try_write_rewarded_set( + deps.as_mut(), + env.clone(), + authorised_sender.clone(), + full_rewarded_set.clone(), + current_state.params.mixnode_active_set_size + ) + ); + + // after successful rewarded set write, all internal storage structures are updated appropriately + env.block.height = last_update + crate::constants::REWARDED_SET_REFRESH_BLOCKS; + let expected_response = Response::new().add_event(new_change_rewarded_set_event( + current_state.params.mixnode_active_set_size, + current_state.params.mixnode_rewarded_set_size, + full_rewarded_set.len() as u32, + 0, + )); + + assert_eq!( + Ok(expected_response), + try_write_rewarded_set( + deps.as_mut(), + env.clone(), + authorised_sender, + full_rewarded_set.clone(), + current_state.params.mixnode_active_set_size + ) + ); + + for (i, rewarded_node) in full_rewarded_set.into_iter().enumerate() { + if (i as u32) < current_state.params.mixnode_active_set_size { + assert_eq!( + RewardedSetNodeStatus::Active, + storage::REWARDED_SET + .load(deps.as_ref().storage, (env.block.height, rewarded_node)) + .unwrap() + ) + } else { + assert_eq!( + RewardedSetNodeStatus::Standby, + storage::REWARDED_SET + .load(deps.as_ref().storage, (env.block.height, rewarded_node)) + .unwrap() + ) + } + } + assert!(storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL + .has(deps.as_ref().storage, (0, env.block.height))); + assert_eq!( + env.block.height, + storage::CURRENT_REWARDED_SET_HEIGHT + .load(deps.as_ref().storage) + .unwrap() + ); + } + + #[test] + fn advancing_interval() { + let mut env = mock_env(); + let mut deps = test_helpers::init_contract(); + + // 1609459200 = 2021-01-01 + // 1640995200 = 2022-01-01 + // 1641081600 = 2022-01-02 + // 1643673600 = 2022-02-01 + // 1672531200 = 2023-01-01 + + let current_interval = Interval::new( + 0, + OffsetDateTime::from_unix_timestamp(1640995200).unwrap(), + Duration::from_secs(60 * 60 * 720), + ); + let next_interval = current_interval.next_interval(); + storage::CURRENT_INTERVAL + .save(deps.as_mut().storage, ¤t_interval) + .unwrap(); + + // fails if the current interval hasn't finished yet i.e. the new interval hasn't begun + env.block.time = Timestamp::from_seconds(1641081600); + assert_eq!( + Err(ContractError::IntervalNotInProgress { + current_block_time: 1641081600, + interval_start: next_interval.start_unix_timestamp(), + interval_end: next_interval.end_unix_timestamp() + }), + try_advance_interval(env.clone(), deps.as_mut().storage) + ); + + // same if the current blocktime is set to BEFORE the first interval has even begun + // (say we decided to set the first interval to be some time in the future at initialisation) + env.block.time = Timestamp::from_seconds(1609459200); + assert_eq!( + Err(ContractError::IntervalNotInProgress { + current_block_time: 1609459200, + interval_start: next_interval.start_unix_timestamp(), + interval_end: next_interval.end_unix_timestamp() + }), + try_advance_interval(env.clone(), deps.as_mut().storage) + ); + + // works otherwise + + // interval that has just finished + env.block.time = + Timestamp::from_seconds(next_interval.start_unix_timestamp() as u64 + 10000); + let expected_new_interval = current_interval.next_interval(); + let expected_response = + Response::new().add_event(new_advance_interval_event(expected_new_interval)); + assert_eq!( + Ok(expected_response), + try_advance_interval(env.clone(), deps.as_mut().storage) + ); + + // interval way back in the past (i.e. 'somebody' failed to advance it for a long time) + env.block.time = Timestamp::from_seconds(1672531200); + storage::CURRENT_INTERVAL + .save(deps.as_mut().storage, ¤t_interval) + .unwrap(); + let expected_new_interval = current_interval.next_interval(); + let expected_response = + Response::new().add_event(new_advance_interval_event(expected_new_interval)); + assert_eq!( + Ok(expected_response), + try_advance_interval(env.clone(), deps.as_mut().storage) + ); + } +} diff --git a/contracts/mixnet/src/lib.rs b/contracts/mixnet/src/lib.rs index 133da53313..281675a90d 100644 --- a/contracts/mixnet/src/lib.rs +++ b/contracts/mixnet/src/lib.rs @@ -1,10 +1,12 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +mod constants; pub mod contract; mod delegations; mod error; mod gateways; +mod interval; mod mixnet_contract_settings; mod mixnodes; mod rewards; diff --git a/contracts/mixnet/src/mixnet_contract_settings/models.rs b/contracts/mixnet/src/mixnet_contract_settings/models.rs index 6b9d5d73f9..dcf9dcbbab 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/models.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/models.rs @@ -11,11 +11,4 @@ pub struct ContractState { pub owner: Addr, // only the owner account can update state pub rewarding_validator_address: Addr, pub params: ContractStateParams, - - // keep track of the changes to the current rewarding interval, - // i.e. at which block has the latest rewarding occurred - // and whether another run is already in progress - pub rewarding_interval_starting_block: u64, - pub latest_rewarding_interval_nonce: u32, - pub rewarding_in_progress: bool, } diff --git a/contracts/mixnet/src/mixnet_contract_settings/queries.rs b/contracts/mixnet/src/mixnet_contract_settings/queries.rs index 68427a8173..e36791b947 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/queries.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/queries.rs @@ -3,26 +3,14 @@ use super::storage; use cosmwasm_std::{Deps, StdResult}; -use mixnet_contract_common::{ - ContractStateParams, MixnetContractVersion, RewardingIntervalResponse, -}; +use mixnet_contract_common::{ContractStateParams, MixnetContractVersion}; -pub(crate) fn query_contract_settings_params(deps: Deps) -> StdResult { +pub(crate) fn query_contract_settings_params(deps: Deps<'_>) -> StdResult { storage::CONTRACT_STATE .load(deps.storage) .map(|settings| settings.params) } -pub(crate) fn query_rewarding_interval(deps: Deps) -> StdResult { - let state = storage::CONTRACT_STATE.load(deps.storage)?; - - Ok(RewardingIntervalResponse { - current_rewarding_interval_starting_block: state.rewarding_interval_starting_block, - current_rewarding_interval_nonce: state.latest_rewarding_interval_nonce, - rewarding_in_progress: state.rewarding_in_progress, - }) -} - pub(crate) fn query_contract_version() -> MixnetContractVersion { // as per docs // env! macro will expand to the value of the named environment variable at @@ -57,11 +45,7 @@ pub(crate) mod tests { minimum_gateway_pledge: 456u128.into(), mixnode_rewarded_set_size: 1000, mixnode_active_set_size: 500, - active_set_work_factor: 10, }, - rewarding_interval_starting_block: 123, - latest_rewarding_interval_nonce: 0, - rewarding_in_progress: false, }; storage::CONTRACT_STATE diff --git a/contracts/mixnet/src/mixnet_contract_settings/storage.rs b/contracts/mixnet/src/mixnet_contract_settings/storage.rs index 2f2bd48664..f7c2171e3f 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/storage.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/storage.rs @@ -7,8 +7,8 @@ use cosmwasm_std::Storage; use cw_storage_plus::Item; use mixnet_contract_common::{Layer, LayerDistribution}; -pub(crate) const CONTRACT_STATE: Item = Item::new("config"); -pub(crate) const LAYERS: Item = Item::new("layers"); +pub(crate) const CONTRACT_STATE: Item<'_, ContractState> = Item::new("config"); +pub(crate) const LAYERS: Item<'_, LayerDistribution> = Item::new("layers"); pub fn increment_layer_count(storage: &mut dyn Storage, layer: Layer) -> StdResult<()> { LAYERS diff --git a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs index e087584c4d..2901270b5b 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs @@ -10,7 +10,7 @@ use mixnet_contract_common::events::new_settings_update_event; use mixnet_contract_common::ContractStateParams; pub(crate) fn try_update_contract_settings( - deps: DepsMut, + deps: DepsMut<'_>, info: MessageInfo, params: ContractStateParams, ) -> Result { @@ -63,7 +63,6 @@ pub mod tests { minimum_gateway_pledge: INITIAL_GATEWAY_PLEDGE, mixnode_rewarded_set_size: 100, mixnode_active_set_size: 50, - active_set_work_factor: 10, }; let initial_params = storage::CONTRACT_STATE diff --git a/contracts/mixnet/src/mixnodes/bonding_queries.rs b/contracts/mixnet/src/mixnodes/bonding_queries.rs index e916e7263e..62c9805470 100644 --- a/contracts/mixnet/src/mixnodes/bonding_queries.rs +++ b/contracts/mixnet/src/mixnodes/bonding_queries.rs @@ -9,7 +9,7 @@ use mixnet_contract_common::{ }; pub fn query_mixnodes_paged( - deps: Deps, + deps: Deps<'_>, start_after: Option, limit: Option, ) -> StdResult { @@ -39,7 +39,7 @@ pub fn query_mixnodes_paged( Ok(PagedMixnodeResponse::new(nodes, limit, start_next_after)) } -pub fn query_owns_mixnode(deps: Deps, address: String) -> StdResult { +pub fn query_owns_mixnode(deps: Deps<'_>, address: String) -> StdResult { let validated_addr = deps.api.addr_validate(&address)?; let stored_bond = storage::mixnodes() .idx diff --git a/contracts/mixnet/src/mixnodes/layer_queries.rs b/contracts/mixnet/src/mixnodes/layer_queries.rs index 7a45c58167..1764e7211e 100644 --- a/contracts/mixnet/src/mixnodes/layer_queries.rs +++ b/contracts/mixnet/src/mixnodes/layer_queries.rs @@ -5,6 +5,6 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage; use cosmwasm_std::{Deps, StdResult}; use mixnet_contract_common::LayerDistribution; -pub(crate) fn query_layer_distribution(deps: Deps) -> StdResult { +pub(crate) fn query_layer_distribution(deps: Deps<'_>) -> StdResult { mixnet_params_storage::LAYERS.load(deps.storage) } diff --git a/contracts/mixnet/src/mixnodes/storage.rs b/contracts/mixnet/src/mixnodes/storage.rs index 15d365f0d9..d16820ad74 100644 --- a/contracts/mixnet/src/mixnodes/storage.rs +++ b/contracts/mixnet/src/mixnodes/storage.rs @@ -17,7 +17,7 @@ const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno"; pub(crate) const BOND_PAGE_MAX_LIMIT: u32 = 75; pub(crate) const BOND_PAGE_DEFAULT_LIMIT: u32 = 50; -pub(crate) const TOTAL_DELEGATION: Map = +pub(crate) const TOTAL_DELEGATION: Map<'_, IdentityKeyRef<'_>, Uint128> = Map::new(TOTAL_DELEGATION_NAMESPACE); pub(crate) struct MixnodeBondIndex<'a> { @@ -96,7 +96,7 @@ impl StoredMixnodeBond { } impl Display for StoredMixnodeBond { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, "amount: {}, owner: {}, identity: {}", @@ -107,7 +107,7 @@ impl Display for StoredMixnodeBond { pub(crate) fn read_full_mixnode_bond( storage: &dyn Storage, - mix_identity: IdentityKeyRef, + mix_identity: IdentityKeyRef<'_>, ) -> StdResult> { let stored_bond = mixnodes().may_load(storage, mix_identity)?; match stored_bond { diff --git a/contracts/mixnet/src/mixnodes/transactions.rs b/contracts/mixnet/src/mixnodes/transactions.rs index 252128324a..0ca1769779 100644 --- a/contracts/mixnet/src/mixnodes/transactions.rs +++ b/contracts/mixnet/src/mixnodes/transactions.rs @@ -9,14 +9,15 @@ use crate::mixnodes::storage::StoredMixnodeBond; use crate::support::helpers::{ensure_no_existing_bond, validate_node_identity_signature}; use config::defaults::DENOM; use cosmwasm_std::{ - coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, + wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, }; use mixnet_contract_common::events::{new_mixnode_bonding_event, new_mixnode_unbonding_event}; use mixnet_contract_common::MixNode; -use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::one_unym; pub fn try_add_mixnode( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, mix_node: MixNode, @@ -41,7 +42,7 @@ pub fn try_add_mixnode( } pub fn try_add_mixnode_on_behalf( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, mix_node: MixNode, @@ -68,7 +69,7 @@ pub fn try_add_mixnode_on_behalf( } fn _try_add_mixnode( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, mix_node: MixNode, pledge_amount: Coin, @@ -143,7 +144,7 @@ fn _try_add_mixnode( } pub fn try_remove_mixnode_on_behalf( - deps: DepsMut, + deps: DepsMut<'_>, info: MessageInfo, owner: String, ) -> Result { @@ -151,12 +152,12 @@ pub fn try_remove_mixnode_on_behalf( _try_remove_mixnode(deps, &owner, Some(proxy)) } -pub fn try_remove_mixnode(deps: DepsMut, info: MessageInfo) -> Result { +pub fn try_remove_mixnode(deps: DepsMut<'_>, info: MessageInfo) -> Result { _try_remove_mixnode(deps, info.sender.as_ref(), None) } pub(crate) fn _try_remove_mixnode( - deps: DepsMut, + deps: DepsMut<'_>, owner: &str, proxy: Option, ) -> Result { @@ -192,7 +193,7 @@ pub(crate) fn _try_remove_mixnode( // decrement layer count mixnet_params_storage::decrement_layer_count(deps.storage, mixnode_bond.layer)?; - let mut response = Response::new().add_message(return_tokens); + let mut response = Response::new(); if let Some(proxy) = &proxy { let msg = VestingContractExecuteMsg::TrackUnbondMixnode { @@ -200,10 +201,12 @@ pub(crate) fn _try_remove_mixnode( amount: mixnode_bond.pledge_amount(), }; - let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?; + let track_unbond_message = wasm_execute(proxy, &msg, vec![one_unym()])?; response = response.add_message(track_unbond_message); } + let response = response.add_message(return_tokens); + Ok(response.add_event(new_mixnode_unbonding_event( &owner, &proxy, @@ -213,7 +216,7 @@ pub(crate) fn _try_remove_mixnode( } pub(crate) fn try_update_mixnode_config( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, profit_margin_percent: u8, diff --git a/contracts/mixnet/src/rewards/helpers.rs b/contracts/mixnet/src/rewards/helpers.rs index e532c948f5..096b3b07aa 100644 --- a/contracts/mixnet/src/rewards/helpers.rs +++ b/contracts/mixnet/src/rewards/helpers.rs @@ -12,7 +12,7 @@ use mixnet_contract_common::{ pub(crate) fn update_post_rewarding_storage( storage: &mut dyn Storage, - mix_identity: IdentityKeyRef, + mix_identity: IdentityKeyRef<'_>, operator_reward: Uint128, delegators_reward: Uint128, ) -> Result<(), ContractError> { @@ -55,7 +55,7 @@ pub(crate) fn update_post_rewarding_storage( pub(crate) fn update_rewarding_status( storage: &mut dyn Storage, - rewarding_interval_nonce: u32, + interval_id: u32, mix_identity: IdentityKey, rewarding_results: RewardingResult, next_start: Option, @@ -64,7 +64,7 @@ pub(crate) fn update_rewarding_status( if let Some(next_start) = next_start { storage::REWARDING_STATUS.save( storage, - (rewarding_interval_nonce, mix_identity), + (interval_id, mix_identity), &RewardingStatus::PendingNextDelegatorPage(PendingDelegatorRewarding { running_results: rewarding_results, next_start, @@ -74,7 +74,7 @@ pub(crate) fn update_rewarding_status( } else { storage::REWARDING_STATUS.save( storage, - (rewarding_interval_nonce, mix_identity), + (interval_id, mix_identity), &RewardingStatus::Complete(rewarding_results), )?; } diff --git a/contracts/mixnet/src/rewards/queries.rs b/contracts/mixnet/src/rewards/queries.rs index d9f82ef7b8..74e8ab6f4f 100644 --- a/contracts/mixnet/src/rewards/queries.rs +++ b/contracts/mixnet/src/rewards/queries.rs @@ -6,21 +6,20 @@ use cosmwasm_std::Uint128; use cosmwasm_std::{Deps, StdResult}; use mixnet_contract_common::{IdentityKey, MixnodeRewardingStatusResponse}; -pub(crate) fn query_reward_pool(deps: Deps) -> StdResult { +pub(crate) fn query_reward_pool(deps: Deps<'_>) -> StdResult { storage::REWARD_POOL.load(deps.storage) } -pub(crate) fn query_circulating_supply(deps: Deps) -> StdResult { +pub(crate) fn query_circulating_supply(deps: Deps<'_>) -> StdResult { storage::circulating_supply(deps.storage) } pub(crate) fn query_rewarding_status( - deps: Deps, + deps: Deps<'_>, mix_identity: IdentityKey, - rewarding_interval_nonce: u32, + interval_id: u32, ) -> StdResult { - let status = storage::REWARDING_STATUS - .may_load(deps.storage, (rewarding_interval_nonce, mix_identity))?; + let status = storage::REWARDING_STATUS.may_load(deps.storage, (interval_id, mix_identity))?; Ok(MixnodeRewardingStatusResponse { status }) } @@ -35,12 +34,11 @@ pub(crate) mod tests { #[cfg(test)] mod querying_for_rewarding_status { - use super::storage; use super::*; + use crate::constants; use crate::delegations::transactions::try_delegate_to_mixnode; use crate::rewards::transactions::{ - try_begin_mixnode_rewarding, try_finish_mixnode_rewarding, try_reward_mixnode, - try_reward_next_mixnode_delegators, + try_reward_mixnode, try_reward_next_mixnode_delegators, }; use config::defaults::DENOM; use cosmwasm_std::{coin, Addr}; @@ -70,19 +68,17 @@ pub(crate) mod tests { .is_none() ); - // node was rewarded but for different epoch + // node was rewarded but for different interval let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap(); try_reward_mixnode( deps.as_mut(), env, - info.clone(), + info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ) .unwrap(); - try_finish_mixnode_rewarding(deps.as_mut(), info, 1).unwrap(); assert!(query_rewarding_status(deps.as_ref(), node_identity, 2) .unwrap() @@ -107,22 +103,20 @@ pub(crate) mod tests { deps.as_mut(), ); - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap(); try_reward_mixnode( deps.as_mut(), env.clone(), - info.clone(), + info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ) .unwrap(); - try_finish_mixnode_rewarding(deps.as_mut(), info, 1).unwrap(); - let res = query_rewarding_status(deps.as_ref(), node_identity, 1).unwrap(); + let res = query_rewarding_status(deps.as_ref(), node_identity, 0).unwrap(); assert!(matches!(res.status, Some(RewardingStatus::Complete(..)))); match res.status.unwrap() { @@ -157,10 +151,10 @@ pub(crate) mod tests { .unwrap(); } - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; + test_helpers::update_env_and_progress_interval(&mut env, deps.as_mut().storage); let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 2).unwrap(); try_reward_mixnode( deps.as_mut(), @@ -168,15 +162,15 @@ pub(crate) mod tests { info.clone(), node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 2, + 1, ) .unwrap(); // rewards all pending - try_reward_next_mixnode_delegators(deps.as_mut(), info, node_identity.to_string(), 2) + try_reward_next_mixnode_delegators(deps.as_mut(), info, node_identity.to_string(), 1) .unwrap(); - let res = query_rewarding_status(deps.as_ref(), node_identity, 2).unwrap(); + let res = query_rewarding_status(deps.as_ref(), node_identity, 1).unwrap(); assert!(matches!(res.status, Some(RewardingStatus::Complete(..)))); match res.status.unwrap() { @@ -220,10 +214,9 @@ pub(crate) mod tests { .unwrap(); } - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap(); try_reward_mixnode( deps.as_mut(), @@ -231,11 +224,11 @@ pub(crate) mod tests { info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ) .unwrap(); - let res = query_rewarding_status(deps.as_ref(), node_identity, 1).unwrap(); + let res = query_rewarding_status(deps.as_ref(), node_identity, 0).unwrap(); assert!(matches!( res.status, Some(RewardingStatus::PendingNextDelegatorPage(..)) diff --git a/contracts/mixnet/src/rewards/storage.rs b/contracts/mixnet/src/rewards/storage.rs index 838b68f092..ca5b0538b6 100644 --- a/contracts/mixnet/src/rewards/storage.rs +++ b/contracts/mixnet/src/rewards/storage.rs @@ -7,15 +7,9 @@ use cosmwasm_std::{StdResult, Storage, Uint128}; use cw_storage_plus::{Item, Map}; use mixnet_contract_common::{IdentityKey, RewardingStatus}; -pub(crate) const REWARD_POOL: Item = Item::new("pool"); +pub(crate) const REWARD_POOL: Item<'_, Uint128> = Item::new("pool"); // TODO: Do we need a migration for this? -pub(crate) const REWARDING_STATUS: Map<(u32, IdentityKey), RewardingStatus> = Map::new("rm"); - -// approximately 1 day (assuming 5s per block) -pub(crate) const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 17280; - -// approximately 30min (assuming 5s per block) -pub(crate) const MAX_REWARDING_DURATION_IN_BLOCKS: u64 = 360; +pub(crate) const REWARDING_STATUS: Map<'_, (u32, IdentityKey), RewardingStatus> = Map::new("rm"); #[allow(dead_code)] pub fn incr_reward_pool( diff --git a/contracts/mixnet/src/rewards/transactions.rs b/contracts/mixnet/src/rewards/transactions.rs index 5a838239d0..e166155385 100644 --- a/contracts/mixnet/src/rewards/transactions.rs +++ b/contracts/mixnet/src/rewards/transactions.rs @@ -2,17 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 use super::storage; +use crate::constants; use crate::delegations::storage as delegations_storage; use crate::error::ContractError; +use crate::interval::storage as interval_storage; use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::mixnodes::storage as mixnodes_storage; use crate::rewards::helpers; use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, StdResult, Storage, Uint128}; use cw_storage_plus::{Bound, PrimaryKey}; use mixnet_contract_common::events::{ - new_begin_rewarding_event, new_finish_rewarding_event, new_mix_delegators_rewarding_event, - new_mix_operator_rewarding_event, new_not_found_mix_operator_rewarding_event, - new_too_fresh_bond_mix_operator_rewarding_event, new_zero_uptime_mix_operator_rewarding_event, + new_mix_delegators_rewarding_event, new_mix_operator_rewarding_event, + new_not_found_mix_operator_rewarding_event, new_too_fresh_bond_mix_operator_rewarding_event, + new_zero_uptime_mix_operator_rewarding_event, }; use mixnet_contract_common::mixnode::{DelegatorRewardParams, NodeRewardParams}; use mixnet_contract_common::{ @@ -28,18 +30,17 @@ struct MixDelegationRewardingResult { /// Checks whether under the current context, any rewarding-related functionalities can be called. /// The following must be true: /// - the call has originated from the address of the authorised rewarding validator, -/// - the rewarding procedure has been initialised and has not concluded yet, /// - the call has been made with the nonce corresponding to the current rewarding procedure, /// /// # Arguments /// /// * `storage`: reference (kinda) to the underlying storage pool of the contract used to read the current state /// * `info`: contains the essential info for authorization, such as identity of the call -/// * `rewarding_interval_nonce`: nonce of the rewarding procedure sent alongside the call +/// * `interval_id`: expected id of the current interval sent alongside the call fn verify_rewarding_state( storage: &dyn Storage, info: MessageInfo, - rewarding_interval_nonce: u32, + interval_id: u32, ) -> Result<(), ContractError> { let state = mixnet_params_storage::CONTRACT_STATE.load(storage)?; @@ -48,72 +49,21 @@ fn verify_rewarding_state( return Err(ContractError::Unauthorized); } - // check if rewarding is currently in progress, if not reject the transaction - if !state.rewarding_in_progress { - return Err(ContractError::RewardingNotInProgress); - } + let current_interval = interval_storage::CURRENT_INTERVAL.load(storage)?; - // make sure the transaction is sent for the correct rewarding interval + // make sure the transaction is sent for the correct interval // (guard ourselves against somebody trying to send stale results; // realistically it's never going to happen in a single rewarding validator case - // but this check is not expensive (since we already had to read the state), - // so we might as well) - if rewarding_interval_nonce != state.latest_rewarding_interval_nonce { - Err(ContractError::InvalidRewardingIntervalNonce { - received: rewarding_interval_nonce, - expected: state.latest_rewarding_interval_nonce, + if interval_id != current_interval.id() { + Err(ContractError::InvalidIntervalId { + received: interval_id, + expected: current_interval.id(), }) } else { Ok(()) } } -// Note: this function is designed to work with only a single validator entity distributing rewards -// The main purpose of this function is to update `latest_rewarding_interval_nonce` which -// will trigger a different seed selection for the pseudorandom generation of the "demanded" set of mixnodes. -pub(crate) fn try_begin_mixnode_rewarding( - deps: DepsMut, - env: Env, - info: MessageInfo, - rewarding_interval_nonce: u32, -) -> Result { - let mut state = mixnet_params_storage::CONTRACT_STATE.load(deps.storage)?; - - // check if this is executed by the permitted validator, if not reject the transaction - if info.sender != state.rewarding_validator_address { - return Err(ContractError::Unauthorized); - } - - // check whether sufficient number of blocks already elapsed since the previous rewarding happened - // (this implies the validator responsible for rewarding in the previous interval did not call - // `try_finish_mixnode_rewarding` - perhaps they crashed or something. Regardless of the reason - // it shouldn't prevent anyone from distributing rewards in the following interval) - // Do note, however, that calling `try_finish_mixnode_rewarding` is crucial as otherwise the - // "demanded" set won't get updated on the validator API side - if state.rewarding_in_progress - && state.rewarding_interval_starting_block + storage::MAX_REWARDING_DURATION_IN_BLOCKS - > env.block.height - { - return Err(ContractError::RewardingInProgress); - } - - // make sure the validator is in sync with the contract state - if rewarding_interval_nonce != state.latest_rewarding_interval_nonce + 1 { - return Err(ContractError::InvalidRewardingIntervalNonce { - received: rewarding_interval_nonce, - expected: state.latest_rewarding_interval_nonce + 1, - }); - } - - state.rewarding_interval_starting_block = env.block.height; - state.latest_rewarding_interval_nonce = rewarding_interval_nonce; - state.rewarding_in_progress = true; - - mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &state)?; - - Ok(Response::new().add_event(new_begin_rewarding_event(rewarding_interval_nonce))) -} - fn reward_mix_delegators( storage: &mut dyn Storage, mix_identity: IdentityKey, @@ -167,7 +117,7 @@ fn reward_mix_delegators( // and for each of them increase the stake proportionally to the reward // if at least `MINIMUM_BLOCK_AGE_FOR_REWARDING` blocks have been created // since they delegated - if delegation.block_height + storage::MINIMUM_BLOCK_AGE_FOR_REWARDING + if delegation.block_height + constants::MINIMUM_BLOCK_AGE_FOR_REWARDING <= params.node_reward_params().reward_blockstamp() { let reward = params.determine_delegation_reward(delegation.amount.amount); @@ -192,17 +142,14 @@ fn reward_mix_delegators( } pub(crate) fn try_reward_next_mixnode_delegators( - deps: DepsMut, + deps: DepsMut<'_>, info: MessageInfo, mix_identity: IdentityKey, - rewarding_interval_nonce: u32, + interval_id: u32, ) -> Result { - verify_rewarding_state(deps.storage, info, rewarding_interval_nonce)?; + verify_rewarding_state(deps.storage, info, interval_id)?; - match storage::REWARDING_STATUS.may_load( - deps.storage, - (rewarding_interval_nonce, mix_identity.clone()), - )? { + match storage::REWARDING_STATUS.may_load(deps.storage, (interval_id, mix_identity.clone()))? { None => { // we haven't called 'regular' try_reward_mixnode, i.e. the operator itself // was not rewarded yet @@ -239,7 +186,7 @@ pub(crate) fn try_reward_next_mixnode_delegators( helpers::update_rewarding_status( deps.storage, - rewarding_interval_nonce, + interval_id, mix_identity.clone(), rewarding_results, delegation_rewarding_result.start_next, @@ -248,7 +195,7 @@ pub(crate) fn try_reward_next_mixnode_delegators( Ok( Response::new().add_event(new_mix_delegators_rewarding_event( - rewarding_interval_nonce, + interval_id, &mix_identity, round_increase, more_delegators, @@ -259,20 +206,17 @@ pub(crate) fn try_reward_next_mixnode_delegators( } pub(crate) fn try_reward_mixnode( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, mix_identity: IdentityKey, params: NodeRewardParams, - rewarding_interval_nonce: u32, + interval_id: u32, ) -> Result { - verify_rewarding_state(deps.storage, info, rewarding_interval_nonce)?; + verify_rewarding_state(deps.storage, info, interval_id)?; // check if the mixnode hasn't been rewarded in this rewarding interval already - match storage::REWARDING_STATUS.may_load( - deps.storage, - (rewarding_interval_nonce, mix_identity.clone()), - )? { + match storage::REWARDING_STATUS.may_load(deps.storage, (interval_id, mix_identity.clone()))? { None => (), Some(RewardingStatus::Complete(_)) => { return Err(ContractError::MixnodeAlreadyRewarded { @@ -293,7 +237,7 @@ pub(crate) fn try_reward_mixnode( None => { return Ok( Response::new().add_event(new_not_found_mix_operator_rewarding_event( - rewarding_interval_nonce, + interval_id, &mix_identity, )), ) @@ -301,16 +245,16 @@ pub(crate) fn try_reward_mixnode( }; // check if node is old enough for rewarding - if current_bond.block_height + storage::MINIMUM_BLOCK_AGE_FOR_REWARDING > env.block.height { + if current_bond.block_height + constants::MINIMUM_BLOCK_AGE_FOR_REWARDING > env.block.height { storage::REWARDING_STATUS.save( deps.storage, - (rewarding_interval_nonce, mix_identity.clone()), + (interval_id, mix_identity.clone()), &RewardingStatus::Complete(Default::default()), )?; return Ok( Response::new().add_event(new_too_fresh_bond_mix_operator_rewarding_event( - rewarding_interval_nonce, + interval_id, &mix_identity, )), ); @@ -320,13 +264,13 @@ pub(crate) fn try_reward_mixnode( if params.uptime() == 0 { storage::REWARDING_STATUS.save( deps.storage, - (rewarding_interval_nonce, mix_identity.clone()), + (interval_id, mix_identity.clone()), &RewardingStatus::Complete(Default::default()), )?; return Ok( Response::new().add_event(new_zero_uptime_mix_operator_rewarding_event( - rewarding_interval_nonce, + interval_id, &mix_identity, )), ); @@ -360,7 +304,7 @@ pub(crate) fn try_reward_mixnode( helpers::update_rewarding_status( deps.storage, - rewarding_interval_nonce, + interval_id, mix_identity.clone(), rewarding_results, delegation_rewarding_result.start_next, @@ -368,7 +312,7 @@ pub(crate) fn try_reward_mixnode( )?; Ok(Response::new().add_event(new_mix_operator_rewarding_event( - rewarding_interval_nonce, + interval_id, &mix_identity, node_reward_result, operator_reward, @@ -377,48 +321,16 @@ pub(crate) fn try_reward_mixnode( ))) } -pub(crate) fn try_finish_mixnode_rewarding( - deps: DepsMut, - info: MessageInfo, - rewarding_interval_nonce: u32, -) -> Result { - let mut state = mixnet_params_storage::CONTRACT_STATE.load(deps.storage)?; - - // check if this is executed by the permitted validator, if not reject the transaction - if info.sender != state.rewarding_validator_address { - return Err(ContractError::Unauthorized); - } - - if !state.rewarding_in_progress { - return Err(ContractError::RewardingNotInProgress); - } - - // make sure the validator is in sync with the contract state - if rewarding_interval_nonce != state.latest_rewarding_interval_nonce { - return Err(ContractError::InvalidRewardingIntervalNonce { - received: rewarding_interval_nonce, - expected: state.latest_rewarding_interval_nonce, - }); - } - - state.rewarding_in_progress = false; - mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &state)?; - - Ok(Response::new().add_event(new_finish_rewarding_event(rewarding_interval_nonce))) -} - #[cfg(test)] pub mod tests { use super::*; - use crate::contract::DEFAULT_SYBIL_RESISTANCE_PERCENT; + use crate::constants::SYBIL_RESISTANCE_PERCENT; use crate::delegations::transactions::try_delegate_to_mixnode; use crate::error::ContractError; use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::mixnodes::storage as mixnodes_storage; use crate::mixnodes::storage::StoredMixnodeBond; - use crate::rewards::transactions::{ - try_begin_mixnode_rewarding, try_finish_mixnode_rewarding, try_reward_mixnode, - }; + use crate::rewards::transactions::try_reward_mixnode; use crate::support::tests; use crate::support::tests::test_helpers; use config::defaults::DENOM; @@ -434,352 +346,10 @@ pub mod tests { use mixnet_contract_common::mixnode::NodeRewardParams; use mixnet_contract_common::{Delegation, IdentityKey, Layer, MixNode}; - #[cfg(test)] - mod beginning_mixnode_rewarding { - use super::*; - use crate::rewards::transactions::try_begin_mixnode_rewarding; - use crate::support::tests::test_helpers; - use cosmwasm_std::testing::mock_env; - - #[test] - fn can_only_be_called_by_specified_validator_address() { - let mut deps = test_helpers::init_contract(); - let env = mock_env(); - let current_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - let rewarding_validator_address = current_state.rewarding_validator_address; - - let res = try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info("not-the-approved-validator", &[]), - 1, - ); - assert_eq!(Err(ContractError::Unauthorized), res); - - let res = try_begin_mixnode_rewarding( - deps.as_mut(), - env, - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ); - assert!(res.is_ok()) - } - - #[test] - fn cannot_be_called_if_rewarding_is_already_in_progress_with_little_day() { - let mut deps = test_helpers::init_contract(); - let env = mock_env(); - let current_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - let rewarding_validator_address = current_state.rewarding_validator_address; - - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - - let res = try_begin_mixnode_rewarding( - deps.as_mut(), - env, - mock_info(rewarding_validator_address.as_ref(), &[]), - 2, - ); - assert_eq!(Err(ContractError::RewardingInProgress), res); - } - - #[test] - fn can_be_called_if_rewarding_is_in_progress_if_sufficient_number_of_blocks_elapsed() { - let mut deps = test_helpers::init_contract(); - let env = mock_env(); - let current_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - let rewarding_validator_address = current_state.rewarding_validator_address; - - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - - let mut new_env = env.clone(); - - new_env.block.height = env.block.height + storage::MAX_REWARDING_DURATION_IN_BLOCKS; - - let res = try_begin_mixnode_rewarding( - deps.as_mut(), - new_env, - mock_info(rewarding_validator_address.as_ref(), &[]), - 2, - ); - assert!(res.is_ok()); - } - - #[test] - fn provided_nonce_must_be_equal_the_current_plus_one() { - let mut deps = test_helpers::init_contract(); - let env = mock_env(); - let mut current_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - current_state.latest_rewarding_interval_nonce = 42; - mixnet_params_storage::CONTRACT_STATE - .save(deps.as_mut().storage, ¤t_state) - .unwrap(); - - let rewarding_validator_address = current_state.rewarding_validator_address; - - let res = try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 11, - ); - assert_eq!( - Err(ContractError::InvalidRewardingIntervalNonce { - received: 11, - expected: 43 - }), - res - ); - - let res = try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 44, - ); - assert_eq!( - Err(ContractError::InvalidRewardingIntervalNonce { - received: 44, - expected: 43 - }), - res - ); - - let res = try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 42, - ); - assert_eq!( - Err(ContractError::InvalidRewardingIntervalNonce { - received: 42, - expected: 43 - }), - res - ); - - let res = try_begin_mixnode_rewarding( - deps.as_mut(), - env, - mock_info(rewarding_validator_address.as_ref(), &[]), - 43, - ); - assert!(res.is_ok()) - } - - #[test] - fn updates_contract_state() { - let mut deps = test_helpers::init_contract(); - let env = mock_env(); - let start_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - let rewarding_validator_address = start_state.rewarding_validator_address; - - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - - let new_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - assert!(new_state.rewarding_in_progress); - assert_eq!( - new_state.rewarding_interval_starting_block, - env.block.height - ); - assert_eq!( - start_state.latest_rewarding_interval_nonce + 1, - new_state.latest_rewarding_interval_nonce - ); - } - } - - #[cfg(test)] - mod finishing_mixnode_rewarding { - use super::*; - use crate::rewards::transactions::{ - try_begin_mixnode_rewarding, try_finish_mixnode_rewarding, - }; - use crate::support::tests::test_helpers; - - #[test] - fn can_only_be_called_by_specified_validator_address() { - let mut deps = test_helpers::init_contract(); - let env = mock_env(); - let current_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - let rewarding_validator_address = current_state.rewarding_validator_address; - - try_begin_mixnode_rewarding( - deps.as_mut(), - env, - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - - let res = try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info("not-the-approved-validator", &[]), - 1, - ); - assert_eq!(Err(ContractError::Unauthorized), res); - - let res = try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ); - assert!(res.is_ok()) - } - - #[test] - fn cannot_be_called_if_rewarding_is_not_in_progress() { - let mut deps = test_helpers::init_contract(); - let current_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - let rewarding_validator_address = current_state.rewarding_validator_address; - - let res = try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 0, - ); - assert_eq!(Err(ContractError::RewardingNotInProgress), res); - } - - #[test] - fn provided_nonce_must_be_equal_the_current_one() { - let mut deps = test_helpers::init_contract(); - let env = mock_env(); - let mut current_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - current_state.latest_rewarding_interval_nonce = 42; - mixnet_params_storage::CONTRACT_STATE - .save(deps.as_mut().storage, ¤t_state) - .unwrap(); - - let rewarding_validator_address = current_state.rewarding_validator_address; - - try_begin_mixnode_rewarding( - deps.as_mut(), - env, - mock_info(rewarding_validator_address.as_ref(), &[]), - 43, - ) - .unwrap(); - - let res = try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 11, - ); - assert_eq!( - Err(ContractError::InvalidRewardingIntervalNonce { - received: 11, - expected: 43 - }), - res - ); - - let res = try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 44, - ); - assert_eq!( - Err(ContractError::InvalidRewardingIntervalNonce { - received: 44, - expected: 43 - }), - res - ); - - let res = try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 42, - ); - assert_eq!( - Err(ContractError::InvalidRewardingIntervalNonce { - received: 42, - expected: 43 - }), - res - ); - - let res = try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 43, - ); - assert!(res.is_ok()) - } - - #[test] - fn updates_contract_state() { - let mut deps = test_helpers::init_contract(); - let env = mock_env(); - let current_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - let rewarding_validator_address = current_state.rewarding_validator_address; - - try_begin_mixnode_rewarding( - deps.as_mut(), - env, - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - - try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - - let new_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - assert!(!new_state.rewarding_in_progress); - } - } - #[test] - fn rewarding_mixnodes_outside_rewarding_period() { + fn rewarding_mixnodes_with_incorrect_interval_id() { let mut deps = test_helpers::init_contract(); - let env = mock_env(); + let mut env = mock_env(); let current_state = mixnet_params_storage::CONTRACT_STATE .load(deps.as_mut().storage) .unwrap(); @@ -794,6 +364,7 @@ pub mod tests { ); let info = mock_info(rewarding_validator_address.as_ref(), &[]); + let res = try_reward_mixnode( deps.as_mut(), env.clone(), @@ -802,52 +373,10 @@ pub mod tests { tests::fixtures::node_rewarding_params_fixture(100), 1, ); - assert_eq!(Err(ContractError::RewardingNotInProgress), res); - - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap(); - - let res = try_reward_mixnode( - deps.as_mut(), - env, - info, - node_identity, - tests::fixtures::node_rewarding_params_fixture(100), - 1, - ); - assert!(res.is_ok()) - } - - #[test] - fn rewarding_mixnodes_with_incorrect_rewarding_nonce() { - let mut deps = test_helpers::init_contract(); - let env = mock_env(); - let current_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - let rewarding_validator_address = current_state.rewarding_validator_address; - - // bond the node - let node_owner: Addr = Addr::unchecked("node-owner"); - let node_identity = test_helpers::add_mixnode( - node_owner.as_str(), - tests::fixtures::good_mixnode_pledge(), - deps.as_mut(), - ); - - let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap(); - let res = try_reward_mixnode( - deps.as_mut(), - env.clone(), - info.clone(), - node_identity.clone(), - tests::fixtures::node_rewarding_params_fixture(100), - 0, - ); assert_eq!( - Err(ContractError::InvalidRewardingIntervalNonce { - received: 0, - expected: 1 + Err(ContractError::InvalidIntervalId { + received: 1, + expected: 0 }), res ); @@ -861,28 +390,40 @@ pub mod tests { 2, ); assert_eq!( - Err(ContractError::InvalidRewardingIntervalNonce { + Err(ContractError::InvalidIntervalId { received: 2, - expected: 1 + expected: 0 }), res ); let res = try_reward_mixnode( deps.as_mut(), - env, + env.clone(), + info.clone(), + node_identity.clone(), + tests::fixtures::node_rewarding_params_fixture(100), + 0, + ); + assert!(res.is_ok()); + + test_helpers::update_env_and_progress_interval(&mut env, deps.as_mut().storage); + + let res = try_reward_mixnode( + deps.as_mut(), + env.clone(), info, node_identity, tests::fixtures::node_rewarding_params_fixture(100), 1, ); - assert!(res.is_ok()) + assert!(res.is_ok()); } #[test] fn attempting_rewarding_mixnode_multiple_times_per_interval() { let mut deps = test_helpers::init_contract(); - let env = mock_env(); + let mut env = mock_env(); let current_state = mixnet_params_storage::CONTRACT_STATE .load(deps.as_mut().storage) .unwrap(); @@ -897,7 +438,6 @@ pub mod tests { ); let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap(); // first reward goes through just fine let res = try_reward_mixnode( @@ -906,7 +446,7 @@ pub mod tests { info.clone(), node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ); assert!(res.is_ok()); @@ -917,7 +457,7 @@ pub mod tests { info.clone(), node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ); assert_eq!( Err(ContractError::MixnodeAlreadyRewarded { @@ -927,8 +467,7 @@ pub mod tests { ); // but rewarding the same node in the following interval is fine again - try_finish_mixnode_rewarding(deps.as_mut(), info.clone(), 1).unwrap(); - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 2).unwrap(); + test_helpers::update_env_and_progress_interval(&mut env, deps.as_mut().storage); let res = try_reward_mixnode( deps.as_mut(), @@ -936,7 +475,7 @@ pub mod tests { info, node_identity, tests::fixtures::node_rewarding_params_fixture(100), - 2, + 1, ); assert!(res.is_ok()); } @@ -979,7 +518,7 @@ pub mod tests { .unwrap(); // delegation happens later, but not later enough - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING - 1; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING - 1; delegations_storage::delegations() .save( @@ -996,17 +535,16 @@ pub mod tests { .unwrap(); let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap(); + let res = try_reward_mixnode( deps.as_mut(), env.clone(), info.clone(), node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ) .unwrap(); - try_finish_mixnode_rewarding(deps.as_mut(), info, 1).unwrap(); assert_eq!( initial_bond, @@ -1030,19 +568,18 @@ pub mod tests { // reward can happen now, but only for bonded node env.block.height += 1; + test_helpers::update_env_and_progress_interval(&mut env, deps.as_mut().storage); let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 2).unwrap(); let res = try_reward_mixnode( deps.as_mut(), env.clone(), info.clone(), node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 2, + 1, ) .unwrap(); - try_finish_mixnode_rewarding(deps.as_mut(), info, 2).unwrap(); assert!( test_helpers::read_mixnode_pledge_amount(deps.as_ref().storage, &node_identity) @@ -1073,7 +610,8 @@ pub mod tests { ); // reward happens now, both for node owner and delegators - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING - 1; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING - 1; + test_helpers::update_env_and_progress_interval(&mut env, deps.as_mut().storage); let pledge_before_rewarding = test_helpers::read_mixnode_pledge_amount(deps.as_ref().storage, &node_identity) @@ -1081,17 +619,15 @@ pub mod tests { .u128(); let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 3).unwrap(); let res = try_reward_mixnode( deps.as_mut(), env, info.clone(), node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 3, + 2, ) .unwrap(); - try_finish_mixnode_rewarding(deps.as_mut(), info, 3).unwrap(); assert!( test_helpers::read_mixnode_pledge_amount(deps.as_ref().storage, &node_identity) @@ -1124,7 +660,8 @@ pub mod tests { #[test] fn test_tokenomics_rewarding() { - use crate::contract::{EPOCH_REWARD_PERCENT, INITIAL_REWARD_POOL}; + use crate::constants::INTERVAL_REWARD_PERCENT; + use crate::contract::INITIAL_REWARD_POOL; type U128 = fixed::types::U75F53; @@ -1134,7 +671,7 @@ pub mod tests { .load(deps.as_ref().storage) .unwrap(); let rewarding_validator_address = current_state.rewarding_validator_address; - let period_reward_pool = (INITIAL_REWARD_POOL / 100) * EPOCH_REWARD_PERCENT as u128; + let period_reward_pool = (INITIAL_REWARD_POOL / 100) * INTERVAL_REWARD_PERCENT as u128; assert_eq!(period_reward_pool, 5_000_000_000_000); let rewarded_set_size = 200; // Imagining our reward set size is 200 let active_set_size = 100; @@ -1166,15 +703,7 @@ pub mod tests { .unwrap(); let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - - env.block.height += 2 * storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; + env.block.height += 2 * constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; let mix_1 = mixnodes_storage::read_full_mixnode_bond(&deps.storage, &node_identity) .unwrap() @@ -1188,7 +717,7 @@ pub mod tests { 0, circulating_supply, mix_1_uptime, - DEFAULT_SYBIL_RESISTANCE_PERCENT, + SYBIL_RESISTANCE_PERCENT, true, active_set_work_factor, ); @@ -1231,7 +760,7 @@ pub mod tests { .u128(); assert_eq!(pre_reward_delegation, 10_000_000_000); - try_reward_mixnode(deps.as_mut(), env, info, node_identity.clone(), params, 1).unwrap(); + try_reward_mixnode(deps.as_mut(), env, info, node_identity.clone(), params, 0).unwrap(); assert_eq!( test_helpers::read_mixnode_pledge_amount(&deps.storage, &node_identity) @@ -1257,7 +786,7 @@ pub mod tests { // it's all correctly saved match storage::REWARDING_STATUS - .load(deps.as_ref().storage, (1u32, node_identity)) + .load(deps.as_ref().storage, (0u32, node_identity)) .unwrap() { RewardingStatus::Complete(result) => assert_eq!( @@ -1314,24 +843,16 @@ pub mod tests { .unwrap(); } - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - let res = try_reward_mixnode( deps.as_mut(), env, info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ) .unwrap(); assert_eq!( @@ -1339,13 +860,6 @@ pub mod tests { must_find_attribute(&res.events[0], FURTHER_DELEGATIONS_TO_REWARD_KEY) ); - try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - for i in 0..10 { let delegation = test_helpers::read_delegation( &deps.storage, @@ -1396,24 +910,16 @@ pub mod tests { .unwrap(); } - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - let res = try_reward_mixnode( deps.as_mut(), env, info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ) .unwrap(); assert_eq!( @@ -1421,13 +927,6 @@ pub mod tests { must_find_attribute(&res.events[0], FURTHER_DELEGATIONS_TO_REWARD_KEY) ); - try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - for i in 0..MIXNODE_DELEGATORS_PAGE_LIMIT { let delegation = test_helpers::read_delegation( &deps.storage, @@ -1478,24 +977,16 @@ pub mod tests { .unwrap(); } - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - let res = try_reward_mixnode( deps.as_mut(), env, info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ) .unwrap(); assert_eq!( @@ -1503,13 +994,6 @@ pub mod tests { must_find_attribute(&res.events[0], FURTHER_DELEGATIONS_TO_REWARD_KEY) ); - try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - for i in 0..MIXNODE_DELEGATORS_PAGE_LIMIT { let delegation = test_helpers::read_delegation( &deps.storage, @@ -1567,7 +1051,7 @@ pub mod tests { .unwrap(); } - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING + 1; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING + 1; let mut node_rewarding_params = tests::fixtures::node_rewarding_params_fixture(100); node_rewarding_params.set_reward_blockstamp(env.block.height); @@ -1622,7 +1106,7 @@ pub mod tests { .unwrap(); } - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING + 1; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING + 1; let mut node_rewarding_params = tests::fixtures::node_rewarding_params_fixture(100); node_rewarding_params.set_reward_blockstamp(env.block.height); @@ -1707,46 +1191,19 @@ pub mod tests { assert_eq!(Err(ContractError::Unauthorized), res); } - #[test] - fn cannot_be_called_if_rewarding_is_not_in_progress() { - let mut deps = test_helpers::init_contract(); - let current_state = mixnet_params_storage::CONTRACT_STATE - .load(deps.as_mut().storage) - .unwrap(); - let rewarding_validator_address = current_state.rewarding_validator_address; - - let res = try_reward_next_mixnode_delegators( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - "alice's mixnode".to_string(), - 1, - ); - - assert_eq!(Err(ContractError::RewardingNotInProgress), res); - } - #[test] fn cannot_be_called_if_mixnodes_operator_wasnt_rewarded() { let mut deps = test_helpers::init_contract(); - let env = mock_env(); let current_state = mixnet_params_storage::CONTRACT_STATE .load(deps.as_mut().storage) .unwrap(); let rewarding_validator_address = current_state.rewarding_validator_address; - try_begin_mixnode_rewarding( - deps.as_mut(), - env, - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - let res = try_reward_next_mixnode_delegators( deps.as_mut(), mock_info(rewarding_validator_address.as_ref(), &[]), "alice's mixnode".to_string(), - 1, + 0, ); assert_eq!( @@ -1776,15 +1233,7 @@ pub mod tests { deps.as_mut(), ); - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; - - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; try_reward_mixnode( deps.as_mut(), @@ -1792,7 +1241,7 @@ pub mod tests { mock_info(rewarding_validator_address.as_ref(), &[]), node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ) .unwrap(); @@ -1800,7 +1249,7 @@ pub mod tests { deps.as_mut(), mock_info(rewarding_validator_address.as_ref(), &[]), node_identity.clone(), - 1, + 0, ); assert_eq!( @@ -1810,13 +1259,6 @@ pub mod tests { res ); - try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - // there was another page of delegators, but they were already dealt with let node_owner: Addr = Addr::unchecked("bob"); @@ -1837,24 +1279,17 @@ pub mod tests { .unwrap(); } - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; + test_helpers::update_env_and_progress_interval(&mut env, deps.as_mut().storage); let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 2, - ) - .unwrap(); - try_reward_mixnode( deps.as_mut(), env, info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 2, + 1, ) .unwrap(); @@ -1863,7 +1298,7 @@ pub mod tests { deps.as_mut(), mock_info(rewarding_validator_address.as_ref(), &[]), node_identity.clone(), - 2, + 1, ) .unwrap(); @@ -1871,7 +1306,7 @@ pub mod tests { deps.as_mut(), mock_info(rewarding_validator_address.as_ref(), &[]), node_identity.clone(), - 2, + 1, ); assert_eq!( @@ -1880,13 +1315,6 @@ pub mod tests { }), res ); - - try_finish_mixnode_rewarding( - deps.as_mut(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 2, - ) - .unwrap(); } #[test] @@ -1928,24 +1356,16 @@ pub mod tests { .unwrap(); } - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - try_reward_mixnode( deps.as_mut(), env, info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ) .unwrap(); @@ -1954,14 +1374,14 @@ pub mod tests { deps.as_mut(), mock_info(rewarding_validator_address.as_ref(), &[]), node_identity.clone(), - 1, + 0, ) .unwrap(); try_reward_next_mixnode_delegators( deps.as_mut(), mock_info(rewarding_validator_address.as_ref(), &[]), node_identity.clone(), - 1, + 0, ) .unwrap(); @@ -2026,7 +1446,7 @@ pub mod tests { .unwrap(); } - env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING; + env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; // update some delegations (on 'main' page and the secondary call) try_delegate_to_mixnode( @@ -2051,21 +1471,13 @@ pub mod tests { env.block.height += 123; let info = mock_info(rewarding_validator_address.as_ref(), &[]); - try_begin_mixnode_rewarding( - deps.as_mut(), - env.clone(), - mock_info(rewarding_validator_address.as_ref(), &[]), - 1, - ) - .unwrap(); - try_reward_mixnode( deps.as_mut(), env, info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), - 1, + 0, ) .unwrap(); @@ -2074,7 +1486,7 @@ pub mod tests { deps.as_mut(), mock_info(rewarding_validator_address.as_ref(), &[]), node_identity.clone(), - 1, + 0, ) .unwrap(); diff --git a/contracts/mixnet/src/support/helpers.rs b/contracts/mixnet/src/support/helpers.rs index 94c273879e..2290cbc823 100644 --- a/contracts/mixnet/src/support/helpers.rs +++ b/contracts/mixnet/src/support/helpers.rs @@ -48,10 +48,10 @@ pub(crate) fn ensure_no_existing_bond( } pub(crate) fn validate_node_identity_signature( - deps: Deps, + deps: Deps<'_>, owner: &Addr, signature: String, - identity: IdentityKeyRef, + identity: IdentityKeyRef<'_>, ) -> Result<(), ContractError> { let owner_bytes = owner.as_bytes(); diff --git a/contracts/mixnet/src/support/tests/fixtures.rs b/contracts/mixnet/src/support/tests/fixtures.rs index cd1861e344..f9f3927a16 100644 --- a/contracts/mixnet/src/support/tests/fixtures.rs +++ b/contracts/mixnet/src/support/tests/fixtures.rs @@ -1,7 +1,5 @@ -use crate::contract::{ - DEFAULT_SYBIL_RESISTANCE_PERCENT, EPOCH_REWARD_PERCENT, INITIAL_MIXNODE_PLEDGE, - INITIAL_REWARD_POOL, -}; +use crate::constants::{INTERVAL_REWARD_PERCENT, SYBIL_RESISTANCE_PERCENT}; +use crate::contract::{INITIAL_MIXNODE_PLEDGE, INITIAL_REWARD_POOL}; use crate::mixnodes::storage as mixnodes_storage; use crate::{mixnodes::storage::StoredMixnodeBond, support::tests}; use config::defaults::{DENOM, TOTAL_SUPPLY}; @@ -79,13 +77,13 @@ pub fn good_gateway_pledge() -> Vec { // when exact values are irrelevant and what matters is the action of rewarding pub fn node_rewarding_params_fixture(uptime: u128) -> NodeRewardParams { NodeRewardParams::new( - (INITIAL_REWARD_POOL / 100) * EPOCH_REWARD_PERCENT as u128, + (INITIAL_REWARD_POOL / 100) * INTERVAL_REWARD_PERCENT as u128, 50 as u128, 25 as u128, 0, TOTAL_SUPPLY - INITIAL_REWARD_POOL, uptime, - DEFAULT_SYBIL_RESISTANCE_PERCENT, + SYBIL_RESISTANCE_PERCENT, true, 10, ) diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index 547c68ffdd..d38ad0907a 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -12,11 +12,12 @@ pub mod test_helpers { use crate::contract::instantiate; use crate::delegations::storage as delegations_storage; use crate::gateways::transactions::try_add_gateway; + use crate::interval; + use crate::interval::storage as interval_storage; use crate::mixnodes::storage as mixnodes_storage; use crate::mixnodes::transactions::try_add_mixnode; use crate::support::tests; use config::defaults::DENOM; - use cosmwasm_std::coin; use cosmwasm_std::testing::mock_dependencies; use cosmwasm_std::testing::mock_env; use cosmwasm_std::testing::mock_info; @@ -25,13 +26,14 @@ pub mod test_helpers { use cosmwasm_std::Coin; use cosmwasm_std::DepsMut; use cosmwasm_std::OwnedDeps; + use cosmwasm_std::{coin, Env, Timestamp}; use cosmwasm_std::{Addr, StdResult, Storage}; use cosmwasm_std::{Empty, MemoryStorage}; use cw_storage_plus::PrimaryKey; use mixnet_contract_common::{Delegation, Gateway, IdentityKeyRef, InstantiateMsg, MixNode}; use rand::thread_rng; - pub fn add_mixnode(sender: &str, stake: Vec, deps: DepsMut) -> String { + pub fn add_mixnode(sender: &str, stake: Vec, deps: DepsMut<'_>) -> String { let keypair = crypto::asymmetric::identity::KeyPair::new(&mut thread_rng()); let owner_signature = keypair .private_key() @@ -55,7 +57,7 @@ pub mod test_helpers { key } - pub fn add_gateway(sender: &str, stake: Vec, deps: DepsMut) -> String { + pub fn add_gateway(sender: &str, stake: Vec, deps: DepsMut<'_>) -> String { let keypair = crypto::asymmetric::identity::KeyPair::new(&mut thread_rng()); let owner_signature = keypair .private_key() @@ -85,14 +87,14 @@ pub mod test_helpers { }; let env = mock_env(); let info = mock_info("creator", &[]); - instantiate(deps.as_mut(), env.clone(), info, msg).unwrap(); + instantiate(deps.as_mut(), env, info, msg).unwrap(); deps } // currently not used outside tests pub(crate) fn read_mixnode_pledge_amount( storage: &dyn Storage, - identity: IdentityKeyRef, + identity: IdentityKeyRef<'_>, ) -> StdResult { let node = mixnodes_storage::mixnodes().load(storage, identity)?; Ok(node.pledge_amount.amount) @@ -125,4 +127,18 @@ pub mod test_helpers { .may_load(storage, (mix.into(), owner.into()).joined_key()) .unwrap() } + + pub(crate) fn update_env_and_progress_interval(env: &mut Env, storage: &mut dyn Storage) { + // make sure current block time is within the expected next interval + env.block.time = Timestamp::from_seconds( + (interval_storage::CURRENT_INTERVAL + .load(storage) + .unwrap() + .next_interval() + .start_unix_timestamp() + + 123) as u64, + ); + + interval::transactions::try_advance_interval(env.clone(), storage).unwrap(); + } } diff --git a/contracts/vesting/Cargo.toml b/contracts/vesting/Cargo.toml index dd61e3c325..00d21fd8b6 100644 --- a/contracts/vesting/Cargo.toml +++ b/contracts/vesting/Cargo.toml @@ -2,7 +2,7 @@ name = "vesting-contract" version = "0.1.0" authors = ["Drazen Urch "] -edition = "2018" +edition = "2021" exclude = [ # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. @@ -18,9 +18,9 @@ mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet- vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } config = { path = "../../common/config" } -cosmwasm-std = { version = "1.0.0-beta3"} +cosmwasm-std = { version = "1.0.0-beta4"} cw-storage-plus = { version = "0.11.1", features = ["iterator"] } schemars = "0.8" -serde = { version = "1.0.103", default-features = false, features = ["derive"] } -thiserror = { version = "1.0.23" } \ No newline at end of file +serde = { version = "1.0", default-features = false, features = ["derive"] } +thiserror = { version = "1.0" } \ No newline at end of file diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index 89f469f1b5..a8186bd55d 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -1,11 +1,10 @@ use crate::errors::ContractError; -use crate::messages::{ExecuteMsg, InitMsg, QueryMsg}; -use crate::storage::account_from_address; +use crate::storage::{account_from_address, ADMIN, MIXNET_CONTRACT_ADDRESS}; use crate::traits::{ DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount, }; -use crate::vesting::{populate_vesting_periods, Account}; -use config::defaults::{DEFAULT_MIXNET_CONTRACT_ADDRESS, DENOM}; +use crate::vesting::{populate_vesting_periods, Account, PledgeData}; +use config::defaults::DENOM; use cosmwasm_std::{ coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Timestamp, Uint128, @@ -16,47 +15,54 @@ use vesting_contract_common::events::{ new_staking_address_update_event, new_track_gateway_unbond_event, new_track_mixnode_unbond_event, new_track_undelegation_event, new_vested_coins_withdraw_event, }; - -// We're using a 24 month vesting period with 3 months sub-periods. -// There are 8 three month periods in two years -// and duration of a single period is 30 days. -pub const NUM_VESTING_PERIODS: usize = 8; -pub const VESTING_PERIOD: u64 = 3 * 30 * 86400; -// Address of the account set to be contract admin -pub const ADMIN_ADDRESS: &str = "admin"; +use vesting_contract_common::messages::{ + ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification, +}; #[entry_point] pub fn instantiate( - _deps: DepsMut, + deps: DepsMut<'_>, _env: Env, - _info: MessageInfo, - _msg: InitMsg, + info: MessageInfo, + msg: InitMsg, ) -> Result { + // ADMIN is set to the address that instantiated the contract, TODO: make this updatable + ADMIN.save(deps.storage, &info.sender.to_string())?; + MIXNET_CONTRACT_ADDRESS.save(deps.storage, &msg.mixnet_contract_address)?; + Ok(Response::default()) +} + +#[entry_point] +pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { Ok(Response::default()) } #[entry_point] pub fn execute( - deps: DepsMut, + deps: DepsMut<'_>, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::DelegateToMixnode { mix_identity } => { - try_delegate_to_mixnode(mix_identity, info, env, deps) + ExecuteMsg::UpdateMixnetAddress { address } => { + try_update_mixnet_address(address, info, deps) } + ExecuteMsg::DelegateToMixnode { + mix_identity, + amount, + } => try_delegate_to_mixnode(mix_identity, amount, info, env, deps), ExecuteMsg::UndelegateFromMixnode { mix_identity } => { try_undelegate_from_mixnode(mix_identity, info, deps) } ExecuteMsg::CreateAccount { owner_address, staking_address, - start_time, + vesting_spec, } => try_create_periodic_vesting_account( &owner_address, staking_address, - start_time, + vesting_spec, info, env, deps, @@ -72,7 +78,8 @@ pub fn execute( ExecuteMsg::BondMixnode { mix_node, owner_signature, - } => try_bond_mixnode(mix_node, owner_signature, info, env, deps), + amount, + } => try_bond_mixnode(mix_node, owner_signature, amount, info, env, deps), ExecuteMsg::UnbondMixnode {} => try_unbond_mixnode(info, deps), ExecuteMsg::TrackUnbondMixnode { owner, amount } => { try_track_unbond_mixnode(&owner, amount, info, deps) @@ -80,7 +87,8 @@ pub fn execute( ExecuteMsg::BondGateway { gateway, owner_signature, - } => try_bond_gateway(gateway, owner_signature, info, env, deps), + amount, + } => try_bond_gateway(gateway, owner_signature, amount, info, env, deps), ExecuteMsg::UnbondGateway {} => try_unbond_gateway(info, deps), ExecuteMsg::TrackUnbondGateway { owner, amount } => { try_track_unbond_gateway(&owner, amount, info, deps) @@ -94,12 +102,25 @@ pub fn execute( } } -// Only owner +// Only contract admin, set at init +pub fn try_update_mixnet_address( + address: String, + info: MessageInfo, + deps: DepsMut<'_>, +) -> Result { + if info.sender != ADMIN.load(deps.storage)? { + return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); + } + MIXNET_CONTRACT_ADDRESS.save(deps.storage, &address)?; + Ok(Response::default()) +} + +// Only contract owner of vesting account pub fn try_withdraw_vested_coins( amount: Coin, env: Env, info: MessageInfo, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { if amount.denom != DENOM { return Err(ContractError::WrongDenom(amount.denom, DENOM.to_string())); @@ -141,7 +162,7 @@ pub fn try_withdraw_vested_coins( fn try_transfer_ownership( to_address: String, info: MessageInfo, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { let address = info.sender.clone(); let to_address = deps.api.addr_validate(&to_address)?; @@ -157,7 +178,7 @@ fn try_transfer_ownership( fn try_update_staking_address( to_address: Option, info: MessageInfo, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { let address = info.sender.clone(); let to_address = to_address.and_then(|x| deps.api.addr_validate(&x).ok()); @@ -175,16 +196,17 @@ fn try_update_staking_address( pub fn try_bond_gateway( gateway: Gateway, owner_signature: String, + amount: Coin, info: MessageInfo, env: Env, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { - let pledge = validate_funds(&info.funds)?; + let pledge = validate_funds(&[amount])?; let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_bond_gateway(gateway, owner_signature, pledge, &env, deps.storage) } -pub fn try_unbond_gateway(info: MessageInfo, deps: DepsMut) -> Result { +pub fn try_unbond_gateway(info: MessageInfo, deps: DepsMut<'_>) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_unbond_gateway(deps.storage) } @@ -193,9 +215,9 @@ pub fn try_track_unbond_gateway( owner: &str, amount: Coin, info: MessageInfo, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { - if info.sender != DEFAULT_MIXNET_CONTRACT_ADDRESS { + if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? { return Err(ContractError::NotMixnetContract(info.sender)); } let account = account_from_address(owner, deps.storage, deps.api)?; @@ -206,16 +228,17 @@ pub fn try_track_unbond_gateway( pub fn try_bond_mixnode( mix_node: MixNode, owner_signature: String, + amount: Coin, info: MessageInfo, env: Env, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { - let pledge = validate_funds(&info.funds)?; + let pledge = validate_funds(&[amount])?; let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_bond_mixnode(mix_node, owner_signature, pledge, &env, deps.storage) } -pub fn try_unbond_mixnode(info: MessageInfo, deps: DepsMut) -> Result { +pub fn try_unbond_mixnode(info: MessageInfo, deps: DepsMut<'_>) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_unbond_mixnode(deps.storage) } @@ -224,9 +247,9 @@ pub fn try_track_unbond_mixnode( owner: &str, amount: Coin, info: MessageInfo, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { - if info.sender != DEFAULT_MIXNET_CONTRACT_ADDRESS { + if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? { return Err(ContractError::NotMixnetContract(info.sender)); } let account = account_from_address(owner, deps.storage, deps.api)?; @@ -239,9 +262,9 @@ fn try_track_undelegation( mix_identity: IdentityKey, amount: Coin, info: MessageInfo, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { - if info.sender != DEFAULT_MIXNET_CONTRACT_ADDRESS { + if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? { return Err(ContractError::NotMixnetContract(info.sender)); } let account = account_from_address(address, deps.storage, deps.api)?; @@ -251,11 +274,12 @@ fn try_track_undelegation( fn try_delegate_to_mixnode( mix_identity: IdentityKey, + amount: Coin, info: MessageInfo, env: Env, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { - let amount = validate_funds(&info.funds)?; + let amount = validate_funds(&[amount])?; let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_delegate_to_mixnode(mix_identity, amount, &env, deps.storage) } @@ -263,7 +287,7 @@ fn try_delegate_to_mixnode( fn try_undelegate_from_mixnode( mix_identity: IdentityKey, info: MessageInfo, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_undelegate_from_mixnode(mix_identity, deps.storage) @@ -272,14 +296,23 @@ fn try_undelegate_from_mixnode( fn try_create_periodic_vesting_account( owner_address: &str, staking_address: Option, - start_time: Option, + vesting_spec: Option, info: MessageInfo, env: Env, - deps: DepsMut, + deps: DepsMut<'_>, ) -> Result { - if info.sender != ADMIN_ADDRESS { + if info.sender != ADMIN.load(deps.storage)? { return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); } + let account_exists = account_from_address(owner_address, deps.storage, deps.api).is_ok(); + if account_exists { + return Err(ContractError::AccountAlreadyExists( + owner_address.to_string(), + )); + } + + let vesting_spec = vesting_spec.unwrap_or_default(); + let coin = validate_funds(&info.funds)?; let owner_address = deps.api.addr_validate(owner_address)?; let staking_address = if let Some(staking_address) = staking_address { @@ -287,8 +320,11 @@ fn try_create_periodic_vesting_account( } else { None }; - let start_time = start_time.unwrap_or_else(|| env.block.time.seconds()); - let periods = populate_vesting_periods(start_time, NUM_VESTING_PERIODS); + let start_time = vesting_spec + .start_time() + .unwrap_or_else(|| env.block.time.seconds()); + + let periods = populate_vesting_periods(start_time, vesting_spec); let start_time = Timestamp::from_seconds(start_time); Account::new( @@ -299,18 +335,35 @@ fn try_create_periodic_vesting_account( periods, deps.storage, )?; - Ok( - Response::new().add_event(new_periodic_vesting_account_event( - &owner_address, - &coin, - &staking_address, - start_time, - )), - ) + + let mut response = Response::new(); + + let send_tokens_owner = BankMsg::Send { + to_address: owner_address.as_str().to_string(), + amount: vec![Coin::new(1_000_000, DENOM)], + }; + + response = response.add_message(send_tokens_owner); + + if let Some(staking_address) = staking_address.as_ref() { + let send_tokens_staking = BankMsg::Send { + to_address: staking_address.clone().as_str().to_string(), + amount: vec![Coin::new(1_000_000, DENOM)], + }; + + response = response.add_message(send_tokens_staking); + } + + Ok(response.add_event(new_periodic_vesting_account_event( + &owner_address, + &coin, + &staking_address, + start_time, + ))) } #[entry_point] -pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { +pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result { let query_res = match msg { QueryMsg::LockedCoins { vesting_account_address, @@ -375,16 +428,33 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(&try_get_account(&address, deps)?), + QueryMsg::GetMixnode { address } => to_binary(&try_get_mixnode(&address, deps)?), + QueryMsg::GetGateway { address } => to_binary(&try_get_gateway(&address, deps)?), }; Ok(query_res?) } +pub fn try_get_mixnode(address: &str, deps: Deps<'_>) -> Result, ContractError> { + let account = account_from_address(address, deps.storage, deps.api)?; + account.load_mixnode_pledge(deps.storage) +} + +pub fn try_get_gateway(address: &str, deps: Deps<'_>) -> Result, ContractError> { + let account = account_from_address(address, deps.storage, deps.api)?; + account.load_gateway_pledge(deps.storage) +} + +pub fn try_get_account(address: &str, deps: Deps<'_>) -> Result { + account_from_address(address, deps.storage, deps.api) +} + pub fn try_get_locked_coins( vesting_account_address: &str, block_time: Option, env: Env, - deps: Deps, + deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.locked_coins(block_time, &env, deps.storage) @@ -394,7 +464,7 @@ pub fn try_get_spendable_coins( vesting_account_address: &str, block_time: Option, env: Env, - deps: Deps, + deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.spendable_coins(block_time, &env, deps.storage) @@ -404,7 +474,7 @@ pub fn try_get_vested_coins( vesting_account_address: &str, block_time: Option, env: Env, - deps: Deps, + deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.get_vested_coins(block_time, &env) @@ -414,7 +484,7 @@ pub fn try_get_vesting_coins( vesting_account_address: &str, block_time: Option, env: Env, - deps: Deps, + deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.get_vesting_coins(block_time, &env) @@ -422,7 +492,7 @@ pub fn try_get_vesting_coins( pub fn try_get_start_time( vesting_account_address: &str, - deps: Deps, + deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; Ok(account.get_start_time()) @@ -430,7 +500,7 @@ pub fn try_get_start_time( pub fn try_get_end_time( vesting_account_address: &str, - deps: Deps, + deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; Ok(account.get_end_time()) @@ -438,7 +508,7 @@ pub fn try_get_end_time( pub fn try_get_original_vesting( vesting_account_address: &str, - deps: Deps, + deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; Ok(account.get_original_vesting()) @@ -448,7 +518,7 @@ pub fn try_get_delegated_free( block_time: Option, vesting_account_address: &str, env: Env, - deps: Deps, + deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.get_delegated_free(block_time, &env, deps.storage) @@ -458,7 +528,7 @@ pub fn try_get_delegated_vesting( block_time: Option, vesting_account_address: &str, env: Env, - deps: Deps, + deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.get_delegated_vesting(block_time, &env, deps.storage) diff --git a/contracts/vesting/src/errors.rs b/contracts/vesting/src/errors.rs index fa1b8fc5f7..4962e27ad2 100644 --- a/contracts/vesting/src/errors.rs +++ b/contracts/vesting/src/errors.rs @@ -4,42 +4,44 @@ use thiserror::Error; #[derive(Error, Debug, PartialEq)] pub enum ContractError { - #[error("{0}")] + #[error("VESTING ({}): {0}", line!())] Std(#[from] StdError), - #[error("Account does not exist - {0}")] + #[error("VESTING ({}): Account does not exist - {0}", line!())] NoAccountForAddress(String), - #[error("Only admin can perform this action, {0} is not admin")] + #[error("VESTING ({}): Only admin can perform this action, {0} is not admin", line!())] NotAdmin(String), - #[error("Balance not found for existing account ({0}), this is a bug")] + #[error("VESTING ({}): Balance not found for existing account ({0}), this is a bug", line!())] NoBalanceForAddress(String), - #[error("Insufficient balance for address {0} -> {1}")] + #[error("VESTING ({}): Insufficient balance for address {0} -> {1}", line!())] InsufficientBalance(String, u128), - #[error("Insufficient spendable balance for address {0} -> {1}")] + #[error("VESTING ({}): Insufficient spendable balance for address {0} -> {1}", line!())] InsufficientSpendable(String, u128), #[error( - "Only delegation owner can perform delegation actions, {0} is not the delegation owner" - )] + "VESTING ({}):Only delegation owner can perform delegation actions, {0} is not the delegation owner" + , line!())] NotDelegate(String), - #[error("Total vesting amount is inprobably low -> {0}, this is likely an error")] + #[error("VESTING ({}): Total vesting amount is inprobably low -> {0}, this is likely an error", line!())] ImprobableVestingAmount(u128), - #[error("Address {0} has already bonded a node")] + #[error("VESTING ({}): Address {0} has already bonded a node", line!())] AlreadyBonded(String), - #[error("Received empty funds vector")] + #[error("VESTING ({}): Received empty funds vector", line!())] EmptyFunds, - #[error("Received wrong denom: {0}, expected {1}")] + #[error("VESTING ({}): Received wrong denom: {0}, expected {1}", line!())] WrongDenom(String, String), - #[error("Received multiple denoms, expected 1")] + #[error("VESTING ({}): Received multiple denoms, expected 1", line!())] MultipleDenoms, - #[error("No delegations found for account {0}, mix_identity {1}")] + #[error("VESTING ({}): No delegations found for account {0}, mix_identity {1}", line!())] NoSuchDelegation(Addr, IdentityKey), - #[error("Only mixnet contract can perform this operation, got {0}")] + #[error("VESTING ({}): Only mixnet contract can perform this operation, got {0}", line!())] NotMixnetContract(Addr), - #[error("Calculation underflowed")] + #[error("VESTING ({}): Calculation underflowed", line!())] Underflow, - #[error("No bond found for account {0}")] + #[error("VESTING ({}): No bond found for account {0}", line!())] NoBondFound(String), - #[error("Action can only be executed by account owner -> {0}")] + #[error("VESTING ({}): Action can only be executed by account owner -> {0}", line!())] NotOwner(String), - #[error("Invalid address: {0}")] + #[error("VESTING ({}): Invalid address: {0}", line!())] InvalidAddress(String), + #[error("VESTING ({}): Account already exists: {0}", line!())] + AccountAlreadyExists(String), } diff --git a/contracts/vesting/src/lib.rs b/contracts/vesting/src/lib.rs index d437267939..4e57232fe5 100644 --- a/contracts/vesting/src/lib.rs +++ b/contracts/vesting/src/lib.rs @@ -1,7 +1,6 @@ pub mod contract; mod errors; -pub mod messages; mod storage; mod support; mod traits; -mod vesting; +pub mod vesting; diff --git a/contracts/vesting/src/storage.rs b/contracts/vesting/src/storage.rs index eb24ba5409..190f4cb565 100644 --- a/contracts/vesting/src/storage.rs +++ b/contracts/vesting/src/storage.rs @@ -1,22 +1,108 @@ use crate::errors::ContractError; use crate::vesting::Account; -use cosmwasm_std::{Addr, Api, Storage}; +use crate::vesting::PledgeData; +use cosmwasm_std::{Addr, Api, Storage, Uint128}; use cw_storage_plus::{Item, Map}; +use mixnet_contract_common::IdentityKey; -pub const KEY: Item = Item::new("key"); -const ACCOUNTS: Map = Map::new("acc"); +type BlockHeight = u64; + +pub const KEY: Item<'_, u32> = Item::new("key"); +const ACCOUNTS: Map<'_, String, Account> = Map::new("acc"); +// Holds data related to individual accounts +const BALANCES: Map<'_, u32, Uint128> = Map::new("blc"); +const BOND_PLEDGES: Map<'_, u32, PledgeData> = Map::new("bnd"); +const GATEWAY_PLEDGES: Map<'_, u32, PledgeData> = Map::new("gtw"); +pub const DELEGATIONS: Map<'_, (u32, IdentityKey, BlockHeight), Uint128> = Map::new("dlg"); +pub const ADMIN: Item<'_, String> = Item::new("adm"); +pub const MIXNET_CONTRACT_ADDRESS: Item<'_, String> = Item::new("mix"); + +pub fn save_delegation( + key: (u32, IdentityKey, BlockHeight), + amount: Uint128, + storage: &mut dyn Storage, +) -> Result<(), ContractError> { + DELEGATIONS.save(storage, key, &amount)?; + Ok(()) +} + +pub fn remove_delegation( + key: (u32, IdentityKey, BlockHeight), + storage: &mut dyn Storage, +) -> Result<(), ContractError> { + DELEGATIONS.remove(storage, key); + Ok(()) +} pub fn delete_account(address: &Addr, storage: &mut dyn Storage) -> Result<(), ContractError> { - ACCOUNTS.remove(storage, address.to_owned()); + ACCOUNTS.remove(storage, address.to_owned().to_string()); + Ok(()) +} + +pub fn load_balance(key: u32, storage: &dyn Storage) -> Result { + Ok(BALANCES + .may_load(storage, key) + .unwrap_or(None) + .unwrap_or_else(Uint128::zero)) +} + +pub fn save_balance( + key: u32, + value: Uint128, + storage: &mut dyn Storage, +) -> Result<(), ContractError> { + BALANCES.save(storage, key, &value)?; + Ok(()) +} + +pub fn load_bond_pledge( + key: u32, + storage: &dyn Storage, +) -> Result, ContractError> { + Ok(BOND_PLEDGES.may_load(storage, key).unwrap_or(None)) +} + +pub fn remove_bond_pledge(key: u32, storage: &mut dyn Storage) -> Result<(), ContractError> { + BOND_PLEDGES.remove(storage, key); + Ok(()) +} + +pub fn save_bond_pledge( + key: u32, + value: &PledgeData, + storage: &mut dyn Storage, +) -> Result<(), ContractError> { + BOND_PLEDGES.save(storage, key, value)?; + Ok(()) +} + +pub fn load_gateway_pledge( + key: u32, + storage: &dyn Storage, +) -> Result, ContractError> { + Ok(GATEWAY_PLEDGES.may_load(storage, key).unwrap_or(None)) +} + +pub fn save_gateway_pledge( + key: u32, + value: &PledgeData, + storage: &mut dyn Storage, +) -> Result<(), ContractError> { + GATEWAY_PLEDGES.save(storage, key, value)?; + Ok(()) +} + +pub fn remove_gateway_pledge(key: u32, storage: &mut dyn Storage) -> Result<(), ContractError> { + GATEWAY_PLEDGES.remove(storage, key); Ok(()) } pub fn save_account(account: &Account, storage: &mut dyn Storage) -> Result<(), ContractError> { // This is a bit dirty, but its a simple way to allow for both staking account and owner to load it from storage if let Some(staking_address) = account.staking_address() { - ACCOUNTS.save(storage, staking_address.to_owned(), account)?; + ACCOUNTS.save(storage, staking_address.to_owned().to_string(), account)?; } - ACCOUNTS.save(storage, account.owner_address(), account)?; + ACCOUNTS.save(storage, account.owner_address().to_string(), account)?; Ok(()) } @@ -24,7 +110,9 @@ pub fn load_account( address: &Addr, storage: &dyn Storage, ) -> Result, ContractError> { - Ok(ACCOUNTS.may_load(storage, address.to_owned())?) + Ok(ACCOUNTS + .may_load(storage, address.to_owned().to_string()) + .unwrap_or(None)) } fn validate_account(address: &Addr, storage: &dyn Storage) -> Result { diff --git a/contracts/vesting/src/support/tests.rs b/contracts/vesting/src/support/tests.rs index 704fff6e3c..facefb00f8 100644 --- a/contracts/vesting/src/support/tests.rs +++ b/contracts/vesting/src/support/tests.rs @@ -1,24 +1,27 @@ #[cfg(test)] pub mod helpers { - use crate::contract::{instantiate, NUM_VESTING_PERIODS}; - use crate::messages::InitMsg; + use crate::contract::instantiate; use crate::vesting::{populate_vesting_periods, Account}; use config::defaults::DENOM; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; use cosmwasm_std::{Addr, Coin, Empty, Env, MemoryStorage, OwnedDeps, Storage, Uint128}; + use vesting_contract_common::messages::{InitMsg, VestingSpecification}; pub fn init_contract() -> OwnedDeps> { let mut deps = mock_dependencies(); - let msg = InitMsg {}; + let msg = InitMsg { + mixnet_contract_address: "test".to_string(), + }; let env = mock_env(); - let info = mock_info("creator", &[]); + let info = mock_info("admin", &[]); instantiate(deps.as_mut(), env.clone(), info, msg).unwrap(); return deps; } pub fn vesting_account_fixture(storage: &mut dyn Storage, env: &Env) -> Account { let start_time = env.block.time; - let periods = populate_vesting_periods(start_time.seconds(), NUM_VESTING_PERIODS); + let periods = + populate_vesting_periods(start_time.seconds(), VestingSpecification::default()); Account::new( Addr::unchecked("owner"), diff --git a/contracts/vesting/src/traits/delegating_account.rs b/contracts/vesting/src/traits/delegating_account.rs index c2d7104893..166ecb86bb 100644 --- a/contracts/vesting/src/traits/delegating_account.rs +++ b/contracts/vesting/src/traits/delegating_account.rs @@ -1,5 +1,5 @@ use crate::errors::ContractError; -use cosmwasm_std::{Coin, Env, Response, Storage, Timestamp, Uint128}; +use cosmwasm_std::{Coin, Env, Response, Storage, Uint128}; use mixnet_contract_common::IdentityKey; pub trait DelegatingAccount { @@ -18,12 +18,12 @@ pub trait DelegatingAccount { ) -> Result; // track_delegation performs internal vesting accounting necessary when - // delegating from a vesting account. It accepts the current block time, the + // delegating from a vesting account. It accepts the current block height, the // delegation amount and balance of all coins whose denomination exists in // the account's original vesting balance. fn track_delegation( &self, - block_time: Timestamp, + block_height: u64, mix_identity: IdentityKey, // Save some gas by passing it in current_balance: Uint128, diff --git a/contracts/vesting/src/vesting/account/delegating_account.rs b/contracts/vesting/src/vesting/account/delegating_account.rs index 845279f355..00aeeaaf7d 100644 --- a/contracts/vesting/src/vesting/account/delegating_account.rs +++ b/contracts/vesting/src/vesting/account/delegating_account.rs @@ -1,13 +1,14 @@ use crate::errors::ContractError; +use crate::storage::save_delegation; +use crate::storage::MIXNET_CONTRACT_ADDRESS; use crate::traits::DelegatingAccount; -use config::defaults::{DEFAULT_MIXNET_CONTRACT_ADDRESS, DENOM}; -use cosmwasm_std::{wasm_execute, Coin, Env, Order, Response, Storage, Timestamp, Uint128}; -use cw_storage_plus::Map; +use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; use mixnet_contract_common::ExecuteMsg as MixnetExecuteMsg; use mixnet_contract_common::IdentityKey; use vesting_contract_common::events::{ new_vesting_delegation_event, new_vesting_undelegation_event, }; +use vesting_contract_common::one_unym; use super::Account; @@ -32,9 +33,18 @@ impl DelegatingAccount for Account { mix_identity: mix_identity.clone(), delegate: self.owner_address().into_string(), }; - let delegate_to_mixnode = - wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![coin.clone()])?; - self.track_delegation(env.block.time, mix_identity, current_balance, coin, storage)?; + let delegate_to_mixnode = wasm_execute( + MIXNET_CONTRACT_ADDRESS.load(storage)?, + &msg, + vec![coin.clone()], + )?; + self.track_delegation( + env.block.height, + mix_identity, + current_balance, + coin, + storage, + )?; Ok(Response::new() .add_message(delegate_to_mixnode) @@ -58,12 +68,9 @@ impl DelegatingAccount for Account { delegate: self.owner_address().into_string(), }; let undelegate_from_mixnode = wasm_execute( - DEFAULT_MIXNET_CONTRACT_ADDRESS, + MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, - vec![Coin { - amount: Uint128::new(0), - denom: DENOM.to_string(), - }], + vec![one_unym()], )?; Ok(Response::new() @@ -73,29 +80,19 @@ impl DelegatingAccount for Account { fn track_delegation( &self, - block_time: Timestamp, + block_height: u64, mix_identity: IdentityKey, current_balance: Uint128, delegation: Coin, storage: &mut dyn Storage, ) -> Result<(), ContractError> { - let delegation_key = (mix_identity.as_bytes(), block_time.seconds()); - let delegations_storage_key = self.delegations_key(); - let delegations: Map<(&[u8], u64), Uint128> = Map::new(&delegations_storage_key); - - let new_delegation = - if let Some(existing_delegation) = delegations.may_load(storage, delegation_key)? { - existing_delegation + delegation.amount - } else { - delegation.amount - }; - - delegations.save(storage, delegation_key, &new_delegation)?; - + save_delegation( + (self.storage_key(), mix_identity, block_height), + delegation.amount, + storage, + )?; let new_balance = Uint128::new(current_balance.u128() - delegation.amount.u128()); - self.save_balance(new_balance, storage)?; - Ok(()) } @@ -105,26 +102,9 @@ impl DelegatingAccount for Account { amount: Coin, storage: &mut dyn Storage, ) -> Result<(), ContractError> { - let mix_bytes = mix_identity.as_bytes(); - let delegations_key = self.delegations_key(); - let delegations: Map<(&[u8], u64), Uint128> = Map::new(&delegations_key); - - // Iterate over keys matching the prefix and remove them from the map - let block_times = delegations - .prefix(mix_bytes) - .keys(storage, None, None, Order::Ascending) - // Scan will blow up on first error - .scan((), |_, x| x.ok()) - .collect::>(); - - for t in block_times { - delegations.remove(storage, (mix_bytes, t)) - } - + self.remove_delegations_for_mix(&mix_identity, storage)?; let new_balance = Uint128::new(self.load_balance(storage)?.u128() + amount.amount.u128()); - self.save_balance(new_balance, storage)?; - Ok(()) } } diff --git a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs index ce54d8ab60..3500e55b57 100644 --- a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs @@ -1,12 +1,13 @@ use super::PledgeData; use crate::errors::ContractError; +use crate::storage::MIXNET_CONTRACT_ADDRESS; use crate::traits::GatewayBondingAccount; -use config::defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway}; use vesting_contract_common::events::{ new_vesting_gateway_bonding_event, new_vesting_gateway_unbonding_event, }; +use vesting_contract_common::one_unym; use super::Account; @@ -47,7 +48,8 @@ impl GatewayBondingAccount for Account { let new_balance = Uint128::new(current_balance.u128() - pledge.amount.u128()); - let bond_gateway_msg = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![pledge])?; + let bond_gateway_msg = + wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![pledge])?; self.save_balance(new_balance, storage)?; self.save_gateway_pledge(pledge_data, storage)?; @@ -63,7 +65,11 @@ impl GatewayBondingAccount for Account { }; if let Some(_bond) = self.load_gateway_pledge(storage)? { - let unbond_msg = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![])?; + let unbond_msg = wasm_execute( + MIXNET_CONTRACT_ADDRESS.load(storage)?, + &msg, + vec![one_unym()], + )?; Ok(Response::new() .add_message(unbond_msg) diff --git a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs index ea78f580e1..1c4f148a27 100644 --- a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs @@ -1,12 +1,13 @@ use super::PledgeData; use crate::errors::ContractError; +use crate::storage::MIXNET_CONTRACT_ADDRESS; use crate::traits::MixnodeBondingAccount; -use config::defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, MixNode}; use vesting_contract_common::events::{ new_vesting_mixnode_bonding_event, new_vesting_mixnode_unbonding_event, }; +use vesting_contract_common::one_unym; use super::Account; @@ -47,7 +48,8 @@ impl MixnodeBondingAccount for Account { let new_balance = Uint128::new(current_balance.u128() - pledge.amount.u128()); - let bond_mixnode_mag = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![pledge])?; + let bond_mixnode_mag = + wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![pledge])?; self.save_balance(new_balance, storage)?; self.save_mixnode_pledge(pledge_data, storage)?; @@ -63,7 +65,11 @@ impl MixnodeBondingAccount for Account { }; if self.load_mixnode_pledge(storage)?.is_some() { - let unbond_msg = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![])?; + let unbond_msg = wasm_execute( + MIXNET_CONTRACT_ADDRESS.load(storage)?, + &msg, + vec![one_unym()], + )?; Ok(Response::new() .add_message(unbond_msg) diff --git a/contracts/vesting/src/vesting/account/mod.rs b/contracts/vesting/src/vesting/account/mod.rs index b10e60f81e..0e2a48df41 100644 --- a/contracts/vesting/src/vesting/account/mod.rs +++ b/contracts/vesting/src/vesting/account/mod.rs @@ -1,9 +1,12 @@ use super::{PledgeData, VestingPeriod}; -use crate::contract::NUM_VESTING_PERIODS; use crate::errors::ContractError; -use crate::storage::{save_account, KEY}; +use crate::storage::{ + load_balance, load_bond_pledge, load_gateway_pledge, remove_bond_pledge, remove_delegation, + remove_gateway_pledge, save_account, save_balance, save_bond_pledge, save_gateway_pledge, + DELEGATIONS, KEY, +}; use cosmwasm_std::{Addr, Coin, Order, Storage, Timestamp, Uint128}; -use cw_storage_plus::{Item, Map}; +use cw_storage_plus::Bound; use mixnet_contract_common::IdentityKey; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -13,11 +16,6 @@ mod gateway_bonding_account; mod mixnode_bonding_account; mod vesting_account; -const DELEGATIONS_SUFFIX: &str = "de"; -const BALANCE_SUFFIX: &str = "ba"; -const PLEDGE_SUFFIX: &str = "bo"; -const GATEWAY_SUFFIX: &str = "ga"; - fn generate_storage_key(storage: &mut dyn Storage) -> Result { let key = KEY.may_load(storage)?.unwrap_or(0) + 1; KEY.save(storage, &key)?; @@ -31,7 +29,7 @@ pub struct Account { start_time: Timestamp, periods: Vec, coin: Coin, - storage_key: String, + storage_key: u32, } impl Account { @@ -43,7 +41,7 @@ impl Account { periods: Vec, storage: &mut dyn Storage, ) -> Result { - let storage_key = generate_storage_key(storage)?.to_string(); + let storage_key = generate_storage_key(storage)?; let amount = coin.amount; let account = Account { owner_address, @@ -58,20 +56,12 @@ impl Account { Ok(account) } - pub fn delegations_key(&self) -> String { - format!("{}{}", self.storage_key, DELEGATIONS_SUFFIX) + pub fn num_vesting_periods(&self) -> usize { + self.periods.len() } - pub fn balance_key(&self) -> String { - format!("{}{}", self.storage_key, BALANCE_SUFFIX) - } - - pub fn mixnode_pledge_key(&self) -> String { - format!("{}{}", self.storage_key, PLEDGE_SUFFIX) - } - - pub fn gateway_pledge_key(&self) -> String { - format!("{}{}", self.storage_key, GATEWAY_SUFFIX) + pub fn storage_key(&self) -> u32 { + self.storage_key } pub fn owner_address(&self) -> Addr { @@ -94,11 +84,11 @@ impl Account { pub fn tokens_per_period(&self) -> Result { let amount = self.coin.amount.u128(); - if amount < NUM_VESTING_PERIODS as u128 { + if amount < self.num_vesting_periods() as u128 { Err(ContractError::ImprobableVestingAmount(amount)) } else { // Remainder tokens will be lumped into the last period. - Ok(amount / NUM_VESTING_PERIODS as u128) + Ok(amount / self.num_vesting_periods() as u128) } } @@ -124,9 +114,7 @@ impl Account { } pub fn load_balance(&self, storage: &dyn Storage) -> Result { - let key = self.balance_key(); - let balance = Item::new(&key); - Ok(balance.may_load(storage)?.unwrap_or_else(Uint128::zero)) + load_balance(self.storage_key(), storage) } pub fn save_balance( @@ -134,18 +122,14 @@ impl Account { amount: Uint128, storage: &mut dyn Storage, ) -> Result<(), ContractError> { - let key = self.balance_key(); - let balance = Item::new(&key); - Ok(balance.save(storage, &amount)?) + save_balance(self.storage_key(), amount, storage) } pub fn load_mixnode_pledge( &self, storage: &dyn Storage, ) -> Result, ContractError> { - let key = self.mixnode_pledge_key(); - let mixnode_pledge = Item::new(&key); - Ok(mixnode_pledge.may_load(storage)?) + load_bond_pledge(self.storage_key(), storage) } pub fn save_mixnode_pledge( @@ -153,25 +137,18 @@ impl Account { pledge: PledgeData, storage: &mut dyn Storage, ) -> Result<(), ContractError> { - let key = self.mixnode_pledge_key(); - let mixnode_pledge = Item::new(&key); - Ok(mixnode_pledge.save(storage, &pledge)?) + save_bond_pledge(self.storage_key(), &pledge, storage) } pub fn remove_mixnode_pledge(&self, storage: &mut dyn Storage) -> Result<(), ContractError> { - let key = self.mixnode_pledge_key(); - let mixnode_pledge: Item = Item::new(&key); - mixnode_pledge.remove(storage); - Ok(()) + remove_bond_pledge(self.storage_key(), storage) } pub fn load_gateway_pledge( &self, storage: &dyn Storage, ) -> Result, ContractError> { - let key = self.gateway_pledge_key(); - let gateway_pledge = Item::new(&key); - Ok(gateway_pledge.may_load(storage)?) + load_gateway_pledge(self.storage_key(), storage) } pub fn save_gateway_pledge( @@ -179,70 +156,75 @@ impl Account { pledge: PledgeData, storage: &mut dyn Storage, ) -> Result<(), ContractError> { - let key = self.gateway_pledge_key(); - let gateway_pledge = Item::new(&key); - Ok(gateway_pledge.save(storage, &pledge)?) + save_gateway_pledge(self.storage_key(), &pledge, storage) } pub fn remove_gateway_pledge(&self, storage: &mut dyn Storage) -> Result<(), ContractError> { - let key = self.gateway_pledge_key(); - let gateway_pledge: Item = Item::new(&key); - gateway_pledge.remove(storage); - Ok(()) - } - - // Returns block_time part of the delegation key - pub fn delegation_keys_for_mix(&self, mix: &str, storage: &dyn Storage) -> Vec { - let key = self.delegations_key(); - let delegations: Map<(&[u8], u64), Uint128> = Map::new(&key); - delegations - .prefix(mix.as_bytes()) - .keys(storage, None, None, Order::Ascending) - // Scan will blow up on first error - .scan((), |_, x| x.ok()) - .collect::>() + remove_gateway_pledge(self.storage_key(), storage) } pub fn any_delegation_for_mix(&self, mix: &str, storage: &dyn Storage) -> bool { - !self.delegation_keys_for_mix(mix, storage).is_empty() + DELEGATIONS + .prefix((self.storage_key(), mix.to_string())) + .range(storage, None, None, Order::Ascending) + .next() + .is_some() } - pub fn delegations_for_mix( + pub fn remove_delegations_for_mix( &self, - mix: IdentityKey, - storage: &dyn Storage, - ) -> Result, ContractError> { - let mix_bytes = mix.as_bytes(); - let keys = self.delegation_keys_for_mix(&mix, storage); - let delegations_key = self.delegations_key(); - let delegations: Map<(&[u8], u64), Uint128> = Map::new(&delegations_key); + mix: &str, + storage: &mut dyn Storage, + ) -> Result<(), ContractError> { + let limit = 50; + let mut start_after = None; + let mut block_heights = Vec::new(); + let mut prev_len = 0; + // TODO: Test this + loop { + block_heights.extend( + DELEGATIONS + .prefix((self.storage_key(), mix.to_string())) + .keys(storage, start_after, None, Order::Ascending) + .take(limit) + .filter_map(|key| key.ok()), + ); - let mut delegation_amounts = Vec::new(); - for key in keys { - delegation_amounts.push(delegations.load(storage, (mix_bytes, key))?) + if prev_len == block_heights.len() { + break; + } + + prev_len = block_heights.len(); + + start_after = block_heights.last().map(|last| Bound::exclusive_int(*last)); + if start_after.is_none() { + break; + } } - Ok(delegation_amounts) + for block_height in block_heights { + remove_delegation((self.storage_key(), mix.to_string(), block_height), storage)?; + } + Ok(()) } - #[allow(dead_code)] pub fn total_delegations_for_mix( &self, mix: IdentityKey, storage: &dyn Storage, ) -> Result { - Ok(self - .delegations_for_mix(mix, storage)? - .iter() - .fold(Uint128::zero(), |acc, x| acc + *x)) + Ok(DELEGATIONS + .prefix((self.storage_key(), mix)) + .range(storage, None, None, Order::Ascending) + .filter_map(|x| x.ok()) + .fold(Uint128::zero(), |acc, (_key, val)| acc + val)) } pub fn total_delegations(&self, storage: &dyn Storage) -> Result { - let delegations_key = self.delegations_key(); - let delegations: Map<(&[u8], u64), Uint128> = Map::new(&delegations_key); - Ok(delegations + Ok(DELEGATIONS + .sub_prefix(self.storage_key()) .range(storage, None, None, Order::Ascending) - .scan((), |_, x| x.ok()) - .fold(Uint128::zero(), |acc, (_, x)| acc + x)) + .filter_map(|x| x.ok()) + .fold(Uint128::zero(), |acc, (_key, val)| acc + val)) } } diff --git a/contracts/vesting/src/vesting/account/vesting_account.rs b/contracts/vesting/src/vesting/account/vesting_account.rs index 8ffc6f6852..5abe3ef8ff 100644 --- a/contracts/vesting/src/vesting/account/vesting_account.rs +++ b/contracts/vesting/src/vesting/account/vesting_account.rs @@ -1,10 +1,8 @@ -use crate::contract::NUM_VESTING_PERIODS; use crate::errors::ContractError; -use crate::storage::{delete_account, save_account}; +use crate::storage::{delete_account, save_account, DELEGATIONS}; use crate::traits::VestingAccount; use config::defaults::DENOM; use cosmwasm_std::{Addr, Coin, Env, Order, Storage, Timestamp, Uint128}; -use cw_storage_plus::Map; use super::Account; @@ -98,7 +96,7 @@ impl VestingAccount for Account { } fn get_end_time(&self) -> Timestamp { - self.periods[(NUM_VESTING_PERIODS - 1) as usize].end_time() + self.periods[(self.num_vesting_periods() - 1) as usize].end_time() } fn get_original_vesting(&self) -> Coin { @@ -115,21 +113,17 @@ impl VestingAccount for Account { let period = self.get_current_vesting_period(block_time); let max_vested = self.tokens_per_period()? * period as u128; let start_time = self.periods[period].start_time; - let delegations_key = self.delegations_key(); - let delegations: Map<(&[u8], u64), Uint128> = Map::new(&delegations_key); - let delegations_keys = delegations - .keys(storage, None, None, Order::Ascending) - .scan((), |_, x| x.ok()) - .filter(|(_mix, block_time)| *block_time < start_time) - .map(|(mix, block_time)| (mix, block_time)) - .collect::, u64)>>(); + let coin = DELEGATIONS + .sub_prefix(self.storage_key()) + .range(storage, None, None, Order::Ascending) + .filter_map(|x| x.ok()) + .filter(|((_mix, block_time), _amount)| *block_time < start_time) + .fold(Uint128::zero(), |acc, ((_mix, _block_time), amount)| { + acc + amount + }); - let mut amount = Uint128::zero(); - for (mix, block_time) in delegations_keys { - amount += delegations.load(storage, (&mix, block_time))? - } - amount = Uint128::new(amount.u128().min(max_vested)); + let amount = Uint128::new(coin.u128().min(max_vested)); Ok(Coin { amount, diff --git a/contracts/vesting/src/vesting/mod.rs b/contracts/vesting/src/vesting/mod.rs index b0a0f6e71a..7b6c260f7f 100644 --- a/contracts/vesting/src/vesting/mod.rs +++ b/contracts/vesting/src/vesting/mod.rs @@ -1,4 +1,3 @@ -use crate::contract::VESTING_PERIOD; use cosmwasm_std::{Timestamp, Uint128}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -6,14 +5,17 @@ use serde::{Deserialize, Serialize}; mod account; pub use account::*; +use vesting_contract_common::messages::VestingSpecification; + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct VestingPeriod { pub start_time: u64, + pub period_seconds: u64, } impl VestingPeriod { pub fn end_time(&self) -> Timestamp { - Timestamp::from_seconds(self.start_time + VESTING_PERIOD) + Timestamp::from_seconds(self.start_time + self.period_seconds as u64) } } @@ -23,11 +25,15 @@ pub struct PledgeData { block_time: Timestamp, } -pub fn populate_vesting_periods(start_time: u64, n: usize) -> Vec { - let mut periods = Vec::with_capacity(n as usize); - for i in 0..n { +pub fn populate_vesting_periods( + start_time: u64, + vesting_spec: VestingSpecification, +) -> Vec { + let mut periods = Vec::with_capacity(vesting_spec.num_periods() as usize); + for i in 0..vesting_spec.num_periods() { let period = VestingPeriod { - start_time: start_time + i as u64 * VESTING_PERIOD, + start_time: start_time + i as u64 * vesting_spec.period_seconds(), + period_seconds: vesting_spec.period_seconds(), }; periods.push(period); } @@ -36,8 +42,7 @@ pub fn populate_vesting_periods(start_time: u64, n: usize) -> Vec #[cfg(test)] mod tests { - use crate::contract::{execute, ADMIN_ADDRESS, NUM_VESTING_PERIODS, VESTING_PERIOD}; - use crate::messages::ExecuteMsg; + use crate::contract::execute; use crate::storage::load_account; use crate::support::tests::helpers::{init_contract, vesting_account_fixture}; use crate::traits::DelegatingAccount; @@ -47,6 +52,7 @@ mod tests { use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{coins, Addr, Coin, Timestamp, Uint128}; use mixnet_contract_common::{Gateway, MixNode}; + use vesting_contract_common::messages::ExecuteMsg; #[test] fn test_account_creation() { @@ -56,13 +62,14 @@ mod tests { let msg = ExecuteMsg::CreateAccount { owner_address: "owner".to_string(), staking_address: Some("staking".to_string()), - start_time: None, + vesting_spec: None, }; - let response = execute(deps.as_mut(), env.clone(), info, msg.clone()); + // Try creating an account when not admin + let response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()); assert!(response.is_err()); - let info = mock_info(ADMIN_ADDRESS, &coins(1_000_000_000_000, DENOM)); - let _response = execute(deps.as_mut(), env.clone(), info, msg.clone()); + let info = mock_info("admin", &coins(1_000_000_000_000, DENOM)); + let _response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()); let created_account = load_account(&Addr::unchecked("owner"), &deps.storage) .unwrap() .unwrap(); @@ -75,11 +82,13 @@ mod tests { created_account.load_balance(&deps.storage).unwrap(), Uint128::new(1_000_000_000_000) ); - // Test key collision avoidance + // Try create the same account again + let _response = execute(deps.as_mut(), env.clone(), info, msg.clone()); + assert!(response.is_err()); let account_again = vesting_account_fixture(&mut deps.storage, &env); - assert_eq!(created_account.balance_key(), "1ba".to_string()); - assert_ne!(created_account.balance_key(), account_again.balance_key()); + assert_eq!(created_account.storage_key(), 1); + assert_ne!(created_account.storage_key(), account_again.storage_key()); } #[test] @@ -165,17 +174,18 @@ mod tests { fn test_period_logic() { let mut deps = init_contract(); let env = mock_env(); + let num_vesting_periods = 8; + let vesting_period = 3 * 30 * 86400; let account = vesting_account_fixture(&mut deps.storage, &env); - assert_eq!(account.periods().len(), NUM_VESTING_PERIODS as usize); - assert_eq!(account.periods().len(), 8); + assert_eq!(account.periods().len(), num_vesting_periods as usize); let current_period = account.get_current_vesting_period(Timestamp::from_seconds(0)); assert_eq!(0, current_period); let block_time = - Timestamp::from_seconds(account.start_time().seconds() + VESTING_PERIOD + 1); + Timestamp::from_seconds(account.start_time().seconds() + vesting_period + 1); let current_period = account.get_current_vesting_period(block_time); assert_eq!(current_period, 1); let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap(); @@ -183,19 +193,19 @@ mod tests { assert_eq!( vested_coins.amount, Uint128::new( - account.get_original_vesting().amount.u128() / NUM_VESTING_PERIODS as u128 + account.get_original_vesting().amount.u128() / num_vesting_periods as u128 ) ); assert_eq!( vesting_coins.amount, Uint128::new( account.get_original_vesting().amount.u128() - - account.get_original_vesting().amount.u128() / NUM_VESTING_PERIODS as u128 + - account.get_original_vesting().amount.u128() / num_vesting_periods as u128 ) ); let block_time = - Timestamp::from_seconds(account.start_time().seconds() + 5 * VESTING_PERIOD + 1); + Timestamp::from_seconds(account.start_time().seconds() + 5 * vesting_period + 1); let current_period = account.get_current_vesting_period(block_time); assert_eq!(current_period, 5); let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap(); @@ -203,7 +213,7 @@ mod tests { assert_eq!( vested_coins.amount, Uint128::new( - 5 * account.get_original_vesting().amount.u128() / NUM_VESTING_PERIODS as u128 + 5 * account.get_original_vesting().amount.u128() / num_vesting_periods as u128 ) ); assert_eq!( @@ -211,7 +221,7 @@ mod tests { Uint128::new( account.get_original_vesting().amount.u128() - 5 * account.get_original_vesting().amount.u128() - / NUM_VESTING_PERIODS as u128 + / num_vesting_periods as u128 ) ); } diff --git a/docker-compose.yml b/docker-compose.yml index 44f0288627..c171939da4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ version: '3.7' x-bech32-prefix: &BECH32_PREFIX - punk + nymt x-wasmd-version: &WASMD_VERSION v0.21.0 x-wasmd-commit-hash: &WASMD_COMMIT_HASH diff --git a/explorer-api/Cargo.toml b/explorer-api/Cargo.toml index 24a9357530..f0084b203d 100644 --- a/explorer-api/Cargo.toml +++ b/explorer-api/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "explorer-api" version = "0.1.0" -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/explorer-api/src/mix_node/cache.rs b/explorer-api/src/cache/mod.rs similarity index 65% rename from explorer-api/src/mix_node/cache.rs rename to explorer-api/src/cache/mod.rs index 6b66ae2e2f..b00b25a1f2 100644 --- a/explorer-api/src/mix_node/cache.rs +++ b/explorer-api/src/cache/mod.rs @@ -13,19 +13,27 @@ impl Cache { } } - pub(crate) fn get(&self, identity_key: &str) -> Option - where - T: Clone, - { + pub(crate) fn len(&self) -> usize { + self.inner.len() + } + + pub(crate) fn get_all(&self) -> Vec { self.inner - .get(identity_key) + .values() + .map(|cache_item| cache_item.value.clone()) + .collect() + } + + pub(crate) fn get(&self, key: &str) -> Option { + self.inner + .get(key) .filter(|cache_item| cache_item.valid_until > SystemTime::now()) .map(|cache_item| cache_item.value.clone()) } - pub(crate) fn set(&mut self, identity_key: &str, value: T) { + pub(crate) fn set(&mut self, key: &str, value: T) { self.inner.insert( - identity_key.to_string(), + key.to_string(), CacheItem { valid_until: SystemTime::now() + Duration::from_secs(60 * 30), value, diff --git a/explorer-api/src/client.rs b/explorer-api/src/client.rs new file mode 100644 index 0000000000..9eb945c2a1 --- /dev/null +++ b/explorer-api/src/client.rs @@ -0,0 +1,21 @@ +use network_defaults::{ + default_api_endpoints, default_network, default_nymd_endpoints, DEFAULT_MIXNET_CONTRACT_ADDRESS, +}; +use validator_client::nymd::QueryNymdClient; + +pub(crate) fn new_nymd_client() -> validator_client::Client { + let network = default_network(); + let mixnet_contract = DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string(); + let nymd_url = default_nymd_endpoints()[0].clone(); + let api_url = default_api_endpoints()[0].clone(); + + let client_config = validator_client::Config::new( + network, + nymd_url, + api_url, + Some(mixnet_contract.parse().unwrap()), + None, + ); + + validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!") +} diff --git a/explorer-api/src/country_statistics/country_nodes_distribution.rs b/explorer-api/src/country_statistics/country_nodes_distribution.rs index 8b5bf85226..f08e1cc330 100644 --- a/explorer-api/src/country_statistics/country_nodes_distribution.rs +++ b/explorer-api/src/country_statistics/country_nodes_distribution.rs @@ -5,13 +5,13 @@ use tokio::sync::RwLock; pub type CountryNodesDistribution = HashMap; #[derive(Clone)] -pub struct ConcurrentCountryNodesDistribution { +pub struct ThreadsafeCountryNodesDistribution { inner: Arc>, } -impl ConcurrentCountryNodesDistribution { +impl ThreadsafeCountryNodesDistribution { pub(crate) fn new() -> Self { - ConcurrentCountryNodesDistribution { + ThreadsafeCountryNodesDistribution { inner: Arc::new(RwLock::new(CountryNodesDistribution::new())), } } @@ -19,7 +19,7 @@ impl ConcurrentCountryNodesDistribution { pub(crate) fn new_from_distribution( country_node_distribution: CountryNodesDistribution, ) -> Self { - ConcurrentCountryNodesDistribution { + ThreadsafeCountryNodesDistribution { inner: Arc::new(RwLock::new(country_node_distribution)), } } diff --git a/explorer-api/src/country_statistics/distribution.rs b/explorer-api/src/country_statistics/distribution.rs index dba66ecab6..ebbc1eca9b 100644 --- a/explorer-api/src/country_statistics/distribution.rs +++ b/explorer-api/src/country_statistics/distribution.rs @@ -30,7 +30,7 @@ impl CountryStatisticsDistributionTask { /// Retrieves the current list of mixnodes from the validators and calculates how many nodes are in each country async fn calculate_nodes_per_country(&mut self) { - let cache = self.state.inner.mix_nodes.get_location_cache().await; + let cache = self.state.inner.mixnodes.get_locations().await; let three_letter_iso_country_codes: Vec = cache .values() diff --git a/explorer-api/src/country_statistics/geolocate.rs b/explorer-api/src/country_statistics/geolocate.rs index 55cd428798..170d4f5cc1 100644 --- a/explorer-api/src/country_statistics/geolocate.rs +++ b/explorer-api/src/country_statistics/geolocate.rs @@ -41,7 +41,7 @@ impl GeoLocateTask { let mixnode_bonds = self .state .inner - .mix_nodes + .mixnodes .get_mixnodes() .await .unwrap_or_default(); @@ -50,7 +50,7 @@ impl GeoLocateTask { if self .state .inner - .mix_nodes + .mixnodes .is_location_valid(&cache_item.mix_node.identity_key) .await { @@ -79,7 +79,7 @@ impl GeoLocateTask { self.state .inner - .mix_nodes + .mixnodes .set_location(&cache_item.mix_node.identity_key, Some(location)) .await; @@ -98,7 +98,7 @@ impl GeoLocateTask { ); self.state .inner - .mix_nodes + .mixnodes .set_location(&cache_item.mix_node.identity_key, None) .await; }, diff --git a/explorer-api/src/gateways/http.rs b/explorer-api/src/gateways/http.rs new file mode 100644 index 0000000000..084507eed5 --- /dev/null +++ b/explorer-api/src/gateways/http.rs @@ -0,0 +1,24 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use rocket::response::status::NotFound; +use rocket::serde::json::Json; +use rocket::{Route, State}; +use rocket_okapi::okapi::openapi3::OpenApi; +use rocket_okapi::openapi_get_routes_spec; +use rocket_okapi::settings::OpenApiSettings; + +use crate::state::ExplorerApiStateContext; +use mixnet_contract_common::GatewayBond; + +pub fn gateways_make_default_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { + openapi_get_routes_spec![settings: list] +} + +#[openapi(tag = "gateways")] +#[get("/")] +pub(crate) async fn list( + state: &State, +) -> Result>, NotFound> { + Ok(Json(state.inner.gateways.get_gateways().await)) +} diff --git a/explorer-api/src/gateways/mod.rs b/explorer-api/src/gateways/mod.rs new file mode 100644 index 0000000000..5df938f83c --- /dev/null +++ b/explorer-api/src/gateways/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod http; +pub(crate) mod models; diff --git a/explorer-api/src/gateways/models.rs b/explorer-api/src/gateways/models.rs new file mode 100644 index 0000000000..690aa7cb7e --- /dev/null +++ b/explorer-api/src/gateways/models.rs @@ -0,0 +1,55 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::sync::Arc; + +use serde::Serialize; +use tokio::sync::RwLock; + +use mixnet_contract_common::GatewayBond; + +use crate::cache::Cache; + +pub(crate) struct GatewayCache { + pub(crate) gateways: Cache, +} + +#[derive(Clone, Debug, Serialize, JsonSchema)] +pub(crate) struct GatewaySummary { + pub count: usize, +} + +#[derive(Clone)] +pub(crate) struct ThreadsafeGatewayCache { + inner: Arc>, +} + +impl ThreadsafeGatewayCache { + pub(crate) fn new() -> Self { + ThreadsafeGatewayCache { + inner: Arc::new(RwLock::new(GatewayCache { + gateways: Cache::new(), + })), + } + } + + pub(crate) async fn get_gateways(&self) -> Vec { + self.inner.read().await.gateways.get_all() + } + + pub(crate) async fn get_gateway_summary(&self) -> GatewaySummary { + GatewaySummary { + count: self.inner.read().await.gateways.len(), + } + } + + pub(crate) async fn update_cache(&self, gateways: Vec) { + let mut guard = self.inner.write().await; + + for gateway in gateways { + guard + .gateways + .set(gateway.clone().gateway.identity_key.as_str(), gateway) + } + } +} diff --git a/explorer-api/src/http/mod.rs b/explorer-api/src/http/mod.rs index 0cb78e6b4e..683f67b672 100644 --- a/explorer-api/src/http/mod.rs +++ b/explorer-api/src/http/mod.rs @@ -1,15 +1,19 @@ use log::info; +use okapi::openapi3::OpenApi; use rocket::http::Method; use rocket::{Build, Request, Rocket}; use rocket_cors::{AllowedHeaders, AllowedOrigins}; use rocket_okapi::swagger_ui::make_swagger_ui; use crate::country_statistics::http::country_statistics_make_default_routes; +use crate::gateways::http::gateways_make_default_routes; use crate::http::swagger::get_docs; use crate::mix_node::http::mix_node_make_default_routes; use crate::mix_nodes::http::mix_nodes_make_default_routes; +use crate::overview::http::overview_make_default_routes; use crate::ping::http::ping_make_default_routes; use crate::state::ExplorerApiStateContext; +use crate::validators::http::validators_make_default_routes; mod swagger; @@ -38,14 +42,20 @@ fn configure_rocket(state: ExplorerApiStateContext) -> Rocket { let config = rocket::config::Config::release_default(); let mut building_rocket = rocket::build().configure(config); + let custom_route_spec = (vec![], custom_openapi_spec()); + mount_endpoints_and_merged_docs! { building_rocket, "/v1".to_owned(), openapi_settings, - "/ping" => ping_make_default_routes(&openapi_settings), + "/" => custom_route_spec, "/countries" => country_statistics_make_default_routes(&openapi_settings), + "/gateways" => gateways_make_default_routes(&openapi_settings), "/mix-node" => mix_node_make_default_routes(&openapi_settings), "/mix-nodes" => mix_nodes_make_default_routes(&openapi_settings), + "/overview" => overview_make_default_routes(&openapi_settings), + "/ping" => ping_make_default_routes(&openapi_settings), + "/validators" => validators_make_default_routes(&openapi_settings), }; building_rocket @@ -56,6 +66,35 @@ fn configure_rocket(state: ExplorerApiStateContext) -> Rocket { } #[catch(404)] -pub(crate) fn not_found(req: &Request) -> String { +pub(crate) fn not_found(req: &Request<'_>) -> String { format!("I couldn't find '{}'. Try something else?", req.uri()) } + +fn custom_openapi_spec() -> OpenApi { + use rocket_okapi::okapi::openapi3::*; + OpenApi { + openapi: OpenApi::default_version(), + info: Info { + title: "Network Explorer API".to_owned(), + description: None, + terms_of_service: None, + contact: None, + license: None, + version: env!("CARGO_PKG_VERSION").to_owned(), + ..Default::default() + }, + servers: get_servers(), + ..Default::default() + } +} + +fn get_servers() -> Vec { + if std::env::var_os("CARGO").is_some() { + return vec![]; + } + return vec![rocket_okapi::okapi::openapi3::Server { + url: std::env::var("OPEN_API_BASE").unwrap_or_else(|_| "/api/v1/".to_owned()), + description: Some("API".to_owned()), + ..Default::default() + }]; +} diff --git a/explorer-api/src/main.rs b/explorer-api/src/main.rs index 999e9f3fc7..8ec90aaf4b 100644 --- a/explorer-api/src/main.rs +++ b/explorer-api/src/main.rs @@ -5,12 +5,18 @@ extern crate rocket_okapi; use log::info; +pub(crate) mod cache; +mod client; mod country_statistics; +mod gateways; mod http; mod mix_node; -mod mix_nodes; +pub(crate) mod mix_nodes; +mod overview; mod ping; mod state; +mod tasks; +mod validators; const GEO_IP_SERVICE: &str = "https://api.freegeoip.app/json"; const COUNTRY_DATA_REFRESH_INTERVAL: u64 = 60 * 15; // every 15 minutes @@ -40,7 +46,7 @@ impl ExplorerApi { info!("Using validator API - {}", validator_api_url); // spawn concurrent tasks - mix_nodes::tasks::MixNodesTasks::new(self.state.clone(), validator_api_url).start(); + crate::tasks::ExplorerApiTasks::new(self.state.clone()).start(); country_statistics::distribution::CountryStatisticsDistributionTask::new( self.state.clone(), ) diff --git a/explorer-api/src/mix_node/http.rs b/explorer-api/src/mix_node/http.rs index c3888f393e..d948327f2a 100644 --- a/explorer-api/src/mix_node/http.rs +++ b/explorer-api/src/mix_node/http.rs @@ -1,26 +1,47 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::mix_node::models::{NodeDescription, NodeStats}; -use crate::mix_nodes::delegations::{get_mixnode_delegations, get_single_mixnode_delegations}; -use crate::state::ExplorerApiStateContext; -use mixnet_contract_common::Delegation; use reqwest::Error as ReqwestError; +use rocket::response::status::NotFound; use rocket::serde::json::Json; use rocket::{Route, State}; use rocket_okapi::okapi::openapi3::OpenApi; use rocket_okapi::openapi_get_routes_spec; use rocket_okapi::settings::OpenApiSettings; +use mixnet_contract_common::Delegation; + +use crate::mix_node::models::{NodeDescription, NodeStats, PrettyDetailedMixNodeBond}; +use crate::mix_nodes::delegations::{get_mixnode_delegations, get_single_mixnode_delegations}; +use crate::state::ExplorerApiStateContext; + pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { openapi_get_routes_spec![ settings: get_delegations, + get_by_id, get_all_delegations, get_description, get_stats, ] } +#[openapi(tag = "mix_nodes")] +#[get("/")] +pub(crate) async fn get_by_id( + pubkey: &str, + state: &State, +) -> Result, NotFound> { + match state + .inner + .mixnodes + .get_detailed_mixnode_by_id(pubkey) + .await + { + Some(mixnode) => Ok(Json(mixnode)), + None => Err(NotFound("Mixnode not found".to_string())), + } +} + #[openapi(tag = "mix_node")] #[get("//delegations")] pub(crate) async fn get_delegations(pubkey: &str) -> Json> { @@ -39,13 +60,7 @@ pub(crate) async fn get_description( pubkey: &str, state: &State, ) -> Option> { - match state - .inner - .mix_node_cache - .clone() - .get_description(pubkey) - .await - { + match state.inner.mixnode.clone().get_description(pubkey).await { Some(cache_value) => { trace!("Returning cached value for {}", pubkey); Some(Json(cache_value)) @@ -64,7 +79,7 @@ pub(crate) async fn get_description( // cache the response and return as the HTTP response state .inner - .mix_node_cache + .mixnode .set_description(pubkey, response.clone()) .await; Some(Json(response)) @@ -90,7 +105,7 @@ pub(crate) async fn get_stats( pubkey: &str, state: &State, ) -> Option> { - match state.inner.mix_node_cache.get_node_stats(pubkey).await { + match state.inner.mixnode.get_node_stats(pubkey).await { Some(cache_value) => { trace!("Returning cached value for {}", pubkey); Some(Json(cache_value)) @@ -106,7 +121,7 @@ pub(crate) async fn get_stats( // cache the response and return as the HTTP response state .inner - .mix_node_cache + .mixnode .set_node_stats(pubkey, response.clone()) .await; Some(Json(response)) diff --git a/explorer-api/src/mix_node/mod.rs b/explorer-api/src/mix_node/mod.rs index b57132ec2c..5df938f83c 100644 --- a/explorer-api/src/mix_node/mod.rs +++ b/explorer-api/src/mix_node/mod.rs @@ -1,3 +1,2 @@ -mod cache; pub(crate) mod http; pub(crate) mod models; diff --git a/explorer-api/src/mix_node/models.rs b/explorer-api/src/mix_node/models.rs index 835bfe3991..c99f1ac77e 100644 --- a/explorer-api/src/mix_node/models.rs +++ b/explorer-api/src/mix_node/models.rs @@ -1,7 +1,7 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::mix_node::cache::Cache; +use crate::cache::Cache; use crate::mix_nodes::location::Location; use mixnet_contract_common::{Addr, Coin, Layer, MixNode}; use serde::Deserialize; @@ -10,7 +10,7 @@ use std::sync::Arc; use std::time::SystemTime; use tokio::sync::RwLock; -#[derive(Clone, Debug, Serialize, JsonSchema)] +#[derive(Clone, Debug, Serialize, JsonSchema, PartialEq)] #[serde(rename_all = "snake_case")] pub(crate) enum MixnodeStatus { Active, // in both the active set and the rewarded set diff --git a/explorer-api/src/mix_nodes/delegations.rs b/explorer-api/src/mix_nodes/delegations.rs index a6f0ee1c05..00fd75ccab 100644 --- a/explorer-api/src/mix_nodes/delegations.rs +++ b/explorer-api/src/mix_nodes/delegations.rs @@ -4,7 +4,7 @@ use mixnet_contract_common::Delegation; pub(crate) async fn get_single_mixnode_delegations(pubkey: &str) -> Vec { - let client = super::utils::new_nymd_client(); + let client = crate::client::new_nymd_client(); let delegates = match client .get_all_nymd_single_mixnode_delegations(pubkey.to_string()) .await @@ -19,7 +19,7 @@ pub(crate) async fn get_single_mixnode_delegations(pubkey: &str) -> Vec Vec { - let client = super::utils::new_nymd_client(); + let client = crate::client::new_nymd_client(); let delegates = match client.get_all_network_delegations().await { Ok(result) => result, Err(e) => { diff --git a/explorer-api/src/mix_nodes/http.rs b/explorer-api/src/mix_nodes/http.rs index a2b8b0eaba..6e57e568c7 100644 --- a/explorer-api/src/mix_nodes/http.rs +++ b/explorer-api/src/mix_nodes/http.rs @@ -1,4 +1,5 @@ -use crate::mix_node::models::PrettyDetailedMixNodeBond; +use crate::mix_node::models::{MixnodeStatus, PrettyDetailedMixNodeBond}; +use crate::mix_nodes::models::{MixNodeActiveSetSummary, MixNodeSummary}; use crate::state::ExplorerApiStateContext; use rocket::serde::json::Json; use rocket::{Route, State}; @@ -7,7 +8,13 @@ use rocket_okapi::openapi_get_routes_spec; use rocket_okapi::settings::OpenApiSettings; pub fn mix_nodes_make_default_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { - openapi_get_routes_spec![settings: list] + openapi_get_routes_spec![ + settings: list, + list_active_set, + list_inactive_set, + list_standby_set, + summary + ] } #[openapi(tag = "mix_nodes")] @@ -15,5 +22,69 @@ pub fn mix_nodes_make_default_routes(settings: &OpenApiSettings) -> (Vec, pub(crate) async fn list( state: &State, ) -> Json> { - Json(state.inner.mix_nodes.get_detailed_mixnodes().await) + Json(state.inner.mixnodes.get_detailed_mixnodes().await) +} + +#[openapi(tag = "mix_nodes")] +#[get("/active-set/active")] +pub(crate) async fn list_active_set( + state: &State, +) -> Json> { + Json(get_mixnodes_by_status( + state.inner.mixnodes.get_detailed_mixnodes().await, + MixnodeStatus::Active, + )) +} + +#[openapi(tag = "mix_nodes")] +#[get("/active-set/inactive")] +pub(crate) async fn list_inactive_set( + state: &State, +) -> Json> { + Json(get_mixnodes_by_status( + state.inner.mixnodes.get_detailed_mixnodes().await, + MixnodeStatus::Inactive, + )) +} + +#[openapi(tag = "mix_nodes")] +#[get("/active-set/standby")] +pub(crate) async fn list_standby_set( + state: &State, +) -> Json> { + Json(get_mixnodes_by_status( + state.inner.mixnodes.get_detailed_mixnodes().await, + MixnodeStatus::Standby, + )) +} + +#[openapi(tag = "mix_nodes")] +#[get("/summary")] +pub(crate) async fn summary(state: &State) -> Json { + Json(get_mixnode_summary(state).await) +} + +pub(crate) async fn get_mixnode_summary(state: &State) -> MixNodeSummary { + let mixnodes = state.inner.mixnodes.get_detailed_mixnodes().await; + let active = get_mixnodes_by_status(mixnodes.clone(), MixnodeStatus::Active).len(); + let standby = get_mixnodes_by_status(mixnodes.clone(), MixnodeStatus::Standby).len(); + let inactive = get_mixnodes_by_status(mixnodes.clone(), MixnodeStatus::Inactive).len(); + MixNodeSummary { + count: mixnodes.len(), + activeset: MixNodeActiveSetSummary { + active, + standby, + inactive, + }, + } +} + +fn get_mixnodes_by_status( + all_mixnodes: Vec, + status: MixnodeStatus, +) -> Vec { + all_mixnodes + .into_iter() + .filter(|mixnode| mixnode.status == status) + .collect() } diff --git a/explorer-api/src/mix_nodes/mod.rs b/explorer-api/src/mix_nodes/mod.rs index dacff38e32..cd25a4ca43 100644 --- a/explorer-api/src/mix_nodes/mod.rs +++ b/explorer-api/src/mix_nodes/mod.rs @@ -7,8 +7,7 @@ pub(crate) mod delegations; pub(crate) mod http; pub(crate) mod location; pub(crate) mod models; -pub(crate) mod tasks; pub(crate) mod utils; -pub(crate) const MIXNODES_CACHE_REFRESH_RATE: Duration = Duration::from_secs(30); -pub(crate) const MIXNODES_CACHE_ENTRY_TTL: Duration = Duration::from_secs(60); +pub(crate) const CACHE_REFRESH_RATE: Duration = Duration::from_secs(30); +pub(crate) const CACHE_ENTRY_TTL: Duration = Duration::from_secs(60); diff --git a/explorer-api/src/mix_nodes/models.rs b/explorer-api/src/mix_nodes/models.rs index 7a07131fc1..bd2ebe4fe8 100644 --- a/explorer-api/src/mix_nodes/models.rs +++ b/explorer-api/src/mix_nodes/models.rs @@ -1,15 +1,32 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::mix_node::models::{MixnodeStatus, PrettyDetailedMixNodeBond}; -use crate::mix_nodes::location::{Location, LocationCache, LocationCacheItem}; -use crate::mix_nodes::MIXNODES_CACHE_ENTRY_TTL; -use mixnet_contract_common::MixNodeBond; use std::collections::{HashMap, HashSet}; use std::sync::Arc; use std::time::{Duration, SystemTime}; + +use serde::Serialize; use tokio::sync::RwLock; +use mixnet_contract_common::MixNodeBond; + +use crate::mix_node::models::{MixnodeStatus, PrettyDetailedMixNodeBond}; +use crate::mix_nodes::location::{Location, LocationCache, LocationCacheItem}; +use crate::mix_nodes::CACHE_ENTRY_TTL; + +#[derive(Clone, Debug, Serialize, JsonSchema)] +pub(crate) struct MixNodeActiveSetSummary { + pub active: usize, + pub standby: usize, + pub inactive: usize, +} + +#[derive(Clone, Debug, Serialize, JsonSchema)] +pub(crate) struct MixNodeSummary { + pub count: usize, + pub activeset: MixNodeActiveSetSummary, +} + #[derive(Clone, Debug)] pub(crate) struct MixNodesResult { pub(crate) valid_until: SystemTime, @@ -60,28 +77,28 @@ impl MixNodesResult { } #[derive(Clone)] -pub(crate) struct ThreadsafeMixNodesResult { - mixnode_results: Arc>, - location_cache: Arc>, +pub(crate) struct ThreadsafeMixNodesCache { + mixnodes: Arc>, + locations: Arc>, } -impl ThreadsafeMixNodesResult { +impl ThreadsafeMixNodesCache { pub(crate) fn new() -> Self { - ThreadsafeMixNodesResult { - mixnode_results: Arc::new(RwLock::new(MixNodesResult::new())), - location_cache: Arc::new(RwLock::new(LocationCache::new())), + ThreadsafeMixNodesCache { + mixnodes: Arc::new(RwLock::new(MixNodesResult::new())), + locations: Arc::new(RwLock::new(LocationCache::new())), } } - pub(crate) fn new_with_location_cache(location_cache: LocationCache) -> Self { - ThreadsafeMixNodesResult { - mixnode_results: Arc::new(RwLock::new(MixNodesResult::new())), - location_cache: Arc::new(RwLock::new(location_cache)), + pub(crate) fn new_with_location_cache(locations: LocationCache) -> Self { + ThreadsafeMixNodesCache { + mixnodes: Arc::new(RwLock::new(MixNodesResult::new())), + locations: Arc::new(RwLock::new(locations)), } } pub(crate) async fn is_location_valid(&self, identity_key: &str) -> bool { - self.location_cache + self.locations .read() .await .get(identity_key) @@ -89,29 +106,53 @@ impl ThreadsafeMixNodesResult { .unwrap_or(false) } - pub(crate) async fn get_location_cache(&self) -> LocationCache { - self.location_cache.read().await.clone() + pub(crate) async fn get_locations(&self) -> LocationCache { + self.locations.read().await.clone() } pub(crate) async fn set_location(&self, identity_key: &str, location: Option) { // cache the location for this mix node so that it can be used when the mix node list is refreshed - self.location_cache.write().await.insert( + self.locations.write().await.insert( identity_key.to_string(), LocationCacheItem::new_from_location(location), ); } pub(crate) async fn get_mixnode(&self, pubkey: &str) -> Option { - self.mixnode_results.read().await.get_mixnode(pubkey) + self.mixnodes.read().await.get_mixnode(pubkey) } pub(crate) async fn get_mixnodes(&self) -> Option> { - self.mixnode_results.read().await.get_mixnodes() + self.mixnodes.read().await.get_mixnodes() + } + + pub(crate) async fn get_detailed_mixnode_by_id( + &self, + identity_key: &str, + ) -> Option { + let mixnodes_guard = self.mixnodes.read().await; + let location_guard = self.locations.read().await; + + let bond = mixnodes_guard.get_mixnode(identity_key); + let location = location_guard.get(identity_key); + + match bond { + Some(bond) => Some(PrettyDetailedMixNodeBond { + location: location.and_then(|l| l.location.clone()), + status: mixnodes_guard.determine_node_status(&bond.mix_node.identity_key), + pledge_amount: bond.pledge_amount, + total_delegation: bond.total_delegation, + owner: bond.owner, + layer: bond.layer, + mix_node: bond.mix_node, + }), + None => None, + } } pub(crate) async fn get_detailed_mixnodes(&self) -> Vec { - let mixnodes_guard = self.mixnode_results.read().await; - let location_guard = self.location_cache.read().await; + let mixnodes_guard = self.mixnodes.read().await; + let location_guard = self.locations.read().await; mixnodes_guard .all_mixnodes @@ -138,13 +179,13 @@ impl ThreadsafeMixNodesResult { rewarded_nodes: HashSet, active_nodes: HashSet, ) { - let mut guard = self.mixnode_results.write().await; + let mut guard = self.mixnodes.write().await; guard.all_mixnodes = all_bonds .into_iter() .map(|bond| (bond.mix_node.identity_key.to_string(), bond)) .collect(); guard.rewarded_mixnodes = rewarded_nodes; guard.active_mixnodes = active_nodes; - guard.valid_until = SystemTime::now() + MIXNODES_CACHE_ENTRY_TTL; + guard.valid_until = SystemTime::now() + CACHE_ENTRY_TTL; } } diff --git a/explorer-api/src/mix_nodes/tasks.rs b/explorer-api/src/mix_nodes/tasks.rs deleted file mode 100644 index b6257c1660..0000000000 --- a/explorer-api/src/mix_nodes/tasks.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::mix_nodes::MIXNODES_CACHE_REFRESH_RATE; -use crate::state::ExplorerApiStateContext; -use mixnet_contract_common::MixNodeBond; -use reqwest::Url; -use std::future::Future; -use validator_client::ValidatorClientError; - -pub(crate) struct MixNodesTasks { - state: ExplorerApiStateContext, - validator_api_client: validator_client::ApiClient, -} - -impl MixNodesTasks { - pub(crate) fn new(state: ExplorerApiStateContext, validator_api_endpoint: Url) -> Self { - MixNodesTasks { - state, - validator_api_client: validator_client::ApiClient::new(validator_api_endpoint), - } - } - - // a helper to remove duplicate code when grabbing active/rewarded/all mixnodes - async fn retrieve_mixnodes<'a, F, Fut>(&'a self, f: F) -> Vec - where - F: FnOnce(&'a validator_client::ApiClient) -> Fut, - Fut: Future, ValidatorClientError>>, - { - let bonds = match f(&self.validator_api_client).await { - Ok(result) => result, - Err(e) => { - error!("Unable to retrieve mixnode bonds: {:?}", e); - vec![] - } - }; - - info!("Fetched {} mixnode bonds", bonds.len()); - bonds - } - - async fn retrieve_all_mixnodes(&self) -> Vec { - info!("About to retrieve all mixnode bonds..."); - self.retrieve_mixnodes(validator_client::ApiClient::get_cached_mixnodes) - .await - } - - async fn retrieve_rewarded_mixnodes(&self) -> Vec { - info!("About to retrieve rewarded mixnode bonds..."); - self.retrieve_mixnodes(validator_client::ApiClient::get_cached_rewarded_mixnodes) - .await - } - - async fn retrieve_active_mixnodes(&self) -> Vec { - info!("About to retrieve active mixnode bonds..."); - self.retrieve_mixnodes(validator_client::ApiClient::get_cached_active_mixnodes) - .await - } - - async fn update_mixnode_cache(&self) { - let all_bonds = self.retrieve_all_mixnodes().await; - let rewarded_nodes = self - .retrieve_rewarded_mixnodes() - .await - .into_iter() - .map(|bond| bond.mix_node.identity_key) - .collect(); - let active_nodes = self - .retrieve_active_mixnodes() - .await - .into_iter() - .map(|bond| bond.mix_node.identity_key) - .collect(); - self.state - .inner - .mix_nodes - .update_cache(all_bonds, rewarded_nodes, active_nodes) - .await; - } - - pub(crate) fn start(self) { - info!("Spawning mix nodes task runner..."); - tokio::spawn(async move { - let mut interval_timer = tokio::time::interval(MIXNODES_CACHE_REFRESH_RATE); - loop { - // wait for the next interval tick - interval_timer.tick().await; - - info!("Updating mix node cache..."); - self.update_mixnode_cache().await; - info!("Done"); - } - }); - } -} diff --git a/explorer-api/src/mix_nodes/utils.rs b/explorer-api/src/mix_nodes/utils.rs index 542bfe8660..a151ec318a 100644 --- a/explorer-api/src/mix_nodes/utils.rs +++ b/explorer-api/src/mix_nodes/utils.rs @@ -3,10 +3,6 @@ use crate::mix_nodes::location::GeoLocation; use isocountry::CountryCode; -use network_defaults::{ - default_api_endpoints, default_nymd_endpoints, DEFAULT_MIXNET_CONTRACT_ADDRESS, -}; -use validator_client::nymd::QueryNymdClient; pub(crate) fn map_2_letter_to_3_letter_country_code(geo: &GeoLocation) -> String { match CountryCode::for_alpha2(&geo.country_code) { @@ -20,18 +16,3 @@ pub(crate) fn map_2_letter_to_3_letter_country_code(geo: &GeoLocation) -> String } } } - -pub(crate) fn new_nymd_client() -> validator_client::Client { - let mixnet_contract = DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string(); - let nymd_url = default_nymd_endpoints()[0].clone(); - let api_url = default_api_endpoints()[0].clone(); - - let client_config = validator_client::Config::new( - nymd_url, - api_url, - Some(mixnet_contract.parse().unwrap()), - None, - ); - - validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!") -} diff --git a/explorer-api/src/node_numbers/http.rs b/explorer-api/src/node_numbers/http.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/explorer-api/src/node_numbers/network_size_recorder.rs b/explorer-api/src/node_numbers/network_size_recorder.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/explorer-api/src/overview/http.rs b/explorer-api/src/overview/http.rs new file mode 100644 index 0000000000..a5cfb127c5 --- /dev/null +++ b/explorer-api/src/overview/http.rs @@ -0,0 +1,23 @@ +use rocket::serde::json::Json; +use rocket::{Route, State}; +use rocket_okapi::okapi::openapi3::OpenApi; +use rocket_okapi::openapi_get_routes_spec; +use rocket_okapi::settings::OpenApiSettings; + +use crate::mix_nodes::http::get_mixnode_summary; +use crate::overview::models::OverviewSummary; +use crate::state::ExplorerApiStateContext; + +pub fn overview_make_default_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { + openapi_get_routes_spec![settings: summary] +} + +#[openapi(tag = "overview")] +#[get("/summary")] +pub(crate) async fn summary(state: &State) -> Json { + Json(OverviewSummary { + mixnodes: get_mixnode_summary(state).await, + validators: state.inner.validators.get_validator_summary().await, + gateways: state.inner.gateways.get_gateway_summary().await, + }) +} diff --git a/explorer-api/src/overview/mod.rs b/explorer-api/src/overview/mod.rs new file mode 100644 index 0000000000..058ccb1d8b --- /dev/null +++ b/explorer-api/src/overview/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub(crate) mod http; +mod models; diff --git a/explorer-api/src/overview/models.rs b/explorer-api/src/overview/models.rs new file mode 100644 index 0000000000..b396e2e3fb --- /dev/null +++ b/explorer-api/src/overview/models.rs @@ -0,0 +1,15 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use serde::Serialize; + +use crate::gateways::models::GatewaySummary; +use crate::mix_nodes::models::MixNodeSummary; +use crate::validators::models::ValidatorSummary; + +#[derive(Clone, Debug, Serialize, JsonSchema)] +pub(crate) struct OverviewSummary { + pub mixnodes: MixNodeSummary, + pub gateways: GatewaySummary, + pub validators: ValidatorSummary, +} diff --git a/explorer-api/src/ping/http.rs b/explorer-api/src/ping/http.rs index 67a2730bb7..379084436e 100644 --- a/explorer-api/src/ping/http.rs +++ b/explorer-api/src/ping/http.rs @@ -25,7 +25,7 @@ pub(crate) async fn index( pubkey: &str, state: &State, ) -> Option> { - match state.inner.ping_cache.clone().get(pubkey).await { + match state.inner.ping.clone().get(pubkey).await { Some(cache_value) => { trace!("Returning cached value for {}", pubkey); Some(Json(PingResponse { @@ -39,7 +39,7 @@ pub(crate) async fn index( match state.inner.get_mix_node(pubkey).await { Some(bond) => { // set status to pending, so that any HTTP requests are pending - state.inner.ping_cache.set_pending(pubkey).await; + state.inner.ping.set_pending(pubkey).await; // do the check let ports = Some(port_check(&bond).await); @@ -51,7 +51,7 @@ pub(crate) async fn index( // cache for 1 min trace!("Caching value for {}", pubkey); - state.inner.ping_cache.set(pubkey, response.clone()).await; + state.inner.ping.set(pubkey, response.clone()).await; // return response Some(Json(response)) diff --git a/explorer-api/src/state.rs b/explorer-api/src/state.rs index 676c4f9423..b971f76269 100644 --- a/explorer-api/src/state.rs +++ b/explorer-api/src/state.rs @@ -8,27 +8,31 @@ use serde::{Deserialize, Serialize}; use mixnet_contract_common::MixNodeBond; use crate::country_statistics::country_nodes_distribution::{ - ConcurrentCountryNodesDistribution, CountryNodesDistribution, + CountryNodesDistribution, ThreadsafeCountryNodesDistribution, }; +use crate::gateways::models::ThreadsafeGatewayCache; use crate::mix_node::models::ThreadsafeMixNodeCache; use crate::mix_nodes::location::LocationCache; -use crate::mix_nodes::models::ThreadsafeMixNodesResult; +use crate::mix_nodes::models::ThreadsafeMixNodesCache; use crate::ping::models::ThreadsafePingCache; +use crate::validators::models::ThreadsafeValidatorCache; // TODO: change to an environment variable with a default value const STATE_FILE: &str = "explorer-api-state.json"; #[derive(Clone)] pub struct ExplorerApiState { - pub(crate) country_node_distribution: ConcurrentCountryNodesDistribution, - pub(crate) mix_nodes: ThreadsafeMixNodesResult, - pub(crate) mix_node_cache: ThreadsafeMixNodeCache, - pub(crate) ping_cache: ThreadsafePingCache, + pub(crate) country_node_distribution: ThreadsafeCountryNodesDistribution, + pub(crate) gateways: ThreadsafeGatewayCache, + pub(crate) mixnode: ThreadsafeMixNodeCache, + pub(crate) mixnodes: ThreadsafeMixNodesCache, + pub(crate) ping: ThreadsafePingCache, + pub(crate) validators: ThreadsafeValidatorCache, } impl ExplorerApiState { pub(crate) async fn get_mix_node(&self, pubkey: &str) -> Option { - self.mix_nodes.get_mixnode(pubkey).await + self.mixnodes.get_mixnode(pubkey).await } } @@ -61,14 +65,16 @@ impl ExplorerApiStateContext { info!("Loaded state from file {:?}: {:?}", json_file, state); ExplorerApiState { country_node_distribution: - ConcurrentCountryNodesDistribution::new_from_distribution( + ThreadsafeCountryNodesDistribution::new_from_distribution( state.country_node_distribution, ), - mix_nodes: ThreadsafeMixNodesResult::new_with_location_cache( + gateways: ThreadsafeGatewayCache::new(), + mixnode: ThreadsafeMixNodeCache::new(), + mixnodes: ThreadsafeMixNodesCache::new_with_location_cache( state.location_cache, ), - mix_node_cache: ThreadsafeMixNodeCache::new(), - ping_cache: ThreadsafePingCache::new(), + ping: ThreadsafePingCache::new(), + validators: ThreadsafeValidatorCache::new(), } } _ => { @@ -78,10 +84,12 @@ impl ExplorerApiStateContext { ); ExplorerApiState { - country_node_distribution: ConcurrentCountryNodesDistribution::new(), - mix_nodes: ThreadsafeMixNodesResult::new(), - mix_node_cache: ThreadsafeMixNodeCache::new(), - ping_cache: ThreadsafePingCache::new(), + country_node_distribution: ThreadsafeCountryNodesDistribution::new(), + gateways: ThreadsafeGatewayCache::new(), + mixnode: ThreadsafeMixNodeCache::new(), + mixnodes: ThreadsafeMixNodesCache::new(), + ping: ThreadsafePingCache::new(), + validators: ThreadsafeValidatorCache::new(), } } } @@ -93,7 +101,7 @@ impl ExplorerApiStateContext { let file = File::create(json_file_path).expect("unable to create state json file"); let state = ExplorerApiStateOnDisk { country_node_distribution: self.inner.country_node_distribution.get_all().await, - location_cache: self.inner.mix_nodes.get_location_cache().await, + location_cache: self.inner.mixnodes.get_locations().await, as_at: Utc::now(), }; serde_json::to_writer(file, &state).expect("error writing state to disk"); diff --git a/explorer-api/src/tasks.rs b/explorer-api/src/tasks.rs new file mode 100644 index 0000000000..1f09074285 --- /dev/null +++ b/explorer-api/src/tasks.rs @@ -0,0 +1,146 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::future::Future; + +use mixnet_contract_common::{GatewayBond, MixNodeBond}; +use validator_client::nymd::error::NymdError; +use validator_client::nymd::{Paging, QueryNymdClient, ValidatorResponse}; +use validator_client::ValidatorClientError; + +use crate::client::new_nymd_client; +use crate::mix_nodes::CACHE_REFRESH_RATE; +use crate::state::ExplorerApiStateContext; + +pub(crate) struct ExplorerApiTasks { + state: ExplorerApiStateContext, + validator_client: validator_client::Client, +} + +impl ExplorerApiTasks { + pub(crate) fn new(state: ExplorerApiStateContext) -> Self { + ExplorerApiTasks { + state, + validator_client: new_nymd_client(), + } + } + + // a helper to remove duplicate code when grabbing active/rewarded/all mixnodes + async fn retrieve_mixnodes<'a, F, Fut>(&'a self, f: F) -> Vec + where + F: FnOnce(&'a validator_client::Client) -> Fut, + Fut: Future, ValidatorClientError>>, + { + let bonds = match f(&self.validator_client).await { + Ok(result) => result, + Err(e) => { + error!("Unable to retrieve mixnode bonds: {:?}", e); + vec![] + } + }; + + info!("Fetched {} mixnode bonds", bonds.len()); + bonds + } + + async fn retrieve_all_mixnodes(&self) -> Vec { + info!("About to retrieve all mixnode bonds..."); + self.retrieve_mixnodes(validator_client::Client::get_cached_mixnodes) + .await + } + + async fn retrieve_all_gateways(&self) -> Result, ValidatorClientError> { + info!("About to retrieve all gateways..."); + self.validator_client.get_cached_gateways().await + } + + async fn retrieve_all_validators(&self) -> Result { + info!("About to retrieve all validators..."); + let height = self + .validator_client + .nymd + .get_current_block_height() + .await?; + let response: ValidatorResponse = self + .validator_client + .nymd + .get_validators(height.value(), Paging::All) + .await?; + info!("Fetched {} validators", response.validators.len()); + Ok(response) + } + + async fn retrieve_rewarded_mixnodes(&self) -> Vec { + info!("About to retrieve rewarded mixnode bonds..."); + self.retrieve_mixnodes(validator_client::Client::get_cached_rewarded_mixnodes) + .await + } + + async fn retrieve_active_mixnodes(&self) -> Vec { + info!("About to retrieve active mixnode bonds..."); + self.retrieve_mixnodes(validator_client::Client::get_cached_active_mixnodes) + .await + } + + async fn update_mixnode_cache(&self) { + let all_bonds = self.retrieve_all_mixnodes().await; + let rewarded_nodes = self + .retrieve_rewarded_mixnodes() + .await + .into_iter() + .map(|bond| bond.mix_node.identity_key) + .collect(); + let active_nodes = self + .retrieve_active_mixnodes() + .await + .into_iter() + .map(|bond| bond.mix_node.identity_key) + .collect(); + self.state + .inner + .mixnodes + .update_cache(all_bonds, rewarded_nodes, active_nodes) + .await; + } + + async fn update_validators_cache(&self) { + match self.retrieve_all_validators().await { + Ok(response) => self.state.inner.validators.update_cache(response).await, + Err(e) => { + error!("Failed to get validators: {:?}", e) + } + } + } + + async fn update_gateways_cache(&self) { + match self.retrieve_all_gateways().await { + Ok(response) => self.state.inner.gateways.update_cache(response).await, + Err(e) => { + error!("Failed to get gateways: {:?}", e) + } + } + } + + pub(crate) fn start(self) { + info!("Spawning mix nodes task runner..."); + tokio::spawn(async move { + let mut interval_timer = tokio::time::interval(CACHE_REFRESH_RATE); + loop { + // wait for the next interval tick + interval_timer.tick().await; + + info!("Updating validator cache..."); + self.update_validators_cache().await; + info!("Done"); + + info!("Updating gateway cache..."); + self.update_gateways_cache().await; + info!("Done"); + + info!("Updating mix node cache..."); + self.update_mixnode_cache().await; + info!("Done"); + } + }); + } +} diff --git a/explorer-api/src/validators/http.rs b/explorer-api/src/validators/http.rs new file mode 100644 index 0000000000..88a06eea01 --- /dev/null +++ b/explorer-api/src/validators/http.rs @@ -0,0 +1,24 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use rocket::response::status::NotFound; +use rocket::serde::json::Json; +use rocket::{Route, State}; +use rocket_okapi::okapi::openapi3::OpenApi; +use rocket_okapi::openapi_get_routes_spec; +use rocket_okapi::settings::OpenApiSettings; + +use crate::state::ExplorerApiStateContext; +use crate::validators::models::PrettyValidatorInfo; + +pub fn validators_make_default_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { + openapi_get_routes_spec![settings: list] +} + +#[openapi(tag = "validators")] +#[get("/")] +pub(crate) async fn list( + state: &State, +) -> Result>, NotFound> { + Ok(Json(state.inner.validators.get_validators().await)) +} diff --git a/explorer-api/src/validators/mod.rs b/explorer-api/src/validators/mod.rs new file mode 100644 index 0000000000..5df938f83c --- /dev/null +++ b/explorer-api/src/validators/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod http; +pub(crate) mod models; diff --git a/explorer-api/src/validators/models.rs b/explorer-api/src/validators/models.rs new file mode 100644 index 0000000000..4f17cbe14a --- /dev/null +++ b/explorer-api/src/validators/models.rs @@ -0,0 +1,79 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::sync::Arc; + +use serde::Serialize; +use tokio::sync::RwLock; + +use validator_client::nymd::ValidatorResponse; + +use crate::cache::Cache; + +#[derive(Clone, Debug, Serialize, JsonSchema)] +pub(crate) struct PrettyValidatorInfo { + pub address: String, + pub pub_key: String, + pub voting_power: u64, + pub name: Option, +} + +pub(crate) struct ValidatorCache { + pub(crate) validators: Cache, + pub(crate) summary: ValidatorSummary, +} + +#[derive(Clone, Debug, Serialize, JsonSchema)] +pub(crate) struct ValidatorSummary { + pub(crate) count: i32, + pub(crate) block_height: u64, +} + +#[derive(Clone)] +pub(crate) struct ThreadsafeValidatorCache { + inner: Arc>, +} + +impl ThreadsafeValidatorCache { + pub(crate) fn new() -> Self { + ThreadsafeValidatorCache { + inner: Arc::new(RwLock::new(ValidatorCache { + validators: Cache::new(), + summary: ValidatorSummary { + block_height: 0, + count: 0, + }, + })), + } + } + + pub(crate) async fn get_validators(&self) -> Vec { + self.inner.read().await.validators.get_all() + } + + pub(crate) async fn get_validator_summary(&self) -> ValidatorSummary { + self.inner.read().await.summary.clone() + } + + pub(crate) async fn update_cache(&self, validator_response: ValidatorResponse) { + let mut guard = self.inner.write().await; + + for validator in validator_response.validators { + let address = validator.address.to_string(); + guard.validators.set( + address.clone().as_str(), + PrettyValidatorInfo { + address, + pub_key: validator.pub_key.to_hex(), + name: validator.name, + voting_power: validator.power.value(), + }, + ) + } + + guard.summary = ValidatorSummary { + count: validator_response.total, + block_height: validator_response.block_height.value(), + }; + } +} diff --git a/explorer/package-lock.json b/explorer/package-lock.json index b1c9eee465..8dce0ee469 100644 --- a/explorer/package-lock.json +++ b/explorer/package-lock.json @@ -24,6 +24,7 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-google-charts": "^3.0.15", + "react-identicons": "^1.2.5", "react-router-dom": "^5.3.0", "react-simple-maps": "^2.3.0", "react-tooltip": "^4.2.21" @@ -13680,6 +13681,15 @@ "react-dom": ">=16.3.0" } }, + "node_modules/react-identicons": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/react-identicons/-/react-identicons-1.2.5.tgz", + "integrity": "sha512-x7prkDoc2pD7wSl2C1pGxS+XAoSdq1ABWJWTBUimVTDVJArKOLd0B4wRUJpDm4r+9y7pgf8ylyPGsmlWSV5n2g==", + "peerDependencies": { + "react": "^17.0.1", + "react-dom": "^17.0.1" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -27550,6 +27560,12 @@ "react-load-script": "^0.0.6" } }, + "react-identicons": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/react-identicons/-/react-identicons-1.2.5.tgz", + "integrity": "sha512-x7prkDoc2pD7wSl2C1pGxS+XAoSdq1ABWJWTBUimVTDVJArKOLd0B4wRUJpDm4r+9y7pgf8ylyPGsmlWSV5n2g==", + "requires": {} + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/explorer/package.json b/explorer/package.json index 553adb4ecb..039f1ca1a1 100644 --- a/explorer/package.json +++ b/explorer/package.json @@ -19,6 +19,7 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-google-charts": "^3.0.15", + "react-identicons": "^1.2.5", "react-router-dom": "^5.3.0", "react-simple-maps": "^2.3.0", "react-tooltip": "^4.2.21" diff --git a/explorer/src/api/constants.ts b/explorer/src/api/constants.ts index 99f749f927..0dc59790da 100644 --- a/explorer/src/api/constants.ts +++ b/explorer/src/api/constants.ts @@ -4,6 +4,7 @@ export const VALIDATOR_API_BASE_URL = process.env.VALIDATOR_API_URL; export const BIG_DIPPER = process.env.BIG_DIPPER_URL; // specific API routes +export const OVERVIEW_API = `${API_BASE_URL}/overview`; export const MIXNODE_PING = `${API_BASE_URL}/ping`; export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`; export const MIXNODE_API = `${API_BASE_URL}/mix-node`; @@ -17,8 +18,4 @@ export const UPTIME_STORY_API = `${VALIDATOR_API_BASE_URL}/api/v1/status/mixnode export const MIXNODE_API_ERROR = "We're having trouble finding that record, please try again or Contact Us."; -// socials -export const TELEGRAM_LINK = 'https://t.me/nymchan'; -export const TWITTER_LINK = 'https://twitter.com/nymproject'; -export const GITHUB_LINK = 'https://github.com/nymtech'; export const NYM_WEBSITE = 'https://nymtech.net'; diff --git a/explorer/src/api/index.ts b/explorer/src/api/index.ts index 808c8bfc5f..43c2b52fa8 100644 --- a/explorer/src/api/index.ts +++ b/explorer/src/api/index.ts @@ -1,24 +1,28 @@ import { - GATEWAYS_API, - MIXNODES_API, - VALIDATORS_API, BLOCK_API, COUNTRY_DATA_API, - MIXNODE_PING, - UPTIME_STORY_API, + GATEWAYS_API, MIXNODE_API, + MIXNODE_PING, + MIXNODES_API, + OVERVIEW_API, + UPTIME_STORY_API, + VALIDATORS_API, } from './constants'; import { - MixNodeResponse, - GatewayResponse, - ValidatorsResponse, CountryDataResponse, - MixNodeResponseItem, DelegationsResponse, + GatewayResponse, + MixNodeDescriptionResponse, + MixNodeResponse, + MixNodeResponseItem, + MixnodeStatus, StatsResponse, StatusResponse, + SummaryOverviewResponse, UptimeStoryResponse, + ValidatorsResponse, } from '../typeDefs/explorer-api'; function getFromCache(key: string) { @@ -33,8 +37,21 @@ function getFromCache(key: string) { function storeInCache(key: string, data: any) { localStorage.setItem(key, data); + localStorage.setItem('ts', Date.now().toString()); } + export class Api { + static fetchOverviewSummary = async (): Promise => { + const cache = getFromCache('overview-summary'); + if (cache) { + return cache; + } + const res = await fetch(`${OVERVIEW_API}/summary`); + const json = await res.json(); + storeInCache('overview-summary', JSON.stringify(json)); + return json; + }; + static fetchMixnodes = async (): Promise => { const cachedMixnodes = getFromCache('mixnodes'); if (cachedMixnodes) { @@ -43,18 +60,33 @@ export class Api { const res = await fetch(MIXNODES_API); const json = await res.json(); storeInCache('mixnodes', JSON.stringify(json)); - storeInCache('ts', Date.now()); + return json; + }; + + static fetchMixnodesActiveSetByStatus = async ( + status: MixnodeStatus, + ): Promise => { + const cachedMixnodes = getFromCache(`mixnodes-${status}`); + if (cachedMixnodes) { + return cachedMixnodes; + } + const res = await fetch(`${MIXNODES_API}/active-set/${status}`); + const json = await res.json(); + storeInCache(`mixnodes-${status}`, JSON.stringify(json)); return json; }; static fetchMixnodeByID = async ( id: string, ): Promise => { - const allMixnodes: MixNodeResponse = await Api.fetchMixnodes(); - const matchedByID = allMixnodes.filter( - (eachRecord) => eachRecord.mix_node.identity_key === id, - ); - return (matchedByID.length && matchedByID[0]) || undefined; + const response = await fetch(`${MIXNODE_API}/${id}`); + + // when the mixnode is not found, returned undefined + if (response.status === 404) { + return undefined; + } + + return response.json(); }; static fetchGateways = async (): Promise => { @@ -93,6 +125,11 @@ export class Api { static fetchStatsById = async (id: string): Promise => (await fetch(`${MIXNODE_API}/${id}/stats`)).json(); + static fetchMixnodeDescriptionById = async ( + id: string, + ): Promise => + (await fetch(`${MIXNODE_API}/${id}/description`)).json(); + static fetchStatusById = async (id: string): Promise => (await fetch(`${MIXNODE_PING}/${id}`)).json(); diff --git a/explorer/src/components/BondBreakdown.tsx b/explorer/src/components/BondBreakdown.tsx deleted file mode 100644 index d655e2b855..0000000000 --- a/explorer/src/components/BondBreakdown.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import * as React from 'react'; -import { Alert, Box, CircularProgress, useMediaQuery } from '@mui/material'; -import { useTheme } from '@mui/material/styles'; -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableContainer from '@mui/material/TableContainer'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; -import Paper from '@mui/material/Paper'; -import { useMainContext } from 'src/context/main'; -import { ExpandMore } from '@mui/icons-material'; -import { currencyToString } from '../utils/currency'; - -export const BondBreakdownTable: React.FC = () => { - const { mixnodeDetailInfo, delegations } = useMainContext(); - const [allContentLoaded, setAllContentLoaded] = - React.useState(false); - const [showError, setShowError] = React.useState(false); - const [showDelegations, toggleShowDelegations] = - React.useState(false); - - const [bonds, setBonds] = React.useState({ - delegations: '0', - pledges: '0', - bondsTotal: '0', - hasLoaded: false, - }); - const theme = useTheme(); - const matches = useMediaQuery(theme.breakpoints.down('sm')); - - React.useEffect(() => { - if (mixnodeDetailInfo && mixnodeDetailInfo.data?.length) { - const thisMixnode = mixnodeDetailInfo?.data[0]; - - // delegations - const decimalisedDelegations = currencyToString( - thisMixnode.total_delegation.amount.toString(), - thisMixnode.total_delegation.denom, - ); - - // pledges - const decimalisedPledges = currencyToString( - thisMixnode.pledge_amount.amount.toString(), - thisMixnode.pledge_amount.denom, - ); - - // bonds total (del + pledges) - const pledgesSum = Number(thisMixnode.pledge_amount.amount); - const delegationsSum = Number(thisMixnode.total_delegation.amount); - const bondsTotal = currencyToString( - (delegationsSum + pledgesSum).toString(), - ); - - setBonds({ - delegations: decimalisedDelegations, - pledges: decimalisedPledges, - bondsTotal, - hasLoaded: true, - }); - } - }, [mixnodeDetailInfo]); - - React.useEffect(() => { - const hasError = Boolean(mixnodeDetailInfo?.error || delegations?.error); - const hasAllMixnodeInfo = Boolean( - mixnodeDetailInfo?.data !== undefined && - mixnodeDetailInfo?.data[0].mix_node, - ); - const hasAllDelegationsInfo = Boolean( - delegations?.data !== undefined && delegations?.data, - ); - const hasAllData = Boolean( - !hasError && hasAllMixnodeInfo && hasAllDelegationsInfo, - ); - setShowError(hasError); - setAllContentLoaded(hasAllData); - }, [mixnodeDetailInfo, delegations]); - - const expandDelegations = () => { - if (delegations?.data && delegations.data.length > 0) { - toggleShowDelegations(!showDelegations); - } - }; - const calcBondPercentage = (num: number) => { - if (mixnodeDetailInfo?.data !== undefined && mixnodeDetailInfo?.data[0]) { - const rawDelegationAmount = Number( - mixnodeDetailInfo.data[0].total_delegation.amount, - ); - const rawPledgeAmount = Number( - mixnodeDetailInfo.data[0].pledge_amount.amount, - ); - const rawTotalBondsAmount = rawDelegationAmount + rawPledgeAmount; - return ((num * 100) / rawTotalBondsAmount).toFixed(1); - } - return 0; - }; - - if (mixnodeDetailInfo?.isLoading) { - return ; - } - - if (showError) { - return ( - - We are unable to retrieve a Mixnode with that ID. Please try later or - Contact Us. - - ); - } - - if (!showError && allContentLoaded) { - return ( - <> - - - - - - Bond total - - - {bonds.bondsTotal} - - - - Pledge total - - {bonds.pledges} - - - - - - Delegation total {'\u00A0'} - {delegations?.data && delegations?.data?.length > 0 && ( - - )} - - - - {bonds.delegations} - - - -
- - {showDelegations && ( - - - - - - Delegators - - - Stake - - - Share from bond - - - - - - {delegations?.data?.map( - ({ owner, amount: { amount, denom } }) => ( - - - {owner} - - - {currencyToString(amount.toString(), denom)} - - - {calcBondPercentage(amount)}% - - - ), - )} - -
-
- )} -
- - ); - } - return null; -}; diff --git a/explorer/src/components/ContentCard.tsx b/explorer/src/components/ContentCard.tsx index 6c34396cf0..e1e7cf1407 100644 --- a/explorer/src/components/ContentCard.tsx +++ b/explorer/src/components/ContentCard.tsx @@ -2,7 +2,7 @@ import { Card, CardHeader, CardContent, Typography } from '@mui/material'; import React, { ReactEventHandler } from 'react'; type ContentCardProps = { - title?: string | React.ReactNode; + title?: React.ReactNode; subtitle?: string; Icon?: React.ReactNode; Action?: React.ReactNode; diff --git a/explorer/src/components/CustomColumnHeading.tsx b/explorer/src/components/CustomColumnHeading.tsx index c7b084b14e..00836a2d54 100644 --- a/explorer/src/components/CustomColumnHeading.tsx +++ b/explorer/src/components/CustomColumnHeading.tsx @@ -14,7 +14,7 @@ export const CustomColumnHeading: React.FC<{ headingTitle: string }> = ({ {columnsData?.map(({ field, title, flex }) => ( - + {title} ))} @@ -65,6 +65,7 @@ export const DetailTable: React.FC<{ ...cellStyles, padding: 2, width: 200, + fontSize: 14, }} data-testid={`${_.title.replace(/ /g, '-')}-value`} > diff --git a/explorer/src/components/Footer.tsx b/explorer/src/components/Footer.tsx index f0b2cd57e3..60a84d1cd1 100644 --- a/explorer/src/components/Footer.tsx +++ b/explorer/src/components/Footer.tsx @@ -42,7 +42,7 @@ export const Footer: React.FC = () => { color: theme.palette.nym.text.footer, }} > - © 2021 Nym Technologies SA, all rights reserved + © {new Date().getFullYear()} Nym Technologies SA, all rights reserved ); diff --git a/explorer/src/components/Gateways.tsx b/explorer/src/components/Gateways.tsx new file mode 100644 index 0000000000..8a004285d1 --- /dev/null +++ b/explorer/src/components/Gateways.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { GatewayResponse } from '../typeDefs/explorer-api'; + +export type GatewayRowType = { + id: string; + owner: string; + identity_key: string; + bond: number; + host: string; + location: string; +}; + +export function gatewayToGridRow( + arrayOfGateways: GatewayResponse, +): GatewayRowType[] { + return !arrayOfGateways + ? [] + : arrayOfGateways.map((gw) => ({ + id: gw.owner, + owner: gw.owner, + identity_key: gw.gateway.identity_key || '', + location: gw?.gateway?.location || '', + bond: gw.pledge_amount.amount || 0, + host: gw.gateway.host || '', + })); +} diff --git a/explorer/src/components/Icons.tsx b/explorer/src/components/Icons.tsx new file mode 100644 index 0000000000..5ba3c23c1b --- /dev/null +++ b/explorer/src/components/Icons.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; +import PauseCircleOutlineIcon from '@mui/icons-material/PauseCircleOutline'; +import CircleOutlinedIcon from '@mui/icons-material/CircleOutlined'; +import { MixnodeStatus } from '../typeDefs/explorer-api'; + +export const Icons = { + mixnodes: { + status: { + active: CheckCircleOutlineIcon, + standby: PauseCircleOutlineIcon, + inactive: CircleOutlinedIcon, + }, + }, +}; + +export const getMixNodeIcon = (value: any) => { + if (value && typeof value === 'string') { + switch (value) { + case MixnodeStatus.active: + return Icons.mixnodes.status.active; + case MixnodeStatus.standby: + return Icons.mixnodes.status.standby; + default: + return Icons.mixnodes.status.inactive; + } + } + return Icons.mixnodes.status.inactive; +}; diff --git a/explorer/src/components/MixNodes/BondBreakdown.tsx b/explorer/src/components/MixNodes/BondBreakdown.tsx new file mode 100644 index 0000000000..186ad2ab59 --- /dev/null +++ b/explorer/src/components/MixNodes/BondBreakdown.tsx @@ -0,0 +1,194 @@ +import * as React from 'react'; +import { Alert, Box, CircularProgress, useMediaQuery } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import TableRow from '@mui/material/TableRow'; +import Paper from '@mui/material/Paper'; +import { ExpandMore } from '@mui/icons-material'; +import { currencyToString } from '../../utils/currency'; +import { useMixnodeContext } from '../../context/mixnode'; + +export const BondBreakdownTable: React.FC = () => { + const { mixNode, delegations } = useMixnodeContext(); + const [showDelegations, toggleShowDelegations] = + React.useState(false); + + const [bonds, setBonds] = React.useState({ + delegations: '0', + pledges: '0', + bondsTotal: '0', + hasLoaded: false, + }); + const theme = useTheme(); + const matches = useMediaQuery(theme.breakpoints.down('sm')); + + React.useEffect(() => { + if (mixNode?.data) { + // delegations + const decimalisedDelegations = currencyToString( + mixNode.data.total_delegation.amount.toString(), + mixNode.data.total_delegation.denom, + ); + + // pledges + const decimalisedPledges = currencyToString( + mixNode.data.pledge_amount.amount.toString(), + mixNode.data.pledge_amount.denom, + ); + + // bonds total (del + pledges) + const pledgesSum = Number(mixNode.data.pledge_amount.amount); + const delegationsSum = Number(mixNode.data.total_delegation.amount); + const bondsTotal = currencyToString( + (delegationsSum + pledgesSum).toString(), + ); + + setBonds({ + delegations: decimalisedDelegations, + pledges: decimalisedPledges, + bondsTotal, + hasLoaded: true, + }); + } + }, [mixNode]); + + const expandDelegations = () => { + if (delegations?.data && delegations.data.length > 0) { + toggleShowDelegations(!showDelegations); + } + }; + const calcBondPercentage = (num: number) => { + if (mixNode?.data) { + const rawDelegationAmount = Number(mixNode.data.total_delegation.amount); + const rawPledgeAmount = Number(mixNode.data.pledge_amount.amount); + const rawTotalBondsAmount = rawDelegationAmount + rawPledgeAmount; + return ((num * 100) / rawTotalBondsAmount).toFixed(1); + } + return 0; + }; + + if (mixNode?.isLoading || delegations?.isLoading) { + return ; + } + + if (mixNode?.error) { + return Mixnode not found; + } + if (delegations?.error) { + return ( + Unable to get delegations for mixnode + ); + } + + return ( + <> + + + + + + Bond total + + + {bonds.bondsTotal} + + + + Pledge total + + {bonds.pledges} + + + + + + Delegation total {'\u00A0'} + {delegations?.data && delegations?.data?.length > 0 && ( + + )} + + + + {bonds.delegations} + + + +
+ + {showDelegations && ( + + + + + + Delegators + + + Stake + + + Share from bond + + + + + + {delegations?.data?.map( + ({ owner, amount: { amount, denom } }) => ( + + + {owner} + + + {currencyToString(amount.toString(), denom)} + + + {calcBondPercentage(amount)}% + + + ), + )} + +
+
+ )} +
+ + ); +}; diff --git a/explorer/src/components/MixNodes/DetailSection.tsx b/explorer/src/components/MixNodes/DetailSection.tsx new file mode 100644 index 0000000000..e3c235ceb3 --- /dev/null +++ b/explorer/src/components/MixNodes/DetailSection.tsx @@ -0,0 +1,106 @@ +import { Box, Button, Grid, Typography, useMediaQuery } from '@mui/material'; +import * as React from 'react'; +import Identicon from 'react-identicons'; +import { useTheme } from '@mui/material/styles'; +import { MixnodeRowType } from '.'; +import { getMixNodeStatusText, MixNodeStatus } from './Status'; +import { MixNodeDescriptionResponse } from '../../typeDefs/explorer-api'; + +interface MixNodeDetailProps { + mixNodeRow: MixnodeRowType; + mixnodeDescription: MixNodeDescriptionResponse; +} + +export const MixNodeDetailSection: React.FC = ({ + mixNodeRow, + mixnodeDescription, +}) => { + const theme = useTheme(); + const palette = [theme.palette.text.primary]; + const matches = useMediaQuery(theme.breakpoints.down('sm')); + const statusText = React.useMemo( + () => getMixNodeStatusText(mixNodeRow.status), + [mixNodeRow.status], + ); + return ( + + + + + + + + {mixnodeDescription.name} + + {(mixnodeDescription.description || '').slice(0, 1000)} + + + + + + + + + Node status: + + + + + + This node is {statusText} in this epoch + + + + + ); +}; diff --git a/explorer/src/components/MixNodes/Status.tsx b/explorer/src/components/MixNodes/Status.tsx new file mode 100644 index 0000000000..fe94fcb1b5 --- /dev/null +++ b/explorer/src/components/MixNodes/Status.tsx @@ -0,0 +1,53 @@ +import { Typography } from '@mui/material'; +import * as React from 'react'; +import { Theme, useTheme } from '@mui/material/styles'; +import { MixnodeRowType } from '.'; +import { getMixNodeIcon } from '../Icons'; +import { MixnodeStatus } from '../../typeDefs/explorer-api'; + +interface MixNodeStatusProps { + status: MixnodeStatus; +} + +export const MixNodeStatus: React.FC = ({ status }) => { + const theme = useTheme(); + const Icon = React.useMemo(() => getMixNodeIcon(status), [status]); + const color = React.useMemo(() => getMixNodeStatusColor(theme, status), [status, theme]); + + return ( + + + + {`${status[0].toUpperCase()}${status.slice(1)}`} + + + ); +}; + +export const getMixNodeStatusColor = (theme: Theme, status: MixnodeStatus) => { + let color; + switch (status) { + case MixnodeStatus.active: + color = theme.palette.nym.networkExplorer.mixnodes.status.active; + break; + case MixnodeStatus.standby: + color = theme.palette.nym.networkExplorer.mixnodes.status.standby; + break; + default: + color = theme.palette.nym.networkExplorer.mixnodes.status.inactive; + break; + } + return color; +}; + +// TODO: should be done with i18n +export const getMixNodeStatusText = (status: MixnodeStatus) => { + switch (status) { + case MixnodeStatus.active: + return 'active'; + case MixnodeStatus.standby: + return 'on standby'; + default: + return 'inactive'; + } +}; diff --git a/explorer/src/components/MixNodes/StatusDropdown.tsx b/explorer/src/components/MixNodes/StatusDropdown.tsx new file mode 100644 index 0000000000..420b726738 --- /dev/null +++ b/explorer/src/components/MixNodes/StatusDropdown.tsx @@ -0,0 +1,96 @@ +import { MenuItem, useMediaQuery } from '@mui/material'; +import * as React from 'react'; +import Select from '@mui/material/Select'; +import { SelectInputProps } from '@mui/material/Select/SelectInput'; +import { useTheme } from '@mui/material/styles'; +import { SxProps } from '@mui/system'; +import { MixNodeStatus } from './Status'; +import { + MixnodeStatus, + MixnodeStatusWithAll, +} from '../../typeDefs/explorer-api'; + +// TODO: replace with i18n +const ALL_NODES = 'All nodes'; + +interface MixNodeStatusDropdownProps { + status?: MixnodeStatusWithAll; + sx?: SxProps; + onSelectionChanged?: (status?: MixnodeStatusWithAll) => void; +} + +export const MixNodeStatusDropdown: React.FC = ({ + status, + onSelectionChanged, + sx, +}) => { + const theme = useTheme(); + const matches = useMediaQuery(theme.breakpoints.down('sm')); + const [statusValue, setStatusValue] = React.useState( + status || MixnodeStatusWithAll.all, + ); + const onChange: SelectInputProps['onChange'] = + React.useCallback( + ({ target: { value } }) => { + setStatusValue(value); + if (onSelectionChanged) { + onSelectionChanged(value); + } + }, + [onSelectionChanged], + ); + + return ( + + ); +}; + +MixNodeStatusDropdown.defaultProps = { + onSelectionChanged: undefined, + status: undefined, + sx: undefined, +}; diff --git a/explorer/src/components/MixNodes/index.ts b/explorer/src/components/MixNodes/index.ts new file mode 100644 index 0000000000..356cf6c38c --- /dev/null +++ b/explorer/src/components/MixNodes/index.ts @@ -0,0 +1,44 @@ +/* eslint-disable camelcase */ +import { + MixNodeResponse, + MixNodeResponseItem, + MixnodeStatus, +} from '../../typeDefs/explorer-api'; + +export type MixnodeRowType = { + id: string; + status: MixnodeStatus; + owner: string; + location: string; + identity_key: string; + bond: number; + self_percentage: string; + host: string; + layer: string; +}; + +export function mixnodeToGridRow( + arrayOfMixnodes?: MixNodeResponse, +): MixnodeRowType[] { + return (arrayOfMixnodes || []).map(mixNodeResponseItemToMixnodeRowType); +} + +export function mixNodeResponseItemToMixnodeRowType( + item: MixNodeResponseItem, +): MixnodeRowType { + const pledge = Number(item.pledge_amount.amount) || 0; + const delegations = Number(item.total_delegation.amount) || 0; + const totalBond = pledge + delegations; + const selfPercentage = ((pledge * 100) / totalBond).toFixed(2); + return { + id: item.owner, + status: item.status, + owner: item.owner, + identity_key: item.mix_node.identity_key || '', + bond: totalBond || 0, + location: item?.location?.country_name || '', + self_percentage: selfPercentage, + host: item?.mix_node?.host || '', + layer: item?.layer || '', + }; +} diff --git a/explorer/src/components/MobileNav.tsx b/explorer/src/components/MobileNav.tsx index 42189d148b..cfca612af8 100644 --- a/explorer/src/components/MobileNav.tsx +++ b/explorer/src/components/MobileNav.tsx @@ -81,7 +81,7 @@ export const MobileNav: React.FC<{ children: React.ReactNode }> = ({ sx={{ color: theme.palette.nym.networkExplorer.nav.text, fontSize: '18px', - fontWeight: 800, + fontWeight: 600, ml: 2, }} > diff --git a/explorer/src/components/Nav.tsx b/explorer/src/components/Nav.tsx index 0f94a3223e..2cbccea908 100644 --- a/explorer/src/components/Nav.tsx +++ b/explorer/src/components/Nav.tsx @@ -186,7 +186,7 @@ export const ExpandableButton: React.FC = ({ if (isChild) { setDynamicStyle({ background: palette.nym.networkExplorer.nav.selected.nested, - fontWeight: 800, + fontWeight: 600, }); } if (!nested && !isChild) { @@ -239,7 +239,7 @@ export const ExpandableButton: React.FC = ({ }} primaryTypographyProps={{ style: { - fontWeight: isActive ? 800 : 300, + fontWeight: isActive ? 600 : 400, }, }} /> @@ -314,6 +314,7 @@ export const Nav: React.FC = ({ children }) => { { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', - width: 205, ml: 0.5, }} > @@ -342,7 +342,7 @@ export const Nav: React.FC = ({ children }) => { sx={{ color: theme.palette.nym.networkExplorer.nav.text, fontSize: '18px', - fontWeight: 800, + fontWeight: 600, }} > { PaperProps={{ style: { background: theme.palette.nym.networkExplorer.nav.background, + borderRadius: 0, }, }} > @@ -427,7 +428,7 @@ export const Nav: React.FC = ({ children }) => { ))} - + {children}