Compare commits

...

32 Commits

Author SHA1 Message Date
Jędrzej Stuczyński 0c1dec4b21 adjusted makefile 2023-02-24 15:55:55 +00:00
Jędrzej Stuczyński 0492af4bf9 bunch of moving things around and feature-locking 2023-02-24 15:46:20 +00:00
Jędrzej Stuczyński 2e6b2b49dc added some sample acceptance tests for mixnet contract 2023-02-24 15:09:00 +00:00
Jędrzej Stuczyński ca180ca6c2 conditionally implementing 'TestableContract' inside corresponding crates 2023-02-24 15:08:45 +00:00
Jędrzej Stuczyński e5efc18912 changed dependencies to dev-dependencies to calm down our build system 2023-02-24 14:45:59 +00:00
Jędrzej Stuczyński f01713c1ff showing different way of reading state 2023-02-24 14:45:59 +00:00
Jędrzej Stuczyński fd19fb529c moved the integration test to contracts directory/workspace 2023-02-24 14:45:59 +00:00
Jędrzej Stuczyński 66b2f1f051 added better sample test assertions + fixed MockApi 2023-02-24 14:45:58 +00:00
Jędrzej Stuczyński 090efa7263 'handlers' -> 'entry_points' 2023-02-24 14:45:58 +00:00
Jędrzej Stuczyński 310de00694 support for contract instantiation 2023-02-24 14:45:58 +00:00
Jędrzej Stuczyński 080356f46b Removed redundant error struct 2023-02-24 14:45:58 +00:00
Jędrzej Stuczyński 7543e4b997 clippy 2023-02-24 14:45:58 +00:00
Jędrzej Stuczyński f42588a985 improved error handling 2023-02-24 14:45:58 +00:00
Jędrzej Stuczyński f8b4faf974 fixed import path in doc test 2023-02-24 14:45:58 +00:00
Jędrzej Stuczyński 2d7b2b1fda sample integration test for mixnet-vesting contracts 2023-02-24 14:45:58 +00:00
Jędrzej Stuczyński fdd5b55e14 initial contract testing utilities 2023-02-24 14:45:58 +00:00
Tommy Verrall aa78bf702c Merge pull request #3104 from nymtech/feature/nym-api-tests
some small api test fixes
2023-02-24 15:09:32 +02:00
benedettadavico cd9cdfa5bb some small api test fixes 2023-02-24 14:06:56 +01:00
Pierre Dommerc 7e1e86bc77 build(nc-mobile): prepare for fdroid inclusion (#3102)
* ci(nc-android): remove uneeded deps

* chore(nc-mobile): improve android build

* ci(nc-mobile): remove useless system deps

* ci(nc-mobile): remove useless system deps

* Revert "ci(nc-mobile): remove useless system deps"

This reverts commit 011db2c58e.

* ci(nc-mobile): restore uneeded sys deps (<3 debian based distro)
2023-02-24 14:06:41 +01:00
Raphaël Walther 2178f2b509 Github Actions: fix notification display issue 2023-02-24 11:12:53 +01:00
Tommy Verrall 8ed808124e Update build-and-upload-binaries-ci.yml
publish credential binary
2023-02-23 17:15:32 +01:00
Tommy Verrall 0deef37778 Update ci-binary-checker.yml
correct positioning on install
2023-02-23 16:56:25 +01:00
Tommy Verrall d759462e4e Update ci-binary-checker.yml
install jq
2023-02-23 16:53:23 +01:00
Tommy Verrall f36cb3a00b Update ci-binary-checker.yml
use custom runner
2023-02-23 16:50:02 +01:00
Tommy Verrall 6428f90b5a Update README.md 2023-02-23 15:38:20 +01:00
Tommy Verrall e2d00fb002 Merge pull request #3098 from nymtech/feature/update-checker-to-use-master
Feature/update checker to use master
2023-02-23 16:37:37 +02:00
Tommy Verrall dc556706c1 Update ci-binary-checker.yml
update the to run from the branch
2023-02-23 13:25:46 +01:00
Tommy Verrall 18a3366cf3 Update ci-binary-checker.yml 2023-02-23 12:37:41 +01:00
Tommy Verrall ea161329dc Update ci-binary-checker.yml
testing manually the changes for the timebeing
2023-02-23 12:34:26 +01:00
Tommy Verrall 02a621ed8b Update ci-binary-checker.yml 2023-02-23 12:16:50 +01:00
Tommy Verrall 5784e7519f Merge pull request #3096 from nymtech/binary-checker-action
Create ci-binary-checker.yml
2023-02-23 13:12:25 +02:00
Tommy Verrall 323d5fbb3c Create ci-binary-checker.yml 2023-02-23 12:10:39 +01:00
72 changed files with 2343 additions and 325 deletions
@@ -69,7 +69,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --workspace --release args: --workspace --release --all
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -95,7 +95,8 @@ jobs:
cp target/release/nym-network-requester $OUTPUT_DIR cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-network-statistics $OUTPUT_DIR cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR cp target/release/nym-cli $OUTPUT_DIR
cp target/release/credential $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
+56
View File
@@ -0,0 +1,56 @@
name: Run config checks on all binaries
on:
workflow_dispatch:
release:
types: [created]
push:
paths:
- 'clients/**'
- 'common/**'
- 'contracts/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
pull_request:
paths:
- 'clients/**'
- 'common/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
env:
NETWORK: mainnet
jobs:
publish-nym:
strategy:
fail-fast: false
matrix:
platform: [custom-runner-linux]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install jq vim libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
continue-on-error: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Branch name
run: echo running on branch ${GITHUB_REF##*/}
- name: Run tests against binaries
run: ./build_and_run.sh ${{ github.head_ref || github.ref_name }}
working-directory: tests/
+7 -3
View File
@@ -23,15 +23,12 @@ jobs:
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get -y install \ sudo apt-get -y install \
libwebkit2gtk-4.0-dev \
build-essential \ build-essential \
unzip \ unzip \
curl \ curl \
wget \ wget \
libssl-dev \ libssl-dev \
libgtk-3-dev \
squashfs-tools \ squashfs-tools \
libayatana-appindicator3-dev \
librsvg2-dev librsvg2-dev
- name: Checkout - name: Checkout
@@ -64,6 +61,11 @@ jobs:
- name: Install Rust toolchain - name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
# TODO this step takes a considerable amount of time
# We could avoid to compile from source tauri-cli and use instead
# pre-compiled binary provided by the node package `@tauri-apps/cli`
# But when using the later the build fails for some reason
# so keep installing and using tauri-cli
- name: Install tauri cli - name: Install tauri cli
run: cargo install tauri-cli --version "^2.0.0-alpha.2" run: cargo install tauri-cli --version "^2.0.0-alpha.2"
@@ -93,11 +95,13 @@ jobs:
- name: Build APK - name: Build APK
working-directory: nym-connect/mobile working-directory: nym-connect/mobile
env: env:
# NODE_TAURI_CLI=${{ github.workspace }}/nym-connect/mobile/node_modules/.bin/tauri
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }} ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
WRY_ANDROID_PACKAGE: net.nymtech.nym_connect WRY_ANDROID_PACKAGE: net.nymtech.nym_connect
WRY_ANDROID_LIBRARY: nym_connect WRY_ANDROID_LIBRARY: nym_connect
# TODO build with release profile (--release), it will requires # TODO build with release profile (--release), it will requires
# to sign the APK. For now build with debug profile to avoid that # to sign the APK. For now build with debug profile to avoid that
# TODO build using `yarn tauri`, provide NODE_TAURI_CLI, see TODO notes above
run: cargo tauri android build --debug --apk --split-per-abi -t aarch64 run: cargo tauri android build --debug --apk --split-per-abi -t aarch64
# TODO add the version number to APK name # TODO add the version number to APK name
-1
View File
@@ -49,7 +49,6 @@ jobs:
librsvg2-dev \ librsvg2-dev \
libsoup-3.0-dev \ libsoup-3.0-dev \
libjavascriptcoregtk-4.1-dev libjavascriptcoregtk-4.1-dev
#continue-on-error: true
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -1,9 +1,14 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry: > 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message: Commit message:
``` ```
@@ -1,10 +1,16 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/ > :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
>
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION_STORYBOOK }}.{{ env.NYM_CI_WWW_BASE }} > `storybook`: https://{{ env.NYM_CI_WWW_LOCATION_STORYBOOK }}.{{ env.NYM_CI_WWW_BASE }}
>
> ✅ **SUCCESS** > ✅ **SUCCESS**
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}: Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
``` ```
@@ -151,7 +151,7 @@ async function getMessageBody(context) {
return `${icon} ${job.conclusion}: ${job.name} - ${job.html_url}`; return `${icon} ${job.conclusion}: ${job.name} - ${job.html_url}`;
}) })
// and join with newlines for display in the template // and join with newlines for display in the template
.join('\n'); .join('\n\n');
return template({ ...context, jobResults }); return template({ ...context, jobResults });
} }
@@ -1,9 +1,15 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry: > 🔴 **FAILURE** :cry:
>
> `when` {{ timestamp }} > `when` {{ timestamp }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
{{ jobResults }} {{ jobResults }}
@@ -1,9 +1,15 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> ✅ **SUCCESS** > ✅ **SUCCESS**
>
> `when` {{ timestamp }} > `when` {{ timestamp }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
{{ jobResults }} {{ jobResults }}
@@ -5,6 +5,7 @@ const localStorage = new LocalStorage('./scratch');
const { const {
LocalStorageCryptoStore, LocalStorageCryptoStore,
} = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store'); } = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
var showdown = require('showdown');
// hide all matrix client output // hide all matrix client output
console.error = (error) => console.log('❌ error: ', error); console.error = (error) => console.log('❌ error: ', error);
@@ -54,7 +55,9 @@ function createClient(context, room, message) {
} }
async function sendMatrixMessage(contextArg, messageAsMarkdown, roomId) { async function sendMatrixMessage(contextArg, messageAsMarkdown, roomId) {
const client = createClient(contextArg, roomId, messageAsMarkdown); const converter = new showdown.Converter();
const messageAsHtml = converter.makeHtml(messageAsMarkdown);
const client = createClient(contextArg, roomId, messageAsHtml);
await client.initCrypto(); await client.initCrypto();
await client.startClient({ initialSyncLimit: 1 }); await client.startClient({ initialSyncLimit: 1 });
} }
@@ -1,9 +1,14 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry: > 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message: Commit message:
``` ```
@@ -1,9 +1,14 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View storybook:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/ > :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View storybook:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
>
> ✅ **SUCCESS** > ✅ **SUCCESS**
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}: Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
``` ```
@@ -1,9 +1,14 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry: > 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message: Commit message:
``` ```
@@ -1,13 +1,18 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> ✅ **SUCCESS** > ✅ **SUCCESS**
>
> ➡️➡️➡️➡️➡️ **View output:** > ➡️➡️➡️➡️➡️ **View output:**
>
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }} > `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}: Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
``` ```
@@ -17,6 +17,7 @@
"remark-emoji": "^2.2.0", "remark-emoji": "^2.2.0",
"remark-html": "^13.0.2", "remark-html": "^13.0.2",
"remark-parse": "^9.0.0", "remark-parse": "^9.0.0",
"showdown": "^2.1.0",
"to-vfile": "^6.1.0", "to-vfile": "^6.1.0",
"unified": "^9.2.2" "unified": "^9.2.2"
}, },
@@ -1,9 +1,14 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry: > 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message: Commit message:
``` ```
@@ -1,14 +1,20 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> ✅ **SUCCESS** > ✅ **SUCCESS**
>
> ➡️➡️➡️➡️➡️ **View output:** > ➡️➡️➡️➡️➡️ **View output:**
>
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }} > `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}
>
> `example`: https://{{ env.NYM_CI_WWW_LOCATION }}-example.{{ env.NYM_CI_WWW_BASE }} > `example`: https://{{ env.NYM_CI_WWW_LOCATION }}-example.{{ env.NYM_CI_WWW_BASE }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}: Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
``` ```
Generated
+25
View File
@@ -902,6 +902,21 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "cosmwasm-contract-testing"
version = "0.1.0"
dependencies = [
"base64 0.21.0",
"cosmwasm-std",
"cosmwasm-storage",
"cw-storage-plus",
"hex",
"rand_chacha 0.3.1",
"serde",
"serde_json",
"thiserror",
]
[[package]] [[package]]
name = "cosmwasm-crypto" name = "cosmwasm-crypto"
version = "1.0.0" version = "1.0.0"
@@ -941,6 +956,16 @@ dependencies = [
"uint", "uint",
] ]
[[package]]
name = "cosmwasm-storage"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d18403b07304d15d304dad11040d45bbcaf78d603b4be3fb5e2685c16f9229b5"
dependencies = [
"cosmwasm-std",
"serde",
]
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.5" version = "0.2.5"
+1
View File
@@ -36,6 +36,7 @@ members = [
"common/cosmwasm-smart-contracts/group-contract", "common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract", "common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/testing",
"common/cosmwasm-smart-contracts/vesting-contract", "common/cosmwasm-smart-contracts/vesting-contract",
"common/mobile-storage", "common/mobile-storage",
"common/credential-storage", "common/credential-storage",
+5 -3
View File
@@ -4,7 +4,7 @@ no-clippy: build cargo-test wasm fmt
happy: fmt clippy-happy test happy: fmt clippy-happy test
clippy-all: clippy-main clippy-main-examples clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all-connect-mobile clippy-all-wasm-client clippy-all: clippy-main clippy-main-examples clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all-connect-mobile clippy-all-wasm-client
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect clippy-happy-connect-mobile clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect clippy-happy-connect-mobile
cargo-test: test-main test-contracts test-wallet test-connect test-connect-mobile cargo-test: test-main test-contracts test-contracts-integration test-wallet test-connect test-connect-mobile
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive
build: build-contracts build-wallet build-main build-main-examples build-connect build-connect-mobile build-wasm-client build: build-contracts build-wallet build-main build-main-examples build-connect build-connect-mobile build-wasm-client
fmt: fmt-main fmt-contracts fmt-wallet fmt-connect fmt-connect-mobile fmt-wasm-client fmt: fmt-main fmt-contracts fmt-wallet fmt-connect fmt-connect-mobile fmt-wasm-client
@@ -33,9 +33,8 @@ clippy-main-examples:
clippy-wasm: clippy-wasm:
cargo clippy --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --workspace -- -D warnings cargo clippy --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --workspace -- -D warnings
clippy-all-contracts: clippy-all-contracts:
cargo clippy --workspace --manifest-path contracts/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings cargo clippy --workspace --manifest-path contracts/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
clippy-all-wallet: clippy-all-wallet:
cargo clippy --workspace --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings cargo clippy --workspace --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings
@@ -61,6 +60,9 @@ test-contracts:
test-contracts-expensive: test-contracts-expensive:
cargo test --manifest-path contracts/Cargo.toml --all-features -- --ignored cargo test --manifest-path contracts/Cargo.toml --all-features -- --ignored
test-contracts-integration:
cargo test --manifest-path contracts/integration-tests/Cargo.toml
test-wallet: test-wallet:
cargo test --manifest-path nym-wallet/Cargo.toml --all-features cargo test --manifest-path nym-wallet/Cargo.toml --all-features
@@ -0,0 +1,25 @@
[package]
name = "cosmwasm-contract-testing"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = { version = "0.21.0", optional = true }
cosmwasm-std = { version = "1.0.0" }
cosmwasm-storage = { version = "1.0.0", optional = true }
cw-storage-plus = { version = "0.13.4", optional = true }
hex = { version = "0.4.3", optional = true }
rand_chacha = { version = "0.3", optional = true }
serde = { version = "1", features=["derive"] }
serde_json = { version = "1", optional = true }
thiserror = { version = "1.0.38" }
[features]
default = ["full"]
full = ["testable-trait", "contract-mocks", "rand", "state-importing", "cosmwasm-storage", "cw-storage-plus", "serde_json"]
rand = ["rand_chacha"]
contract-mocks = []
state-importing = ["base64", "hex"]
testable-trait = []
@@ -0,0 +1,291 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::mock_api::CW12MockApi;
use crate::raw_state::{DecodingError, EncodingError, ImportedContractState, KeyValue};
use crate::AVERAGE_BLOCKTIME_SECS;
use cosmwasm_std::testing::{mock_env, MockQuerier, MockStorage};
use cosmwasm_std::{
Addr, Coin, Deps, DepsMut, Env, Order, QuerierWrapper, StdResult, Storage, Timestamp,
TransactionInfo,
};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::collections::HashMap;
use std::path::Path;
#[cfg(feature = "cw-storage-plus")]
use cw_storage_plus::{Item, Map, PrimaryKey};
// extracted into separate struct for easier cloning, access to mock structs, etc.
// we also had to redefine the MockApi
struct MockedDependencies {
storage: MockStorage,
api: CW12MockApi,
querier: MockQuerier,
// that's a bit annoying. We have to keep track of all balance changes for when we clone the state
// as there's no easy way of obtaining the up to date list of all balances from the querier...
_balances: HashMap<String, Vec<Coin>>,
}
impl MockedDependencies {
fn new_mock() -> MockedDependencies {
MockedDependencies {
storage: Default::default(),
api: Default::default(),
querier: Default::default(),
_balances: Default::default(),
}
}
fn clone_state(&self) -> MockedDependencies {
let new_querier = MockQuerier::new(
&self
._balances
.iter()
.map(|(k, v)| (k.as_ref(), v.as_ref()))
.collect::<Vec<_>>(),
);
let mut new_storage = MockStorage::new();
for (k, v) in self.storage.range(None, None, Order::Ascending) {
new_storage.set(&k, &v)
}
MockedDependencies {
storage: new_storage,
api: self.api,
querier: new_querier,
_balances: self._balances.clone(),
}
}
fn from_raw(kvs: Vec<KeyValue>) -> Self {
let mut new = Self::new_mock();
for kv in kvs {
new.storage.set(&kv.key, &kv.value)
}
new
}
}
pub struct ContractState {
deps: MockedDependencies,
env: Env,
}
impl ContractState {
pub fn new() -> Self {
ContractState {
deps: MockedDependencies::new_mock(),
env: mock_env(),
}
}
pub fn new_with_env(env: Env) -> Self {
ContractState {
deps: MockedDependencies::new_mock(),
env,
}
}
pub fn clone_state(&self) -> Self {
ContractState {
deps: self.deps.clone_state(),
env: self.env.clone(),
}
}
// set a new balance for the given address and return the old balance
pub fn update_account_balance(
&mut self,
addr: impl Into<String>,
balance: Vec<Coin>,
) -> Option<Vec<Coin>> {
// that's a bit annoying. We have to keep track of all balance changes for when we clone the state
// as there's no easy way of obtaining the up to date list of all balances from the querier...
let addr = addr.into();
self.deps._balances.insert(addr.clone(), balance.clone());
self.deps.querier.update_balance(addr, balance)
}
pub fn account_balance(
&self,
address: impl Into<String>,
denom: impl Into<String>,
) -> StdResult<Coin> {
self.deps().querier.query_balance(address, denom)
}
pub fn all_account_balances(&self, address: impl Into<String>) -> StdResult<Vec<Coin>> {
self.deps().querier.query_all_balances(address)
}
#[cfg(feature = "cw-storage-plus")]
pub fn save_map_value<'a, K, T>(&mut self, map: &Map<'a, K, T>, k: K, data: &T) -> StdResult<()>
where
T: Serialize + DeserializeOwned,
K: PrimaryKey<'a>,
{
map.save(&mut self.deps.storage, k, data)
}
#[cfg(feature = "cw-storage-plus")]
pub fn load_map_value<'a, K, T>(&self, map: &Map<'a, K, T>, k: K) -> StdResult<T>
where
T: Serialize + DeserializeOwned,
K: PrimaryKey<'a>,
{
map.load(&self.deps.storage, k)
}
#[cfg(feature = "cw-storage-plus")]
pub fn may_load_map_value<'a, K, T>(&self, map: &Map<'a, K, T>, k: K) -> StdResult<Option<T>>
where
T: Serialize + DeserializeOwned,
K: PrimaryKey<'a>,
{
map.may_load(&self.deps.storage, k)
}
#[cfg(feature = "cw-storage-plus")]
pub fn save_item<T>(&mut self, item: &Item<T>, data: &T) -> StdResult<()>
where
T: Serialize + DeserializeOwned,
{
item.save(&mut self.deps.storage, data)
}
#[cfg(feature = "cw-storage-plus")]
pub fn load_item<T>(&self, item: &Item<T>) -> StdResult<T>
where
T: Serialize + DeserializeOwned,
{
item.load(&self.deps.storage)
}
#[cfg(feature = "cw-storage-plus")]
pub fn may_load_item<T>(&self, item: &Item<T>) -> StdResult<Option<T>>
where
T: Serialize + DeserializeOwned,
{
item.may_load(&self.deps.storage)
}
pub fn read_key(&self, key: &[u8]) -> Option<Vec<u8>> {
self.deps.storage.get(key)
}
pub fn set_key_value(&mut self, key: &[u8], value: &[u8]) {
self.deps.storage.set(key, value)
}
pub fn deps(&self) -> Deps<'_> {
Deps {
storage: &self.deps.storage,
api: &self.deps.api,
querier: QuerierWrapper::new(&self.deps.querier),
}
}
pub fn deps_mut(&mut self) -> DepsMut<'_> {
DepsMut {
storage: &mut self.deps.storage,
api: &self.deps.api,
querier: QuerierWrapper::new(&self.deps.querier),
}
}
pub fn advance_blocks(&mut self, new_blocks: u64) {
self.advance_block_height(new_blocks);
self.advance_blocktime(new_blocks * AVERAGE_BLOCKTIME_SECS)
}
pub fn advance_block_height(&mut self, by: u64) {
self.env.block.height += by;
}
pub fn advance_blocktime(&mut self, by_secs: u64) {
self.env.block.time = self.env.block.time.plus_seconds(by_secs)
}
pub fn env(&self) -> &Env {
&self.env
}
pub fn env_cloned(&self) -> Env {
self.env.clone()
}
pub fn contract_address(&self) -> &Addr {
&self.env.contract.address
}
pub fn with_contract_address(mut self, address: impl Into<String>) -> Self {
self.env.contract.address = Addr::unchecked(address);
self
}
pub fn with_transaction_info(mut self, transaction: Option<TransactionInfo>) -> Self {
self.env.transaction = transaction;
self
}
#[cfg(feature = "state-importing")]
pub(crate) fn from_state_dump(state: ImportedContractState, custom_env: Option<Env>) -> Self {
let env = custom_env.unwrap_or_else(|| {
// this is not ideal, but we're making an assumption here that block time is approximately 5s
// at block 5000000, we had a timestamp of 1672411689
let mut env = mock_env();
env.block.chain_id = "nyx".to_string();
env.block.height = state.height;
if state.height > 5000000 {
let diff = state.height - 5000000;
env.block.time =
Timestamp::from_seconds(1672411689 + diff * AVERAGE_BLOCKTIME_SECS);
} else {
let diff = 5000000 - state.height;
env.block.time =
Timestamp::from_seconds(1672411689 - diff * AVERAGE_BLOCKTIME_SECS);
}
env
});
let deps = MockedDependencies::from_raw(state.data);
ContractState { deps, env }
}
#[cfg(feature = "state-importing")]
pub fn try_from_state_dump<P: AsRef<Path>>(
path: P,
custom_env: Option<Env>,
) -> Result<Self, DecodingError> {
Ok(ImportedContractState::try_load_from_file(path)?.into_test_mock(custom_env))
}
#[cfg(feature = "state-importing")]
pub fn dump_state<P: AsRef<Path>>(&self, output_path: P) -> Result<(), EncodingError> {
let mut data = Vec::new();
for (key, value) in self.deps.storage.range(None, None, Order::Ascending) {
data.push(KeyValue { key, value })
}
let state = ImportedContractState {
height: self.env.block.height,
data,
};
state.encode().to_file(output_path)
}
}
impl Default for ContractState {
fn default() -> Self {
ContractState {
deps: MockedDependencies::new_mock(),
env: mock_env(),
}
}
}
@@ -0,0 +1,34 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Addr, StdError};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum MockingError {
#[error(transparent)]
StdError {
#[from]
source: StdError,
},
#[error("attempted to add another contract mock that has the same address as an existing one - {address}")]
DuplicateContractAddress { address: Addr },
#[error("attempted to use a contract that doesn't exist - {address}")]
NonExistentContract { address: Addr },
#[error("contract execution failed with error: {error}. We called {contract} with {message}")]
ContractExecutionError {
message: String,
contract: Addr,
error: String,
},
#[error("contract query failed with error: {error}. We called {contract} with {message}")]
ContractQueryError {
message: String,
contract: Addr,
error: String,
},
}
@@ -0,0 +1,142 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::helpers::raw_msg_to_string;
use cosmwasm_std::{Addr, BankMsg, Binary, Coin, Event};
fn format_coins(coins: &[Coin]) -> String {
if coins.is_empty() {
"<zero>".to_string()
} else {
coins
.iter()
.map(|c| c.to_string())
.collect::<Vec<_>>()
.join(",")
}
}
// specifically for tokens included in `Execute` that go into the contract
#[derive(Debug, Clone)]
pub struct CrossContractTokenMove {
pub amount: Vec<Coin>,
pub sender: Addr,
pub receiver: Addr,
}
impl CrossContractTokenMove {
pub fn new(amount: Vec<Coin>, sender: Addr, receiver: Addr) -> Self {
Self {
amount,
sender,
receiver,
}
}
pub fn pretty(&self) -> String {
let total_amount = format_coins(&self.amount);
format!(
"{total_amount} will be transferred from {} to {} (CONTRACTS)",
self.sender, self.receiver
)
}
}
#[derive(Debug, Default)]
pub struct ExecutionResult {
pub steps: Vec<ExecutionStepResult>,
}
impl ExecutionResult {
pub fn new() -> Self {
Self { steps: Vec::new() }
}
pub fn pretty(&self) -> String {
let mut out = String::new();
for (i, step) in self.steps.iter().enumerate() {
out.push_str(&format!("STEP {}\n", i + 1));
out.push_str(&format!("{}\n", step.pretty()));
}
out
}
}
#[derive(Debug, Clone)]
pub struct FurtherExecution {
pub contract: Addr,
pub msg: Binary,
pub funds: Vec<Coin>,
}
impl FurtherExecution {
pub fn new(contract: String, msg: Binary, funds: Vec<Coin>) -> Self {
Self {
contract: Addr::unchecked(contract),
msg,
funds,
}
}
pub fn pretty(&self) -> String {
let msg = raw_msg_to_string(&self.msg);
let total_funds = format_coins(&self.funds);
format!(
"{} will be called with msg {msg} and {total_funds} funds",
self.contract
)
}
}
#[derive(Debug, Clone)]
pub struct ExecutionStepResult {
pub events: Vec<Event>,
pub incoming_tokens: Vec<CrossContractTokenMove>,
pub bank_msgs: Vec<BankMsg>,
pub further_execution: Vec<FurtherExecution>,
}
impl ExecutionStepResult {
pub fn pretty(&self) -> String {
let mut out = String::new();
// let's keep them squished for now...
let events = format!("EVENTS: {:?}\n", self.events);
out.push_str(&events);
if self.incoming_tokens.iter().any(|c| !c.amount.is_empty()) {
out.push_str("MOVED TOKENS (CONTRACTS):\n");
for incoming in &self.incoming_tokens {
if !incoming.amount.is_empty() {
out.push_str(&format!("{}\n", incoming.pretty()))
}
}
}
if !self.bank_msgs.is_empty() {
out.push_str("MOVED TOKENS (BANK):\n");
for bank in &self.bank_msgs {
let formatted = match bank {
BankMsg::Send { to_address, amount } => format!(
"{} will be transferred to {to_address}",
format_coins(amount)
),
BankMsg::Burn { amount } => format!("{} WILL BE BURNT", format_coins(amount)),
_ => "unknown variant of BankMsg was introduced!".to_string(),
};
out.push_str(&format!("{formatted}\n"))
}
}
if !self.further_execution.is_empty() {
out.push_str("FURTHER CONTRACT CALLS:\n");
for further_exec in &self.further_execution {
out.push_str(&format!("{}\n", further_exec.pretty()))
}
}
out
}
}
@@ -0,0 +1,41 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::{Binary, BlockInfo, Env, StdResult};
use serde::de::DeserializeOwned;
use serde::Serialize;
#[cfg(feature = "rand")]
pub fn test_rng() -> rand_chacha::ChaCha20Rng {
use rand_chacha::rand_core::SeedableRng;
let dummy_seed = [42u8; 32];
rand_chacha::ChaCha20Rng::from_seed(dummy_seed)
}
pub fn env_with_block_info(info: BlockInfo) -> Env {
let mut env = mock_env();
env.block = info;
env
}
pub fn deserialize_msg<M: DeserializeOwned>(raw: &Binary) -> StdResult<M> {
cosmwasm_std::from_binary(raw)
}
pub fn serialize_msg<M: Serialize>(msg: &M) -> StdResult<Binary> {
cosmwasm_std::to_binary(msg)
}
// used only for purposes of providing more informative error messages
pub(crate) fn raw_msg_to_string(raw: &Binary) -> String {
#[cfg(not(feature = "serde_json"))]
return "<serde_json feature is not enabled - can't format the message>".to_string();
#[cfg(feature = "serde_json")]
match serde_json::from_slice::<serde_json::Value>(raw.as_slice()) {
Ok(deserialized) => deserialized.to_string(),
Err(_) => "ERR: COULD NOT RECOVER THE ORIGINAL MESSAGE".to_string(),
}
}
@@ -0,0 +1,35 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod contract_mock;
mod error;
mod execution;
mod helpers;
mod mock_api;
mod multi_contract_mock;
mod raw_state;
mod single_contract_mock;
mod traits;
pub use contract_mock::ContractState;
pub use error::MockingError;
pub use helpers::{deserialize_msg, env_with_block_info, serialize_msg};
#[cfg(feature = "state-importing")]
pub use raw_state::ImportedContractState;
#[cfg(feature = "contract-mocks")]
pub use multi_contract_mock::MultiContractMock;
#[cfg(feature = "contract-mocks")]
pub use single_contract_mock::SingleContractMock;
#[cfg(feature = "contract-mocks")]
pub use execution::{
CrossContractTokenMove, ExecutionResult, ExecutionStepResult, FurtherExecution,
};
#[cfg(feature = "testable-trait")]
pub use traits::TestableContract;
pub const AVERAGE_BLOCKTIME_SECS: u64 = 5;
@@ -0,0 +1,143 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// unfortunately we have to redefine cosmwasm' MockApi,
// as in cw1.0 they have set `CANONICAL_LENGTH` to 54 which makes
// `addr_validate` of our existing contracts fail (say of 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw')
// this has changed in 1.2 but we can't use that version yet...
use cosmwasm_std::testing::{digit_sum, riffle_shuffle, MockApi};
use cosmwasm_std::{
Addr, Api, CanonicalAddr, RecoverPubkeyError, StdError, StdResult, VerificationError,
};
const CANONICAL_LENGTH: usize = 90; // n = 45
const SHUFFLES_ENCODE: usize = 10;
const SHUFFLES_DECODE: usize = 2;
#[derive(Copy, Clone)]
pub(crate) struct CW12MockApi {
inner: MockApi,
canonical_length: usize,
}
impl Default for CW12MockApi {
fn default() -> Self {
CW12MockApi {
inner: MockApi::default(),
canonical_length: CANONICAL_LENGTH,
}
}
}
// whatever we can, hand over to 1.0 MockApi
impl Api for CW12MockApi {
fn addr_validate(&self, input: &str) -> StdResult<Addr> {
let canonical = self.addr_canonicalize(input)?;
let normalized = self.addr_humanize(&canonical)?;
if input != normalized {
return Err(StdError::generic_err(
"Invalid input: address not normalized",
));
}
Ok(Addr::unchecked(input))
}
fn addr_canonicalize(&self, input: &str) -> StdResult<CanonicalAddr> {
// Dummy input validation. This is more sophisticated for formats like bech32, where format and checksum are validated.
let min_length = 3;
let max_length = self.canonical_length;
if input.len() < min_length {
return Err(StdError::generic_err(
format!("Invalid input: human address too short for this mock implementation (must be >= {min_length})."),
));
}
if input.len() > max_length {
return Err(StdError::generic_err(
format!("Invalid input: human address too long for this mock implementation (must be <= {max_length})."),
));
}
// mimicks formats like hex or bech32 where different casings are valid for one address
let normalized = input.to_lowercase();
let mut out = Vec::from(normalized);
// pad to canonical length with NULL bytes
out.resize(self.canonical_length, 0x00);
// content-dependent rotate followed by shuffle to destroy
// the most obvious structure (https://github.com/CosmWasm/cosmwasm/issues/552)
let rotate_by = digit_sum(&out) % self.canonical_length;
out.rotate_left(rotate_by);
for _ in 0..SHUFFLES_ENCODE {
out = riffle_shuffle(&out);
}
Ok(out.into())
}
fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult<Addr> {
if canonical.len() != self.canonical_length {
return Err(StdError::generic_err(
"Invalid input: canonical address length not correct",
));
}
let mut tmp: Vec<u8> = canonical.clone().into();
// Shuffle two more times which restored the original value (24 elements are back to original after 20 rounds)
for _ in 0..SHUFFLES_DECODE {
tmp = riffle_shuffle(&tmp);
}
// Rotate back
let rotate_by = digit_sum(&tmp) % self.canonical_length;
tmp.rotate_right(rotate_by);
// Remove NULL bytes (i.e. the padding)
let trimmed = tmp.into_iter().filter(|&x| x != 0x00).collect();
// decode UTF-8 bytes into string
let human = String::from_utf8(trimmed)?;
Ok(Addr::unchecked(human))
}
fn secp256k1_verify(
&self,
message_hash: &[u8],
signature: &[u8],
public_key: &[u8],
) -> Result<bool, VerificationError> {
self.inner
.secp256k1_verify(message_hash, signature, public_key)
}
fn secp256k1_recover_pubkey(
&self,
message_hash: &[u8],
signature: &[u8],
recovery_param: u8,
) -> Result<Vec<u8>, RecoverPubkeyError> {
self.inner
.secp256k1_recover_pubkey(message_hash, signature, recovery_param)
}
fn ed25519_verify(
&self,
message: &[u8],
signature: &[u8],
public_key: &[u8],
) -> Result<bool, VerificationError> {
self.inner.ed25519_verify(message, signature, public_key)
}
fn ed25519_batch_verify(
&self,
messages: &[&[u8]],
signatures: &[&[u8]],
public_keys: &[&[u8]],
) -> Result<bool, VerificationError> {
self.inner
.ed25519_batch_verify(messages, signatures, public_keys)
}
fn debug(&self, message: &str) {
self.inner.debug(message)
}
}
@@ -0,0 +1,405 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::contract_mock::ContractState;
use crate::execution::{
CrossContractTokenMove, ExecutionResult, ExecutionStepResult, FurtherExecution,
};
use crate::helpers::raw_msg_to_string;
use crate::traits::sealed;
use crate::{serialize_msg, MockingError, TestableContract};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{
Addr, Binary, CosmosMsg, Env, MessageInfo, QueryResponse, ReplyOn, Response, WasmMsg,
};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::collections::HashMap;
struct MockedContract {
state: ContractState,
entry_points: Box<dyn sealed::ErasedTestableContract>,
}
impl MockedContract {
fn new<C: TestableContract + 'static>(state: ContractState) -> Self {
MockedContract {
state,
entry_points: Box::new(C::new()),
}
}
}
#[derive(Default)]
pub struct MultiContractMock {
contracts: HashMap<Addr, MockedContract>,
}
impl MultiContractMock {
#[cfg(feature = "rand")]
fn generate_new_contract_address(&self) -> Addr {
use rand_chacha::rand_core::RngCore;
let mut rng = crate::helpers::test_rng();
loop {
// for the testing purposes u64 contains enough entropy
// (I could even argue u8 would be sufficient)
// as I doubt anyone would want to generate so many contract names
// they would have started colliding...
let candidate_id = rng.next_u64();
let name = Addr::unchecked(format!("new-contract{candidate_id}"));
if !self.contracts.contains_key(&name) {
return name;
}
}
}
pub fn new() -> Self {
MultiContractMock {
contracts: Default::default(),
}
}
pub fn add_contract<C: TestableContract + 'static>(
&mut self,
contract_state: ContractState,
) -> Result<(), MockingError> {
let address = contract_state.contract_address().clone();
if self
.contracts
.contains_key(contract_state.contract_address())
{
Err(MockingError::DuplicateContractAddress { address })
} else {
let mocked = MockedContract::new::<C>(contract_state);
self.contracts.insert(address, mocked);
Ok(())
}
}
pub fn with_contract<C: TestableContract + 'static>(
mut self,
state: ContractState,
) -> Result<Self, MockingError> {
self.add_contract::<C>(state)?;
Ok(self)
}
pub fn advance_blocks(&mut self, new_blocks: u64) {
for contract in self.contracts.values_mut() {
contract.state.advance_blocks(new_blocks)
}
}
pub fn advance_block_height(&mut self, by: u64) {
for contract in self.contracts.values_mut() {
contract.state.advance_block_height(by)
}
}
pub fn advance_blocktime(&mut self, by_secs: u64) {
for contract in self.contracts.values_mut() {
contract.state.advance_blocktime(by_secs)
}
}
fn execute_step(
&mut self,
contract_address: impl Into<String>,
info: MessageInfo,
binary_msg: Binary,
) -> Result<ExecutionStepResult, MockingError> {
let addr = Addr::unchecked(contract_address.into());
let contract =
self.contracts
.get_mut(&addr)
.ok_or_else(|| MockingError::NonExistentContract {
address: addr.clone(),
})?;
let env = contract.state.env_cloned();
let deps = contract.state.deps_mut();
let res = match contract
.entry_points
.execute(deps, env, info, binary_msg.clone())
{
Ok(res) => res,
Err(error) => {
return Err(MockingError::ContractExecutionError {
message: raw_msg_to_string(&binary_msg),
contract: addr,
error,
})
}
};
let mut bank_msgs = Vec::new();
let mut further_execution = Vec::new();
let mut incoming_tokens = Vec::new();
for sub_msg in res.messages {
if sub_msg.reply_on != ReplyOn::Never {
unimplemented!("currently there's no support for 'reply_on'")
}
match sub_msg.msg {
CosmosMsg::Bank(bank_msg) => bank_msgs.push(bank_msg),
CosmosMsg::Wasm(wasm_msg) => {
match wasm_msg {
WasmMsg::Execute { contract_addr, msg, funds } => {
incoming_tokens.push(CrossContractTokenMove::new(funds.clone(), addr.clone(), Addr::unchecked(&contract_addr)));
further_execution.push(FurtherExecution::new(contract_addr, msg, funds))
}
_ => unimplemented!("currently we only support 'ExecuteMsg' for 'WasmMsg'")
}
}
// other variants might get support later on
_ => unimplemented!("currently there's no support for sub msgs different from 'WasmMsg' or 'BankMsg")
}
}
Ok(ExecutionStepResult {
events: res.events,
incoming_tokens,
bank_msgs,
further_execution,
})
}
// TODO: verify that this is the actual order of execution of sub messages in cosmwasm
fn execute_branch(
&mut self,
res: &mut ExecutionResult,
contract: String,
info: MessageInfo,
msg: Binary,
) -> Result<(), MockingError> {
let step_res = self.execute_step(contract.clone(), info, msg)?;
res.steps.push(step_res.clone());
for further_exec in step_res.further_execution {
let info = mock_info(&contract, &further_exec.funds);
self.execute_branch(
res,
further_exec.contract.into_string(),
info,
further_exec.msg,
)?
}
Ok(())
}
pub fn contract_state(
&self,
contract_address: impl Into<String>,
) -> Result<&ContractState, MockingError> {
let addr = Addr::unchecked(contract_address.into());
let contract =
self.contracts
.get(&addr)
.ok_or_else(|| MockingError::NonExistentContract {
address: addr.clone(),
})?;
Ok(&contract.state)
}
pub fn contract_state_mut(
&mut self,
contract_address: impl Into<String>,
) -> Result<&mut ContractState, MockingError> {
let addr = Addr::unchecked(contract_address.into());
let contract =
self.contracts
.get_mut(&addr)
.ok_or_else(|| MockingError::NonExistentContract {
address: addr.clone(),
})?;
Ok(&mut contract.state)
}
// TODO: add support for sub msgs in instantiate response
pub fn instantiate<C>(
&mut self,
custom_env: Option<Env>,
info: MessageInfo,
msg: C::InstantiateMsg,
) -> Result<Response, C::ContractError>
where
C: TestableContract + 'static,
{
// if custom environment wasn't provided, generate a pseudorandom address so that it wouldn't
// clash with any existing contracts
let env = custom_env.unwrap_or_else(|| {
let mut env = mock_env();
env.contract.address = self.generate_new_contract_address();
env
});
let mut state = ContractState::new_with_env(env);
let env = state.env_cloned();
let deps = state.deps_mut();
C::instantiate(deps, env, info, msg)
}
pub fn execute_full<C>(
&mut self,
initial_contract: impl Into<String>,
info: MessageInfo,
msg: C::ExecuteMsg,
) -> Result<ExecutionResult, MockingError>
where
C: TestableContract + 'static,
C::ExecuteMsg: Serialize,
{
let mut execution_result = ExecutionResult::new();
let serialized_msg = serialize_msg(&msg)?;
self.execute_branch(
&mut execution_result,
initial_contract.into(),
info,
serialized_msg,
)?;
Ok(execution_result)
}
// provide unchecked variant of execute to return original error enum
pub fn unchecked_execute<C>(
&mut self,
contract_address: impl Into<String>,
info: MessageInfo,
msg: C::ExecuteMsg,
) -> Result<Response, C::ContractError>
where
C: TestableContract + 'static,
{
let addr = Addr::unchecked(contract_address.into());
let contract = self
.contracts
.get_mut(&addr)
.expect("specified contract does not exist");
let env = contract.state.env_cloned();
let deps = contract.state.deps_mut();
C::execute(deps, env, info, msg)
}
// executes only the top level message
pub fn execute<C>(
&mut self,
contract_address: impl Into<String>,
info: MessageInfo,
msg: C::ExecuteMsg,
) -> Result<Response, MockingError>
where
C: TestableContract + 'static,
C::ExecuteMsg: Serialize,
{
let addr = Addr::unchecked(contract_address.into());
let contract =
self.contracts
.get_mut(&addr)
.ok_or_else(|| MockingError::NonExistentContract {
address: addr.clone(),
})?;
let env = contract.state.env_cloned();
let deps = contract.state.deps_mut();
let serialized_msg = serialize_msg(&msg)?;
C::execute(deps, env, info, msg).map_err(|err| MockingError::ContractExecutionError {
message: raw_msg_to_string(&serialized_msg),
contract: addr,
error: err.to_string(),
})
}
// provide unchecked variant of query to return original error enum
pub fn unchecked_query<C, T>(
&self,
contract_address: impl Into<String>,
msg: C::QueryMsg,
) -> Result<T, C::ContractError>
where
C: TestableContract + 'static,
T: DeserializeOwned,
{
let addr = Addr::unchecked(contract_address.into());
let contract = self
.contracts
.get(&addr)
.expect("specified contract does not exist");
let env = contract.state.env_cloned();
let deps = contract.state.deps();
C::query(deps, env, msg).map(|res| serde_json::from_slice(&res).unwrap())
}
pub fn query<C>(
&self,
contract_address: impl Into<String>,
msg: C::QueryMsg,
) -> Result<QueryResponse, MockingError>
where
C: TestableContract + 'static,
C::QueryMsg: Serialize,
{
let addr = Addr::unchecked(contract_address.into());
let contract =
self.contracts
.get(&addr)
.ok_or_else(|| MockingError::NonExistentContract {
address: addr.clone(),
})?;
let env = contract.state.env_cloned();
let deps = contract.state.deps();
let serialized_msg = serialize_msg(&msg)?;
C::query(deps, env, msg).map_err(|err| MockingError::ContractQueryError {
message: raw_msg_to_string(&serialized_msg),
contract: addr,
error: err.to_string(),
})
}
pub fn query_de<C, T>(
&self,
contract_address: impl Into<String>,
msg: C::QueryMsg,
) -> Result<T, MockingError>
where
C: TestableContract + 'static,
C::QueryMsg: Serialize,
T: DeserializeOwned,
{
self.query::<C>(contract_address, msg)
.map(|res| serde_json::from_slice(&res).unwrap())
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[test]
fn converting_msg_to_string() {
#[derive(Serialize, Deserialize)]
struct Dummy {
field1: String,
field2: u32,
field3: Vec<u32>,
}
let dummy = Dummy {
field1: "aaaa".to_string(),
field2: 42,
field3: vec![1, 2, 3, 4],
};
let bin = serialize_msg(&dummy).unwrap();
let expected = r#"{"field1":"aaaa","field2":42,"field3":[1,2,3,4]}"#;
let stringified = raw_msg_to_string(&bin);
assert_eq!(expected, stringified)
}
}
@@ -0,0 +1,160 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::contract_mock::ContractState;
use base64::{engine::general_purpose, Engine};
use cosmwasm_std::Env;
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io;
use std::num::ParseIntError;
use std::path::Path;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum DecodingError {
#[error("failed to parse the block height information of the state dump: {source}")]
MalformedBlockHeight {
#[from]
source: ParseIntError,
},
#[error("failed to open the specified state dump: {source}")]
FileOpenError {
#[from]
source: io::Error,
},
#[error("failed to decode the provided json state: {source}")]
JsonDecodeError {
#[from]
source: serde_json::Error,
},
#[error("failed to decode one of the state keys: {source}")]
HexDecodeError {
#[from]
source: hex::FromHexError,
},
#[error("failed to decode one of the state values: {source}")]
Base64DecodeError {
#[from]
source: base64::DecodeError,
},
}
#[derive(Debug, Error)]
pub enum EncodingError {
#[error("failed to open the specified state dump file: {source}")]
FileOpenError {
#[from]
source: io::Error,
},
#[error("failed to encode the provided json state: {source}")]
JsonEncodeError {
#[from]
source: serde_json::Error,
},
}
pub struct ImportedContractState {
pub height: u64,
pub data: Vec<KeyValue>,
}
pub struct KeyValue {
pub key: Vec<u8>,
pub value: Vec<u8>,
}
impl ImportedContractState {
pub fn try_from_json(value: &str) -> Result<Self, DecodingError> {
RawContractState::from_json(value)?.decode()
}
pub fn try_load_from_file<P: AsRef<Path>>(path: P) -> Result<Self, DecodingError> {
RawContractState::from_file(path)?.decode()
}
pub fn find_value(&self, key: &[u8]) -> Option<&[u8]> {
self.data
.iter()
.find(|kv| kv.key == key)
.map(|kv| kv.value.as_ref())
}
pub fn into_test_mock(self, custom_env: Option<Env>) -> ContractState {
ContractState::from_state_dump(self, custom_env)
}
pub(crate) fn encode(self) -> RawContractState {
RawContractState {
height: self.height.to_string(),
result: self.data.into_iter().map(Into::into).collect(),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct RawContractState {
height: String,
result: Vec<RawKeyValue>,
}
impl RawContractState {
fn decode(self) -> Result<ImportedContractState, DecodingError> {
Ok(ImportedContractState {
height: self.height.parse()?,
data: self
.result
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, _>>()?,
})
}
fn from_json(value: &str) -> Result<Self, DecodingError> {
Ok(serde_json::from_str(value)?)
}
fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, DecodingError> {
let file = File::open(path)?;
Ok(serde_json::from_reader(file)?)
}
pub(crate) fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), EncodingError> {
let file = File::open(path)?;
Ok(serde_json::to_writer(file, &self)?)
}
}
#[derive(Serialize, Deserialize, Debug)]
struct RawKeyValue {
// encoded as hex
key: String,
// encoded as base64
value: String,
}
impl TryFrom<RawKeyValue> for KeyValue {
type Error = DecodingError;
fn try_from(raw: RawKeyValue) -> Result<Self, Self::Error> {
Ok(KeyValue {
key: hex::decode(&raw.key)?,
value: general_purpose::STANDARD.decode(&raw.value)?,
})
}
}
impl From<KeyValue> for RawKeyValue {
fn from(decoded: KeyValue) -> Self {
RawKeyValue {
key: hex::encode(decoded.key),
value: general_purpose::STANDARD.encode(decoded.value),
}
}
}
@@ -0,0 +1,84 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{ContractState, TestableContract};
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::{from_slice, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response};
use serde::de::DeserializeOwned;
use std::marker::PhantomData;
pub struct SingleContractMock<C> {
pub state: ContractState,
_contract: PhantomData<C>,
}
impl<C: TestableContract> SingleContractMock<C> {
pub fn new_empty() -> Self {
SingleContractMock {
state: Default::default(),
_contract: PhantomData,
}
}
pub fn new(state: ContractState) -> Self {
SingleContractMock {
state,
_contract: PhantomData,
}
}
pub fn deps(&self) -> Deps<'_> {
self.state.deps()
}
pub fn deps_mut(&mut self) -> DepsMut<'_> {
self.state.deps_mut()
}
pub fn env(&self) -> &Env {
self.state.env()
}
pub fn env_cloned(&self) -> Env {
self.state.env_cloned()
}
pub fn instantiate(
custom_env: Option<Env>,
info: MessageInfo,
msg: C::InstantiateMsg,
) -> Result<(Self, Response), C::ContractError> {
// if we're instantiating fresh contract it means there was no pre-existing state
let env = custom_env.unwrap_or_else(mock_env);
let state = ContractState::new_with_env(env);
let mut this = Self::new(state);
let env = this.state.env_cloned();
let deps = this.state.deps_mut();
let res = C::instantiate(deps, env, info, msg)?;
Ok((this, res))
}
pub fn execute(
&mut self,
info: MessageInfo,
msg: C::ExecuteMsg,
) -> Result<Response, C::ContractError> {
let env = self.state.env_cloned();
let deps = self.state.deps_mut();
C::execute(deps, env, info, msg)
}
pub fn query(&self, msg: C::QueryMsg) -> Result<QueryResponse, C::ContractError> {
let env = self.state.env_cloned();
let deps = self.state.deps();
C::query(deps, env, msg)
}
pub fn query_de<T: DeserializeOwned>(&self, msg: C::QueryMsg) -> Result<T, C::ContractError> {
self.query(msg).map(|res| from_slice(&res).unwrap())
}
}
@@ -0,0 +1,175 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Deps, DepsMut, Env, MessageInfo, QueryResponse, Response};
use serde::de::DeserializeOwned;
// TODO: see if it's possible to create a macro to auto-derive it
// if you intend to use the MultiContractMock, you need to implement this trait
// for your contract
/// ```
/// use cosmwasm_std::{
/// entry_point, Deps, DepsMut, Env, MessageInfo, Querier, QueryResponse, Response, StdError,
/// Storage,
/// };
/// use cosmwasm_contract_testing::TestableContract;
///
/// type ExecuteMsg = ();
/// type QueryMsg = ();
/// type InstantiateMsg = ();
/// type ContractError = StdError;
///
/// #[entry_point]
/// pub fn instantiate (
/// deps: DepsMut,
/// env: Env,
/// info: MessageInfo,
/// msg: InstantiateMsg,
/// ) -> Result<Response, ContractError> {
/// Ok(Default::default())
/// }
///
/// #[entry_point]
/// pub fn execute(
/// deps: DepsMut,
/// env: Env,
/// info: MessageInfo,
/// msg: ExecuteMsg,
/// ) -> Result<Response, ContractError> {
/// Ok(Default::default())
/// }
///
/// #[entry_point]
/// pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
/// Ok(Default::default())
/// }
///
/// struct MyContract;
///
/// impl TestableContract for MyContract {
/// type ContractError = ContractError;
/// type InstantiateMsg = InstantiateMsg;
/// type ExecuteMsg = ExecuteMsg;
/// type QueryMsg = QueryMsg;
///
/// fn new() -> Self {
/// MyContract
/// }
///
/// fn instantiate(
/// deps: DepsMut<'_>,
/// env: Env,
/// info: MessageInfo,
/// msg: Self::InstantiateMsg,
/// ) -> Result<Response, Self::ContractError> {
/// instantiate(deps, env, info, msg)
/// }
///
/// fn execute(
/// deps: DepsMut<'_>,
/// env: Env,
/// info: MessageInfo,
/// msg: Self::ExecuteMsg,
/// ) -> Result<Response, Self::ContractError> {
/// execute(deps, env, info, msg)
/// }
///
/// fn query(
/// deps: Deps<'_>,
/// env: Env,
/// msg: Self::QueryMsg,
/// ) -> Result<QueryResponse, Self::ContractError> {
/// query(deps, env, msg)
/// }
/// }
/// ```
pub trait TestableContract {
type ContractError: ToString;
type InstantiateMsg: DeserializeOwned;
type ExecuteMsg: DeserializeOwned;
type QueryMsg: DeserializeOwned;
fn new() -> Self;
fn instantiate(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: Self::InstantiateMsg,
) -> Result<Response, Self::ContractError>;
fn execute(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: Self::ExecuteMsg,
) -> Result<Response, Self::ContractError>;
fn query(
deps: Deps<'_>,
env: Env,
msg: Self::QueryMsg,
) -> Result<QueryResponse, Self::ContractError>;
}
pub(crate) mod sealed {
use crate::deserialize_msg;
use crate::traits::TestableContract;
use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response};
pub(crate) trait ErasedTestableContract {
fn query(&self, deps: Deps<'_>, env: Env, raw_msg: Binary)
-> Result<QueryResponse, String>;
fn execute(
&self,
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
raw_msg: Binary,
) -> Result<Response, String>;
fn instantiate(
&self,
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
raw_msg: Binary,
) -> Result<Response, String>;
}
impl<T: TestableContract> ErasedTestableContract for T {
fn query(
&self,
deps: Deps<'_>,
env: Env,
raw_msg: Binary,
) -> Result<QueryResponse, String> {
let msg = deserialize_msg(&raw_msg).expect("failed to deserialize 'QueryMsg'");
<Self as TestableContract>::query(deps, env, msg).map_err(|err| err.to_string())
}
fn execute(
&self,
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
raw_msg: Binary,
) -> Result<Response, String> {
let msg = deserialize_msg(&raw_msg).expect("failed to deserialize 'ExecuteMsg'");
<Self as TestableContract>::execute(deps, env, info, msg).map_err(|err| err.to_string())
}
fn instantiate(
&self,
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
raw_msg: Binary,
) -> Result<Response, String> {
let msg = deserialize_msg(&raw_msg).expect("failed to deserialize 'InstantiateMsg'");
<Self as TestableContract>::instantiate(deps, env, info, msg)
.map_err(|err| err.to_string())
}
}
}
+3 -1
View File
@@ -6,9 +6,11 @@ members = [
"multisig/cw3-flex-multisig", "multisig/cw3-flex-multisig",
"multisig/cw4-group", "multisig/cw4-group",
"coconut-test", "coconut-test",
"coconut-dkg" "coconut-dkg",
] ]
exclude = ["integration-tests"]
[workspace.package] [workspace.package]
authors = ["Nym Technologies SA"] authors = ["Nym Technologies SA"]
repository = "https://github.com/nymtech/nym" repository = "https://github.com/nymtech/nym"
+2 -2
View File
@@ -9,7 +9,7 @@ use cw_storage_plus::Bound;
use crate::storage::{self, SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT, SPEND_CREDENTIAL_PAGE_MAX_LIMIT}; use crate::storage::{self, SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT, SPEND_CREDENTIAL_PAGE_MAX_LIMIT};
pub(crate) fn query_all_spent_credentials_paged( pub fn query_all_spent_credentials_paged(
deps: Deps<'_>, deps: Deps<'_>,
start_after: Option<String>, start_after: Option<String>,
limit: Option<u32>, limit: Option<u32>,
@@ -37,7 +37,7 @@ pub(crate) fn query_all_spent_credentials_paged(
)) ))
} }
pub(crate) fn query_spent_credential( pub fn query_spent_credential(
deps: Deps<'_>, deps: Deps<'_>,
blinded_serial_number: String, blinded_serial_number: String,
) -> StdResult<SpendCredentialResponse> { ) -> StdResult<SpendCredentialResponse> {
@@ -6,19 +6,17 @@ use crate::error::ContractError;
use coconut_dkg_common::types::{Epoch, InitialReplacementData}; use coconut_dkg_common::types::{Epoch, InitialReplacementData};
use cosmwasm_std::Storage; use cosmwasm_std::Storage;
pub(crate) fn query_current_epoch(storage: &dyn Storage) -> Result<Epoch, ContractError> { pub fn query_current_epoch(storage: &dyn Storage) -> Result<Epoch, ContractError> {
CURRENT_EPOCH CURRENT_EPOCH
.load(storage) .load(storage)
.map_err(|_| ContractError::EpochNotInitialised) .map_err(|_| ContractError::EpochNotInitialised)
} }
pub(crate) fn query_current_epoch_threshold( pub fn query_current_epoch_threshold(storage: &dyn Storage) -> Result<Option<u64>, ContractError> {
storage: &dyn Storage,
) -> Result<Option<u64>, ContractError> {
Ok(THRESHOLD.may_load(storage)?) Ok(THRESHOLD.may_load(storage)?)
} }
pub(crate) fn query_initial_dealers( pub fn query_initial_dealers(
storage: &dyn Storage, storage: &dyn Storage,
) -> Result<Option<InitialReplacementData>, ContractError> { ) -> Result<Option<InitialReplacementData>, ContractError> {
Ok(INITIAL_REPLACEMENT_DATA.may_load(storage)?) Ok(INITIAL_REPLACEMENT_DATA.may_load(storage)?)
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "integration-tests"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dev-dependencies]
cosmwasm-std = "1.0.0"
cw-storage-plus = "0.13.4"
cosmwasm-contract-testing = { path = "../../common/cosmwasm-smart-contracts/testing" }
nym-mixnet-contract-common = { path= "../../common/cosmwasm-smart-contracts/mixnet-contract" }
nym-vesting-contract-common = { path= "../../common/cosmwasm-smart-contracts/vesting-contract" }
nym-vesting-contract = { path = "../vesting", features = ["testing-mocks"] }
nym-mixnet-contract = { path = "../mixnet", features = ["testing-mocks"] }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
@@ -0,0 +1,128 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_contract_testing::{env_with_block_info, ContractState, MultiContractMock};
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{Addr, BankMsg, BlockInfo, Timestamp};
use cw_storage_plus::Map;
use mixnet_contract::MixnetContract;
use nym_mixnet_contract_common::rewarding::PendingRewardResponse;
use vesting_contract::vesting::Account;
use vesting_contract::VestingContract;
// this is not directly exported by the vesting contract, but we can easily recreate it
const VESTING_ACCOUNTS: Map<'_, Addr, Account> = Map::new("acc");
// hardcoded values from the data dump sources
const MIXNET_CONTRACT_ADDRESS: &str =
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
const VESTING_CONTRACT_ADDRESS: &str =
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
fn set_mock() -> MultiContractMock {
let current_block = BlockInfo {
height: 1928125,
time: Timestamp::from_seconds(1676482616),
chain_id: "nymnet".to_string(),
};
let custom_env = env_with_block_info(current_block);
let mix_mock = ContractState::try_from_state_dump(
"contract-states/15.02.23-173000-qwerty-mixnet.json",
Some(custom_env.clone()),
)
.unwrap()
.with_contract_address(MIXNET_CONTRACT_ADDRESS);
let vesting_mock = ContractState::try_from_state_dump(
"contract-states/15.02.23-173000-qwerty-vesting.json",
Some(custom_env),
)
.unwrap()
.with_contract_address(VESTING_CONTRACT_ADDRESS);
let mut multi_mock = MultiContractMock::new();
multi_mock.add_contract::<MixnetContract>(mix_mock).unwrap();
multi_mock
.add_contract::<VestingContract>(vesting_mock)
.unwrap();
multi_mock
}
#[test]
fn claiming_vesting_delegator_rewards() {
let mut multi_mock = set_mock();
let dummy_account = Addr::unchecked("n1ktpuwtweku40uaxcl4uq7mdkkmjeh698g3l3c8");
// do some queries to verify state is updated correctly for both contracts
let pending_reward: PendingRewardResponse = multi_mock
.query_de::<MixnetContract, _>(
MIXNET_CONTRACT_ADDRESS,
nym_mixnet_contract_common::QueryMsg::GetPendingDelegatorReward {
address: dummy_account.to_string(),
mix_id: 8,
proxy: Some(VESTING_CONTRACT_ADDRESS.to_string()),
},
)
.unwrap();
let pending_reward_amount = pending_reward.amount_earned.unwrap().amount;
// we can also get whatever we want directly from storage!
let contract_state = multi_mock.contract_state(VESTING_CONTRACT_ADDRESS).unwrap();
let vesting_account = contract_state
.load_map_value(&VESTING_ACCOUNTS, dummy_account.clone())
.unwrap();
let vesting_balance = vesting_account
.load_balance(contract_state.deps().storage)
.unwrap();
let res = multi_mock.execute_full::<VestingContract>(
VESTING_CONTRACT_ADDRESS,
mock_info(dummy_account.as_str(), &[]),
nym_vesting_contract_common::ExecuteMsg::ClaimDelegatorReward { mix_id: 8 },
);
match res {
Ok(success) => {
println!("{}", success.pretty());
// check the output
// unfortunately `ClaimDelegatorReward` doesn't emit any events, but we can see
// it's going to result into a call into the mixnet contract
assert_eq!(
success.steps[0].further_execution[0].contract.as_str(),
MIXNET_CONTRACT_ADDRESS
);
// mixnet contract will emit a `v2_withdraw_delegator_reward` event
// and call the vesting contract again
assert_eq!(
"v2_withdraw_delegator_reward",
success.steps[1].events[0].ty
);
assert_eq!(
success.steps[1].further_execution[0].contract.as_str(),
VESTING_CONTRACT_ADDRESS
);
// and will move our reward amount into the vesting contract...
assert!(matches!(
&success.steps[1].bank_msgs[0],
BankMsg::Send { to_address, amount }
if to_address == VESTING_CONTRACT_ADDRESS && amount[0].amount == pending_reward_amount
));
// and finally the vesting contract will emit the mistyped `track_reaward` event
assert_eq!("track_reaward", success.steps[2].events[0].ty);
}
Err(err) => panic!("{err}"),
}
// state after execution (we can still read values the 'normal' way)
let updated_state = multi_mock.contract_state(VESTING_CONTRACT_ADDRESS).unwrap();
let deps = updated_state.deps();
let vesting_account = VESTING_ACCOUNTS.load(deps.storage, dummy_account).unwrap();
let new_vesting_balance = vesting_account.load_balance(deps.storage).unwrap();
assert_eq!(new_vesting_balance, vesting_balance + pending_reward_amount)
}
+9 -1
View File
@@ -36,15 +36,23 @@ serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" } thiserror = { version = "1.0.23" }
time = { version = "0.3", features = ["macros"] } time = { version = "0.3", features = ["macros"] }
cosmwasm-contract-testing = { path = "../../common/cosmwasm-smart-contracts/testing", default-features = false, features = ["testable-trait"], optional = true }
[dev-dependencies] [dev-dependencies]
cosmwasm-schema = "1.0.0" cosmwasm-schema = "1.0.0"
rand_chacha = "0.2" rand_chacha = "0.2"
#rand = "0.7"
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] } nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
cosmwasm-contract-testing = { path = "../../common/cosmwasm-smart-contracts/testing" }
[build-dependencies] [build-dependencies]
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc"] } vergen = { version = "5", default-features = false, features = ["build", "git", "rustc"] }
[[example]]
name = "mock_testing"
required-features = ["testing-mocks"]
[features] [features]
default = [] default = []
contract-testing = ["mixnet-contract-common/contract-testing"] contract-testing = ["mixnet-contract-common/contract-testing"]
testing-mocks = ["contract-testing", "cosmwasm-contract-testing"]
+185
View File
@@ -0,0 +1,185 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_contract_testing::{env_with_block_info, ContractState, SingleContractMock};
use cosmwasm_std::from_slice;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{BlockInfo, Timestamp};
use mixnet_contract::mixnet_contract_settings::storage::CONTRACT_STATE;
use mixnet_contract::mixnodes::queries::query_mixnode_details;
use mixnet_contract::MixnetContract;
use mixnet_contract::{mixnet_contract_settings, mixnodes};
use mixnet_contract_common::{ContractState as MixnetContractState, ExecuteMsg, Layer, QueryMsg};
const MIXNET_CONTRACT_ADDRESS: &str =
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
fn set_mock() -> SingleContractMock<MixnetContract> {
let current_block = BlockInfo {
height: 1928125,
time: Timestamp::from_seconds(1676482616),
chain_id: "nymnet".to_string(),
};
let custom_env = env_with_block_info(current_block);
let mix_state = ContractState::try_from_state_dump(
"../integration-tests/contract-states/15.02.23-173000-qwerty-mixnet.json",
Some(custom_env.clone()),
)
.unwrap()
.with_contract_address(MIXNET_CONTRACT_ADDRESS);
SingleContractMock::new(mix_state)
}
fn normal_queries() {
let mock = set_mock();
// the simplest example of a query: 'what's the current contract state?'
let query = QueryMsg::GetState {};
let result: MixnetContractState = mock.query_de(query).unwrap();
// println!("{:?}", result);
assert_eq!(
"n1fxwdqgwht4j2suv5pr55304kt9z0avrvxs9ls0",
result.owner.as_ref()
);
}
fn queries_with_native_functions() {
let mock = set_mock();
// access exactly the same information as before, but this time with native functions
let deps = mock.deps();
let result = mixnet_contract_settings::queries::query_contract_state(deps).unwrap();
// println!("{:?}", result);
assert_eq!(
"n1fxwdqgwht4j2suv5pr55304kt9z0avrvxs9ls0",
result.owner.as_ref()
);
}
fn raw_storage_reads() {
// we can also read any arbitrary data that's normally not exposed via queries
// for this example, let's read exactly the same data again
let mock = set_mock();
// wrapped in a cw-storage-plus 'item'
let result = mock.state.load_item(&CONTRACT_STATE).unwrap();
// println!("{:?}", result);
assert_eq!(
"n1fxwdqgwht4j2suv5pr55304kt9z0avrvxs9ls0",
result.owner.as_ref()
);
// a raw key-value read
let result_raw = mock.state.read_key(b"state").unwrap();
let result: MixnetContractState = from_slice(&result_raw).unwrap();
assert_eq!(
"n1fxwdqgwht4j2suv5pr55304kt9z0avrvxs9ls0",
result.owner.as_ref()
);
}
fn normal_transactions() {
let mut mock = set_mock();
// pretend you're the rewarding validator and force assign somebody's layer!
let current_mixnode = query_mixnode_details(mock.deps(), 7).unwrap();
assert_eq!(
current_mixnode
.mixnode_details
.unwrap()
.bond_information
.layer,
Layer::One
);
let rewarding_validator = mixnet_contract_settings::queries::query_contract_state(mock.deps())
.unwrap()
.rewarding_validator_address;
let msg_sender = mock_info(rewarding_validator.as_ref(), &[]);
let msg = ExecuteMsg::AssignNodeLayer {
mix_id: 7,
layer: Layer::Two,
};
mock.execute(msg_sender, msg).unwrap();
let updated_mixnode = query_mixnode_details(mock.deps(), 7).unwrap();
assert_eq!(
updated_mixnode
.mixnode_details
.unwrap()
.bond_information
.layer,
Layer::Two
);
}
fn changing_state_with_native_functions() {
// do the same thing but this time calling contract methods directly
let mut mock = set_mock();
let current_mixnode = query_mixnode_details(mock.deps(), 7).unwrap();
assert_eq!(
current_mixnode
.mixnode_details
.unwrap()
.bond_information
.layer,
Layer::One
);
let rewarding_validator = mixnet_contract_settings::queries::query_contract_state(mock.deps())
.unwrap()
.rewarding_validator_address;
let msg_sender = mock_info(rewarding_validator.as_ref(), &[]);
let deps = mock.deps_mut();
mixnodes::transactions::assign_mixnode_layer(deps, msg_sender, 7, Layer::Two).unwrap();
let updated_mixnode = query_mixnode_details(mock.deps(), 7).unwrap();
assert_eq!(
updated_mixnode
.mixnode_details
.unwrap()
.bond_information
.layer,
Layer::Two
);
}
fn writing_to_raw_storage() {
// bypass this whole transaction business, authorization checks, etc and just write to the storage yourself
let mut mock = set_mock();
let mut mix_bond = mixnodes::storage::mixnode_bonds()
.load(mock.deps().storage, 7)
.unwrap();
assert_eq!(mix_bond.layer, Layer::One);
mix_bond.layer = Layer::Two;
mixnodes::storage::mixnode_bonds()
.save(mock.deps_mut().storage, 7, &mix_bond)
.unwrap();
let updated_mixnode = query_mixnode_details(mock.deps(), 7).unwrap();
assert_eq!(
updated_mixnode
.mixnode_details
.unwrap()
.bond_information
.layer,
Layer::Two
);
}
// run with `cargo run --example mock_testing --features="testing-mocks"`
#[cfg(not(target_arch = "wasm32"))]
fn main() {
normal_queries();
queries_with_native_functions();
raw_storage_reads();
normal_transactions();
changing_state_with_native_functions();
writing_to_raw_storage();
}
+4 -4
View File
@@ -16,7 +16,7 @@ use mixnet_contract_common::{
PagedMixNodeDelegationsResponse, PagedMixNodeDelegationsResponse,
}; };
pub(crate) fn query_mixnode_delegations_paged( pub fn query_mixnode_delegations_paged(
deps: Deps<'_>, deps: Deps<'_>,
mix_id: MixId, mix_id: MixId,
start_after: Option<String>, start_after: Option<String>,
@@ -47,7 +47,7 @@ pub(crate) fn query_mixnode_delegations_paged(
)) ))
} }
pub(crate) fn query_delegator_delegations_paged( pub fn query_delegator_delegations_paged(
deps: Deps<'_>, deps: Deps<'_>,
delegation_owner: String, delegation_owner: String,
start_after: Option<(MixId, OwnerProxySubKey)>, start_after: Option<(MixId, OwnerProxySubKey)>,
@@ -83,7 +83,7 @@ pub(crate) fn query_delegator_delegations_paged(
} }
// queries for delegation value of given address for particular node // queries for delegation value of given address for particular node
pub(crate) fn query_mixnode_delegation( pub fn query_mixnode_delegation(
deps: Deps<'_>, deps: Deps<'_>,
mix_id: MixId, mix_id: MixId,
delegation_owner: String, delegation_owner: String,
@@ -109,7 +109,7 @@ pub(crate) fn query_mixnode_delegation(
)) ))
} }
pub(crate) fn query_all_delegations_paged( pub fn query_all_delegations_paged(
deps: Deps<'_>, deps: Deps<'_>,
start_after: Option<delegation::StorageKey>, start_after: Option<delegation::StorageKey>,
limit: Option<u32>, limit: Option<u32>,
+2 -5
View File
@@ -9,7 +9,7 @@ use mixnet_contract_common::{
GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, IdentityKey, PagedGatewayResponse, GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, IdentityKey, PagedGatewayResponse,
}; };
pub(crate) fn query_gateways_paged( pub fn query_gateways_paged(
deps: Deps<'_>, deps: Deps<'_>,
start_after: Option<IdentityKey>, start_after: Option<IdentityKey>,
limit: Option<u32>, limit: Option<u32>,
@@ -31,10 +31,7 @@ pub(crate) fn query_gateways_paged(
Ok(PagedGatewayResponse::new(nodes, limit, start_next_after)) Ok(PagedGatewayResponse::new(nodes, limit, start_next_after))
} }
pub(crate) fn query_owned_gateway( pub fn query_owned_gateway(deps: Deps<'_>, address: String) -> StdResult<GatewayOwnershipResponse> {
deps: Deps<'_>,
address: String,
) -> StdResult<GatewayOwnershipResponse> {
let validated_addr = deps.api.addr_validate(&address)?; let validated_addr = deps.api.addr_validate(&address)?;
let gateway = storage::gateways() let gateway = storage::gateways()
+12 -9
View File
@@ -4,16 +4,19 @@
#![warn(clippy::expect_used)] #![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)] #![warn(clippy::unwrap_used)]
mod constants; pub mod constants;
pub mod contract; pub mod contract;
mod delegations; pub mod delegations;
mod families; pub mod families;
mod gateways; pub mod gateways;
mod interval; pub mod interval;
mod mixnet_contract_settings; pub mod mixnet_contract_settings;
mod mixnodes; pub mod mixnodes;
mod rewards; pub mod rewards;
mod support; pub mod support;
#[cfg(feature = "contract-testing")] #[cfg(feature = "contract-testing")]
mod testing; mod testing;
#[cfg(feature = "testing-mocks")]
pub use testing::mock_helpers::MixnetContract;
@@ -5,23 +5,23 @@ use super::storage;
use cosmwasm_std::{Deps, StdResult}; use cosmwasm_std::{Deps, StdResult};
use mixnet_contract_common::{ContractBuildInformation, ContractState, ContractStateParams}; use mixnet_contract_common::{ContractBuildInformation, ContractState, ContractStateParams};
pub(crate) fn query_contract_state(deps: Deps<'_>) -> StdResult<ContractState> { pub fn query_contract_state(deps: Deps<'_>) -> StdResult<ContractState> {
storage::CONTRACT_STATE.load(deps.storage) storage::CONTRACT_STATE.load(deps.storage)
} }
pub(crate) fn query_contract_settings_params(deps: Deps<'_>) -> StdResult<ContractStateParams> { pub fn query_contract_settings_params(deps: Deps<'_>) -> StdResult<ContractStateParams> {
storage::CONTRACT_STATE storage::CONTRACT_STATE
.load(deps.storage) .load(deps.storage)
.map(|settings| settings.params) .map(|settings| settings.params)
} }
pub(crate) fn query_rewarding_validator_address(deps: Deps<'_>) -> StdResult<String> { pub fn query_rewarding_validator_address(deps: Deps<'_>) -> StdResult<String> {
storage::CONTRACT_STATE storage::CONTRACT_STATE
.load(deps.storage) .load(deps.storage)
.map(|settings| settings.rewarding_validator_address.to_string()) .map(|settings| settings.rewarding_validator_address.to_string())
} }
pub(crate) fn query_contract_version() -> ContractBuildInformation { pub fn query_contract_version() -> ContractBuildInformation {
// as per docs // as per docs
// env! macro will expand to the value of the named environment variable at // env! macro will expand to the value of the named environment variable at
// compile time, yielding an expression of type `&'static str` // compile time, yielding an expression of type `&'static str`
@@ -8,7 +8,7 @@ use cw_storage_plus::Item;
use mixnet_contract_common::error::MixnetContractError; use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::ContractState; use mixnet_contract_common::ContractState;
pub(crate) const CONTRACT_STATE: Item<'_, ContractState> = Item::new(CONTRACT_STATE_KEY); pub const CONTRACT_STATE: Item<'_, ContractState> = Item::new(CONTRACT_STATE_KEY);
pub fn rewarding_validator_address(storage: &dyn Storage) -> Result<Addr, MixnetContractError> { pub fn rewarding_validator_address(storage: &dyn Storage) -> Result<Addr, MixnetContractError> {
Ok(CONTRACT_STATE Ok(CONTRACT_STATE
+1 -1
View File
@@ -256,7 +256,7 @@ pub fn query_stake_saturation(deps: Deps<'_>, mix_id: MixId) -> StdResult<StakeS
}) })
} }
pub(crate) fn query_layer_distribution(deps: Deps<'_>) -> StdResult<LayerDistribution> { pub fn query_layer_distribution(deps: Deps<'_>) -> StdResult<LayerDistribution> {
storage::LAYERS.load(deps.storage) storage::LAYERS.load(deps.storage)
} }
+3 -3
View File
@@ -48,10 +48,10 @@ pub(crate) fn unbonded_mixnodes<'a>(
IndexedMap::new(UNBONDED_MIXNODES_PK_NAMESPACE, indexes) IndexedMap::new(UNBONDED_MIXNODES_PK_NAMESPACE, indexes)
} }
pub(crate) const LAYERS: Item<'_, LayerDistribution> = Item::new(LAYER_DISTRIBUTION_KEY); pub const LAYERS: Item<'_, LayerDistribution> = Item::new(LAYER_DISTRIBUTION_KEY);
pub const MIXNODE_ID_COUNTER: Item<MixId> = Item::new(NODE_ID_COUNTER_KEY); pub const MIXNODE_ID_COUNTER: Item<MixId> = Item::new(NODE_ID_COUNTER_KEY);
pub(crate) struct MixnodeBondIndex<'a> { pub struct MixnodeBondIndex<'a> {
pub(crate) owner: UniqueIndex<'a, Addr, MixNodeBond>, pub(crate) owner: UniqueIndex<'a, Addr, MixNodeBond>,
pub(crate) identity_key: UniqueIndex<'a, IdentityKey, MixNodeBond>, pub(crate) identity_key: UniqueIndex<'a, IdentityKey, MixNodeBond>,
@@ -70,7 +70,7 @@ impl<'a> IndexList<MixNodeBond> for MixnodeBondIndex<'a> {
} }
// mixnode_bonds() is the storage access function. // mixnode_bonds() is the storage access function.
pub(crate) fn mixnode_bonds<'a>() -> IndexedMap<'a, MixId, MixNodeBond, MixnodeBondIndex<'a>> { pub fn mixnode_bonds<'a>() -> IndexedMap<'a, MixId, MixNodeBond, MixnodeBondIndex<'a>> {
let indexes = MixnodeBondIndex { let indexes = MixnodeBondIndex {
owner: UniqueIndex::new(|d| d.owner.clone(), MIXNODES_OWNER_IDX_NAMESPACE), owner: UniqueIndex::new(|d| d.owner.clone(), MIXNODES_OWNER_IDX_NAMESPACE),
identity_key: UniqueIndex::new( identity_key: UniqueIndex::new(
+3 -3
View File
@@ -16,7 +16,7 @@ use mixnet_contract_common::rewarding::{
}; };
use mixnet_contract_common::{Delegation, MixId}; use mixnet_contract_common::{Delegation, MixId};
pub(crate) fn query_rewarding_params(deps: Deps<'_>) -> StdResult<RewardingParams> { pub fn query_rewarding_params(deps: Deps<'_>) -> StdResult<RewardingParams> {
storage::REWARDING_PARAMS.load(deps.storage) storage::REWARDING_PARAMS.load(deps.storage)
} }
@@ -104,7 +104,7 @@ fn zero_reward(
} }
} }
pub(crate) fn query_estimated_current_epoch_operator_reward( pub fn query_estimated_current_epoch_operator_reward(
deps: Deps<'_>, deps: Deps<'_>,
mix_id: MixId, mix_id: MixId,
estimated_performance: Performance, estimated_performance: Performance,
@@ -157,7 +157,7 @@ pub(crate) fn query_estimated_current_epoch_operator_reward(
}) })
} }
pub(crate) fn query_estimated_current_epoch_delegator_reward( pub fn query_estimated_current_epoch_delegator_reward(
deps: Deps<'_>, deps: Deps<'_>,
owner: String, owner: String,
mix_id: MixId, mix_id: MixId,
@@ -0,0 +1,47 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::contract;
use cosmwasm_contract_testing::TestableContract;
use cosmwasm_std::{Deps, DepsMut, Env, MessageInfo, QueryResponse, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{ExecuteMsg, InstantiateMsg, QueryMsg};
pub struct MixnetContract;
impl TestableContract for MixnetContract {
type ContractError = MixnetContractError;
type InstantiateMsg = InstantiateMsg;
type ExecuteMsg = ExecuteMsg;
type QueryMsg = QueryMsg;
fn new() -> Self {
MixnetContract
}
fn instantiate(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: Self::InstantiateMsg,
) -> Result<Response, Self::ContractError> {
contract::instantiate(deps, env, info, msg)
}
fn execute(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: Self::ExecuteMsg,
) -> Result<Response, Self::ContractError> {
contract::execute(deps, env, info, msg)
}
fn query(
deps: Deps<'_>,
env: Env,
msg: Self::QueryMsg,
) -> Result<QueryResponse, Self::ContractError> {
contract::query(deps, env, msg)
}
}
+3
View File
@@ -2,3 +2,6 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub(crate) mod transactions; pub(crate) mod transactions;
#[cfg(feature = "testing-mocks")]
pub mod mock_helpers;
+7 -4
View File
@@ -31,11 +31,14 @@ schemars = "0.8"
serde = { version = "1.0", default-features = false, features = ["derive"] } serde = { version = "1.0", default-features = false, features = ["derive"] }
thiserror = { version = "1.0" } thiserror = { version = "1.0" }
cosmwasm-contract-testing = { path = "../../common/cosmwasm-smart-contracts/testing", default-features = false, features = ["testable-trait"], optional = true }
[dev-dependencies] [dev-dependencies]
rand_chacha = "0.3.1" cosmwasm-contract-testing = { path = "../../common/cosmwasm-smart-contracts/testing" }
base64 = "0.21.0"
hex = "0.4.3"
serde_json = "1.0.66"
[build-dependencies] [build-dependencies]
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc"] } vergen = { version = "5", default-features = false, features = ["build", "git", "rustc"] }
[features]
default = []
testing-mocks = ["cosmwasm-contract-testing"]
+9 -9
View File
@@ -299,7 +299,7 @@ pub fn try_withdraw_vested_coins(
} }
/// Transfer ownership of the entire vesting account. /// Transfer ownership of the entire vesting account.
fn try_transfer_ownership( pub fn try_transfer_ownership(
to_address: String, to_address: String,
info: MessageInfo, info: MessageInfo,
deps: DepsMut<'_>, deps: DepsMut<'_>,
@@ -316,7 +316,7 @@ fn try_transfer_ownership(
} }
/// Set or update staking address for a vesting account. /// Set or update staking address for a vesting account.
fn try_update_staking_address( pub fn try_update_staking_address(
to_address: Option<String>, to_address: Option<String>,
info: MessageInfo, info: MessageInfo,
deps: DepsMut<'_>, deps: DepsMut<'_>,
@@ -434,7 +434,7 @@ pub fn try_track_unbond_mixnode(
} }
/// Track reward collection, invoked by the mixnert contract after sucessful reward compounding or claiming /// Track reward collection, invoked by the mixnert contract after sucessful reward compounding or claiming
fn try_track_reward( pub fn try_track_reward(
deps: DepsMut<'_>, deps: DepsMut<'_>,
info: MessageInfo, info: MessageInfo,
amount: Coin, amount: Coin,
@@ -449,7 +449,7 @@ fn try_track_reward(
} }
/// Track undelegation, invoked by the mixnet contract after sucessful undelegation, message contains coins returned with any accrued rewards. /// Track undelegation, invoked by the mixnet contract after sucessful undelegation, message contains coins returned with any accrued rewards.
fn try_track_undelegation( pub fn try_track_undelegation(
address: &str, address: &str,
mix_id: MixId, mix_id: MixId,
amount: Coin, amount: Coin,
@@ -466,7 +466,7 @@ fn try_track_undelegation(
} }
/// Delegate to mixnode, sends [mixnet_contract_common::ExecuteMsg::DelegateToMixnodeOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].. /// Delegate to mixnode, sends [mixnet_contract_common::ExecuteMsg::DelegateToMixnodeOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS]..
fn try_delegate_to_mixnode( pub fn try_delegate_to_mixnode(
mix_id: MixId, mix_id: MixId,
amount: Coin, amount: Coin,
on_behalf_of: Option<String>, on_behalf_of: Option<String>,
@@ -491,7 +491,7 @@ fn try_delegate_to_mixnode(
} }
/// Claims operator reward, sends [mixnet_contract_common::ExecuteMsg::ClaimOperatorRewardOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS]. /// Claims operator reward, sends [mixnet_contract_common::ExecuteMsg::ClaimOperatorRewardOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].
fn try_claim_operator_reward( pub fn try_claim_operator_reward(
deps: DepsMut<'_>, deps: DepsMut<'_>,
info: MessageInfo, info: MessageInfo,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
@@ -500,7 +500,7 @@ fn try_claim_operator_reward(
} }
/// Claims delegator reward, sends [mixnet_contract_common::ExecuteMsg::ClaimDelegatorRewardOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS]. /// Claims delegator reward, sends [mixnet_contract_common::ExecuteMsg::ClaimDelegatorRewardOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].
fn try_claim_delegator_reward( pub fn try_claim_delegator_reward(
deps: DepsMut<'_>, deps: DepsMut<'_>,
info: MessageInfo, info: MessageInfo,
mix_id: MixId, mix_id: MixId,
@@ -511,7 +511,7 @@ fn try_claim_delegator_reward(
} }
/// Undelegates from a mixnode, sends [mixnet_contract_common::ExecuteMsg::UndelegateFromMixnodeOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS]. /// Undelegates from a mixnode, sends [mixnet_contract_common::ExecuteMsg::UndelegateFromMixnodeOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].
fn try_undelegate_from_mixnode( pub fn try_undelegate_from_mixnode(
mix_id: MixId, mix_id: MixId,
on_behalf_of: Option<String>, on_behalf_of: Option<String>,
info: MessageInfo, info: MessageInfo,
@@ -533,7 +533,7 @@ fn try_undelegate_from_mixnode(
/// Creates a new periodic vesting account, and deposits funds to vest into the contract. /// Creates a new periodic vesting account, and deposits funds to vest into the contract.
/// ///
/// Callable by ADMIN only, see [instantiate]. /// Callable by ADMIN only, see [instantiate].
pub(crate) fn try_create_periodic_vesting_account( pub fn try_create_periodic_vesting_account(
owner_address: &str, owner_address: &str,
staking_address: Option<String>, staking_address: Option<String>,
vesting_spec: Option<VestingSpecification>, vesting_spec: Option<VestingSpecification>,
+4 -1
View File
@@ -5,9 +5,12 @@
#![warn(clippy::unwrap_used)] #![warn(clippy::unwrap_used)]
pub mod contract; pub mod contract;
mod errors; pub mod errors;
mod queued_migrations; mod queued_migrations;
mod storage; mod storage;
mod support; mod support;
mod traits; mod traits;
pub mod vesting; pub mod vesting;
#[cfg(feature = "testing-mocks")]
pub use support::mock_helpers::VestingContract;
@@ -0,0 +1,47 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::contract;
use crate::errors::ContractError;
use cosmwasm_contract_testing::TestableContract;
use cosmwasm_std::{Deps, DepsMut, Env, MessageInfo, QueryResponse, Response};
use vesting_contract_common::{ExecuteMsg, InitMsg, QueryMsg};
pub struct VestingContract;
impl TestableContract for VestingContract {
type ContractError = ContractError;
type InstantiateMsg = InitMsg;
type ExecuteMsg = ExecuteMsg;
type QueryMsg = QueryMsg;
fn new() -> Self {
VestingContract
}
fn instantiate(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: Self::InstantiateMsg,
) -> Result<Response, Self::ContractError> {
contract::instantiate(deps, env, info, msg)
}
fn execute(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: Self::ExecuteMsg,
) -> Result<Response, Self::ContractError> {
contract::execute(deps, env, info, msg)
}
fn query(
deps: Deps<'_>,
env: Env,
msg: Self::QueryMsg,
) -> Result<QueryResponse, Self::ContractError> {
contract::query(deps, env, msg)
}
}
+6
View File
@@ -1 +1,7 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod tests; pub mod tests;
#[cfg(feature = "testing-mocks")]
pub mod mock_helpers;
+39 -131
View File
@@ -1,104 +1,24 @@
#[cfg(test)] #[cfg(test)]
pub mod helpers { pub mod helpers {
// TODO: once https://github.com/nymtech/nym/pull/3040 gets merged,
// the `ContractState` should replace the below
#[allow(unused)]
mod state_dump_decoder {
use base64::{engine::general_purpose, Engine};
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::path::Path;
#[derive(Serialize, Deserialize, Debug)]
pub struct RawState {
pub height: String,
pub result: Vec<RawKV>,
}
impl RawState {
pub fn decode(self) -> DecodedState {
DecodedState {
height: self.height.parse().unwrap(),
result: self
.result
.into_iter()
.map(|raw| DecodedKV {
key: hex::decode(&raw.key).unwrap(),
value: general_purpose::STANDARD.decode(&raw.value).unwrap(),
})
.collect(),
}
}
pub fn from_file<P: AsRef<Path>>(path: P) -> Self {
let file = File::open(path).expect("failed to open specified file");
serde_json::from_reader(file).expect("failed to parse specified file")
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct RawKV {
// hex
pub key: String,
// base64
pub value: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DecodedKV {
pub key: Vec<u8>,
pub value: Vec<u8>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DecodedState {
pub height: u64,
pub result: Vec<DecodedKV>,
}
impl DecodedState {
pub fn find_value(&self, key: &[u8]) -> Option<Vec<u8>> {
self.result
.iter()
.find(|kv| kv.key == key)
.map(|kv| kv.value.clone())
}
}
}
use crate::contract::{instantiate, try_create_periodic_vesting_account}; use crate::contract::{instantiate, try_create_periodic_vesting_account};
use crate::storage::{ACCOUNTS, ADMIN, MIXNET_CONTRACT_ADDRESS, MIX_DENOM}; use crate::storage::{ACCOUNTS, ADMIN, MIXNET_CONTRACT_ADDRESS, MIX_DENOM};
use crate::support::tests::helpers::state_dump_decoder::RawState;
use crate::traits::VestingAccount; use crate::traits::VestingAccount;
use crate::vesting::{populate_vesting_periods, Account}; use crate::vesting::{populate_vesting_periods, Account};
use contracts_common::Percent; use contracts_common::Percent;
use cosmwasm_contract_testing::{env_with_block_info, ContractState};
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
use cosmwasm_std::{ use cosmwasm_std::{
coin, Addr, BlockInfo, Coin, ContractInfo, Deps, DepsMut, Empty, Env, MemoryStorage, coin, Addr, BlockInfo, Coin, Deps, DepsMut, Empty, Env, MemoryStorage, MessageInfo,
MessageInfo, OwnedDeps, Storage, Timestamp, Uint128, OwnedDeps, Storage, Timestamp, Uint128,
}; };
use rand_chacha::rand_core::SeedableRng;
use rand_chacha::ChaCha20Rng;
use std::path::Path; use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use vesting_contract_common::messages::{InitMsg, VestingSpecification}; use vesting_contract_common::messages::{InitMsg, VestingSpecification};
use vesting_contract_common::PledgeCap; use vesting_contract_common::PledgeCap;
// use rng with constant seed for all tests so that they would be deterministic
#[allow(unused)]
pub fn test_rng() -> ChaCha20Rng {
let dummy_seed = [42u8; 32];
rand_chacha::ChaCha20Rng::from_seed(dummy_seed)
}
#[allow(unused)] #[allow(unused)]
pub struct TestSetup { pub struct TestSetup {
pub deps: OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>, pub state: ContractState,
pub env: Env,
pub rng: ChaCha20Rng,
pub admin: MessageInfo, pub admin: MessageInfo,
} }
@@ -109,40 +29,28 @@ pub mod helpers {
let admin = ADMIN.load(deps.as_ref().storage).unwrap(); let admin = ADMIN.load(deps.as_ref().storage).unwrap();
TestSetup { TestSetup {
deps, state: ContractState::new(),
env: mock_env(),
rng: test_rng(),
admin: mock_info(admin.as_str(), &[]), admin: mock_info(admin.as_str(), &[]),
} }
} }
pub fn new_from_state_dump<P: AsRef<Path>>(dump_file: P) -> Self { pub fn new_from_state_dump<P: AsRef<Path>>(dump_file: P) -> Self {
let state = RawState::from_file(dump_file).decode(); let current_block = BlockInfo {
height: 5633424,
let mut deps = mock_dependencies(); time: Timestamp::from_seconds(1676025955),
for kv in state.result { chain_id: "nyx".to_string(),
deps.storage.set(&kv.key, &kv.value)
}
let admin = ADMIN.load(deps.as_ref().storage).unwrap();
let env = Env {
block: BlockInfo {
height: 5633424,
time: Timestamp::from_seconds(1676025955),
chain_id: "nyx".to_string(),
},
transaction: None,
contract: ContractInfo {
address: Addr::unchecked(
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw",
),
},
}; };
let custom_env = env_with_block_info(current_block);
let state = ContractState::try_from_state_dump(dump_file, Some(custom_env.clone()))
.unwrap()
.with_contract_address(
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw",
);
let admin = ADMIN.load(state.deps().storage).unwrap();
TestSetup { TestSetup {
deps, state,
env,
rng: test_rng(),
admin: mock_info(admin.as_str(), &[]), admin: mock_info(admin.as_str(), &[]),
} }
} }
@@ -173,15 +81,15 @@ pub mod helpers {
} }
pub fn deps(&self) -> Deps<'_> { pub fn deps(&self) -> Deps<'_> {
self.deps.as_ref() self.state.deps()
} }
pub fn deps_mut(&mut self) -> DepsMut<'_> { pub fn deps_mut(&mut self) -> DepsMut<'_> {
self.deps.as_mut() self.state.deps_mut()
} }
pub fn env(&self) -> Env { pub fn env(&self) -> Env {
self.env.clone() self.state.env_cloned()
} }
pub fn admin(&self) -> MessageInfo { pub fn admin(&self) -> MessageInfo {
@@ -223,18 +131,18 @@ pub mod helpers {
let pretty = format!( let pretty = format!(
r#" r#"
{:<20}{original} {:<20}{original}
{:<20}{vesting} {:<20}{vesting}
{:<20}{vested} {:<20}{vested}
{:<20}{balance} {:<20}{balance}
{:<20}{withdrawn} {:<20}{withdrawn}
{:<20}{historical_rewards} {:<20}{historical_rewards}
{:<20}{locked} {:<20}{locked}
{:<20}{spendable} {:<20}{spendable}
{:<20}{spendable_vested} {:<20}{spendable_vested}
{:<20}{spendable_reward} {:<20}{spendable_reward}
{:<20}{total_delegated} {:<20}{total_delegated}
"#, "#,
"original", "original",
"vesting", "vesting",
"vested:", "vested:",
@@ -265,37 +173,37 @@ pub mod helpers {
pub fn vesting_coins(&self, account: &Account) -> Coin { pub fn vesting_coins(&self, account: &Account) -> Coin {
account account
.get_vesting_coins(None, &self.env, self.deps().storage) .get_vesting_coins(None, self.state.env(), self.deps().storage)
.unwrap() .unwrap()
} }
pub fn vested_coins(&self, account: &Account) -> Coin { pub fn vested_coins(&self, account: &Account) -> Coin {
account account
.get_vested_coins(None, &self.env, self.deps().storage) .get_vested_coins(None, self.state.env(), self.deps().storage)
.unwrap() .unwrap()
} }
pub fn locked_coins(&self, account: &Account) -> Coin { pub fn locked_coins(&self, account: &Account) -> Coin {
account account
.locked_coins(None, &self.env, self.deps().storage) .locked_coins(None, self.state.env(), self.deps().storage)
.unwrap() .unwrap()
} }
pub fn spendable_coins(&self, account: &Account) -> Coin { pub fn spendable_coins(&self, account: &Account) -> Coin {
account account
.spendable_coins(None, &self.env, self.deps().storage) .spendable_coins(None, self.state.env(), self.deps().storage)
.unwrap() .unwrap()
} }
pub fn spendable_vested_coins(&self, account: &Account) -> Coin { pub fn spendable_vested_coins(&self, account: &Account) -> Coin {
account account
.spendable_vested_coins(None, &self.env, self.deps().storage) .spendable_vested_coins(None, self.state.env(), self.deps().storage)
.unwrap() .unwrap()
} }
pub fn spendable_reward_coins(&self, account: &Account) -> Coin { pub fn spendable_reward_coins(&self, account: &Account) -> Coin {
account account
.spendable_reward_coins(None, &self.env, self.deps().storage) .spendable_reward_coins(None, self.state.env(), self.deps().storage)
.unwrap() .unwrap()
} }
@@ -34,7 +34,7 @@ describe("Get mixnode data", (): void => {
expect(typeof mixnode.bond_information.mix_node.verloc_port).toBe("number"); expect(typeof mixnode.bond_information.mix_node.verloc_port).toBe("number");
expect(typeof mixnode.bond_information.mix_node.mix_port).toBe("number"); expect(typeof mixnode.bond_information.mix_node.mix_port).toBe("number");
expect(mixnode.bond_information.mix_node.mix_port).toStrictEqual(1789); expect(mixnode.bond_information.mix_node.mix_port).toStrictEqual(1789);
expect(mixnode.bond_information.mix_node.verloc_port).toStrictEqual(1790); expect(typeof mixnode.bond_information.mix_node.verloc_port).toBe("number");
const identitykey = mixnode.bond_information.mix_node.identity_key; const identitykey = mixnode.bond_information.mix_node.identity_key;
if (typeof identitykey === "string") { if (typeof identitykey === "string") {
@@ -96,7 +96,7 @@ describe("Get mixnode data", (): void => {
expect(typeof mixnode.mixnode_details.bond_information.mix_node.verloc_port).toBe("number"); expect(typeof mixnode.mixnode_details.bond_information.mix_node.verloc_port).toBe("number");
expect(typeof mixnode.mixnode_details.bond_information.mix_node.mix_port).toBe("number"); expect(typeof mixnode.mixnode_details.bond_information.mix_node.mix_port).toBe("number");
expect(mixnode.mixnode_details.bond_information.mix_node.mix_port).toStrictEqual(1789); expect(mixnode.mixnode_details.bond_information.mix_node.mix_port).toStrictEqual(1789);
expect(mixnode.mixnode_details.bond_information.mix_node.verloc_port).toStrictEqual(1790); expect(typeof mixnode.mixnode_details.bond_information.mix_node.verloc_port).toBe("number");
const identitykey2 = mixnode.mixnode_details.bond_information.mix_node.identity_key const identitykey2 = mixnode.mixnode_details.bond_information.mix_node.identity_key
if (typeof identitykey2 === "string") { if (typeof identitykey2 === "string") {
@@ -15,19 +15,24 @@ describe("Get gateway data", (): void => {
response.forEach((x) => { response.forEach((x) => {
expect(typeof x.gateway_bond.owner).toBe("string"); expect(typeof x.gateway_bond.owner).toBe("string");
expect(typeof x.performance).toBe("string"); expect(typeof x.performance).toBe("string");
expect(typeof x.node_performance.last_24h).toBe("string");
}); });
}); });
it("Get a gateway history", async (): Promise<void> => { it("Get a gateway history", async (): Promise<void> => {
const identity_key = config.environmnetConfig.gateway_identity; const identity_key = config.environmnetConfig.gateway_identity;
const response = await status.getGatewayHistory(identity_key); const response = await status.getGatewayHistory(identity_key);
response.history.forEach((x) => {
expect(typeof x.date).toBe("string");
expect(typeof x.uptime).toBe("number");
});
expect(identity_key).toStrictEqual(response.identity); if ("identity" in response) {
expect(typeof response.owner).toBe("string"); response.history.forEach((x) => {
expect(typeof x.date).toBe("string");
expect(typeof x.uptime).toBe("number");
});
expect(identity_key).toStrictEqual(response.identity);
expect(typeof response.owner).toBe("string");
} else if ("message" in response) {
expect(response.message).toContain("could not find uptime history associated with gateway");
}
}); });
it("Get gateway core status count", async (): Promise<void> => { it("Get gateway core status count", async (): Promise<void> => {
@@ -39,13 +39,16 @@ describe("Get mixnode data", (): void => {
const identity_key = config.environmnetConfig.mix_id; const identity_key = config.environmnetConfig.mix_id;
const response = await status.getMixnodeHistory(identity_key); const response = await status.getMixnodeHistory(identity_key);
response.history.forEach((x) => { if ("mix_id" in response) {
expect(typeof x.date).toBe("string"); response.history.forEach((x) => {
expect(typeof x.uptime).toBe("number"); expect(typeof x.date).toBe("string");
}); expect(typeof x.uptime).toBe("number");
});
expect(identity_key).toStrictEqual(response.mix_id); expect(identity_key).toStrictEqual(response.mix_id);
expect(typeof response.owner).toBe("string"); expect(typeof response.owner).toBe("string");
} else if ("message" in response) {
expect(response.message).toContain("could not find uptime history associated with mixnode");
}
}); });
it("Get a mixnode core count", async (): Promise<void> => { it("Get a mixnode core count", async (): Promise<void> => {
@@ -109,7 +112,7 @@ describe("Compute mixnode reward estimation", (): void => {
config = ConfigHandler.getInstance(); config = ConfigHandler.getInstance();
}); });
it("with correct data", async (): Promise<void> => { it("with correct data", async (): Promise<void> => {
const response = await status.sendMixnodeRewardEstimatedComputation(7); const response = await status.sendMixnodeRewardEstimatedComputation(63);
expect(typeof response.estimation.delegates).toBe("string"); expect(typeof response.estimation.delegates).toBe("string");
}); });
+1 -1
View File
@@ -4,7 +4,7 @@ common:
Content-Type: application/json Content-Type: application/json
qa: qa:
api_base_url: https://qwerty-validator-api.qa.nymte.ch/api/v1 api_base_url: https://qwerty-validator-api.qa.nymte.ch/api/v1
mix_id: 7 mix_id: 63
identity_key: 4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz identity_key: 4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz
gateway_identity: 336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9 gateway_identity: 336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9
log_level: error log_level: error
+3 -2
View File
@@ -7,6 +7,7 @@ import {
InclusionProbabilities, InclusionProbabilities,
InclusionProbability, InclusionProbability,
NodeHistory, NodeHistory,
NoUptime,
Report, Report,
RewardEstimation, RewardEstimation,
StakeSaturation, StakeSaturation,
@@ -36,7 +37,7 @@ export default class Status extends APIClient {
return response.data; return response.data;
} }
public async getGatewayHistory(identity_key: string): Promise<NodeHistory> { public async getGatewayHistory(identity_key: string): Promise<NodeHistory | NoUptime> {
const response = await this.restClient.sendGet({ const response = await this.restClient.sendGet({
route: `/gateway/${identity_key}/history`, route: `/gateway/${identity_key}/history`,
}); });
@@ -111,7 +112,7 @@ export default class Status extends APIClient {
return response.data; return response.data;
} }
public async getMixnodeHistory(mix_id: number): Promise<NodeHistory> { public async getMixnodeHistory(mix_id: number): Promise<NodeHistory | NoUptime> {
const response = await this.restClient.sendGet({ const response = await this.restClient.sendGet({
route: `/mixnode/${mix_id}/history`, route: `/mixnode/${mix_id}/history`,
}); });
+18 -7
View File
@@ -27,13 +27,6 @@ export interface Node {
in_active_set: boolean; in_active_set: boolean;
} }
export interface RewardEstimation {
estimation: Estimation;
reward_params: RewardParams;
epoch: Epoch;
as_at: number;
}
export interface Estimation { export interface Estimation {
total_node_reward: string; total_node_reward: string;
operator: string; operator: string;
@@ -41,6 +34,13 @@ export interface Estimation {
operating_cost: string; operating_cost: string;
} }
export interface RewardEstimation {
estimation: Estimation;
reward_params: RewardParams;
epoch: Epoch;
as_at: number;
}
export interface RewardParams { export interface RewardParams {
interval: Interval; interval: Interval;
rewarded_set_size: number; rewarded_set_size: number;
@@ -129,6 +129,10 @@ export interface NodeHistory {
history: History[]; history: History[];
} }
export interface NoUptime {
message: string;
}
export interface CoreCount { export interface CoreCount {
mix_id: number; mix_id: number;
identity: string; identity: string;
@@ -162,9 +166,16 @@ export interface GatewayBond {
proxy?: any; proxy?: any;
} }
export interface nodePerformance {
most_recent: string;
last_hour: string;
last_24h: string;
}
export interface DetailedGateway { export interface DetailedGateway {
gateway_bond: GatewayBond; gateway_bond: GatewayBond;
performance: string; performance: string;
node_performance: nodePerformance;
} }
export interface OriginalPledge { export interface OriginalPledge {
+2 -1
View File
@@ -26,7 +26,8 @@
"clean:dist": "rm -rf dist", "clean:dist": "rm -rf dist",
"clean:node": "rm -rf node_modules", "clean:node": "rm -rf node_modules",
"clean:rust": "cargo clean --manifest-path src-tauri/Cargo.toml", "clean:rust": "cargo clean --manifest-path src-tauri/Cargo.toml",
"clean:": "run-p clean:node clean:rust" "clean:android": "cd src-tauri/gen/android/nym_connect/ && gradlew clean",
"clean:": "run-p clean:node clean:rust clean:android"
}, },
"dependencies": { "dependencies": {
"@emotion/react": "^11.7.0", "@emotion/react": "^11.7.0",
@@ -1,41 +0,0 @@
[build]
target = 'x86_64-unknown-linux-gnu'
[target.aarch64-linux-android]
linker = '/home/pierre/.local/share/android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang'
rustflags = [
'-L',
'/home/pierre/Documents/nym/nym/nym-connect-new/mobile/src-tauri/.cargo',
'-Clink-arg=-landroid',
'-Clink-arg=-llog',
'-Clink-arg=-lOpenSLES',
]
[target.armv7-linux-androideabi]
linker = '/home/pierre/.local/share/android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang'
rustflags = [
'-L',
'/home/pierre/Documents/nym/nym/nym-connect-new/mobile/src-tauri/.cargo',
'-Clink-arg=-landroid',
'-Clink-arg=-llog',
'-Clink-arg=-lOpenSLES',
]
[target.i686-linux-android]
linker = '/home/pierre/.local/share/android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android24-clang'
rustflags = [
'-L',
'/home/pierre/Documents/nym/nym/nym-connect-new/mobile/src-tauri/.cargo',
'-Clink-arg=-landroid',
'-Clink-arg=-llog',
'-Clink-arg=-lOpenSLES',
]
[target.x86_64-linux-android]
linker = '/home/pierre/.local/share/android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang'
rustflags = [
'-L',
'/home/pierre/Documents/nym/nym/nym-connect-new/mobile/src-tauri/.cargo',
'-Clink-arg=-landroid',
'-Clink-arg=-llog',
'-Clink-arg=-lOpenSLES',
]
@@ -1 +0,0 @@
INPUT(-lunwind)
+1
View File
@@ -2,3 +2,4 @@
# will have compiled files and executables # will have compiled files and executables
/target/ /target/
WixTools WixTools
/.cargo/
+46 -45
View File
@@ -245,9 +245,9 @@ dependencies = [
[[package]] [[package]]
name = "bip39" name = "bip39"
version = "1.0.1" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e89470017230c38e52b82b3ee3f530db1856ba1d434e3a67a3456a8a8dec5f" checksum = "f5b9d9748b5770d1539657653dc5ac3cd9353549e74238dc0d96c22919128b94"
dependencies = [ dependencies = [
"bitcoin_hashes", "bitcoin_hashes",
"rand 0.6.5", "rand 0.6.5",
@@ -258,9 +258,9 @@ dependencies = [
[[package]] [[package]]
name = "bitcoin_hashes" name = "bitcoin_hashes"
version = "0.9.7" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1" checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
@@ -397,9 +397,9 @@ dependencies = [
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.2.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1"
dependencies = [ dependencies = [
"memchr", "memchr",
"serde", "serde",
@@ -571,9 +571,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_complete" name = "clap_complete"
version = "4.1.2" version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd125be87bf4c255ebc50de0b7f4d2a6201e8ac3dc86e39c0ad081dc5e7236fe" checksum = "0012995dc3a54314f4710f5631d74767e73c534b8757221708303e48eef7a19b"
dependencies = [ dependencies = [
"clap", "clap",
] ]
@@ -603,9 +603,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
dependencies = [ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
@@ -636,7 +636,7 @@ dependencies = [
"sqlx 0.6.2", "sqlx 0.6.2",
"tap", "tap",
"thiserror", "thiserror",
"time 0.3.18", "time 0.3.19",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"url", "url",
@@ -2040,15 +2040,15 @@ dependencies = [
[[package]] [[package]]
name = "generator" name = "generator"
version = "0.7.2" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d266041a359dfa931b370ef684cceb84b166beb14f7f0421f4a6a3d0c446d12e" checksum = "33a20a288a94683f5f4da0adecdbe095c94a77c295e514cc6484e9394dd8376e"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
"log", "log",
"rustversion", "rustversion",
"windows 0.39.0", "windows 0.44.0",
] ]
[[package]] [[package]]
@@ -2533,9 +2533,9 @@ dependencies = [
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.8" version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@@ -2826,9 +2826,9 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.3" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
dependencies = [ dependencies = [
"hermit-abi 0.3.1", "hermit-abi 0.3.1",
"io-lifetimes", "io-lifetimes",
@@ -3159,7 +3159,7 @@ dependencies = [
"dirs-next", "dirs-next",
"objc-foundation", "objc-foundation",
"objc_id", "objc_id",
"time 0.3.18", "time 0.3.19",
] ]
[[package]] [[package]]
@@ -3355,11 +3355,12 @@ dependencies = [
[[package]] [[package]]
name = "notify-rust" name = "notify-rust"
version = "4.7.1" version = "4.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "260208751689b605138bb55ab6af43ad75f628619a7e0b818d63bf6629e59467" checksum = "2bfa211d18e360f08e36c364308f394b5eb23a6629150690e109a916dc6f610e"
dependencies = [ dependencies = [
"dbus", "dbus",
"log",
"mac-notification-sys", "mac-notification-sys",
"tauri-winrt-notification", "tauri-winrt-notification",
] ]
@@ -3428,18 +3429,18 @@ dependencies = [
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.5.9" version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
dependencies = [ dependencies = [
"num_enum_derive", "num_enum_derive",
] ]
[[package]] [[package]]
name = "num_enum_derive" name = "num_enum_derive"
version = "0.5.9" version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
@@ -3584,7 +3585,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_repr", "serde_repr",
"thiserror", "thiserror",
"time 0.3.18", "time 0.3.19",
] ]
[[package]] [[package]]
@@ -4348,16 +4349,16 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]] [[package]]
name = "plist" name = "plist"
version = "1.4.0" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5329b8f106a176ab0dce4aae5da86bfcb139bb74fb00882859e03745011f3635" checksum = "9469799ca90293a376f68f6fcb8f11990d9cff55602cfba0ba83893c973a7f46"
dependencies = [ dependencies = [
"base64 0.13.1", "base64 0.21.0",
"indexmap", "indexmap",
"line-wrap", "line-wrap",
"quick-xml 0.26.0", "quick-xml 0.26.0",
"serde", "serde",
"time 0.3.18", "time 0.3.19",
] ]
[[package]] [[package]]
@@ -5444,9 +5445,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
dependencies = [ dependencies = [
"autocfg 1.1.0", "autocfg 1.1.0",
] ]
@@ -5873,9 +5874,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.107" version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -6073,7 +6074,7 @@ dependencies = [
"sha2 0.10.6", "sha2 0.10.6",
"tauri-utils", "tauri-utils",
"thiserror", "thiserror",
"time 0.3.18", "time 0.3.19",
"url", "url",
"uuid 1.3.0", "uuid 1.3.0",
"walkdir", "walkdir",
@@ -6213,7 +6214,7 @@ dependencies = [
"subtle 2.4.1", "subtle 2.4.1",
"subtle-encoding", "subtle-encoding",
"tendermint-proto", "tendermint-proto",
"time 0.3.18", "time 0.3.19",
"zeroize", "zeroize",
] ]
@@ -6246,7 +6247,7 @@ dependencies = [
"serde", "serde",
"serde_bytes", "serde_bytes",
"subtle-encoding", "subtle-encoding",
"time 0.3.18", "time 0.3.19",
] ]
[[package]] [[package]]
@@ -6274,7 +6275,7 @@ dependencies = [
"tendermint-config", "tendermint-config",
"tendermint-proto", "tendermint-proto",
"thiserror", "thiserror",
"time 0.3.18", "time 0.3.19",
"tokio", "tokio",
"tracing", "tracing",
"url", "url",
@@ -6351,9 +6352,9 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.18" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af0097eaf301d576d0b2aead7a59facab6d53cc636340f0291fab8446a2e8613" checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2"
dependencies = [ dependencies = [
"itoa 1.0.5", "itoa 1.0.5",
"js-sys", "js-sys",
@@ -6370,9 +6371,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.6" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c"
dependencies = [ dependencies = [
"time-core", "time-core",
] ]
@@ -6453,9 +6454,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.11" version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"pin-project-lite", "pin-project-lite",
@@ -6839,7 +6840,7 @@ dependencies = [
"rustc_version 0.4.0", "rustc_version 0.4.0",
"rustversion", "rustversion",
"thiserror", "thiserror",
"time 0.3.18", "time 0.3.19",
] ]
[[package]] [[package]]
@@ -13,3 +13,4 @@ build
.externalNativeBuild .externalNativeBuild
.cxx .cxx
local.properties local.properties
app/src/main/jniLibs/**
@@ -1 +0,0 @@
/home/pierre/Documents/nym/nym/nym-connect-new/mobile/src-tauri/target/x86_64-linux-android/debug/libnym_connect.so
@@ -26,17 +26,20 @@ open class BuildTask : DefaultTask() {
val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null")
val target = target ?: throw GradleException("target cannot be null") val target = target ?: throw GradleException("target cannot be null")
val release = release ?: throw GradleException("release cannot be null") val release = release ?: throw GradleException("release cannot be null")
val nodeExecutable = System.getenv("NODE_TAURI_CLI")
val home = (System.getenv("HOME") ?: "") val home = (System.getenv("HOME") ?: "")
val cargoHome = (System.getenv("CARGO_HOME") ?: "$home/.cargo") val cargoHome = (System.getenv("CARGO_HOME") ?: "$home/.cargo")
val tauriCli = "$cargoHome/bin/cargo-tauri" val rustExecutable = "$cargoHome/bin/cargo-tauri"
if (!File(tauriCli).isFile()) { val tauriCli = when {
throw GradleException("$tauriCli no shuch file") !nodeExecutable.isNullOrEmpty() && File(nodeExecutable).isFile() -> nodeExecutable
File(rustExecutable).isFile() -> rustExecutable
else -> throw GradleException("couldn't find tauri-cli executable")
} }
println("gradle Rust plugin, using tauri cli executable: $tauriCli") println("gradle Rust plugin, using tauri cli executable: $tauriCli")
project.exec { project.exec {
workingDir(File(project.projectDir, rootDirRel.path)) workingDir(File(project.projectDir, rootDirRel.path))
executable(tauriCli) executable(tauriCli)
args(listOf("tauri", "android", "android-studio-script")) args(listOf("android", "android-studio-script"))
if (project.logger.isEnabled(LogLevel.DEBUG)) { if (project.logger.isEnabled(LogLevel.DEBUG)) {
args("-vv") args("-vv")
} else if (project.logger.isEnabled(LogLevel.INFO)) { } else if (project.logger.isEnabled(LogLevel.INFO)) {
+2 -6
View File
@@ -2,16 +2,12 @@
A simple tool to ensure that all binaries init with the correct format, using the `assert.sh` library A simple tool to ensure that all binaries init with the correct format, using the `assert.sh` library
Simply run `./build_and_run.sh $RELEASE_BRANCH` `$CURRENT_PRODUCTION_RELEASE_VERSION` Simply run `./build_and_run.sh $WORKING_BRANCH`
For example: For example:
`./build_and_run.sh release/v1.1.11 v1.1.10` `./build_and_run.sh release/v1.1.11`
Currently, this is run on linux based machines as the nym-core binaries are published via a linux build agent Currently, this is run on linux based machines as the nym-core binaries are published via a linux build agent
This will run through all the binaries and check the fields that we expect to be initialised when passing the parameters into nyms core binaries This will run through all the binaries and check the fields that we expect to be initialised when passing the parameters into nyms core binaries
## TODO
- Create GH workflow and Run in CI