├── .gitignore ├── .gitlab-ci.yml ├── .rustfmt.toml ├── CODEOWNERS ├── Cargo.lock ├── Cargo.toml ├── FILE_HEADER ├── LICENSE ├── README.md ├── csv-comparator ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src │ └── main.rs ├── examples ├── contract-introspection │ ├── .gitignore │ ├── Cargo.toml │ └── lib.rs ├── data-structures │ ├── .gitignore │ ├── Cargo.toml │ └── lib.rs ├── seal-code-hash │ ├── .gitignore │ ├── Cargo.toml │ └── lib.rs └── seal-ecdsa │ ├── .gitignore │ ├── Cargo.toml │ └── lib.rs ├── lang_macro ├── Cargo.lock ├── Cargo.toml └── src │ └── lib.rs ├── scripts └── ci │ ├── build-contract.sh │ ├── evaluate-examples-changes.sh │ ├── extract-gas-usage.sh │ └── get-updated-badge-info.sh └── src ├── lib.rs ├── tests ├── contract_introspection.rs ├── contract_terminate.rs ├── contract_transfer.rs ├── data_structures.rs ├── delegator.rs ├── dns.rs ├── erc1155.rs ├── erc20.rs ├── erc721.rs ├── flipper.rs ├── incrementer.rs ├── mod.rs ├── multisig.rs ├── rand_extension.rs ├── seal_code_hash.rs ├── seal_ecdsa.rs ├── set_code_hash.rs ├── trait_erc20.rs ├── trait_flipper.rs └── trait_incrementer.rs ├── uis ├── canvas_ui.rs ├── mod.rs └── polkadot_js.rs └── utils ├── cargo_contract.rs └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /examples/ink 3 | /integration-tests/ink 4 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # .gitlab-ci.yml 2 | # 3 | # ink-waterfall 4 | # 5 | # pipelines can be triggered manually in the web 6 | 7 | stages: 8 | - declare-vars 9 | - test 10 | - compare-build 11 | 12 | variables: 13 | GIT_STRATEGY: fetch 14 | GIT_DEPTH: "100" 15 | RUST_LIB_BACKTRACE: "0" 16 | RUST_LOG: "info" 17 | CI_IMAGE: "paritytech/ci-unified:bullseye-1.69.0-2023-03-21" 18 | INK_EXAMPLES_PATH: "./ink/integration-tests" 19 | DELEGATOR_SUBCONTRACTS: "accumulator adder subber" 20 | UI_URL: "https://polkadotjs-apps.web.app/" 21 | 22 | 23 | 24 | .default-refs: &default-refs 25 | rules: 26 | - if: $CI_PIPELINE_SOURCE == "web" 27 | - if: $CI_PIPELINE_SOURCE == "schedule" 28 | - if: $CI_COMMIT_REF_NAME == "master" 29 | - if: $CI_COMMIT_REF_NAME == "tags" 30 | - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs 31 | - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 32 | 33 | .basic-env: &basic-env 34 | image: "${CI_IMAGE}" 35 | before_script: 36 | - cargo -vV 37 | - rustc -vV 38 | - rustup show 39 | - bash --version 40 | - substrate-contracts-node --version 41 | - cargo-contract --version 42 | - git show 43 | <<: *default-refs 44 | interruptible: true 45 | retry: 46 | max: 2 47 | when: 48 | - runner_system_failure 49 | - unknown_failure 50 | - api_failure 51 | 52 | 53 | .docker-env: &docker-env 54 | <<: *basic-env 55 | tags: 56 | - linux-docker-vm-c2 57 | 58 | .kubernetes-env: &kubernetes-env 59 | <<: *basic-env 60 | before_script: [] 61 | tags: 62 | - kubernetes-parity-build 63 | 64 | 65 | .if-trigger-ref-valid: &if-trigger-ref-valid 66 | before_script: 67 | - if [ "$TRGR_REF" == "master" ] || [ -z "$TRGR_REF" ]; then 68 | echo "It makes no sense to compare $REDIS_SIZES_KEY to $REDIS_SIZES_KEY_MASTER."; 69 | echo "Exiting gracefully."; 70 | exit 0; 71 | fi 72 | 73 | .clone-repo: &clone-repo 74 | - echo ${UPSTREAM_BRANCH} ${UPSTREAM_REPO} 75 | - git clone --verbose --depth 1 --branch ${UPSTREAM_BRANCH} ${UPSTREAM_REPO} 76 | 77 | .clone-repo-past-month: &clone-repo-past-month 78 | - PAST_MONTH=$(date -d "-1 month" +%Y-%m-%d) 79 | - git clone --shallow-since=${PAST_MONTH} --branch ${UPSTREAM_BRANCH} ${UPSTREAM_REPO} 80 | 81 | .choose-ink-waterfall-repo: &choose-ink-waterfall-repo 82 | - echo "choosing repo" 83 | - echo ${UPSTREAM_BRANCH} ${UPSTREAM_REPO} 84 | - if [ "$UPSTREAM_BRANCH" = "features/storage-rework" ]; then 85 | echo "explicitly cloning external branch for storage pr"; 86 | git clone --verbose --depth 1 --branch feature/refactored-storage https://github.com/Supercolony-net/ink-waterfall && cd ./ink-waterfall/ && pwd && git show; 87 | fi 88 | 89 | .start-substrate-contracts-node: &start-substrate-contracts-node 90 | - substrate-contracts-node -lruntime::contracts=debug > /tmp/substrate-contracts-node.log 2>&1 & 91 | 92 | .start-substrate-contracts-node-rand-extension: &start-substrate-contracts-node-rand-extension 93 | - substrate-contracts-node-rand-extension -lruntime::contracts=debug > /tmp/substrate-contracts-node-rand-extension.log 2>&1 & 94 | 95 | .shutdown-substrate-contracts-node: &shutdown-substrate-contracts-node 96 | - pkill -f "substrate-contracts-node" 97 | 98 | .shutdown-substrate-contracts-node-rand-extension: &shutdown-substrate-contracts-node-rand-extension 99 | - pkill -f "substrate-contracts-node-rand-extension" 100 | 101 | 102 | # Needed vars have to be "exported" in an earlier stage 103 | parent-vars: 104 | stage: declare-vars 105 | <<: *kubernetes-env 106 | variables: 107 | CI_IMAGE: "paritytech/tools" 108 | script: 109 | - | 110 | # default values 111 | UPSTREAM_BRANCH="master" 112 | UPSTREAM_BRANCH_REDIS_KEY="master" 113 | UPSTREAM_REPO="https://github.com/paritytech/ink.git" 114 | UPSTREAM_REPO_NAME="ink" 115 | 116 | echo ${TRGR_REF} 117 | if [ -n "$TRGR_REF" ] && [ "$TRGR_REF" != "master" ]; then 118 | PR_JSON=`curl -s https://api.github.com/repos/paritytech/ink/pulls/${TRGR_REF}`; 119 | UPSTREAM_BRANCH=`echo "${PR_JSON}" | jq -r .head.ref`; 120 | # Since we write the branch name to a file we need to remove any forward slashes 121 | # which may exist in the name 122 | UPSTREAM_BRANCH_REDIS_KEY=`echo "${UPSTREAM_BRANCH}" | sed 's/\//-/g'`; 123 | # We need to use https, the unauthenticated git protocol is no longer supported by GitHub. 124 | UPSTREAM_REPO=`echo "${PR_JSON}" | jq -r .head.repo.git_url | sed 's/git:/https:/'`; 125 | UPSTREAM_REPO_NAME=`echo "${PR_JSON}" | jq -r .head.repo.name`; 126 | fi 127 | - echo "UPSTREAM_BRANCH=${UPSTREAM_BRANCH}" | tee -a parent-vars.env 128 | - echo "UPSTREAM_BRANCH_REDIS_KEY=${UPSTREAM_BRANCH_REDIS_KEY}" | tee -a parent-vars.env 129 | - echo "UPSTREAM_REPO=${UPSTREAM_REPO}" | tee -a parent-vars.env 130 | - echo "UPSTREAM_REPO_NAME=${UPSTREAM_REPO_NAME}" | tee -a parent-vars.env 131 | 132 | # REDIS_SIZES_KEY (e.g. ink-waterfall::ink::foo-add-feature::sizes) 133 | # defines a Redis key name where contract sizes will be stored from an upstream above. 134 | - echo "REDIS_SIZES_KEY=${CI_PROJECT_NAME}::${UPSTREAM_REPO_NAME}::${UPSTREAM_BRANCH_REDIS_KEY}::sizes" | tee -a parent-vars.env 135 | - echo "REDIS_SIZES_RAND_EXT_KEY=${CI_PROJECT_NAME}::${UPSTREAM_REPO_NAME}::${UPSTREAM_BRANCH_REDIS_KEY}::sizes_rand_ext" | tee -a parent-vars.env 136 | - echo "REDIS_GAS_USAGE_KEY=${CI_PROJECT_NAME}::${UPSTREAM_REPO_NAME}::${UPSTREAM_BRANCH_REDIS_KEY}::gas_usage" | tee -a parent-vars.env 137 | - echo "REDIS_GAS_USAGE_RAND_EXT_KEY=${CI_PROJECT_NAME}::${UPSTREAM_REPO_NAME}::${UPSTREAM_BRANCH_REDIS_KEY}::gas_usage_rand_ext" | tee -a parent-vars.env 138 | 139 | # REDIS_SIZES_KEY_MASTER (e.g. ink-waterfall::ink::master::sizes) 140 | # defines a Redis key name for an upstream's master reference branch. 141 | # contract sizes stored there will be used for a comparison with contract sizes stored in REDIS_SIZES_KEY. 142 | - echo "REDIS_SIZES_KEY_MASTER=${CI_PROJECT_NAME}::${UPSTREAM_REPO_NAME}::master::sizes" | tee -a parent-vars.env 143 | - echo "REDIS_SIZES_RAND_EXT_KEY_MASTER=${CI_PROJECT_NAME}::${UPSTREAM_REPO_NAME}::master::sizes_rand_ext" | tee -a parent-vars.env 144 | - echo "REDIS_GAS_USAGE_KEY_MASTER=${CI_PROJECT_NAME}::${UPSTREAM_REPO_NAME}::master::gas_usage" | tee -a parent-vars.env 145 | - echo "REDIS_GAS_USAGE_RAND_EXT_KEY_MASTER=${CI_PROJECT_NAME}::${UPSTREAM_REPO_NAME}::master::gas_usage_rand_ext" | tee -a parent-vars.env 146 | artifacts: 147 | reports: 148 | dotenv: parent-vars.env 149 | 150 | 151 | .build-ink-example-contracts: &build-ink-example-contracts 152 | - set -o pipefail 153 | - redis-cli -u $GITLAB_REDIS_URI del $REDIS_SIZES_KEY 154 | - echo "Data will be written to $REDIS_SIZES_KEY" 155 | - for example in ${INK_EXAMPLES_PATH}/*/; do 156 | if [ "$example" == "./ink/integration-tests/lang-err-integration-tests/" ]; then continue; fi; 157 | echo "set -o pipefail; ./scripts/ci/build-contract.sh ${example} | 158 | redis-cli -u ${GITLAB_REDIS_URI} -x rpush ${REDIS_SIZES_KEY}" >> /tmp/cmds; 159 | done 160 | - for contract in ${DELEGATOR_SUBCONTRACTS}; do 161 | echo "./scripts/ci/build-contract.sh ${INK_EXAMPLES_PATH}/delegator/${contract} | 162 | redis-cli -u ${GITLAB_REDIS_URI} -x rpush ${REDIS_SIZES_KEY}" >> /tmp/cmds; 163 | done 164 | # Exit when the first job fails. Kill running jobs 165 | - parallel --halt-on-error now,fail=1 -j 2 -a /tmp/cmds --joblog /tmp/joblog 166 | - cat /tmp/joblog 167 | # all ci/cd keys need to have ttl 168 | - redis-cli -u $GITLAB_REDIS_URI expire $REDIS_SIZES_KEY $GITLAB_REDIS_TTL 169 | 170 | 171 | .build-rand-extension-contract: &build-rand-extension-contract 172 | - set -o pipefail 173 | # delete old list items if the key has existed previously 174 | - redis-cli -u $GITLAB_REDIS_URI del $REDIS_SIZES_RAND_EXT_KEY 175 | - echo "Data will be written to $REDIS_SIZES_RAND_EXT_KEY" 176 | - ./scripts/ci/build-contract.sh ./ink/integration-tests/rand-extension/ | 177 | redis-cli -u ${GITLAB_REDIS_URI} -x rpush ${REDIS_SIZES_RAND_EXT_KEY} 178 | # all ci/cd keys need to have ttl 179 | - redis-cli -u $GITLAB_REDIS_URI expire $REDIS_SIZES_RAND_EXT_KEY $GITLAB_REDIS_TTL 180 | 181 | 182 | .store-ink-examples-gas-usage-to-redis: &store-ink-examples-gas-usage-to-redis 183 | - set -o pipefail 184 | # delete old list items if the key has existed previously 185 | - redis-cli -u $GITLAB_REDIS_URI del $REDIS_GAS_USAGE_KEY 186 | - echo "Data will be written to $REDIS_GAS_USAGE_KEY" 187 | - for example in ${INK_EXAMPLES_PATH}/*/; do 188 | if [ "$example" == "./ink/integration-tests/rand-extension/" ]; then continue; fi; 189 | if [ "$example" == "./ink/integration-tests/lang-err-integration-tests/" ]; then continue; fi; 190 | echo $example; 191 | ./scripts/ci/extract-gas-usage.sh ${example}; 192 | done 193 | - for example in ${INK_EXAMPLES_PATH}/*/; do 194 | if [ "$example" == "./ink/integration-tests/rand-extension/" ]; then continue; fi; 195 | if [ "$example" == "./ink/integration-tests/lang-err-integration-tests/" ]; then continue; fi; 196 | ./scripts/ci/extract-gas-usage.sh ${example} | 197 | redis-cli -u ${GITLAB_REDIS_URI} -x rpush ${REDIS_GAS_USAGE_KEY}; 198 | done 199 | # all ci/cd keys need to have ttl 200 | - redis-cli -u $GITLAB_REDIS_URI expire $REDIS_GAS_USAGE_KEY $GITLAB_REDIS_TTL 201 | 202 | 203 | .store-rand-ext-gas-usage-to-redis: &store-rand-ext-gas-usage-to-redis 204 | - set -o pipefail 205 | # delete old list items if the key has existed previously 206 | - redis-cli -u $GITLAB_REDIS_URI del $REDIS_GAS_USAGE_RAND_EXT_KEY 207 | - echo "Data will be written to $REDIS_GAS_USAGE_RAND_EXT_KEY" 208 | - ./scripts/ci/extract-gas-usage.sh "rand-extension" 209 | - ./scripts/ci/extract-gas-usage.sh "rand-extension" | redis-cli -u ${GITLAB_REDIS_URI} -x rpush ${REDIS_GAS_USAGE_RAND_EXT_KEY} 210 | # all ci/cd keys need to have ttl 211 | - redis-cli -u $GITLAB_REDIS_URI expire $REDIS_GAS_USAGE_RAND_EXT_KEY $GITLAB_REDIS_TTL 212 | 213 | 214 | polkadot-js-ui-ink-examples: &polkadot-js-ui-ink-examples 215 | stage: test 216 | <<: *docker-env 217 | script: 218 | - *choose-ink-waterfall-repo 219 | - *clone-repo 220 | - *start-substrate-contracts-node 221 | - *build-ink-example-contracts 222 | - WATERFALL_SKIP_CONTRACT_BUILD=true cargo test --jobs 1 --features headless,polkadot-js-ui 2>&1 | 223 | tee /tmp/waterfall.log 224 | - *store-ink-examples-gas-usage-to-redis 225 | after_script: 226 | - *shutdown-substrate-contracts-node 227 | dependencies: 228 | - parent-vars 229 | 230 | # fixme: diagnose and fix the rand-extension test 231 | .polkadot-js-ui-rand-extension: 232 | stage: test 233 | <<: *docker-env 234 | script: 235 | - *choose-ink-waterfall-repo 236 | - *clone-repo 237 | - *start-substrate-contracts-node-rand-extension 238 | - *build-rand-extension-contract 239 | - WATERFALL_SKIP_CONTRACT_BUILD=true cargo test --features headless,polkadot-js-ui -- --ignored rand_extension 2>&1 | 240 | tee /tmp/waterfall.log 241 | - *store-rand-ext-gas-usage-to-redis 242 | after_script: 243 | - *shutdown-substrate-contracts-node-rand-extension 244 | dependencies: 245 | - parent-vars 246 | 247 | 248 | evaluate-ink-examples-changes: 249 | stage: compare-build 250 | <<: *kubernetes-env 251 | # Comparison is made only if a parent (trigger) was created by a PR. 252 | # Otherwise we would be comparing `master` with `master`. 253 | <<: *if-trigger-ref-valid 254 | script: 255 | - *choose-ink-waterfall-repo 256 | - *clone-repo-past-month 257 | 258 | # Deserialize comparison data 259 | - redis-cli -u $GITLAB_REDIS_URI --raw lrange $REDIS_SIZES_KEY 0 -1 | sort | tee $REDIS_SIZES_KEY.csv 260 | - redis-cli -u $GITLAB_REDIS_URI --raw lrange $REDIS_SIZES_RAND_EXT_KEY 0 -1 | sort | tee --append $REDIS_SIZES_KEY.csv 261 | 262 | - redis-cli -u $GITLAB_REDIS_URI --raw lrange $REDIS_SIZES_KEY_MASTER 0 -1 | sort | tee $REDIS_SIZES_KEY_MASTER.csv 263 | - redis-cli -u $GITLAB_REDIS_URI --raw lrange $REDIS_SIZES_RAND_EXT_KEY_MASTER 0 -1 | sort | tee --append $REDIS_SIZES_KEY_MASTER.csv 264 | 265 | - redis-cli -u $GITLAB_REDIS_URI --raw lrange $REDIS_GAS_USAGE_KEY 0 -1 | sort | tee $REDIS_GAS_USAGE_KEY.csv 266 | - redis-cli -u $GITLAB_REDIS_URI --raw lrange $REDIS_GAS_USAGE_RAND_EXT_KEY 0 -1 | sort | tee --append $REDIS_GAS_USAGE_KEY.csv 267 | - redis-cli -u $GITLAB_REDIS_URI --raw lrange $REDIS_GAS_USAGE_KEY_MASTER 0 -1 | sort | tee $REDIS_GAS_USAGE_KEY_MASTER.csv 268 | - redis-cli -u $GITLAB_REDIS_URI --raw lrange $REDIS_GAS_USAGE_RAND_EXT_KEY_MASTER 0 -1 | sort | tee --append $REDIS_GAS_USAGE_KEY_MASTER.csv 269 | 270 | - PR_COMMENTS_URL=https://api.github.com/repos/paritytech/ink/issues/${TRGR_REF}/comments 271 | - ./scripts/ci/evaluate-examples-changes.sh $PR_COMMENTS_URL 272 | $REDIS_SIZES_KEY_MASTER.csv $REDIS_SIZES_KEY.csv 273 | $REDIS_GAS_USAGE_KEY_MASTER.csv $REDIS_GAS_USAGE_KEY.csv 274 | dependencies: 275 | - parent-vars 276 | 277 | 278 | build_badge: 279 | stage: compare-build 280 | rules: 281 | only: 282 | refs: 283 | - branches 284 | - master 285 | - tags 286 | <<: *docker-env 287 | before_script: 288 | - chmod +x ./scripts/ci/get-updated-badge-info.sh 289 | script: 290 | - echo "building badge" 291 | after_script: 292 | - ./scripts/ci/get-updated-badge-info.sh 293 | artifacts: 294 | paths: 295 | - badge.json 296 | when: always 297 | expire_in: 4 weeks 298 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 90 # changed 2 | hard_tabs = false 3 | tab_spaces = 4 4 | newline_style = "Auto" 5 | use_small_heuristics = "Default" 6 | indent_style = "Block" 7 | wrap_comments = false 8 | format_code_in_doc_comments = false 9 | comment_width = 80 10 | normalize_comments = true # changed 11 | normalize_doc_attributes = false 12 | license_template_path = "FILE_HEADER" # changed 13 | format_strings = false 14 | format_macro_matchers = false 15 | format_macro_bodies = true 16 | empty_item_single_line = true 17 | struct_lit_single_line = true 18 | fn_single_line = false 19 | where_single_line = false 20 | imports_indent = "Block" 21 | imports_layout = "Vertical" # changed 22 | imports_granularity = "Crate" # changed 23 | reorder_imports = true 24 | reorder_modules = true 25 | reorder_impl_items = false 26 | type_punctuation_density = "Wide" 27 | space_before_colon = false 28 | space_after_colon = true 29 | spaces_around_ranges = false 30 | binop_separator = "Front" 31 | remove_nested_parens = true 32 | combine_control_expr = false # changed 33 | overflow_delimited_expr = false 34 | struct_field_align_threshold = 0 35 | enum_discrim_align_threshold = 0 36 | match_arm_blocks = true 37 | force_multiline_blocks = true # changed 38 | fn_args_layout = "Tall" 39 | brace_style = "SameLineWhere" 40 | control_brace_style = "AlwaysSameLine" 41 | trailing_semicolon = false # changed 42 | trailing_comma = "Vertical" 43 | match_block_trailing_comma = false 44 | blank_lines_upper_bound = 1 45 | blank_lines_lower_bound = 0 46 | edition = "2021" # changed 47 | version = "One" 48 | merge_derives = true 49 | use_try_shorthand = true # changed 50 | use_field_init_shorthand = true # changed 51 | force_explicit_abi = true 52 | condense_wildcard_suffixes = false 53 | color = "Auto" 54 | unstable_features = true # changed 55 | disable_all_formatting = false 56 | skip_children = false 57 | hide_parse_errors = false 58 | error_on_line_overflow = false 59 | error_on_unformatted = false 60 | report_todo = "Never" # changed 61 | report_fixme = "Always" 62 | ignore = [] 63 | 64 | # Below are `rustfmt` internal settings 65 | # 66 | # emit_mode = "Files" 67 | # make_backup = false 68 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lists some code owners. 2 | # 3 | # A codeowner just oversees some part of the codebase. If an owned file is changed then the 4 | # corresponding codeowner receives a review request. An approval of the codeowner might be 5 | # required for merging a PR (depends on repository settings). 6 | # 7 | # For details about syntax, see: 8 | # https://help.github.com/en/articles/about-code-owners 9 | # But here are some important notes: 10 | # 11 | # - Glob syntax is git-like, e.g. `/core` means the core directory in the root, unlike `core` 12 | # which can be everywhere. 13 | # - Multiple owners are supported. 14 | # - Either handle (e.g, @github_user or @github_org/team) or email can be used. Keep in mind, 15 | # that handles might work better because they are more recognizable on GitHub, 16 | # you can use them for mentioning unlike an email. 17 | # - The latest matching rule, if multiple, takes precedence. 18 | 19 | # CI 20 | /.github/ @paritytech/ci @HCastano @cmichi 21 | /scripts/ci/ @paritytech/ci @HCastano @cmichi 22 | /.gitlab-ci.yml @paritytech/ci @HCastano @cmichi 23 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.19" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "async-trait" 16 | version = "0.1.58" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" 19 | dependencies = [ 20 | "proc-macro2", 21 | "quote", 22 | "syn", 23 | ] 24 | 25 | [[package]] 26 | name = "atty" 27 | version = "0.2.14" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 30 | dependencies = [ 31 | "hermit-abi", 32 | "libc", 33 | "winapi", 34 | ] 35 | 36 | [[package]] 37 | name = "autocfg" 38 | version = "1.1.0" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 41 | 42 | [[package]] 43 | name = "base64" 44 | version = "0.12.3" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 47 | 48 | [[package]] 49 | name = "base64" 50 | version = "0.13.1" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" 53 | 54 | [[package]] 55 | name = "bitflags" 56 | version = "1.3.2" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 59 | 60 | [[package]] 61 | name = "bytes" 62 | version = "0.5.6" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" 65 | 66 | [[package]] 67 | name = "bytes" 68 | version = "1.2.1" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" 71 | 72 | [[package]] 73 | name = "cc" 74 | version = "1.0.74" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" 77 | 78 | [[package]] 79 | name = "cfg-if" 80 | version = "1.0.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 83 | 84 | [[package]] 85 | name = "convert_case" 86 | version = "0.4.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" 89 | 90 | [[package]] 91 | name = "cookie" 92 | version = "0.12.0" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" 95 | dependencies = [ 96 | "time 0.1.44", 97 | ] 98 | 99 | [[package]] 100 | name = "cookie" 101 | version = "0.16.1" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "344adc371239ef32293cb1c4fe519592fcf21206c79c02854320afcdf3ab4917" 104 | dependencies = [ 105 | "percent-encoding", 106 | "time 0.3.17", 107 | "version_check", 108 | ] 109 | 110 | [[package]] 111 | name = "core-foundation" 112 | version = "0.9.3" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 115 | dependencies = [ 116 | "core-foundation-sys", 117 | "libc", 118 | ] 119 | 120 | [[package]] 121 | name = "core-foundation-sys" 122 | version = "0.8.3" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 125 | 126 | [[package]] 127 | name = "either" 128 | version = "1.8.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" 131 | 132 | [[package]] 133 | name = "env_logger" 134 | version = "0.8.4" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" 137 | dependencies = [ 138 | "atty", 139 | "humantime", 140 | "log", 141 | "regex", 142 | "termcolor", 143 | ] 144 | 145 | [[package]] 146 | name = "fantoccini" 147 | version = "0.19.3" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "65f0fbe245d714b596ba5802b46f937f5ce68dcae0f32f9a70b5c3b04d3c6f64" 150 | dependencies = [ 151 | "base64 0.13.1", 152 | "cookie 0.16.1", 153 | "futures-core", 154 | "futures-util", 155 | "http", 156 | "hyper", 157 | "hyper-tls", 158 | "mime", 159 | "serde", 160 | "serde_json", 161 | "time 0.3.17", 162 | "tokio", 163 | "url", 164 | "webdriver 0.46.0", 165 | ] 166 | 167 | [[package]] 168 | name = "fastrand" 169 | version = "1.8.0" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 172 | dependencies = [ 173 | "instant", 174 | ] 175 | 176 | [[package]] 177 | name = "fnv" 178 | version = "1.0.7" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 181 | 182 | [[package]] 183 | name = "foreign-types" 184 | version = "0.3.2" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 187 | dependencies = [ 188 | "foreign-types-shared", 189 | ] 190 | 191 | [[package]] 192 | name = "foreign-types-shared" 193 | version = "0.1.1" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 196 | 197 | [[package]] 198 | name = "form_urlencoded" 199 | version = "1.1.0" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 202 | dependencies = [ 203 | "percent-encoding", 204 | ] 205 | 206 | [[package]] 207 | name = "futures-channel" 208 | version = "0.3.25" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" 211 | dependencies = [ 212 | "futures-core", 213 | ] 214 | 215 | [[package]] 216 | name = "futures-core" 217 | version = "0.3.25" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" 220 | 221 | [[package]] 222 | name = "futures-macro" 223 | version = "0.3.25" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" 226 | dependencies = [ 227 | "proc-macro2", 228 | "quote", 229 | "syn", 230 | ] 231 | 232 | [[package]] 233 | name = "futures-task" 234 | version = "0.3.25" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" 237 | 238 | [[package]] 239 | name = "futures-util" 240 | version = "0.3.25" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" 243 | dependencies = [ 244 | "futures-core", 245 | "futures-macro", 246 | "futures-task", 247 | "pin-project-lite", 248 | "pin-utils", 249 | "slab", 250 | ] 251 | 252 | [[package]] 253 | name = "getrandom" 254 | version = "0.2.8" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 257 | dependencies = [ 258 | "cfg-if", 259 | "libc", 260 | "wasi 0.11.0+wasi-snapshot-preview1", 261 | ] 262 | 263 | [[package]] 264 | name = "hermit-abi" 265 | version = "0.1.19" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 268 | dependencies = [ 269 | "libc", 270 | ] 271 | 272 | [[package]] 273 | name = "hex" 274 | version = "0.4.3" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 277 | 278 | [[package]] 279 | name = "http" 280 | version = "0.2.8" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" 283 | dependencies = [ 284 | "bytes 1.2.1", 285 | "fnv", 286 | "itoa", 287 | ] 288 | 289 | [[package]] 290 | name = "http-body" 291 | version = "0.4.5" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 294 | dependencies = [ 295 | "bytes 1.2.1", 296 | "http", 297 | "pin-project-lite", 298 | ] 299 | 300 | [[package]] 301 | name = "httparse" 302 | version = "1.8.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 305 | 306 | [[package]] 307 | name = "httpdate" 308 | version = "1.0.2" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 311 | 312 | [[package]] 313 | name = "humantime" 314 | version = "2.1.0" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 317 | 318 | [[package]] 319 | name = "hyper" 320 | version = "0.14.22" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" 323 | dependencies = [ 324 | "bytes 1.2.1", 325 | "futures-channel", 326 | "futures-core", 327 | "futures-util", 328 | "http", 329 | "http-body", 330 | "httparse", 331 | "httpdate", 332 | "itoa", 333 | "pin-project-lite", 334 | "socket2", 335 | "tokio", 336 | "tower-service", 337 | "tracing", 338 | "want", 339 | ] 340 | 341 | [[package]] 342 | name = "hyper-tls" 343 | version = "0.5.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 346 | dependencies = [ 347 | "bytes 1.2.1", 348 | "hyper", 349 | "native-tls", 350 | "tokio", 351 | "tokio-native-tls", 352 | ] 353 | 354 | [[package]] 355 | name = "idna" 356 | version = "0.3.0" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 359 | dependencies = [ 360 | "unicode-bidi", 361 | "unicode-normalization", 362 | ] 363 | 364 | [[package]] 365 | name = "instant" 366 | version = "0.1.12" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 369 | dependencies = [ 370 | "cfg-if", 371 | ] 372 | 373 | [[package]] 374 | name = "itoa" 375 | version = "1.0.4" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" 378 | 379 | [[package]] 380 | name = "lang_macro" 381 | version = "0.1.0" 382 | dependencies = [ 383 | "proc-macro2", 384 | "quote", 385 | "syn", 386 | ] 387 | 388 | [[package]] 389 | name = "lazy_static" 390 | version = "1.4.0" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 393 | 394 | [[package]] 395 | name = "libc" 396 | version = "0.2.137" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" 399 | 400 | [[package]] 401 | name = "lock_api" 402 | version = "0.4.9" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 405 | dependencies = [ 406 | "autocfg", 407 | "scopeguard", 408 | ] 409 | 410 | [[package]] 411 | name = "log" 412 | version = "0.4.17" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 415 | dependencies = [ 416 | "cfg-if", 417 | ] 418 | 419 | [[package]] 420 | name = "memchr" 421 | version = "2.5.0" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 424 | 425 | [[package]] 426 | name = "mime" 427 | version = "0.3.16" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 430 | 431 | [[package]] 432 | name = "mio" 433 | version = "0.8.5" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" 436 | dependencies = [ 437 | "libc", 438 | "log", 439 | "wasi 0.11.0+wasi-snapshot-preview1", 440 | "windows-sys 0.42.0", 441 | ] 442 | 443 | [[package]] 444 | name = "native-tls" 445 | version = "0.2.11" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" 448 | dependencies = [ 449 | "lazy_static", 450 | "libc", 451 | "log", 452 | "openssl", 453 | "openssl-probe", 454 | "openssl-sys", 455 | "schannel", 456 | "security-framework", 457 | "security-framework-sys", 458 | "tempfile", 459 | ] 460 | 461 | [[package]] 462 | name = "num_cpus" 463 | version = "1.14.0" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" 466 | dependencies = [ 467 | "hermit-abi", 468 | "libc", 469 | ] 470 | 471 | [[package]] 472 | name = "once_cell" 473 | version = "1.16.0" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 476 | 477 | [[package]] 478 | name = "openssl" 479 | version = "0.10.42" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" 482 | dependencies = [ 483 | "bitflags", 484 | "cfg-if", 485 | "foreign-types", 486 | "libc", 487 | "once_cell", 488 | "openssl-macros", 489 | "openssl-sys", 490 | ] 491 | 492 | [[package]] 493 | name = "openssl-macros" 494 | version = "0.1.0" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" 497 | dependencies = [ 498 | "proc-macro2", 499 | "quote", 500 | "syn", 501 | ] 502 | 503 | [[package]] 504 | name = "openssl-probe" 505 | version = "0.1.5" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 508 | 509 | [[package]] 510 | name = "openssl-sys" 511 | version = "0.9.77" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" 514 | dependencies = [ 515 | "autocfg", 516 | "cc", 517 | "libc", 518 | "pkg-config", 519 | "vcpkg", 520 | ] 521 | 522 | [[package]] 523 | name = "parking_lot" 524 | version = "0.12.1" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 527 | dependencies = [ 528 | "lock_api", 529 | "parking_lot_core", 530 | ] 531 | 532 | [[package]] 533 | name = "parking_lot_core" 534 | version = "0.9.4" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" 537 | dependencies = [ 538 | "cfg-if", 539 | "libc", 540 | "redox_syscall", 541 | "smallvec", 542 | "windows-sys 0.42.0", 543 | ] 544 | 545 | [[package]] 546 | name = "percent-encoding" 547 | version = "2.2.0" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 550 | 551 | [[package]] 552 | name = "pin-project-lite" 553 | version = "0.2.9" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 556 | 557 | [[package]] 558 | name = "pin-utils" 559 | version = "0.1.0" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 562 | 563 | [[package]] 564 | name = "pkg-config" 565 | version = "0.3.26" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" 568 | 569 | [[package]] 570 | name = "portpicker" 571 | version = "0.1.1" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "be97d76faf1bfab666e1375477b23fde79eccf0276e9b63b92a39d676a889ba9" 574 | dependencies = [ 575 | "rand", 576 | ] 577 | 578 | [[package]] 579 | name = "ppv-lite86" 580 | version = "0.2.17" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 583 | 584 | [[package]] 585 | name = "proc-macro2" 586 | version = "1.0.47" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 589 | dependencies = [ 590 | "unicode-ident", 591 | ] 592 | 593 | [[package]] 594 | name = "quote" 595 | version = "1.0.21" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 598 | dependencies = [ 599 | "proc-macro2", 600 | ] 601 | 602 | [[package]] 603 | name = "rand" 604 | version = "0.8.5" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 607 | dependencies = [ 608 | "libc", 609 | "rand_chacha", 610 | "rand_core", 611 | ] 612 | 613 | [[package]] 614 | name = "rand_chacha" 615 | version = "0.3.1" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 618 | dependencies = [ 619 | "ppv-lite86", 620 | "rand_core", 621 | ] 622 | 623 | [[package]] 624 | name = "rand_core" 625 | version = "0.6.4" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 628 | dependencies = [ 629 | "getrandom", 630 | ] 631 | 632 | [[package]] 633 | name = "redox_syscall" 634 | version = "0.2.16" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 637 | dependencies = [ 638 | "bitflags", 639 | ] 640 | 641 | [[package]] 642 | name = "regex" 643 | version = "1.7.0" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" 646 | dependencies = [ 647 | "aho-corasick", 648 | "memchr", 649 | "regex-syntax", 650 | ] 651 | 652 | [[package]] 653 | name = "regex-syntax" 654 | version = "0.6.28" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 657 | 658 | [[package]] 659 | name = "remove_dir_all" 660 | version = "0.5.3" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 663 | dependencies = [ 664 | "winapi", 665 | ] 666 | 667 | [[package]] 668 | name = "ryu" 669 | version = "1.0.11" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 672 | 673 | [[package]] 674 | name = "schannel" 675 | version = "0.1.20" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" 678 | dependencies = [ 679 | "lazy_static", 680 | "windows-sys 0.36.1", 681 | ] 682 | 683 | [[package]] 684 | name = "scopeguard" 685 | version = "1.1.0" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 688 | 689 | [[package]] 690 | name = "security-framework" 691 | version = "2.7.0" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" 694 | dependencies = [ 695 | "bitflags", 696 | "core-foundation", 697 | "core-foundation-sys", 698 | "libc", 699 | "security-framework-sys", 700 | ] 701 | 702 | [[package]] 703 | name = "security-framework-sys" 704 | version = "2.6.1" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" 707 | dependencies = [ 708 | "core-foundation-sys", 709 | "libc", 710 | ] 711 | 712 | [[package]] 713 | name = "serde" 714 | version = "1.0.147" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" 717 | dependencies = [ 718 | "serde_derive", 719 | ] 720 | 721 | [[package]] 722 | name = "serde_derive" 723 | version = "1.0.147" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" 726 | dependencies = [ 727 | "proc-macro2", 728 | "quote", 729 | "syn", 730 | ] 731 | 732 | [[package]] 733 | name = "serde_json" 734 | version = "1.0.87" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" 737 | dependencies = [ 738 | "itoa", 739 | "ryu", 740 | "serde", 741 | ] 742 | 743 | [[package]] 744 | name = "signal-hook-registry" 745 | version = "1.4.0" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 748 | dependencies = [ 749 | "libc", 750 | ] 751 | 752 | [[package]] 753 | name = "slab" 754 | version = "0.4.7" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 757 | dependencies = [ 758 | "autocfg", 759 | ] 760 | 761 | [[package]] 762 | name = "smallvec" 763 | version = "1.10.0" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 766 | 767 | [[package]] 768 | name = "socket2" 769 | version = "0.4.7" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 772 | dependencies = [ 773 | "libc", 774 | "winapi", 775 | ] 776 | 777 | [[package]] 778 | name = "syn" 779 | version = "1.0.103" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" 782 | dependencies = [ 783 | "proc-macro2", 784 | "quote", 785 | "unicode-ident", 786 | ] 787 | 788 | [[package]] 789 | name = "tempfile" 790 | version = "3.3.0" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 793 | dependencies = [ 794 | "cfg-if", 795 | "fastrand", 796 | "libc", 797 | "redox_syscall", 798 | "remove_dir_all", 799 | "winapi", 800 | ] 801 | 802 | [[package]] 803 | name = "termcolor" 804 | version = "1.1.3" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 807 | dependencies = [ 808 | "winapi-util", 809 | ] 810 | 811 | [[package]] 812 | name = "time" 813 | version = "0.1.44" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 816 | dependencies = [ 817 | "libc", 818 | "wasi 0.10.0+wasi-snapshot-preview1", 819 | "winapi", 820 | ] 821 | 822 | [[package]] 823 | name = "time" 824 | version = "0.3.17" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" 827 | dependencies = [ 828 | "itoa", 829 | "serde", 830 | "time-core", 831 | "time-macros", 832 | ] 833 | 834 | [[package]] 835 | name = "time-core" 836 | version = "0.1.0" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" 839 | 840 | [[package]] 841 | name = "time-macros" 842 | version = "0.2.6" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" 845 | dependencies = [ 846 | "time-core", 847 | ] 848 | 849 | [[package]] 850 | name = "tinyvec" 851 | version = "1.6.0" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 854 | dependencies = [ 855 | "tinyvec_macros", 856 | ] 857 | 858 | [[package]] 859 | name = "tinyvec_macros" 860 | version = "0.1.0" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 863 | 864 | [[package]] 865 | name = "tokio" 866 | version = "1.21.2" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" 869 | dependencies = [ 870 | "autocfg", 871 | "bytes 1.2.1", 872 | "libc", 873 | "memchr", 874 | "mio", 875 | "num_cpus", 876 | "parking_lot", 877 | "pin-project-lite", 878 | "signal-hook-registry", 879 | "socket2", 880 | "tokio-macros", 881 | "winapi", 882 | ] 883 | 884 | [[package]] 885 | name = "tokio-macros" 886 | version = "1.8.0" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" 889 | dependencies = [ 890 | "proc-macro2", 891 | "quote", 892 | "syn", 893 | ] 894 | 895 | [[package]] 896 | name = "tokio-native-tls" 897 | version = "0.3.0" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 900 | dependencies = [ 901 | "native-tls", 902 | "tokio", 903 | ] 904 | 905 | [[package]] 906 | name = "tower-service" 907 | version = "0.3.2" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 910 | 911 | [[package]] 912 | name = "tracing" 913 | version = "0.1.37" 914 | source = "registry+https://github.com/rust-lang/crates.io-index" 915 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 916 | dependencies = [ 917 | "cfg-if", 918 | "pin-project-lite", 919 | "tracing-core", 920 | ] 921 | 922 | [[package]] 923 | name = "tracing-core" 924 | version = "0.1.30" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 927 | dependencies = [ 928 | "once_cell", 929 | ] 930 | 931 | [[package]] 932 | name = "try-lock" 933 | version = "0.2.3" 934 | source = "registry+https://github.com/rust-lang/crates.io-index" 935 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 936 | 937 | [[package]] 938 | name = "unicode-bidi" 939 | version = "0.3.8" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" 942 | 943 | [[package]] 944 | name = "unicode-ident" 945 | version = "1.0.5" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 948 | 949 | [[package]] 950 | name = "unicode-normalization" 951 | version = "0.1.22" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 954 | dependencies = [ 955 | "tinyvec", 956 | ] 957 | 958 | [[package]] 959 | name = "unicode-segmentation" 960 | version = "1.10.0" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" 963 | 964 | [[package]] 965 | name = "url" 966 | version = "2.3.1" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 969 | dependencies = [ 970 | "form_urlencoded", 971 | "idna", 972 | "percent-encoding", 973 | ] 974 | 975 | [[package]] 976 | name = "vcpkg" 977 | version = "0.2.15" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 980 | 981 | [[package]] 982 | name = "version_check" 983 | version = "0.9.4" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 986 | 987 | [[package]] 988 | name = "want" 989 | version = "0.3.0" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 992 | dependencies = [ 993 | "log", 994 | "try-lock", 995 | ] 996 | 997 | [[package]] 998 | name = "wasi" 999 | version = "0.10.0+wasi-snapshot-preview1" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1002 | 1003 | [[package]] 1004 | name = "wasi" 1005 | version = "0.11.0+wasi-snapshot-preview1" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1008 | 1009 | [[package]] 1010 | name = "waterfall" 1011 | version = "0.1.0" 1012 | dependencies = [ 1013 | "async-trait", 1014 | "convert_case", 1015 | "env_logger", 1016 | "fantoccini", 1017 | "futures-core", 1018 | "futures-util", 1019 | "hex", 1020 | "lang_macro", 1021 | "lazy_static", 1022 | "log", 1023 | "portpicker", 1024 | "rand", 1025 | "regex", 1026 | "serde", 1027 | "serde_json", 1028 | "tokio", 1029 | "webdriver 0.45.0", 1030 | "which", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "webdriver" 1035 | version = "0.45.0" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "35ac57589eec8879f709240c532f6d238d492e204ec6828aeffaf311c20ff580" 1038 | dependencies = [ 1039 | "base64 0.12.3", 1040 | "bytes 0.5.6", 1041 | "cookie 0.12.0", 1042 | "http", 1043 | "log", 1044 | "serde", 1045 | "serde_derive", 1046 | "serde_json", 1047 | "time 0.1.44", 1048 | "unicode-segmentation", 1049 | "url", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "webdriver" 1054 | version = "0.46.0" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "9973cb72c8587d5ad5efdb91e663d36177dc37725e6c90ca86c626b0cc45c93f" 1057 | dependencies = [ 1058 | "base64 0.13.1", 1059 | "bytes 1.2.1", 1060 | "cookie 0.16.1", 1061 | "http", 1062 | "log", 1063 | "serde", 1064 | "serde_derive", 1065 | "serde_json", 1066 | "time 0.3.17", 1067 | "unicode-segmentation", 1068 | "url", 1069 | ] 1070 | 1071 | [[package]] 1072 | name = "which" 1073 | version = "4.3.0" 1074 | source = "registry+https://github.com/rust-lang/crates.io-index" 1075 | checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" 1076 | dependencies = [ 1077 | "either", 1078 | "libc", 1079 | "once_cell", 1080 | ] 1081 | 1082 | [[package]] 1083 | name = "winapi" 1084 | version = "0.3.9" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1087 | dependencies = [ 1088 | "winapi-i686-pc-windows-gnu", 1089 | "winapi-x86_64-pc-windows-gnu", 1090 | ] 1091 | 1092 | [[package]] 1093 | name = "winapi-i686-pc-windows-gnu" 1094 | version = "0.4.0" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1097 | 1098 | [[package]] 1099 | name = "winapi-util" 1100 | version = "0.1.5" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1103 | dependencies = [ 1104 | "winapi", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "winapi-x86_64-pc-windows-gnu" 1109 | version = "0.4.0" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1112 | 1113 | [[package]] 1114 | name = "windows-sys" 1115 | version = "0.36.1" 1116 | source = "registry+https://github.com/rust-lang/crates.io-index" 1117 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 1118 | dependencies = [ 1119 | "windows_aarch64_msvc 0.36.1", 1120 | "windows_i686_gnu 0.36.1", 1121 | "windows_i686_msvc 0.36.1", 1122 | "windows_x86_64_gnu 0.36.1", 1123 | "windows_x86_64_msvc 0.36.1", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "windows-sys" 1128 | version = "0.42.0" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 1131 | dependencies = [ 1132 | "windows_aarch64_gnullvm", 1133 | "windows_aarch64_msvc 0.42.0", 1134 | "windows_i686_gnu 0.42.0", 1135 | "windows_i686_msvc 0.42.0", 1136 | "windows_x86_64_gnu 0.42.0", 1137 | "windows_x86_64_gnullvm", 1138 | "windows_x86_64_msvc 0.42.0", 1139 | ] 1140 | 1141 | [[package]] 1142 | name = "windows_aarch64_gnullvm" 1143 | version = "0.42.0" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 1146 | 1147 | [[package]] 1148 | name = "windows_aarch64_msvc" 1149 | version = "0.36.1" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 1152 | 1153 | [[package]] 1154 | name = "windows_aarch64_msvc" 1155 | version = "0.42.0" 1156 | source = "registry+https://github.com/rust-lang/crates.io-index" 1157 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 1158 | 1159 | [[package]] 1160 | name = "windows_i686_gnu" 1161 | version = "0.36.1" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 1164 | 1165 | [[package]] 1166 | name = "windows_i686_gnu" 1167 | version = "0.42.0" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 1170 | 1171 | [[package]] 1172 | name = "windows_i686_msvc" 1173 | version = "0.36.1" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 1176 | 1177 | [[package]] 1178 | name = "windows_i686_msvc" 1179 | version = "0.42.0" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 1182 | 1183 | [[package]] 1184 | name = "windows_x86_64_gnu" 1185 | version = "0.36.1" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 1188 | 1189 | [[package]] 1190 | name = "windows_x86_64_gnu" 1191 | version = "0.42.0" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 1194 | 1195 | [[package]] 1196 | name = "windows_x86_64_gnullvm" 1197 | version = "0.42.0" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 1200 | 1201 | [[package]] 1202 | name = "windows_x86_64_msvc" 1203 | version = "0.36.1" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 1206 | 1207 | [[package]] 1208 | name = "windows_x86_64_msvc" 1209 | version = "0.42.0" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 1212 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "waterfall" 3 | version = "0.1.0" 4 | authors = ["Michael Mueller "] 5 | edition = "2021" 6 | rust-version = "1.56.1" 7 | 8 | [dependencies] 9 | async-trait = "0.1.50" 10 | convert_case = "0.4.0" 11 | futures-core = "0.3.15" 12 | futures-util = "0.3.15" 13 | tokio = { version = "1", features = [ "full" ] } 14 | webdriver = { version = "0.45", default-features = false } 15 | fantoccini = "0.19.0" 16 | serde = "1.0" 17 | serde_json = "1.0" 18 | rand = "0.8.4" 19 | regex = "1.4" 20 | log = "0.4" 21 | env_logger = "0.8" 22 | which = "4.1.0" 23 | portpicker = "0.1.1" 24 | lazy_static = "1.4.0" 25 | hex = "0.4.3" 26 | 27 | lang_macro = { path = "./lang_macro"} 28 | 29 | [features] 30 | headless = [] 31 | polkadot-js-ui = [] 32 | -------------------------------------------------------------------------------- /FILE_HEADER: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Readme 2 | 3 | >This project has been deprecated. We hope it can serve as inspiration for other End-to-End testing projects! 4 | 5 | [![ci-result][a1]][a2] [![ci-duration][b1]][b2] 6 | 7 | [a1]: https://gitlab.parity.io/parity/ink-waterfall/badges/master/pipeline.svg 8 | [a2]: https://gitlab.parity.io/parity/ink-waterfall/pipelines 9 | [b1]: https://img.shields.io/badge/dynamic/json.svg?label=ci%20execution%20time&url=https://gitlab.parity.io/parity/ink-waterfall/-/jobs/artifacts/master/raw/badge.json?job=build_badge&query=duration&colorB=brightgreen 10 | [b2]: https://gitlab.parity.io/parity/ink-waterfall/pipelines 11 | 12 | This project contains end-to-end tests for this pipeline: 13 | 14 | ``` 15 | ink! ➜ 16 | cargo-contract ➜ 17 | canvas-ui || polkadot-js ➜ 18 | substrate-contracts-node 19 | ``` 20 | 21 | 22 | ## How the tests in this repository work 23 | 24 | * They build [the ink! examples](https://github.com/paritytech/ink/tree/master/examples) 25 | using [`cargo-contract`](https://github.com/paritytech/cargo-contract). 26 | * The resulting `.contract` file is deployed on a local blockchain instance of 27 | [`substrate-contracts-node`](https://github.com/paritytech/substrate-contracts-node). 28 | * The deployment is done using either the [`canvas-ui`](https://github.com/paritytech/canvas-ui) 29 | or [`polkadot-js`](https://github.com/polkadot-js/apps). 30 | * This is done by emulating browser interactions in Firefox (clicking, uploading, …). 31 | * After successful deployment more browser interactions with the contract are 32 | conducted, in order to assert that the contract behaves as expected. 33 | * The `master` branch of all these components is used. 34 | 35 | 36 | ## Required dependencies 37 | 38 | * [`cargo-contract`](https://github.com/paritytech/cargo-contract#installation) with its dependencies 39 | `binaryen` and `rust-src`. 40 | * [`geckodriver`](https://github.com/mozilla/geckodriver/) - is required for emulating interactions with 41 | a browser. Packages are available in some package managers, binary releases are available 42 | [in the repository](https://github.com/mozilla/geckodriver/releases). 43 | * [`substrate-contracts-node`](https://paritytech.github.io/ink-docs/getting-started/setup/#installing-the-substrate-smart-contracts-node) 44 | * [The ink! repository](https://github.com/paritytech/ink) 45 | * Firefox 46 | 47 | For the UI either the [`canvas-ui`](https://github.com/paritytech/canvas-ui) 48 | or the [`polkadot-js`](https://github.com/polkadot-js/apps) UI is an optional 49 | requirement. By default the published versions of those projects are used 50 | ([https://paritytech.github.io/canvas-ui](https://polkadot.js.org/apps/#/), 51 | [https://polkadot.js.org/apps/#/](https://polkadot.js.org/apps/#/)). 52 | 53 | 54 | ## Run it locally 55 | 56 | ```bash 57 | # Create a link to ink! in the local examples of the `ink-waterfall`. 58 | ln -s /path/to/ink/ ./examples/ink 59 | 60 | export INK_EXAMPLES_PATH=/path/to/ink/integration-tests/ 61 | substrate-contracts-node > /tmp/substrate-contracts-node.log 2>&1 & 62 | 63 | # By default you will see the Firefox GUI and the 64 | # tests interacting with it. 65 | cargo test 66 | 67 | # …you can also start the tests headless though, then 68 | # you won't see anything. 69 | cargo test --features headless 70 | 71 | # Handy for debugging: 72 | 73 | # You can prevent the test suite from closing the browser 74 | # window. Then you can still interact with the browser after 75 | # the test failed/succeeded. 76 | export WATERFALL_CLOSE_BROWSER=false 77 | 78 | # Setting the number of parallel jobs to `1` makes it easier 79 | # to follow the tests interacting with the browser. 80 | cargo test --jobs 1 81 | ``` 82 | 83 | By default, the `canvas-ui` published at [https://paritytech.github.io/canvas-ui](https://paritytech.github.io/canvas-ui) 84 | (i.e. the `gh-pages` branch) will be used. But you can also use a local instance: 85 | 86 | ```bash 87 | git clone --depth 1 https://github.com/paritytech/canvas-ui.git 88 | cd canvas-ui/ 89 | yarn install 90 | yarn start > /tmp/canvas-ui.log 2>&1 & 91 | cd .. 92 | 93 | # Check that the UI is ready and a `200 OK` is returned. 94 | curl -I http://localhost:3000/ 95 | 96 | export UI_URL="http://localhost:3000" 97 | cargo test 98 | ``` 99 | 100 | If you want to use the `polkadot-js` UI instead you need to 101 | supply `--features polkadot-js-ui` to `cargo test`. 102 | 103 | 104 | ## Environment variables 105 | 106 | * `INK_EXAMPLES_PATH` ‒ Path to the ink! examples folder. Must be set. 107 | * `UI_URL` ‒ URL of the UI to use. Defaults to the live interface for the chosen UI. 108 | * `WATERFALL_CLOSE_BROWSER` ‒ Close browser window at the end of a test run. 109 | Defaults to `true`. Set it to `false` to prevent closing. 110 | * `WATERFALL_SKIP_CONTRACT_BUILD` ‒ Do not build the contracts, re-use existing artifacts 111 | from their `target` folder. Defaults to `false`. Set it to `true` to skip building. 112 | * `NODE_PORT` ‒ Port under which the `substrate-contracts-node` is running. Defaults to `9944`. 113 | * `RUST_LOG` ‒ Use `RUST_LOG=info` to get output on what the tests are doing. 114 | 115 | 116 | ## Known issue 117 | 118 | The tooltips which show the result of a contract upload or contract 119 | transaction (`ExtrinsicSuccess`, …) disappear after some time. When too 120 | many UI tests are run at the same time the tooltips might disappear 121 | before the test is finished processing them. 122 | 123 | The test will then fail with a `NoSucheElement` error, indicating that 124 | the DOM element is no longer available. The easiest fix for this is to 125 | limit the number of concurrent test threads via e.g. `cargo test --jobs 4`. 126 | -------------------------------------------------------------------------------- /csv-comparator/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /csv-comparator/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bstr" 7 | version = "0.2.17" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" 10 | dependencies = [ 11 | "lazy_static", 12 | "memchr", 13 | "regex-automata", 14 | "serde", 15 | ] 16 | 17 | [[package]] 18 | name = "csv" 19 | version = "1.1.6" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" 22 | dependencies = [ 23 | "bstr", 24 | "csv-core", 25 | "itoa", 26 | "ryu", 27 | "serde", 28 | ] 29 | 30 | [[package]] 31 | name = "csv-comparator" 32 | version = "0.1.0" 33 | dependencies = [ 34 | "csv", 35 | "serde", 36 | "thousands", 37 | ] 38 | 39 | [[package]] 40 | name = "csv-core" 41 | version = "0.1.10" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" 44 | dependencies = [ 45 | "memchr", 46 | ] 47 | 48 | [[package]] 49 | name = "itoa" 50 | version = "0.4.8" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 53 | 54 | [[package]] 55 | name = "lazy_static" 56 | version = "1.4.0" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 59 | 60 | [[package]] 61 | name = "memchr" 62 | version = "2.4.1" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 65 | 66 | [[package]] 67 | name = "proc-macro2" 68 | version = "1.0.29" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" 71 | dependencies = [ 72 | "unicode-xid", 73 | ] 74 | 75 | [[package]] 76 | name = "quote" 77 | version = "1.0.10" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 80 | dependencies = [ 81 | "proc-macro2", 82 | ] 83 | 84 | [[package]] 85 | name = "regex-automata" 86 | version = "0.1.10" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 89 | 90 | [[package]] 91 | name = "ryu" 92 | version = "1.0.5" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 95 | 96 | [[package]] 97 | name = "serde" 98 | version = "1.0.130" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 101 | dependencies = [ 102 | "serde_derive", 103 | ] 104 | 105 | [[package]] 106 | name = "serde_derive" 107 | version = "1.0.130" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 110 | dependencies = [ 111 | "proc-macro2", 112 | "quote", 113 | "syn", 114 | ] 115 | 116 | [[package]] 117 | name = "syn" 118 | version = "1.0.80" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" 121 | dependencies = [ 122 | "proc-macro2", 123 | "quote", 124 | "unicode-xid", 125 | ] 126 | 127 | [[package]] 128 | name = "thousands" 129 | version = "0.2.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" 132 | 133 | [[package]] 134 | name = "unicode-xid" 135 | version = "0.2.2" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 138 | -------------------------------------------------------------------------------- /csv-comparator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "csv-comparator" 3 | version = "0.1.0" 4 | edition = "2018" 5 | authors = ["Hernando Castano ", "Michael Mueller "] 6 | license = "MIT" 7 | readme = "README.md" 8 | publish = false 9 | 10 | [dependencies] 11 | csv = "1.1" 12 | serde = { version = "1.0", features = ["derive"] } 13 | thousands = "0.2.0" 14 | -------------------------------------------------------------------------------- /csv-comparator/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Hernando Castano 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | software and associated documentation files (the “Software”), to deal in the Software 5 | without restriction, including without limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit 7 | persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or 10 | substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 13 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 15 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 16 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 17 | DEALINGS IN THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /csv-comparator/README.md: -------------------------------------------------------------------------------- 1 | # CSV Comparator 2 | 3 | This program is meant to be used as part of the `ink-waterfall` CI in order to see how 4 | the size of different smart contracts changes over time. It does this by taking two CSV 5 | files containing rows in the following form: `contract name, unoptimzed size, optimized size` 6 | and outputing the difference between the new contract sizes and the old contract sizes. 7 | 8 | ## Usage 9 | 10 | ```bash 11 | cargo run old-sizes.csv new-sizes.csv old-gas.csv new-gas.csv 12 | ``` 13 | 14 | The CSV formatted output will be written to `STDOUT`. 15 | -------------------------------------------------------------------------------- /csv-comparator/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | error::Error, 4 | fs::File, 5 | io, 6 | }; 7 | use thousands::Separable; 8 | 9 | use serde::{ 10 | Deserialize, 11 | Serialize, 12 | }; 13 | 14 | type Result = std::result::Result>; 15 | 16 | type OptimizedSize = f32; 17 | type GasUsage = i128; 18 | 19 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 20 | struct Row { 21 | name: String, 22 | optimized_size: OptimizedSize, 23 | total_size: OptimizedSize, 24 | gas_usage: GasUsage, 25 | total_gas_usage: GasUsage, 26 | } 27 | 28 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 29 | struct SizesRow { 30 | name: String, 31 | optimized_size: OptimizedSize, 32 | } 33 | 34 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 35 | struct GasUsageRow { 36 | name: String, 37 | gas_usage: GasUsage, 38 | } 39 | 40 | #[derive(Debug, Default)] 41 | struct CsvComparator { 42 | old_sizes: HashMap, 43 | new_sizes: HashMap, 44 | old_gas_usage: HashMap, 45 | new_gas_usage: HashMap, 46 | } 47 | 48 | impl CsvComparator { 49 | fn new() -> Self { 50 | Default::default() 51 | } 52 | 53 | fn write_old_sizes(&mut self, file: File) -> Result<()> { 54 | read_csv_size(&mut self.old_sizes, file) 55 | } 56 | 57 | fn write_new_sizes(&mut self, file: File) -> Result<()> { 58 | read_csv_size(&mut self.new_sizes, file) 59 | } 60 | 61 | fn write_old_gas_usage(&mut self, file: File) -> Result<()> { 62 | read_csv_gas(&mut self.old_gas_usage, file) 63 | } 64 | 65 | fn write_new_gas_usage(&mut self, file: File) -> Result<()> { 66 | read_csv_gas(&mut self.new_gas_usage, file) 67 | } 68 | 69 | fn get_diffs(&self) -> Result> { 70 | let mut result = Vec::new(); 71 | let mut all_contracts: HashMap = self 72 | .old_sizes 73 | .iter() 74 | .map(|(k, _)| (k.clone(), ())) 75 | .collect(); 76 | self.new_sizes.iter().for_each(|(k, _)| { 77 | all_contracts.insert(k.clone(), ()); 78 | }); 79 | self.old_gas_usage.iter().for_each(|(k, _)| { 80 | all_contracts.insert(k.clone(), ()); 81 | }); 82 | self.new_gas_usage.iter().for_each(|(k, _)| { 83 | all_contracts.insert(k.clone(), ()); 84 | }); 85 | 86 | for (contract, _) in all_contracts { 87 | let def = OptimizedSize::default(); 88 | let opt_size_old = self 89 | .old_sizes 90 | .get(&contract) 91 | .or(Some(&def)) 92 | .unwrap_or_else(|| { 93 | panic!("failed getting old_sizes entry for {:?}", contract) 94 | }); 95 | let opt_size_new = self 96 | .new_sizes 97 | .get(&contract) 98 | .or(Some(&def)) 99 | .unwrap_or_else(|| { 100 | panic!("failed getting new_sizes entry for {:?}", contract) 101 | }); 102 | let opt_size_diff = opt_size_new - opt_size_old; 103 | 104 | let old_gas_usage = self 105 | .old_gas_usage 106 | .get(&contract) 107 | .or(Some(&0)) 108 | .unwrap_or_else(|| { 109 | panic!("failed getting old_gas_usage entry for {:?}", contract) 110 | }); 111 | let new_gas_usage = self 112 | .new_gas_usage 113 | .get(&contract) 114 | .or(Some(&0)) 115 | .unwrap_or_else(|| { 116 | panic!("failed getting new_gas_usage entry for {:?}", contract) 117 | }); 118 | let gas_usage_diff = new_gas_usage - old_gas_usage; 119 | 120 | let row = Row { 121 | name: contract.to_string(), 122 | optimized_size: opt_size_diff, 123 | total_size: *opt_size_new, 124 | gas_usage: gas_usage_diff, 125 | total_gas_usage: *new_gas_usage, 126 | }; 127 | result.push(row); 128 | } 129 | 130 | Ok(result) 131 | } 132 | } 133 | 134 | fn read_csv_size(map: &mut HashMap, file: File) -> Result<()> { 135 | let mut rdr = csv::ReaderBuilder::new() 136 | .has_headers(false) 137 | .trim(csv::Trim::All) 138 | .from_reader(file); 139 | 140 | for result in rdr.deserialize() { 141 | let record: SizesRow = result?; 142 | map.insert(record.name, record.optimized_size); 143 | } 144 | 145 | Ok(()) 146 | } 147 | 148 | fn read_csv_gas(map: &mut HashMap, file: File) -> Result<()> { 149 | let mut rdr = csv::ReaderBuilder::new() 150 | .has_headers(false) 151 | .trim(csv::Trim::All) 152 | .from_reader(file); 153 | 154 | for result in rdr.deserialize() { 155 | let record: GasUsageRow = result?; 156 | map.insert(record.name, record.gas_usage); 157 | } 158 | 159 | Ok(()) 160 | } 161 | 162 | fn main() -> Result<()> { 163 | let args: Vec = std::env::args().collect(); 164 | let old_sizes = File::open(&args[1])?; 165 | let new_sizes = File::open(&args[2])?; 166 | let old_gas_usage = File::open(&args[3])?; 167 | let new_gas_usage = File::open(&args[4])?; 168 | 169 | let mut comparator = CsvComparator::new(); 170 | comparator.write_old_sizes(old_sizes)?; 171 | comparator.write_new_sizes(new_sizes)?; 172 | comparator.write_old_gas_usage(old_gas_usage)?; 173 | comparator.write_new_gas_usage(new_gas_usage)?; 174 | 175 | let mut wtr = csv::WriterBuilder::new() 176 | .has_headers(false) 177 | .from_writer(io::stdout()); 178 | 179 | for row in comparator.get_diffs()? { 180 | let prefix_opt = if row.optimized_size > 0.0 { "+" } else { "" }; 181 | let prefix_gas = if row.gas_usage > 0 { "+" } else { "" }; 182 | let optimized_size = format!("{}{:.2} K", prefix_opt, row.optimized_size); 183 | let gas_usage = format!("{}{}", prefix_gas, row.gas_usage); 184 | let total_size = format!("{:.2} K", row.total_size); 185 | let human_readable_row = ( 186 | format!("`{}`", row.name), 187 | optimized_size, 188 | gas_usage.separate_with_commas().replace(",", "_"), 189 | total_size, 190 | row.total_gas_usage.separate_with_commas().replace(",", "_"), 191 | ); 192 | wtr.serialize(human_readable_row)?; 193 | } 194 | 195 | Ok(()) 196 | } 197 | 198 | #[cfg(test)] 199 | mod tests { 200 | use super::*; 201 | 202 | #[test] 203 | fn entries_existent_only_in_old_csv_must_still_appear_in_diff() { 204 | // given 205 | let mut old_sizes: HashMap = HashMap::new(); 206 | let new_sizes: HashMap = HashMap::new(); 207 | let old_gas_usage: HashMap = HashMap::new(); 208 | let new_gas_usage: HashMap = HashMap::new(); 209 | let optimized_size = 0.1337; 210 | old_sizes.insert("removed_in_new_csv".to_string(), optimized_size); 211 | let comparator = CsvComparator { 212 | old_sizes, 213 | new_sizes, 214 | old_gas_usage, 215 | new_gas_usage, 216 | }; 217 | 218 | // when 219 | let res = comparator.get_diffs().expect("getting diffs failed"); 220 | 221 | // then 222 | let mut iter = res.iter(); 223 | assert_eq!( 224 | iter.next().expect("first diff entry must exist"), 225 | &Row { 226 | name: "removed_in_new_csv".to_string(), 227 | optimized_size: optimized_size * -1.0, 228 | total_size: 0.0, 229 | gas_usage: 0, 230 | total_gas_usage: 0, 231 | } 232 | ); 233 | assert_eq!(iter.next(), None); 234 | } 235 | 236 | #[test] 237 | fn entries_existent_only_in_new_csv_must_still_appear_in_diff() { 238 | // given 239 | let old_sizes: HashMap = HashMap::new(); 240 | let mut new_sizes: HashMap = HashMap::new(); 241 | let old_gas_usage: HashMap = HashMap::new(); 242 | let new_gas_usage: HashMap = HashMap::new(); 243 | let optimized_size = 0.1337; 244 | new_sizes.insert("not_existent_in_old_csv".to_string(), optimized_size); 245 | let comparator = CsvComparator { 246 | old_sizes, 247 | new_sizes, 248 | old_gas_usage, 249 | new_gas_usage, 250 | }; 251 | 252 | // when 253 | let res = comparator.get_diffs().expect("getting diffs failed"); 254 | 255 | // then 256 | let mut iter = res.iter(); 257 | assert_eq!( 258 | iter.next().expect("first diff entry must exist"), 259 | &Row { 260 | name: "not_existent_in_old_csv".to_string(), 261 | optimized_size, 262 | total_size: optimized_size, 263 | gas_usage: 0, 264 | total_gas_usage: 0, 265 | } 266 | ); 267 | assert_eq!(iter.next(), None); 268 | } 269 | 270 | #[test] 271 | fn gas_usage_diff() { 272 | // given 273 | let old_sizes: HashMap = HashMap::new(); 274 | let new_sizes: HashMap = HashMap::new(); 275 | let mut old_gas_usage: HashMap = HashMap::new(); 276 | let mut new_gas_usage: HashMap = HashMap::new(); 277 | old_gas_usage.insert("erc1155".to_string(), 54079); 278 | new_gas_usage.insert("erc1155".to_string(), 53915); 279 | let comparator = CsvComparator { 280 | old_sizes, 281 | new_sizes, 282 | old_gas_usage, 283 | new_gas_usage, 284 | }; 285 | 286 | // when 287 | let res = comparator.get_diffs().expect("getting diffs failed"); 288 | 289 | // then 290 | let mut iter = res.iter(); 291 | assert_eq!( 292 | iter.next().expect("first diff entry must exist"), 293 | &Row { 294 | name: "erc1155".to_string(), 295 | optimized_size: 0.00, 296 | total_size: 0.00, 297 | gas_usage: -164, 298 | total_gas_usage: 53915, 299 | } 300 | ); 301 | assert_eq!(iter.next(), None); 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /examples/contract-introspection/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build artifacts from the local tests sub-crate. 2 | /target/ 3 | 4 | # Ignore backup files creates by cargo fmt. 5 | **/*.rs.bk 6 | 7 | # Remove Cargo.lock when creating an executable, leave it for libraries 8 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 9 | Cargo.lock -------------------------------------------------------------------------------- /examples/contract-introspection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "contract_introspection" 3 | version = "3.0.1" 4 | authors = ["Parity Technologies "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [dependencies] 9 | ink_primitives = { path = "../ink/crates/primitives", default-features = false } 10 | ink_metadata = { path = "../ink/crates/metadata", default-features = false, features = ["derive"], optional = true } 11 | ink_env = { path = "../ink/crates/env", default-features = false } 12 | ink_storage = { path = "../ink/crates/storage", default-features = false } 13 | ink_lang = { path = "../ink/crates/lang", default-features = false } 14 | 15 | scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } 16 | scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } 17 | 18 | [lib] 19 | name = "contract_introspection" 20 | path = "lib.rs" 21 | crate-type = ["cdylib"] 22 | 23 | [features] 24 | default = ["std"] 25 | std = [ 26 | "ink_primitives/std", 27 | "ink_metadata/std", 28 | "ink_env/std", 29 | "ink_storage/std", 30 | "ink_lang/std", 31 | "scale/std", 32 | "scale-info/std", 33 | ] 34 | ink-as-dependency = [] 35 | 36 | [workspace] 37 | -------------------------------------------------------------------------------- /examples/contract-introspection/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | use ink_lang as ink; 4 | 5 | #[ink::contract] 6 | mod contract_introspection { 7 | use ink_env::{ 8 | call::{ 9 | build_call, 10 | Call, 11 | ExecutionInput, 12 | Selector, 13 | }, 14 | CallFlags, 15 | DefaultEnvironment, 16 | }; 17 | 18 | #[ink(storage)] 19 | pub struct ContractInspection {} 20 | 21 | impl ContractInspection { 22 | #[ink(constructor)] 23 | pub fn new() -> Self { 24 | Self {} 25 | } 26 | 27 | /// Returns `true` if the caller is a contract. 28 | #[ink(message, selector = 1)] 29 | pub fn is_caller_contract(&self) -> bool { 30 | self.env().is_contract(&self.env().caller()) 31 | } 32 | 33 | /// Returns `true` if the caller is the origin of this call. 34 | #[ink(message, selector = 2)] 35 | pub fn is_caller_origin(&self) -> bool { 36 | self.env().caller_is_origin() 37 | } 38 | 39 | /// Calls into this contract to the [`is_caller_contract`] function 40 | /// and returns the return value of that function. 41 | #[ink(message)] 42 | pub fn calls_is_caller_contract(&self) -> bool { 43 | build_call::() 44 | .call_type(Call::new().callee(self.env().account_id())) 45 | .exec_input(ExecutionInput::new(Selector::new([0x00, 0x00, 0x00, 0x01]))) 46 | .returns::() 47 | .call_flags(CallFlags::default().set_allow_reentry(true)) 48 | .fire() 49 | .expect("failed executing call") 50 | } 51 | 52 | /// Calls into this contract to the [`is_caller_origin`] function 53 | /// and returns the return value of that function. 54 | #[ink(message)] 55 | pub fn calls_is_caller_origin(&self) -> bool { 56 | build_call::() 57 | .call_type(Call::new().callee(self.env().account_id())) 58 | .exec_input(ExecutionInput::new(Selector::new([0x00, 0x00, 0x00, 0x02]))) 59 | .returns::() 60 | .call_flags(CallFlags::default().set_allow_reentry(true)) 61 | .fire() 62 | .expect("failed executing call") 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/data-structures/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build artifacts from the local tests sub-crate. 2 | /target/ 3 | 4 | # Ignore backup files creates by cargo fmt. 5 | **/*.rs.bk 6 | 7 | # Remove Cargo.lock when creating an executable, leave it for libraries 8 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 9 | Cargo.lock -------------------------------------------------------------------------------- /examples/data-structures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data_structures" 3 | version = "3.0.1" 4 | authors = ["Parity Technologies "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [dependencies] 9 | ink_primitives = { path = "../ink/crates/primitives", default-features = false } 10 | ink_metadata = { path = "../ink/crates/metadata", default-features = false, features = ["derive"], optional = true } 11 | ink_env = { path = "../ink/crates/env", default-features = false } 12 | ink_storage = { path = "../ink/crates/storage", default-features = false } 13 | ink_lang = { path = "../ink/crates/lang", default-features = false } 14 | 15 | scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } 16 | scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } 17 | 18 | [lib] 19 | name = "data_structures" 20 | path = "lib.rs" 21 | crate-type = ["cdylib"] 22 | 23 | [features] 24 | default = ["std"] 25 | std = [ 26 | "ink_primitives/std", 27 | "ink_metadata/std", 28 | "ink_env/std", 29 | "ink_storage/std", 30 | "ink_lang/std", 31 | "scale/std", 32 | "scale-info/std", 33 | ] 34 | ink-as-dependency = [] 35 | 36 | [workspace] 37 | -------------------------------------------------------------------------------- /examples/data-structures/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | use ink_lang as ink; 4 | 5 | #[ink::contract] 6 | mod data_structures { 7 | use ink_storage::Mapping; 8 | 9 | #[ink(storage)] 10 | #[derive(Default)] 11 | #[allow(mapping_initialized)] 12 | pub struct DataStructures { 13 | mapping: Mapping>, 14 | } 15 | 16 | impl DataStructures { 17 | #[ink(constructor)] 18 | pub fn new() -> Self { 19 | Default::default() 20 | } 21 | 22 | /// Insert the given `value` at `key` into `DataStructures::mapping`. 23 | /// 24 | /// Returns the size of the pre-existing value at the specified key if any. 25 | #[ink(message)] 26 | pub fn overwrite_key(&mut self, key: u32, value: bool) -> Option { 27 | self.mapping.insert_return_size(key, &Some(value)) 28 | } 29 | 30 | /// Removes the `value` at `key`. 31 | /// 32 | /// Returns the size of the pre-existing value at the specified key if any. 33 | #[ink(message)] 34 | pub fn remove_key(&mut self, key: u32) -> Option { 35 | let val: Option = None; 36 | self.mapping.insert_return_size(key, &val) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/seal-code-hash/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build artifacts from the local tests sub-crate. 2 | /target/ 3 | 4 | # Ignore backup files creates by cargo fmt. 5 | **/*.rs.bk 6 | 7 | # Remove Cargo.lock when creating an executable, leave it for libraries 8 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 9 | Cargo.lock -------------------------------------------------------------------------------- /examples/seal-code-hash/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "seal_code_hash" 3 | version = "3.0.1" 4 | authors = ["Parity Technologies "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [dependencies] 9 | ink_primitives = { path = "../ink/crates/primitives", default-features = false } 10 | ink_metadata = { path = "../ink/crates/metadata", default-features = false, features = ["derive"], optional = true } 11 | ink_env = { path = "../ink/crates/env", default-features = false } 12 | ink_storage = { path = "../ink/crates/storage", default-features = false } 13 | ink_lang = { path = "../ink/crates/lang", default-features = false } 14 | 15 | scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } 16 | scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } 17 | 18 | [lib] 19 | name = "seal_code_hash" 20 | path = "lib.rs" 21 | crate-type = ["cdylib"] 22 | 23 | [features] 24 | default = ["std"] 25 | std = [ 26 | "ink_primitives/std", 27 | "ink_metadata/std", 28 | "ink_env/std", 29 | "ink_storage/std", 30 | "ink_lang/std", 31 | "scale/std", 32 | "scale-info/std", 33 | ] 34 | ink-as-dependency = [] 35 | 36 | [workspace] 37 | -------------------------------------------------------------------------------- /examples/seal-code-hash/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | //! This contract provides a simple E2E test for the functions 4 | //! `seal_own_code_hash` and `seal_code_hash`. 5 | 6 | use ink_lang as ink; 7 | 8 | #[ink::contract] 9 | mod seal_code_hash { 10 | #[ink(storage)] 11 | pub struct SealCodeHash {} 12 | 13 | impl SealCodeHash { 14 | #[ink(constructor)] 15 | pub fn new() -> Self { 16 | Self {} 17 | } 18 | 19 | /// Returns the code hash of this contract. 20 | #[ink(message)] 21 | pub fn own_code_hash(&self) -> Hash { 22 | self.env().own_code_hash().expect("must exist") 23 | } 24 | 25 | /// Returns the code hash of the contract at `account_id`. 26 | #[ink(message)] 27 | pub fn code_hash(&self, account_id: AccountId) -> Hash { 28 | self.env().code_hash(&account_id).expect("must exist") 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/seal-ecdsa/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build artifacts from the local tests sub-crate. 2 | /target/ 3 | 4 | # Ignore backup files creates by cargo fmt. 5 | **/*.rs.bk 6 | 7 | # Remove Cargo.lock when creating an executable, leave it for libraries 8 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 9 | Cargo.lock -------------------------------------------------------------------------------- /examples/seal-ecdsa/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "seal_ecdsa" 3 | version = "3.0.1" 4 | authors = ["Parity Technologies "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [dependencies] 9 | ink_primitives = { path = "../ink/crates/primitives", default-features = false } 10 | ink_metadata = { path = "../ink/crates/metadata", default-features = false, features = ["derive"], optional = true } 11 | ink_env = { path = "../ink/crates/env", default-features = false } 12 | ink_storage = { path = "../ink/crates/storage", default-features = false } 13 | ink_lang = { path = "../ink/crates/lang", default-features = false } 14 | 15 | scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } 16 | scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } 17 | 18 | [lib] 19 | name = "seal_ecdsa" 20 | path = "lib.rs" 21 | crate-type = ["cdylib"] 22 | 23 | [features] 24 | default = ["std"] 25 | std = [ 26 | "ink_primitives/std", 27 | "ink_metadata/std", 28 | "ink_env/std", 29 | "ink_storage/std", 30 | "ink_lang/std", 31 | "scale/std", 32 | "scale-info/std", 33 | ] 34 | ink-as-dependency = [] 35 | 36 | [workspace] 37 | -------------------------------------------------------------------------------- /examples/seal-ecdsa/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | //! This contract provides simple E2E tests for the functions 4 | //! `seal_ecdsa_recover` and `seal_ecdsa_to_eth_address`. 5 | 6 | use ink_lang as ink; 7 | 8 | #[ink::contract] 9 | mod seal_ecdsa { 10 | #[ink(storage)] 11 | pub struct SealEcdsa {} 12 | 13 | impl SealEcdsa { 14 | #[ink(constructor)] 15 | pub fn new() -> Self { 16 | Self {} 17 | } 18 | 19 | /// Tests `seal_ecdsa_recover`. 20 | #[ink(message)] 21 | pub fn recover(&self, signature: [u8; 65], message_hash: [u8; 32]) -> [u8; 33] { 22 | let mut output = [0; 33]; 23 | ink_env::ecdsa_recover(&signature, &message_hash, &mut output); 24 | output 25 | } 26 | 27 | /// Tests `seal_ecdsa_to_eth_address`. 28 | #[ink(message)] 29 | pub fn to_eth_address(&self, pub_key: [u8; 33]) -> [u8; 20] { 30 | let mut output = [0; 20]; 31 | ink_env::ecdsa_to_eth_address(&pub_key, &mut output); 32 | output 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lang_macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "lang_macro" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "proc-macro2", 10 | "quote", 11 | "syn", 12 | ] 13 | 14 | [[package]] 15 | name = "proc-macro2" 16 | version = "1.0.26" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" 19 | dependencies = [ 20 | "unicode-xid", 21 | ] 22 | 23 | [[package]] 24 | name = "quote" 25 | version = "1.0.9" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 28 | dependencies = [ 29 | "proc-macro2", 30 | ] 31 | 32 | [[package]] 33 | name = "syn" 34 | version = "1.0.69" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" 37 | dependencies = [ 38 | "proc-macro2", 39 | "quote", 40 | "unicode-xid", 41 | ] 42 | 43 | [[package]] 44 | name = "unicode-xid" 45 | version = "0.2.1" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 48 | -------------------------------------------------------------------------------- /lang_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lang_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.56.1" 6 | 7 | [lib] 8 | name = "lang_macro" 9 | proc-macro = true 10 | 11 | [dependencies] 12 | quote = "1" 13 | syn = { version = "1.0", features = ["parsing", "full", "extra-traits"] } 14 | proc-macro2 = "1" 15 | -------------------------------------------------------------------------------- /lang_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use proc_macro::TokenStream; 16 | use quote::quote; 17 | 18 | /// The macro is used to do some initial set-up for a waterfall test and handle 19 | /// the shutdown at the end of a test. 20 | /// 21 | /// # Usage 22 | /// 23 | /// ```no_compile 24 | /// #[waterfall_test] 25 | /// async fn works(mut ui: Ui) -> Result<()> { 26 | /// let _contract_addr = ui.upload(contract_file).await?; 27 | /// Ok(()) 28 | /// } 29 | /// ``` 30 | #[proc_macro_attribute] 31 | pub fn waterfall_test(waterfall_attrs: TokenStream, item: TokenStream) -> TokenStream { 32 | let example = waterfall_attrs 33 | .into_iter() 34 | .find_map(|item| { 35 | if let proc_macro::TokenTree::Literal(lit) = item { 36 | return Some(format!("{}", lit.to_string().replace("\"", ""))) 37 | } 38 | None 39 | }) 40 | .expect("example param must exist"); 41 | 42 | let item_fn = 43 | syn::parse2::(item.into()).expect("no item_fn can be parsed"); 44 | let fn_name = &item_fn.sig.ident; 45 | let block = &item_fn.block; 46 | let fn_return_type = &item_fn.sig.output; 47 | let vis = &item_fn.vis; 48 | let attrs = &item_fn.attrs; 49 | let ret = match fn_return_type { 50 | syn::ReturnType::Default => quote! {}, 51 | syn::ReturnType::Type(rarrow, ret_type) => quote! { #rarrow #ret_type }, 52 | }; 53 | let res = quote! { 54 | #( #attrs )* 55 | #[tokio::test] 56 | async #vis fn #fn_name () #ret { 57 | log::debug!("setting up test for {}", stringify!(#fn_name)); 58 | crate::TEST_NAME.with(|test_name| { 59 | let str = format!("example: {}, test: {}", #example, stringify!(#fn_name)); 60 | *test_name.borrow_mut() = String::from(str); 61 | }); 62 | crate::INIT.call_once(|| { 63 | env_logger::init(); 64 | }); 65 | 66 | use crate::uis::ContractsUi; 67 | log::debug!("creating new ui for {}", stringify!(#fn_name)); 68 | let mut ui = Ui::new().await?; 69 | log::debug!("invoking block for {}", stringify!(#fn_name)); 70 | let __ret = { 71 | #block 72 | }; 73 | ui.shutdown().await?; 74 | log::debug!("shutdown for {} complete", stringify!(#fn_name)); 75 | __ret 76 | } 77 | }; 78 | res.into() 79 | } 80 | -------------------------------------------------------------------------------- /scripts/ci/build-contract.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Write to the stdout the original and optimized sizes of a Wasm file 4 | # as a CSV format line. 5 | # 6 | # Usage: `./build-contract.sh ` 7 | 8 | set -eux 9 | set -o pipefail 10 | 11 | CONTRACT=$(basename $1) 12 | SIZE_OUT=$(RUST_LOG="" cargo +stable contract build --release --manifest-path $1/Cargo.toml --output-json) || exit $? 13 | OPTIMIZED_SIZE=$(echo $SIZE_OUT | jq .optimization_result.optimized_size) 14 | 15 | echo -n "${CONTRACT}, ${OPTIMIZED_SIZE}" 16 | -------------------------------------------------------------------------------- /scripts/ci/evaluate-examples-changes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Evaluates size changes of the ink! examples and posts the results 4 | # as a comment on GitHub. 5 | # 6 | # Usage: 7 | # ./evaluate-examples-size-changes.sh \ 8 | # \ 9 | # \ 10 | # 11 | 12 | set -eu 13 | set -o pipefail 14 | 15 | PR_COMMENTS_URL=$1 16 | BASELINE_FILE=$2 17 | COMPARISON_FILE=$3 18 | 19 | GAS_BASELINE_FILE=$4 20 | GAS_COMPARISON_FILE=$5 21 | 22 | echo "$BASELINE_FILE will be compared to $COMPARISON_FILE" 23 | 24 | echo "BASELINE_FILE:" 25 | cat $BASELINE_FILE 26 | 27 | echo "COMPARISON_FILE:" 28 | cat $COMPARISON_FILE 29 | 30 | echo "$GAS_BASELINE_FILE will be compared to $GAS_COMPARISON_FILE" 31 | 32 | echo "GAS_BASELINE_FILE:" 33 | cat $GAS_BASELINE_FILE 34 | 35 | echo "GAS_COMPARISON_FILE:" 36 | cat $GAS_COMPARISON_FILE 37 | 38 | echo " ,Δ Optimized Size,Δ Used Gas,Total Optimized Size, Total Used Gas" | tee diffs.csv 39 | csv-comparator $BASELINE_FILE $COMPARISON_FILE $GAS_BASELINE_FILE $GAS_COMPARISON_FILE | \ 40 | # Remove 0.00 for display purposes 41 | sed 's/,0.00 K,/,,/g' | 42 | sed 's/,+0.00 K,/,,/g' | 43 | sed 's/,-0.00 K,/,,/g' | 44 | sed 's/,0,/,,/g' | 45 | sed 's/,0$/,/g' | 46 | sort | 47 | tee --append diffs.csv 48 | 49 | csv2md --pretty < diffs.csv | tee diffs.md 50 | 51 | echo "diff:" 52 | cat diffs.csv | tail -n+2 53 | 54 | if cat diffs.csv | tail -n+2 | grep -v ",,,,,"; then 55 | DID_SIZE_CHANGE="true" 56 | else 57 | DID_SIZE_CHANGE="false" 58 | fi 59 | 60 | echo "did size change? " $DID_SIZE_CHANGE 61 | 62 | cat diffs.md | \ 63 | # Align the column texts right. 64 | sed 's/---|/---:|/g' | \ 65 | # Align first column left. 66 | sed --regexp-extended 's/(-+)\:/:\1/' | \ 67 | # Replace `\n` so that it works properly when submitted to the GitHub API. 68 | sed 's/$/\\n/g' | \ 69 | tr -d '\n' | \ 70 | tee diffs-processed.md 71 | COMMENT=$(cat diffs-processed.md) 72 | 73 | if [ "$DID_SIZE_CHANGE" == "false" ]; then 74 | echo "No size changes observed" 75 | COMMENT="_No size changes were observed._" 76 | fi 77 | 78 | # If there is already a comment by the user `paritytech-cicd-pr` in the ink! PR which triggered 79 | # this run, then we can just edit this comment (using `PATCH` instead of `POST`). 80 | POSSIBLY_COMMENT_URL=$(curl --silent $PR_COMMENTS_URL | \ 81 | jq -r ".[] | select(.user.login == \"paritytech-cicd-pr\") | .url" | \ 82 | head -n1 83 | ) 84 | echo $POSSIBLY_COMMENT_URL 85 | 86 | VERB="POST" 87 | if [ ! -z "$POSSIBLY_COMMENT_URL" ]; then 88 | VERB="PATCH"; 89 | PR_COMMENTS_URL="$POSSIBLY_COMMENT_URL" 90 | fi 91 | 92 | echo $VERB 93 | echo $PR_COMMENTS_URL 94 | 95 | INK_MASTER_HEAD=$(curl -s "https://api.github.com/repos/paritytech/ink/commits/master" | jq -r .sha) 96 | HEAD_IN_BRANCH=$(cd ./ink/ && git log | grep -q $INK_MASTER_HEAD; echo $?) 97 | 98 | MASTER_AHEAD="" 99 | if [ "$HEAD_IN_BRANCH" == "1" ]; then 100 | echo "ink! master is ahead" 101 | MASTER_AHEAD="⚠️ **The ink! \`master\` is ahead of your branch, this might skew the comparison data below.**\\n\\n" 102 | fi 103 | 104 | UPDATED=$(TZ='Europe/Berlin' date) 105 | CC_VERSION=$(cargo-contract --version | egrep --only-matching "cargo-contract .*-x86" | sed -s 's/-x86//') 106 | curl -X ${VERB} ${PR_COMMENTS_URL} \ 107 | -H "Cookie: logged_in=no" \ 108 | -H "Authorization: token ${GITHUB_PR_TOKEN}" \ 109 | -H "Content-Type: application/json; charset=utf-8" \ 110 | -d $"{ \ 111 | \"body\": \"## 🦑 📈 ink! Example Contracts ‒ Changes Report 📉 🦑\\n \ 112 | ${MASTER_AHEAD}These are the results when building the \`examples/*\` contracts from this branch with \`$CC_VERSION\` and comparing them to ink! \`master\`: \\n\\n\ 113 | ${COMMENT}\n\n[Link to the run](https://gitlab.parity.io/parity/ink-waterfall/-/pipelines/${CI_PIPELINE_ID}) | Last update: ${UPDATED}\" \ 114 | }" 115 | -------------------------------------------------------------------------------- /scripts/ci/extract-gas-usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Extracts the gas usage from the `ink-waterfall` log. 4 | # 5 | # Usage: 6 | # ./extract-gas-usage.sh 7 | 8 | set -eu 9 | set -o pipefail 10 | 11 | EXAMPLE=$(basename $1) 12 | 13 | # We need to account for the case when an example cannot be found in the log. 14 | # This can be the case when e.g. somebody adds a new ink! example in an ink! PR, 15 | # but the `ink-waterfall` not yet containing any test for this new example. 16 | COUNT=$(grep --count "example: $EXAMPLE, " /tmp/waterfall.log) || true 17 | if [ "$COUNT" -eq "0" ]; then 18 | echo "$EXAMPLE, 0" 19 | exit 0; 20 | fi 21 | 22 | USAGE=$(cat /tmp/waterfall.log | 23 | grep "example: $EXAMPLE, " | 24 | # This next line is an unfortunate hack that we need to have 25 | # for the moment. The reason is that there might be a retry within 26 | # a test, due to the UI to being responsive in time. This retry 27 | # then results in another "estimated gas for transaction" line 28 | # in the log. 29 | # 30 | # Until we have a frontend with RPC for proper checking of the 31 | # consumed gas we need to use this hack to ensure that the gas 32 | # usage per example is the same, independent of how many retries 33 | # there were. 34 | # 35 | # The `sed` command removes everything before the first space, i.e. 36 | # the `[2021-12-01T14:13:42Z INFO` stuff before `INFO` -- this way 37 | # it's possible to uniq-ify the lines to ensure that no duplicate 38 | # "estimated gas for transaction" from some retry is counted. 39 | # This kind of skews the true gas costs, but since this whole 40 | # endeavor is mostly about having a rough idea now, this should 41 | # be fine. 42 | sed 's/[^ ]*//' | sort | uniq | 43 | egrep --only-matching "estimated gas for transaction is [0-9]*" | 44 | egrep --only-matching "[0-9]*" | 45 | awk '{s+=$1} END {print s}') 46 | 47 | echo "$EXAMPLE, $USAGE" 48 | -------------------------------------------------------------------------------- /scripts/ci/get-updated-badge-info.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "collecting stats for badge" 4 | 5 | echo "pipeline created at " ${CI_PIPELINE_CREATED_AT} 6 | 7 | TS_CREATED=`date +%s -d ${CI_PIPELINE_CREATED_AT}` 8 | echo created $TS_CREATED 9 | 10 | TS_NOW=`date +%s` 11 | echo now $TS_NOW 12 | 13 | DIFF=$(( ($TS_NOW - $TS_CREATED) / 60 )) 14 | echo diff $DIFF 15 | 16 | echo "{\"duration\":\"$DIFF mins\"}" > badge.json 17 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #[cfg(test)] 16 | mod uis; 17 | 18 | #[cfg(test)] 19 | mod utils; 20 | 21 | #[cfg(test)] 22 | mod tests; 23 | 24 | use std::{ 25 | cell::RefCell, 26 | sync::Once, 27 | }; 28 | 29 | /// We use this to only initialize `env_logger` once. 30 | pub static INIT: Once = Once::new(); 31 | 32 | // We save the name of the currently executing test here. 33 | thread_local! { 34 | pub static TEST_NAME: RefCell = RefCell::new(String::from("no test")); 35 | } 36 | -------------------------------------------------------------------------------- /src/tests/contract_introspection.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `contract_introspection` example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "contract_introspection")] 32 | #[ignore] 33 | async fn contract_instrospection_works(mut ui: Ui) -> Result<()> { 34 | // given 35 | let contract_path = 36 | cargo_contract::build(&utils::example_path("contract-introspection/Cargo.toml")) 37 | .expect("contract build failed"); 38 | 39 | // when 40 | let addr = ui.execute_upload(Upload::new(contract_path)).await?; 41 | 42 | // then 43 | // the method is called directly via the ui. 44 | assert_eq!( 45 | ui.execute_rpc(Call::new(&addr, "is_caller_contract")) 46 | .await?, 47 | "false" 48 | ); 49 | // the `is_caller_contract` method is called indirectly from this contract method. 50 | assert_eq!( 51 | ui.execute_rpc(Call::new(&addr, "calls_is_caller_contract")) 52 | .await?, 53 | "true" 54 | ); 55 | 56 | // the method is called directly via the ui. 57 | assert_eq!( 58 | ui.execute_rpc(Call::new(&addr, "is_caller_origin")).await?, 59 | "true" 60 | ); 61 | // the `is_caller_origin` method is called indirectly from this contract method. 62 | assert_eq!( 63 | ui.execute_rpc(Call::new(&addr, "calls_is_caller_origin")) 64 | .await?, 65 | "false" 66 | ); 67 | 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /src/tests/contract_terminate.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `contract-terminate `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | TransactionError, 22 | Ui, 23 | Upload, 24 | }, 25 | utils::{ 26 | self, 27 | cargo_contract, 28 | }, 29 | }; 30 | use lang_macro::waterfall_test; 31 | 32 | #[waterfall_test(example = "contract-terminate")] 33 | async fn contract_terminate_works(mut ui: Ui) -> Result<()> { 34 | // given 35 | let manifest_path = utils::example_path("contract-terminate/Cargo.toml"); 36 | let contract_file = 37 | cargo_contract::build(&manifest_path).expect("contract build failed"); 38 | let contract_addr = ui.execute_upload(Upload::new(contract_file)).await?; 39 | 40 | // when 41 | let events = ui 42 | .execute_transaction(Call::new(&contract_addr, "terminate_me")) 43 | .await 44 | .expect("failed to execute transaction"); 45 | assert!(events.contains("system.KilledAccount")); 46 | assert!(events.contains("balances.Withdraw")); 47 | assert!(events.contains("contracts.Terminated")); 48 | 49 | // then 50 | let err = ui 51 | .execute_transaction(Call::new(&contract_addr, "terminate_me")) 52 | .await 53 | .expect_err("successfully executed transaction, but expected it to fail"); 54 | match err { 55 | TransactionError::ExtrinsicFailed(events) => { 56 | assert!(events.contains("contracts.ContractNotFound")) 57 | } 58 | err => panic!("encountered unexpected {:?}", err), 59 | } 60 | Ok(()) 61 | } 62 | -------------------------------------------------------------------------------- /src/tests/contract_transfer.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `contract-transfer `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "contract-transfer")] 32 | async fn contract_must_transfer_value_to_sender(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("contract-transfer/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | let contract_addr = ui 38 | .execute_upload(Upload::new(contract_file).push_initial_value("value", "100")) 39 | .await?; 40 | 41 | let balance_before = ui.balance_postfix("EVE".to_string()).await?; 42 | 43 | // when 44 | let _events = ui 45 | .execute_transaction( 46 | Call::new(&contract_addr, "give_me") 47 | .push_value("value", "100") 48 | .caller("EVE"), 49 | ) 50 | .await 51 | .expect("failed to execute transaction"); 52 | 53 | // then 54 | let balance_after = ui.balance_postfix("EVE".to_string()).await?; 55 | log::info!("balance before: {}", balance_before); 56 | log::info!("balance after: {}", balance_after); 57 | assert_eq!(balance_after - balance_before, 1); 58 | assert!(utils::node_log_contains( 59 | "requested value: 100000000000000\n" 60 | )); 61 | Ok(()) 62 | } 63 | 64 | #[waterfall_test(example = "contract-transfer")] 65 | #[cfg_attr(feature = "polkadot-js-ui", ignore)] 66 | async fn transfer_exactly_ten_to_contract(mut ui: Ui) -> Result<()> { 67 | // given 68 | let manifest_path = utils::example_path("contract-transfer/Cargo.toml"); 69 | let contract_file = 70 | cargo_contract::build(&manifest_path).expect("contract build failed"); 71 | let contract_addr = ui.execute_upload(Upload::new(contract_file)).await?; 72 | 73 | // when 74 | let result = ui 75 | .execute_transaction( 76 | Call::new(&contract_addr, "was_it_ten") 77 | .caller("DAVE") 78 | //.payment("10", "pico"), 79 | .payment("0.000000000010", "Unit"), 80 | ) 81 | .await; 82 | 83 | // then 84 | assert!(result.is_ok()); 85 | assert!(utils::node_log_contains("received payment: 10\n")); 86 | Ok(()) 87 | } 88 | -------------------------------------------------------------------------------- /src/tests/data_structures.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `data_structures` example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "data_structures")] 32 | #[ignore] 33 | async fn data_structures_works(mut ui: Ui) -> Result<()> { 34 | // given 35 | let contract_path = 36 | cargo_contract::build(&utils::example_path("data-structures/Cargo.toml")) 37 | .expect("contract build failed"); 38 | 39 | // when 40 | let addr = ui.execute_upload(Upload::new(contract_path)).await?; 41 | 42 | // then 43 | assert_eq!( 44 | ui.execute_rpc( 45 | Call::new(&addr, "overwrite_key") 46 | .push_value("key", "1") 47 | .push_value("value", "true") 48 | ) 49 | .await?, 50 | "None" 51 | ); 52 | ui.execute_transaction( 53 | Call::new(&addr, "overwrite_key") 54 | .push_value("key", "1") 55 | .push_value("value", "true"), 56 | ) 57 | .await 58 | .expect("failed to execute transaction"); 59 | assert_eq!( 60 | ui.execute_rpc(Call::new(&addr, "remove_key").push_value("key", "1")) 61 | .await?, 62 | "2" 63 | ); 64 | 65 | Ok(()) 66 | } 67 | -------------------------------------------------------------------------------- /src/tests/delegator.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `delegator `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | extract_hash_from_contract_bundle, 28 | }, 29 | }; 30 | use lang_macro::waterfall_test; 31 | 32 | #[waterfall_test(example = "delegator")] 33 | async fn delegator_works(mut ui: Ui) -> Result<()> { 34 | // given 35 | let accumulator_path = 36 | cargo_contract::build(&utils::example_path("delegator/accumulator/Cargo.toml")) 37 | .expect("accumulator build failed"); 38 | let accumulator_hash = extract_hash_from_contract_bundle(&accumulator_path); 39 | 40 | let adder_path = 41 | cargo_contract::build(&utils::example_path("delegator/adder/Cargo.toml")) 42 | .expect("adder build failed"); 43 | let adder_hash = extract_hash_from_contract_bundle(&adder_path); 44 | 45 | let subber_path = 46 | cargo_contract::build(&utils::example_path("delegator/subber/Cargo.toml")) 47 | .expect("subber build failed"); 48 | let subber_hash = extract_hash_from_contract_bundle(&subber_path); 49 | 50 | let delegator_path = 51 | cargo_contract::build(&utils::example_path("delegator/Cargo.toml")) 52 | .expect("delegator build failed"); 53 | 54 | let _accumulator_addr = ui.execute_upload(Upload::new(accumulator_path)).await?; 55 | let _adder_addr = ui.execute_upload(Upload::new(adder_path)).await?; 56 | let _subber_addr = ui.execute_upload(Upload::new(subber_path)).await?; 57 | 58 | // when 59 | let delegator_addr = ui 60 | .execute_upload( 61 | Upload::new(delegator_path) 62 | .push_initial_value("accumulatorCodeHash", &accumulator_hash) 63 | .push_initial_value("adderCodeHash", &adder_hash) 64 | .push_initial_value("subberCodeHash", &subber_hash), 65 | ) 66 | .await?; 67 | 68 | // then 69 | assert_eq!( 70 | ui.execute_rpc(Call::new(&delegator_addr, "get")).await?, 71 | "0" 72 | ); 73 | ui.execute_transaction( 74 | Call::new(&delegator_addr, "change").push_value("by: i32", "13"), 75 | ) 76 | .await 77 | .expect("failed to execute transaction"); 78 | assert_eq!( 79 | ui.execute_rpc(Call::new(&delegator_addr, "get")).await?, 80 | "13" 81 | ); 82 | ui.execute_transaction(Call::new(&delegator_addr, "switch")) 83 | .await 84 | .expect("failed to execute transaction"); 85 | ui.execute_transaction( 86 | Call::new(&delegator_addr, "change").push_value("by: i32", "3"), 87 | ) 88 | .await 89 | .expect("failed to execute transaction"); 90 | assert_eq!( 91 | ui.execute_rpc(Call::new(&delegator_addr, "get")).await?, 92 | "10" 93 | ); 94 | Ok(()) 95 | } 96 | -------------------------------------------------------------------------------- /src/tests/dns.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `dns `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "dns")] 32 | async fn dns_works(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("dns/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | 38 | let contract_addr = ui 39 | .execute_upload(Upload::new(contract_file).caller("ALICE")) 40 | .await?; 41 | 42 | // when registering and setting an address and name 43 | let name = "0x0000000000000000000000000000000000000000000000000000000000000001"; 44 | let owner = "EVE"; 45 | ui.execute_transaction( 46 | Call::new(&contract_addr, "register") 47 | .caller("ALICE") 48 | .push_value("name", name), 49 | ) 50 | .await 51 | .expect("failed to execute `register` transaction"); 52 | ui.execute_transaction( 53 | Call::new(&contract_addr, "set_address") 54 | .caller("ALICE") 55 | .push_value("name", name) 56 | .push_value("newAddress", owner), 57 | ) 58 | .await 59 | .expect("failed to execute `set_address` transaction"); 60 | 61 | // then the name must resolve to the address 62 | assert_eq!( 63 | ui.execute_rpc( 64 | Call::new(&contract_addr, "get_address") 65 | .caller("EVE") 66 | .push_value("name", name) 67 | ) 68 | .await?, 69 | owner 70 | ); 71 | 72 | // when trying to set the address from a different caller (BOB) the transaction must fail 73 | let owner2 = "DAVE"; 74 | // TODO: enable after error forwarding has been implemented in ink! with 75 | // https://github.com/paritytech/ink/issues/641 76 | assert!( 77 | true || ui 78 | .execute_transaction( 79 | Call::new(&contract_addr, "set_address") 80 | .caller("BOB") 81 | .push_value("name", name) 82 | .push_value("newAddress", owner2) 83 | ) 84 | .await 85 | .is_err() 86 | ); 87 | 88 | // but if the owner is transferred to BOB he must be able to set the address 89 | ui.execute_transaction( 90 | Call::new(&contract_addr, "transfer") 91 | .caller("ALICE") 92 | .push_value("name", name) 93 | .push_value("to: AccountId", "BOB"), 94 | ) 95 | .await 96 | .expect("failed to execute `transfer` to BOB transaction"); 97 | ui.execute_transaction( 98 | Call::new(&contract_addr, "set_address") 99 | .caller("BOB") 100 | .push_value("name", name) 101 | .push_value("newAddress", owner2), 102 | ) 103 | .await 104 | .expect("failed to execute `set_address` transaction from BOB"); 105 | assert_eq!( 106 | ui.execute_rpc( 107 | Call::new(&contract_addr, "get_address") 108 | .caller("EVE") 109 | .push_value("name", name) 110 | ) 111 | .await?, 112 | owner2 113 | ); 114 | Ok(()) 115 | } 116 | -------------------------------------------------------------------------------- /src/tests/erc1155.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `ERC-1155 `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "erc1155")] 32 | async fn erc1155(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("erc1155/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | 38 | let contract_addr = ui 39 | .execute_upload(Upload::new(contract_file).caller("BOB")) 40 | .await?; 41 | 42 | ui.execute_transaction( 43 | Call::new(&contract_addr, "create") 44 | .caller("BOB") 45 | .push_value("value", "123"), // initial_supply 46 | ) 47 | .await 48 | .expect("failed to execute transaction"); 49 | 50 | let balance = ui 51 | .execute_rpc( 52 | Call::new(&contract_addr, "erc1155::balance_of") 53 | .push_value("owner", "BOB") 54 | .push_value("tokenId", "1"), 55 | ) 56 | .await?; 57 | assert!(balance == "123,000,000,000,000" || balance == "123.0000 Unit"); 58 | 59 | ui.execute_transaction( 60 | Call::new(&contract_addr, "mint") 61 | .caller("CHARLIE") 62 | .push_value("tokenId", "1") 63 | .push_value("value", "341"), // initial_supply 64 | ) 65 | .await 66 | .expect("failed to execute transaction"); 67 | 68 | let balance = ui 69 | .execute_rpc( 70 | Call::new(&contract_addr, "erc1155::balance_of") 71 | .push_value("owner", "CHARLIE") 72 | .push_value("tokenId", "1"), 73 | ) 74 | .await?; 75 | assert!(balance == "341,000,000,000,000" || balance == "341.0000 Unit"); 76 | 77 | let balance = ui 78 | .execute_rpc( 79 | Call::new(&contract_addr, "erc1155::balance_of_batch") 80 | .add_item("owners", "BOB") 81 | .add_item("owners", "CHARLIE") 82 | .add_item("tokenIds", "0") 83 | .add_item("tokenIds", "1"), 84 | ) 85 | .await?; 86 | assert!( 87 | // [ BOB TokenId 0, BOB TokenId 1, CHARLIE TokenId 0, CHARLIE TokenId 1 ] 88 | balance == "[ 0 123,000,000,000,000 0 341,000,000,000,000 ]" 89 | ); 90 | 91 | let is_approved_for_all = ui 92 | .execute_rpc( 93 | Call::new(&contract_addr, "erc1155::is_approved_for_all") 94 | .push_value("owner", "CHARLIE") 95 | .push_value("operator", "DAVE"), 96 | ) 97 | .await?; 98 | assert_eq!(is_approved_for_all, "false"); 99 | 100 | ui.execute_transaction( 101 | Call::new(&contract_addr, "erc1155::set_approval_for_all") 102 | .caller("CHARLIE") 103 | .push_value("operator", "DAVE") 104 | .push_value("approved", "true"), 105 | ) 106 | .await 107 | .expect("failed to execute transaction"); 108 | 109 | let is_approved_for_all = ui 110 | .execute_rpc( 111 | Call::new(&contract_addr, "erc1155::is_approved_for_all") 112 | .push_value("owner", "CHARLIE") 113 | .push_value("operator", "DAVE"), 114 | ) 115 | .await?; 116 | assert_eq!(is_approved_for_all, "true"); 117 | 118 | ui.execute_transaction( 119 | Call::new(&contract_addr, "erc1155::safe_transfer_from") 120 | .caller("DAVE") 121 | .push_value("from: AccountId", "CHARLIE") 122 | .push_value("to: AccountId", "ALICE") 123 | .push_value("tokenId", "1") 124 | .push_value("value", "41"), 125 | ) 126 | .await 127 | .expect("failed to execute transaction"); 128 | 129 | let balance = ui 130 | .execute_rpc( 131 | Call::new(&contract_addr, "erc1155::balance_of") 132 | .push_value("owner", "CHARLIE") 133 | .push_value("tokenId", "1"), 134 | ) 135 | .await?; 136 | assert!(balance == "300,000,000,000,000" || balance == "300.0000 Unit"); 137 | 138 | let balance = ui 139 | .execute_rpc( 140 | Call::new(&contract_addr, "erc1155::balance_of") 141 | .push_value("owner", "ALICE") 142 | .push_value("tokenId", "1"), 143 | ) 144 | .await?; 145 | assert!(balance == "41,000,000,000,000" || balance == "41.0000 Unit"); 146 | 147 | ui.execute_transaction( 148 | Call::new(&contract_addr, "create") 149 | .caller("ALICE") 150 | .push_value("value", "99"), 151 | ) 152 | .await 153 | .expect("failed to execute transaction"); 154 | 155 | ui.execute_transaction( 156 | Call::new(&contract_addr, "erc1155::safe_batch_transfer_from") 157 | .caller("ALICE") 158 | .push_value("from: AccountId", "ALICE") 159 | .push_value("to: AccountId", "FERDIE") 160 | .add_item("tokenIds", "1") 161 | .add_item("tokenIds", "2") 162 | .add_item("values", "41000000000000") 163 | .add_item("values", "99000000000000"), 164 | ) 165 | .await 166 | .expect("failed to execute transaction"); 167 | 168 | let balance = ui 169 | .execute_rpc( 170 | Call::new(&contract_addr, "erc1155::balance_of") 171 | .push_value("owner", "FERDIE") 172 | .push_value("tokenId", "1"), 173 | ) 174 | .await?; 175 | assert!(balance == "41,000,000,000,000" || balance == "41.0000 Unit"); 176 | 177 | let balance = ui 178 | .execute_rpc( 179 | Call::new(&contract_addr, "erc1155::balance_of") 180 | .push_value("owner", "FERDIE") 181 | .push_value("tokenId", "2"), 182 | ) 183 | .await?; 184 | assert!(balance == "99,000,000,000,000" || balance == "99.0000 Unit"); 185 | 186 | ui.execute_transaction( 187 | Call::new(&contract_addr, "erc1155::set_approval_for_all") 188 | .caller("CHARLIE") 189 | .push_value("operator", "DAVE") 190 | .push_value("approved", "false"), 191 | ) 192 | .await 193 | .expect("failed to execute transaction"); 194 | 195 | let is_approved_for_all = ui 196 | .execute_rpc( 197 | Call::new(&contract_addr, "erc1155::is_approved_for_all") 198 | .push_value("owner", "CHARLIE") 199 | .push_value("operator", "DAVE"), 200 | ) 201 | .await?; 202 | assert_eq!(is_approved_for_all, "false"); 203 | 204 | // TODO 205 | // Has to be ignored until https://github.com/paritytech/ink/issues/641 makes `Result::Err` 206 | // visible in the UI. 207 | assert!( 208 | true || ui 209 | .execute_transaction( 210 | Call::new(&contract_addr, "erc1155::safe_transfer_from") 211 | .caller("DAVE") 212 | .push_value("from: AccountId", "CHARLIE") 213 | .push_value("to: AccountId", "ALICE") 214 | .push_value("tokenId", "1") 215 | .push_value("value", "41") 216 | ) 217 | .await 218 | .is_err() 219 | ); 220 | 221 | Ok(()) 222 | } 223 | 224 | #[waterfall_test(example = "erc1155")] 225 | async fn erc1155_approvals(mut ui: Ui) -> Result<()> { 226 | // given 227 | let manifest_path = utils::example_path("erc1155/Cargo.toml"); 228 | let contract_file = 229 | cargo_contract::build(&manifest_path).expect("contract build failed"); 230 | 231 | let contract_addr = ui 232 | .execute_upload(Upload::new(contract_file).caller("BOB")) 233 | .await?; 234 | 235 | let is_approved_for_all = ui 236 | .execute_rpc( 237 | Call::new(&contract_addr, "erc1155::is_approved_for_all") 238 | .push_value("owner", "CHARLIE") 239 | .push_value("operator", "DAVE"), 240 | ) 241 | .await?; 242 | assert_eq!(is_approved_for_all, "false"); 243 | 244 | ui.execute_transaction( 245 | Call::new(&contract_addr, "erc1155::set_approval_for_all") 246 | .caller("CHARLIE") 247 | .push_value("operator", "DAVE") 248 | .push_value("approved", "true"), 249 | ) 250 | .await 251 | .expect("failed to execute transaction"); 252 | 253 | let is_approved_for_all = ui 254 | .execute_rpc( 255 | Call::new(&contract_addr, "erc1155::is_approved_for_all") 256 | .push_value("owner", "CHARLIE") 257 | .push_value("operator", "DAVE"), 258 | ) 259 | .await?; 260 | assert_eq!(is_approved_for_all, "true"); 261 | 262 | ui.execute_transaction( 263 | Call::new(&contract_addr, "erc1155::set_approval_for_all") 264 | .caller("CHARLIE") 265 | .push_value("operator", "DAVE") 266 | .push_value("approved", "false"), 267 | ) 268 | .await 269 | .expect("failed to execute transaction"); 270 | 271 | let is_approved_for_all = ui 272 | .execute_rpc( 273 | Call::new(&contract_addr, "erc1155::is_approved_for_all") 274 | .push_value("owner", "CHARLIE") 275 | .push_value("operator", "DAVE"), 276 | ) 277 | .await?; 278 | assert_eq!(is_approved_for_all, "false"); 279 | Ok(()) 280 | } 281 | -------------------------------------------------------------------------------- /src/tests/erc20.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `ERC-20 `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "erc20")] 32 | async fn erc20(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("erc20/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | 38 | let contract_addr = ui 39 | .execute_upload( 40 | Upload::new(contract_file) 41 | .caller("BOB") 42 | .push_initial_value("totalSupply", "1000"), 43 | ) 44 | .await?; 45 | let total_supply = ui 46 | .execute_rpc(Call::new(&contract_addr, "total_supply")) 47 | .await?; 48 | assert!(total_supply == "1,000,000,000,000,000" || total_supply == "1.0000 kUnit", "total_supply"); 49 | let balance = ui 50 | .execute_rpc(Call::new(&contract_addr, "balance_of").push_value("owner", "BOB")) 51 | .await?; 52 | assert!(balance == "1,000,000,000,000,000" || balance == "1.0000 kUnit", "balance pre"); 53 | 54 | ui.execute_transaction( 55 | Call::new(&contract_addr, "transfer") 56 | .caller("BOB") 57 | .push_value("to: AccountId", "ALICE") 58 | .push_value("value", "500"), 59 | ) 60 | .await 61 | .expect("failed to execute transaction"); 62 | 63 | let balance = ui 64 | .execute_rpc(Call::new(&contract_addr, "balance_of").push_value("owner", "ALICE")) 65 | .await?; 66 | assert!(balance == "500,000,000,000,000" || balance == "500.0000 Unit", "balance post"); 67 | 68 | Ok(()) 69 | } 70 | 71 | #[waterfall_test(example = "erc20")] 72 | async fn erc20_allowances(mut ui: Ui) -> Result<()> { 73 | // given 74 | let manifest_path = utils::example_path("erc20/Cargo.toml"); 75 | let contract_file = 76 | cargo_contract::build(&manifest_path).expect("contract build failed"); 77 | 78 | let contract_addr = ui 79 | .execute_upload( 80 | Upload::new(contract_file) 81 | .caller("BOB") 82 | .push_initial_value("totalSupply", "1000"), 83 | ) 84 | .await?; 85 | 86 | // Alice tries to transfer tokens on behalf ob Bob 87 | // TODO 88 | // Has to be ignored until https://github.com/paritytech/ink/issues/641 makes `Result::Err` 89 | // visible in the UI. 90 | assert!( 91 | true || ui 92 | .execute_transaction( 93 | Call::new(&contract_addr, "transfer_from") 94 | .caller("ALICE") 95 | .push_value("from: AccountId", "BOB") 96 | .push_value("to: AccountId", "ALICE") 97 | .push_value("value", "400"), 98 | ) 99 | .await 100 | .is_err() 101 | ); 102 | 103 | // Bob approves Alice being able to withdraw up the `value` amount on his behalf. 104 | ui.execute_transaction( 105 | Call::new(&contract_addr, "approve") 106 | .caller("BOB") 107 | .push_value("spender", "ALICE") 108 | .push_value("value", "600"), 109 | ) 110 | .await 111 | .expect("`approve` must succeed"); 112 | let allowance = ui 113 | .execute_rpc( 114 | Call::new(&contract_addr, "allowance") 115 | .push_value("owner", "BOB") 116 | .push_value("spender", "ALICE"), 117 | ) 118 | .await?; 119 | assert!(allowance == "600,000,000,000,000" || allowance == "600.0000 Unit", "allowance"); 120 | 121 | // Alice tries again to transfer tokens on behalf ob Bob 122 | ui.execute_transaction( 123 | Call::new(&contract_addr, "transfer_from") 124 | .caller("ALICE") 125 | .push_value("from: AccountId", "BOB") 126 | .push_value("to: AccountId", "ALICE") 127 | .push_value("value", "400"), 128 | ) 129 | .await 130 | .expect("second `transfer_from` must succeed"); 131 | let balance = ui 132 | .execute_rpc(Call::new(&contract_addr, "balance_of").push_value("owner", "ALICE")) 133 | .await?; 134 | assert!(balance == "400,000,000,000,000" || balance == "400.0000 Unit"); 135 | let balance = ui 136 | .execute_rpc(Call::new(&contract_addr, "balance_of").push_value("owner", "BOB")) 137 | .await?; 138 | assert!(balance == "600,000,000,000,000" || balance == "600.0000 Unit"); 139 | 140 | // Alice tries to transfer even more tokens on behalf ob Bob, this time exhausting the allowance 141 | // TODO 142 | // Has to be ignored until https://github.com/paritytech/ink/issues/641 makes `Result::Err` 143 | // visible in the UI. 144 | assert!( 145 | true || ui 146 | .execute_transaction( 147 | Call::new(&contract_addr, "transfer_from") 148 | .caller("ALICE") 149 | .push_value("from: AccountId", "BOB") 150 | .push_value("to: AccountId", "ALICE") 151 | .push_value("value", "201"), 152 | ) 153 | .await 154 | .is_err() 155 | ); 156 | 157 | // Balance of Bob must have stayed the same 158 | let balance = ui 159 | .execute_rpc(Call::new(&contract_addr, "balance_of").push_value("owner", "BOB")) 160 | .await?; 161 | assert!(balance == "600,000,000,000,000" || balance == "600.0000 Unit"); 162 | 163 | Ok(()) 164 | } 165 | -------------------------------------------------------------------------------- /src/tests/erc721.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `ERC-721 `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "erc721")] 32 | async fn erc721(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("erc721/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | 38 | let contract_addr = ui.execute_upload(Upload::new(contract_file)).await?; 39 | 40 | ui.execute_transaction( 41 | Call::new(&contract_addr, "mint") 42 | .caller("ALICE") 43 | .push_value("id", "123"), 44 | ) 45 | .await 46 | .expect("`mint` must succeed"); 47 | assert_eq!( 48 | ui.execute_rpc( 49 | Call::new(&contract_addr, "balance_of") 50 | .push_value("owner", "ALICE") 51 | .caller("ALICE") 52 | ) 53 | .await?, 54 | "1" 55 | ); 56 | assert_eq!( 57 | ui.execute_rpc(Call::new(&contract_addr, "owner_of").push_value("id", "123")) 58 | .await?, 59 | "ALICE" 60 | ); 61 | 62 | ui.execute_transaction( 63 | Call::new(&contract_addr, "transfer") 64 | .caller("ALICE") 65 | .push_value("destination", "BOB") 66 | .push_value("id", "123"), 67 | ) 68 | .await 69 | .expect("`transfer` must succeed"); 70 | assert_eq!( 71 | ui.execute_rpc( 72 | Call::new(&contract_addr, "balance_of").push_value("owner", "ALICE") 73 | ) 74 | .await?, 75 | "0" 76 | ); 77 | assert_eq!( 78 | ui.execute_rpc( 79 | Call::new(&contract_addr, "balance_of").push_value("owner", "BOB") 80 | ) 81 | .await?, 82 | "1" 83 | ); 84 | assert_eq!( 85 | ui.execute_rpc(Call::new(&contract_addr, "owner_of").push_value("id", "123")) 86 | .await?, 87 | "BOB" 88 | ); 89 | ui.execute_transaction( 90 | Call::new(&contract_addr, "approve") 91 | .caller("BOB") 92 | .push_value("to: AccountId", "CHARLIE") 93 | .push_value("id", "123"), 94 | ) 95 | .await 96 | .expect("`approve` must succeed"); 97 | assert_eq!( 98 | ui.execute_rpc(Call::new(&contract_addr, "get_approved").push_value("id", "123")) 99 | .await?, 100 | "CHARLIE" 101 | ); 102 | assert_eq!( 103 | ui.execute_rpc( 104 | Call::new(&contract_addr, "balance_of").push_value("owner", "ALICE") 105 | ) 106 | .await?, 107 | "0" 108 | ); 109 | 110 | assert_eq!( 111 | ui.execute_rpc( 112 | Call::new(&contract_addr, "balance_of").push_value("owner", "BOB") 113 | ) 114 | .await?, 115 | "1" 116 | ); 117 | assert_eq!( 118 | ui.execute_rpc( 119 | Call::new(&contract_addr, "balance_of").push_value("owner", "CHARLIE") 120 | ) 121 | .await?, 122 | "0" 123 | ); 124 | assert_eq!( 125 | ui.execute_rpc(Call::new(&contract_addr, "owner_of").push_value("id", "123")) 126 | .await?, 127 | "BOB" 128 | ); 129 | 130 | assert_eq!( 131 | ui.execute_rpc( 132 | Call::new(&contract_addr, "balance_of").push_value("owner", "DAVE") 133 | ) 134 | .await?, 135 | "0" 136 | ); 137 | 138 | ui.execute_transaction( 139 | Call::new(&contract_addr, "transfer_from") 140 | .caller("CHARLIE") 141 | .push_value("from: AccountId", "BOB") 142 | .push_value("to: AccountId", "DAVE") 143 | .push_value("id", "123"), 144 | ) 145 | .await 146 | .expect("`transfer_from` must succeed"); 147 | assert_eq!( 148 | ui.execute_rpc( 149 | Call::new(&contract_addr, "balance_of").push_value("owner", "BOB") 150 | ) 151 | .await?, 152 | "0" 153 | ); 154 | assert_eq!( 155 | ui.execute_rpc( 156 | Call::new(&contract_addr, "balance_of").push_value("owner", "CHARLIE") 157 | ) 158 | .await?, 159 | "0" 160 | ); 161 | assert_eq!( 162 | ui.execute_rpc( 163 | Call::new(&contract_addr, "balance_of").push_value("owner", "DAVE") 164 | ) 165 | .await?, 166 | "1" 167 | ); 168 | assert_eq!( 169 | ui.execute_rpc(Call::new(&contract_addr, "owner_of").push_value("id", "123")) 170 | .await?, 171 | "DAVE" 172 | ); 173 | ui.execute_transaction( 174 | Call::new(&contract_addr, "burn") 175 | .caller("DAVE") 176 | .push_value("id", "123"), 177 | ) 178 | .await 179 | .expect("`burn` must succeed"); 180 | assert_eq!( 181 | ui.execute_rpc( 182 | Call::new(&contract_addr, "balance_of").push_value("owner", "DAVE") 183 | ) 184 | .await?, 185 | "0" 186 | ); 187 | assert_eq!( 188 | ui.execute_rpc(Call::new(&contract_addr, "owner_of").push_value("id", "123")) 189 | .await?, 190 | "None" 191 | ); 192 | 193 | Ok(()) 194 | } 195 | 196 | #[waterfall_test(example = "erc721")] 197 | async fn erc721_operator_approvals(mut ui: Ui) -> Result<()> { 198 | // given 199 | let manifest_path = utils::example_path("erc721/Cargo.toml"); 200 | let contract_file = 201 | cargo_contract::build(&manifest_path).expect("contract build failed"); 202 | 203 | let contract_addr = ui.execute_upload(Upload::new(contract_file)).await?; 204 | 205 | ui.execute_transaction( 206 | Call::new(&contract_addr, "mint") 207 | .caller("ALICE") 208 | .push_value("id", "123"), 209 | ) 210 | .await 211 | .expect("`mint` must succeed"); 212 | ui.execute_transaction( 213 | Call::new(&contract_addr, "mint") 214 | .caller("ALICE") 215 | .push_value("id", "321"), 216 | ) 217 | .await 218 | .expect("`mint` must succeed"); 219 | ui.execute_transaction( 220 | Call::new(&contract_addr, "set_approval_for_all") 221 | .caller("ALICE") 222 | .push_value("to: AccountId", "BOB") 223 | .push_value("approved", "true"), 224 | ) 225 | .await 226 | .expect("`approve_for_all` must succeed"); 227 | assert_eq!( 228 | ui.execute_rpc( 229 | Call::new(&contract_addr, "is_approved_for_all") 230 | .push_value("owner", "ALICE") 231 | .push_value("operator", "BOB") 232 | ) 233 | .await?, 234 | "true" 235 | ); 236 | 237 | // when 238 | ui.execute_transaction( 239 | Call::new(&contract_addr, "transfer_from") 240 | .caller("BOB") 241 | .push_value("from: AccountId", "ALICE") 242 | .push_value("to: AccountId", "CHARLIE") 243 | .push_value("id", "123"), 244 | ) 245 | .await 246 | .expect("`transfer` must succeed"); 247 | ui.execute_transaction( 248 | Call::new(&contract_addr, "transfer_from") 249 | .caller("BOB") 250 | .push_value("from: AccountId", "ALICE") 251 | .push_value("to: AccountId", "CHARLIE") 252 | .push_value("id", "321"), 253 | ) 254 | .await 255 | .expect("`transfer` must succeed"); 256 | 257 | // then 258 | assert_eq!( 259 | ui.execute_rpc( 260 | Call::new(&contract_addr, "balance_of").push_value("owner", "ALICE") 261 | ) 262 | .await?, 263 | "0" 264 | ); 265 | assert_eq!( 266 | ui.execute_rpc( 267 | Call::new(&contract_addr, "balance_of").push_value("owner", "CHARLIE") 268 | ) 269 | .await?, 270 | "2" 271 | ); 272 | 273 | Ok(()) 274 | } 275 | -------------------------------------------------------------------------------- /src/tests/flipper.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `flipper `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "flipper")] 32 | async fn flipper_works(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("flipper/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | 38 | let contract_addr = ui.execute_upload(Upload::new(contract_file)).await?; 39 | assert_eq!( 40 | ui.execute_rpc(Call::new(&contract_addr, "get")).await?, 41 | "false" 42 | ); 43 | 44 | // when 45 | ui.execute_transaction(Call::new(&contract_addr, "flip") 46 | // anybody must be able to `flip` 47 | .caller("BOB")) 48 | .await 49 | .expect("failed to execute transaction"); 50 | 51 | // then 52 | assert_eq!( 53 | ui.execute_rpc(Call::new(&contract_addr, "get")).await?, 54 | "true" 55 | ); 56 | Ok(()) 57 | } 58 | 59 | #[waterfall_test(example = "flipper")] 60 | async fn flipper_default_constructor(mut ui: Ui) -> Result<()> { 61 | // given 62 | let manifest_path = utils::example_path("flipper/Cargo.toml"); 63 | let contract_file = 64 | cargo_contract::build(&manifest_path).expect("contract build failed"); 65 | 66 | // when 67 | let contract_addr = ui 68 | .execute_upload(Upload::new(contract_file).constructor("newDefault")) 69 | .await?; 70 | 71 | // then 72 | assert_eq!( 73 | ui.execute_rpc(Call::new(&contract_addr, "get")).await?, 74 | "false" 75 | ); 76 | Ok(()) 77 | } 78 | -------------------------------------------------------------------------------- /src/tests/incrementer.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `incrementer `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "incrementer")] 32 | async fn incrementer_works(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("incrementer/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | 38 | let contract_addr = ui 39 | .execute_upload(Upload::new(contract_file).push_initial_value("initValue", "13")) 40 | .await?; 41 | assert_eq!( 42 | ui.execute_rpc(Call::new(&contract_addr, "get")).await?, 43 | "13" 44 | ); 45 | 46 | // when 47 | ui.execute_transaction(Call::new(&contract_addr, "inc").push_value("by: i32", "14")) 48 | .await 49 | .expect("failed to execute transaction"); 50 | 51 | // then 52 | assert_eq!( 53 | ui.execute_rpc(Call::new(&contract_addr, "get")).await?, 54 | "27" 55 | ); 56 | Ok(()) 57 | } 58 | 59 | #[waterfall_test(example = "incrementer")] 60 | async fn incrementer_default_constructor(mut ui: Ui) -> Result<()> { 61 | // given 62 | let manifest_path = utils::example_path("incrementer/Cargo.toml"); 63 | let contract_file = 64 | cargo_contract::build(&manifest_path).expect("contract build failed"); 65 | 66 | // when 67 | let contract_addr = ui 68 | .execute_upload(Upload::new(contract_file).constructor("default")) 69 | .await?; 70 | 71 | // then 72 | assert_eq!(ui.execute_rpc(Call::new(&contract_addr, "get")).await?, "0"); 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | mod contract_introspection; 16 | mod contract_terminate; 17 | mod contract_transfer; 18 | mod data_structures; 19 | mod delegator; 20 | mod dns; 21 | mod erc1155; 22 | mod erc20; 23 | mod erc721; 24 | mod flipper; 25 | // mod incrementer; 26 | mod multisig; 27 | mod rand_extension; 28 | mod seal_code_hash; 29 | mod seal_ecdsa; 30 | mod set_code_hash; 31 | mod trait_erc20; 32 | mod trait_flipper; 33 | mod trait_incrementer; 34 | -------------------------------------------------------------------------------- /src/tests/multisig.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `multisig` example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "multisig")] 32 | async fn multisig_works_with_flipper_transaction(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("flipper/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | let flipper_contract_addr = ui.execute_upload(Upload::new(contract_file)).await?; 38 | 39 | let manifest_path = utils::example_path("multisig/Cargo.toml"); 40 | let contract_file = 41 | cargo_contract::build(&manifest_path).expect("contract build failed"); 42 | 43 | let contract_addr = ui 44 | .execute_upload( 45 | Upload::new(contract_file) 46 | .caller("ALICE") 47 | .push_initial_value("requirement", "2") 48 | .add_item("owners", "ALICE") 49 | .add_item("owners", "BOB") 50 | .add_item("owners", "EVE"), 51 | ) 52 | .await?; 53 | 54 | ui.execute_transaction( 55 | Call::new(&contract_addr, "submit_transaction") 56 | .caller("ALICE") 57 | .push_value("callee", &flipper_contract_addr) 58 | .push_value("selector", "0x633aa551") // `flip` 59 | .push_value("input", "0x00") 60 | .push_value("transferredValue", "0"), 61 | ) 62 | .await 63 | .expect("failed to `submit_transaction`"); 64 | let id = "0"; 65 | ui.execute_transaction( 66 | Call::new(&contract_addr, "confirm_transaction") 67 | .caller("ALICE") 68 | .push_value("transId", id), 69 | ) 70 | .await 71 | .expect("failed to `confirm_transaction`"); 72 | 73 | ui.execute_transaction( 74 | Call::new(&contract_addr, "confirm_transaction") 75 | .caller("BOB") 76 | .push_value("transId", id), 77 | ) 78 | .await 79 | .expect("failed to `confirm_transaction`"); 80 | 81 | assert_eq!( 82 | ui.execute_rpc(Call::new(&flipper_contract_addr, "get")) 83 | .await?, 84 | "false" 85 | ); 86 | 87 | // when 88 | let call = Call::new(&contract_addr, "invoke_transaction") 89 | .caller("ALICE") 90 | .push_value("transId", id); 91 | ui.execute_transaction(call) 92 | .await 93 | .expect("failed to `invoke_transaction`"); 94 | 95 | // then 96 | assert_eq!( 97 | ui.execute_rpc(Call::new(&flipper_contract_addr, "get")) 98 | .await?, 99 | "true" 100 | ); 101 | 102 | Ok(()) 103 | } 104 | 105 | #[waterfall_test(example = "multisig")] 106 | async fn multisig_works_with_payable_transaction(mut ui: Ui) -> Result<()> { 107 | // given 108 | let manifest_path = utils::example_path("contract-transfer/Cargo.toml"); 109 | let contract_file = 110 | cargo_contract::build(&manifest_path).expect("contract build failed"); 111 | let contract_transfer_addr = ui.execute_upload(Upload::new(contract_file)).await?; 112 | 113 | let manifest_path = utils::example_path("multisig/Cargo.toml"); 114 | let contract_file = 115 | cargo_contract::build(&manifest_path).expect("contract build failed"); 116 | 117 | let contract_addr = ui 118 | .execute_upload( 119 | Upload::new(contract_file) 120 | .caller("ALICE") 121 | .push_initial_value("requirement", "2") 122 | .add_item("owners", "ALICE") 123 | .add_item("owners", "BOB") 124 | .add_item("owners", "EVE"), 125 | ) 126 | .await?; 127 | 128 | let call = Call::new(&contract_addr, "submit_transaction") 129 | .caller("ALICE") 130 | .push_value("callee", &contract_transfer_addr) 131 | .push_value("selector", "0xcafebabe") // `was_it_ten` 132 | .push_value("input", "0x00") 133 | .push_value("transferredValue", "10"); 134 | ui.execute_transaction(call) 135 | .await 136 | .expect("failed to `submit_transaction`"); 137 | let id = "0"; 138 | ui.execute_transaction( 139 | Call::new(&contract_addr, "confirm_transaction") 140 | .caller("ALICE") 141 | .push_value("transId", id), 142 | ) 143 | .await 144 | .expect("failed to `confirm_transaction`"); 145 | 146 | ui.execute_transaction( 147 | Call::new(&contract_addr, "confirm_transaction") 148 | .caller("BOB") 149 | .push_value("transId", id), 150 | ) 151 | .await 152 | .expect("failed to `confirm_transaction`"); 153 | 154 | // when 155 | ui.execute_transaction( 156 | Call::new(&contract_addr, "invoke_transaction") 157 | .caller("ALICE") 158 | .push_value("transId", id) 159 | //.payment("10", "pico"), 160 | .payment("0.000000000010", "Unit"), 161 | ) 162 | .await 163 | .expect("failed to `invoke_transaction`"); 164 | 165 | // then 166 | assert!(utils::node_log_contains("received payment: 10\n")); 167 | 168 | Ok(()) 169 | } 170 | -------------------------------------------------------------------------------- /src/tests/rand_extension.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `rand-extension `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "rand-extension")] 32 | #[ignore] 33 | async fn rand_extension(mut ui: Ui) -> Result<()> { 34 | // given 35 | let manifest_path = utils::example_path("rand-extension/Cargo.toml"); 36 | let contract_file = 37 | cargo_contract::build(&manifest_path).expect("contract build failed"); 38 | let contract_addr = ui.execute_upload(Upload::new(contract_file)).await?; 39 | 40 | // when 41 | assert_eq!( 42 | ui.execute_rpc(Call::new(&contract_addr, "get")) 43 | .await 44 | .expect("failed to execute rpc"), 45 | "" 46 | ); 47 | let _events = ui 48 | .execute_transaction(Call::new(&contract_addr, "update")) 49 | .await 50 | .expect("failed to execute transaction"); 51 | 52 | // then 53 | assert_ne!( 54 | ui.execute_rpc(Call::new(&contract_addr, "get")) 55 | .await 56 | .expect("failed to execute rpc"), 57 | "" 58 | ); 59 | Ok(()) 60 | } 61 | -------------------------------------------------------------------------------- /src/tests/seal_code_hash.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `seal_code_hash` example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "seal_code_hash")] 32 | #[ignore] 33 | async fn delegator_works(mut ui: Ui) -> Result<()> { 34 | // given 35 | let contract_path = 36 | cargo_contract::build(&utils::example_path("seal-code-hash/Cargo.toml")) 37 | .expect("contract build failed"); 38 | let bundle_hash = utils::extract_hash_from_contract_bundle(&contract_path); 39 | 40 | // when 41 | let addr = ui.execute_upload(Upload::new(contract_path)).await?; 42 | 43 | // then 44 | let deployed_hash = ui 45 | .execute_rpc(Call::new(&addr, "code_hash").push_value("account_id", &addr)) 46 | .await?; 47 | let own_code_hash = ui.execute_rpc(Call::new(&addr, "own_code_hash")).await?; 48 | assert_eq!(own_code_hash, deployed_hash); 49 | assert_eq!(own_code_hash, bundle_hash); 50 | 51 | Ok(()) 52 | } 53 | -------------------------------------------------------------------------------- /src/tests/seal_ecdsa.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `seal_ecdsa` example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "seal_ecdsa")] 32 | #[ignore] 33 | async fn seal_ecdsa_recover(mut ui: Ui) -> Result<()> { 34 | // given 35 | let contract_path = 36 | cargo_contract::build(&utils::example_path("seal-ecdsa/Cargo.toml")) 37 | .expect("contract build failed"); 38 | 39 | // when 40 | let addr = ui.execute_upload(Upload::new(contract_path)).await?; 41 | 42 | // then 43 | const SIGNATURE: [u8; 65] = [ 44 | 227, 196, 116, 146, 193, 140, 84, 25, 67, 95, 64, 189, 114, 44, 214, 52, 74, 32, 45 | 9, 185, 163, 194, 83, 85, 255, 125, 97, 140, 103, 93, 122, 219, 107, 154, 147, 46 | 255, 52, 202, 121, 212, 229, 69, 34, 15, 237, 152, 68, 253, 242, 3, 127, 226, 47 | 186, 226, 16, 105, 214, 211, 126, 23, 204, 171, 208, 175, 27, 48 | ]; 49 | 50 | const MESSSAGE_HASH: [u8; 32] = [ 51 | 167, 124, 116, 195, 220, 156, 244, 20, 243, 69, 1, 98, 189, 205, 79, 108, 213, 52 | 78, 65, 65, 230, 30, 17, 37, 184, 220, 237, 135, 1, 209, 101, 229, 53 | ]; 54 | 55 | const EXPECTED_COMPRESSED_PUBLIC_KEY: [u8; 33] = [ 56 | 2, 97, 42, 251, 200, 102, 43, 116, 48, 33, 219, 192, 115, 0, 250, 20, 90, 182, 57 | 249, 92, 249, 154, 93, 168, 75, 81, 252, 149, 98, 128, 103, 248, 221, 58 | ]; 59 | 60 | let recovered_pk = ui 61 | .execute_rpc( 62 | Call::new(&addr, "recover") 63 | .push_value("signature", &format!("0x{}", hex::encode(SIGNATURE))) 64 | .push_value("message_hash", &format!("0x{}", hex::encode(MESSSAGE_HASH))), 65 | ) 66 | .await?; 67 | assert_eq!( 68 | recovered_pk, 69 | format!("0x{}", hex::encode(EXPECTED_COMPRESSED_PUBLIC_KEY)) 70 | ); 71 | 72 | Ok(()) 73 | } 74 | 75 | #[waterfall_test(example = "seal_ecdsa")] 76 | #[ignore] 77 | async fn seal_ecdsa_to_eth_address(mut ui: Ui) -> Result<()> { 78 | // given 79 | let contract_path = 80 | cargo_contract::build(&utils::example_path("seal-ecdsa/Cargo.toml")) 81 | .expect("contract build failed"); 82 | 83 | // when 84 | let addr = ui.execute_upload(Upload::new(contract_path)).await?; 85 | 86 | // then 87 | const PUB_KEY: [u8; 33] = [ 88 | 2, 97, 42, 251, 200, 102, 43, 116, 48, 33, 219, 192, 115, 0, 250, 20, 90, 182, 89 | 249, 92, 249, 154, 93, 168, 75, 81, 252, 149, 98, 128, 103, 248, 221, 90 | ]; 91 | 92 | const EXPECTED_ETH_ADDRESS: [u8; 20] = [ 93 | 76, 189, 93, 201, 157, 117, 248, 100, 17, 91, 55, 147, 239, 207, 152, 134, 24, 94 | 192, 194, 18, 95 | ]; 96 | 97 | let eth_addr = ui 98 | .execute_rpc( 99 | Call::new(&addr, "to_eth_address") 100 | .push_value("pub_key", &format!("0x{}", hex::encode(PUB_KEY))), 101 | ) 102 | .await?; 103 | assert_eq!(eth_addr, format!("0x{}", hex::encode(EXPECTED_ETH_ADDRESS))); 104 | 105 | Ok(()) 106 | } 107 | -------------------------------------------------------------------------------- /src/tests/set_code_hash.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `set-code-hash` example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "set-code-hash")] 32 | #[ignore] 33 | async fn set_code_hash_works(mut ui: Ui) -> Result<()> { 34 | // given 35 | let manifest_path = 36 | utils::example_path("upgradeable-contracts/set-code-hash/Cargo.toml"); 37 | let incrementer_bundle = 38 | cargo_contract::build(&manifest_path).expect("contract build failed"); 39 | let incrementer_addr = ui 40 | .execute_upload(Upload::new(incrementer_bundle.clone()).caller("ALICE")) 41 | .await?; 42 | 43 | let manifest_path = utils::example_path( 44 | "upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml", 45 | ); 46 | let updated_incrementer_bundle = 47 | cargo_contract::build(&manifest_path).expect("contract build failed"); 48 | let updated_incrementer_hash = 49 | utils::extract_hash_from_contract_bundle(&updated_incrementer_bundle); 50 | let _updated_incrementer_addr = ui 51 | .execute_upload(Upload::new(updated_incrementer_bundle.clone())) 52 | .await?; 53 | 54 | ui.execute_transaction(Call::new(&incrementer_addr, "inc")) 55 | .await 56 | .expect("failed to `submit_transaction`"); 57 | assert_eq!( 58 | ui.execute_rpc(Call::new(&incrementer_addr, "get")).await?, 59 | "1" 60 | ); 61 | 62 | let call = Call::new(&incrementer_addr, "set_code") 63 | .push_value("code_hash", &updated_incrementer_hash); 64 | ui.execute_transaction(call).await.expect("must work"); 65 | 66 | ui.execute_transaction(Call::new(&incrementer_addr, "inc")) 67 | .await 68 | .expect("failed to `submit_transaction`"); 69 | assert_eq!( 70 | ui.execute_rpc(Call::new(&incrementer_addr, "get")).await?, 71 | "5" 72 | ); 73 | 74 | Ok(()) 75 | } 76 | -------------------------------------------------------------------------------- /src/tests/trait_erc20.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `trait-erc20 `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "trait-erc20")] 32 | async fn trait_erc20(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("trait-erc20/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | 38 | let contract_addr = ui 39 | .execute_upload( 40 | Upload::new(contract_file) 41 | .caller("BOB") 42 | .push_initial_value("totalSupply", "1000"), 43 | ) 44 | .await?; 45 | let total_supply = ui 46 | .execute_rpc(Call::new(&contract_addr, "baseErc20::total_supply")) 47 | .await?; 48 | assert!(total_supply == "1,000,000,000,000,000" || total_supply == "1.0000 kUnit"); 49 | let balance = ui 50 | .execute_rpc( 51 | Call::new(&contract_addr, "baseErc20::balanceOf").push_value("owner", "BOB"), 52 | ) 53 | .await?; 54 | assert!(balance == "1,000,000,000,000,000" || balance == "1.0000 kUnit"); 55 | 56 | ui.execute_transaction( 57 | Call::new(&contract_addr, "baseErc20::transfer") 58 | .caller("BOB") 59 | .push_value("to: AccountId", "ALICE") 60 | .push_value("value", "500"), 61 | ) 62 | .await 63 | .expect("failed to execute transaction"); 64 | 65 | let balance = ui 66 | .execute_rpc( 67 | Call::new(&contract_addr, "baseErc20::balanceOf") 68 | .push_value("owner", "ALICE"), 69 | ) 70 | .await?; 71 | assert!(balance == "500,000,000,000,000" || balance == "500.0000 Unit"); 72 | 73 | Ok(()) 74 | } 75 | 76 | #[waterfall_test(example = "trait-erc20")] 77 | async fn trait_erc20_allowances(mut ui: Ui) -> Result<()> { 78 | // given 79 | let manifest_path = utils::example_path("trait-erc20/Cargo.toml"); 80 | let contract_file = 81 | cargo_contract::build(&manifest_path).expect("contract build failed"); 82 | 83 | let contract_addr = ui 84 | .execute_upload( 85 | Upload::new(contract_file) 86 | .caller("BOB") 87 | .push_initial_value("totalSupply", "1000"), 88 | ) 89 | .await?; 90 | 91 | // Alice tries to transfer tokens on behalf ob Bob 92 | // TODO 93 | // Has to be ignored until https://github.com/paritytech/ink/issues/641 makes `Result::Err` 94 | // visible in the UI. 95 | assert!( 96 | true || ui 97 | .execute_transaction( 98 | Call::new(&contract_addr, "baseErc20::transferFrom") 99 | .caller("ALICE") 100 | .push_value("from: AccountId", "BOB") 101 | .push_value("to: AccountId", "ALICE") 102 | .push_value("value", "400"), 103 | ) 104 | .await 105 | .is_err() 106 | ); 107 | 108 | // Bob approves Alice being able to withdraw up the `value` amount on his behalf. 109 | ui.execute_transaction( 110 | Call::new(&contract_addr, "baseErc20::approve") 111 | .caller("BOB") 112 | .push_value("spender", "ALICE") 113 | .push_value("value", "600"), 114 | ) 115 | .await 116 | .expect("`approve` must succeed"); 117 | let allowance = ui 118 | .execute_rpc( 119 | Call::new(&contract_addr, "baseErc20::allowance") 120 | .push_value("owner", "BOB") 121 | .push_value("spender", "ALICE"), 122 | ) 123 | .await?; 124 | assert!(allowance == "600,000,000,000,000" || allowance == "600.0000 Unit"); 125 | 126 | // Alice tries again to transfer tokens on behalf ob Bob 127 | ui.execute_transaction( 128 | Call::new(&contract_addr, "baseErc20::transferFrom") 129 | .caller("ALICE") 130 | .push_value("from: AccountId", "BOB") 131 | .push_value("to: AccountId", "ALICE") 132 | .push_value("value", "400"), 133 | ) 134 | .await 135 | .expect("second `transferFrom` must succeed"); 136 | let balance = ui 137 | .execute_rpc( 138 | Call::new(&contract_addr, "baseErc20::balanceOf") 139 | .push_value("owner", "ALICE"), 140 | ) 141 | .await?; 142 | assert!(balance == "400,000,000,000,000" || balance == "400.0000 Unit"); 143 | let balance = ui 144 | .execute_rpc( 145 | Call::new(&contract_addr, "baseErc20::balanceOf").push_value("owner", "BOB"), 146 | ) 147 | .await?; 148 | assert!(balance == "600,000,000,000,000" || balance == "600.0000 Unit"); 149 | 150 | // Alice tries to transfer even more tokens on behalf ob Bob, this time exhausting the allowance 151 | // TODO 152 | // Has to be ignored until https://github.com/paritytech/ink/issues/641 makes `Result::Err` 153 | // visible in the UI. 154 | assert!( 155 | true || ui 156 | .execute_transaction( 157 | Call::new(&contract_addr, "baseErc20::transferFrom") 158 | .caller("ALICE") 159 | .push_value("from: AccountId", "BOB") 160 | .push_value("to: AccountId", "ALICE") 161 | .push_value("value", "201"), 162 | ) 163 | .await 164 | .is_err() 165 | ); 166 | 167 | // Balance of Bob must have stayed the same 168 | let balance = ui 169 | .execute_rpc( 170 | Call::new(&contract_addr, "baseErc20::balanceOf").push_value("owner", "BOB"), 171 | ) 172 | .await?; 173 | assert!(balance == "600,000,000,000,000" || balance == "600.0000 Unit"); 174 | 175 | Ok(()) 176 | } 177 | -------------------------------------------------------------------------------- /src/tests/trait_flipper.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `trait-flipper `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "trait-flipper")] 32 | async fn trait_flipper_works(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("trait-flipper/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | 38 | let contract_addr = ui.execute_upload(Upload::new(contract_file)).await?; 39 | assert_eq!( 40 | ui.execute_rpc(Call::new(&contract_addr, "flip::get")) 41 | .await?, 42 | "false" 43 | ); 44 | 45 | // when 46 | ui.execute_transaction(Call::new(&contract_addr, "flip::flip")) 47 | .await 48 | .expect("failed to execute transaction"); 49 | 50 | // then 51 | assert_eq!( 52 | ui.execute_rpc(Call::new(&contract_addr, "flip::get")) 53 | .await?, 54 | "true" 55 | ); 56 | Ok(()) 57 | } 58 | -------------------------------------------------------------------------------- /src/tests/trait_incrementer.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tests for the `trait-incrementer `example. 16 | 17 | use crate::{ 18 | uis::{ 19 | Call, 20 | Result, 21 | Ui, 22 | Upload, 23 | }, 24 | utils::{ 25 | self, 26 | cargo_contract, 27 | }, 28 | }; 29 | use lang_macro::waterfall_test; 30 | 31 | #[waterfall_test(example = "trait-incrementer")] 32 | async fn trait_incrementer_works(mut ui: Ui) -> Result<()> { 33 | // given 34 | let manifest_path = utils::example_path("trait-incrementer/Cargo.toml"); 35 | let contract_file = 36 | cargo_contract::build(&manifest_path).expect("contract build failed"); 37 | 38 | let contract_addr = ui 39 | .execute_upload(Upload::new(contract_file).push_initial_value("initValue", "13")) 40 | .await?; 41 | assert_eq!( 42 | ui.execute_rpc(Call::new(&contract_addr, "increment::get")) 43 | .await?, 44 | "13" 45 | ); 46 | 47 | // when 48 | ui.execute_transaction(Call::new(&contract_addr, "incBy").push_value("delta", "14")) 49 | .await 50 | .expect("failed to execute transaction"); 51 | 52 | // then 53 | assert_eq!( 54 | ui.execute_rpc(Call::new(&contract_addr, "increment::get")) 55 | .await?, 56 | "27" 57 | ); 58 | ui.execute_transaction(Call::new(&contract_addr, "reset::reset")) 59 | .await 60 | .expect("failed to execute transaction"); 61 | assert_eq!( 62 | ui.execute_rpc(Call::new(&contract_addr, "increment::get")) 63 | .await?, 64 | "0" 65 | ); 66 | Ok(()) 67 | } 68 | -------------------------------------------------------------------------------- /src/uis/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #[cfg(not(feature = "polkadot-js-ui"))] 16 | pub mod canvas_ui; 17 | 18 | #[cfg(feature = "polkadot-js-ui")] 19 | pub mod polkadot_js; 20 | 21 | #[cfg(feature = "polkadot-js-ui")] 22 | use convert_case::{ 23 | Case, 24 | Casing, 25 | }; 26 | 27 | use async_trait::async_trait; 28 | use fantoccini::{ 29 | error::CmdError, 30 | Client, 31 | ClientBuilder, 32 | }; 33 | use lazy_static::lazy_static; 34 | use serde_json::{ 35 | self, 36 | map::Map, 37 | value::Value, 38 | }; 39 | use std::{ 40 | path::PathBuf, 41 | process, 42 | sync::Mutex, 43 | time::Duration, 44 | }; 45 | 46 | lazy_static! { 47 | static ref PICKED_PORTS: Mutex> = Mutex::new(vec![]); 48 | } 49 | 50 | // The result of an interaction with the UI. 51 | pub type Result = std::result::Result>; 52 | 53 | // The result of submitting a transaction. 54 | pub type TransactionResult = std::result::Result; 55 | 56 | // An error which happened when interacting with the UI. 57 | #[derive(Debug)] 58 | pub enum TransactionError { 59 | ExtrinsicFailed(Events), 60 | Other(Box), 61 | } 62 | 63 | impl From for TransactionError { 64 | fn from(cmd_err: CmdError) -> Self { 65 | TransactionError::Other(Box::new(cmd_err)) 66 | } 67 | } 68 | 69 | #[async_trait] 70 | pub trait ContractsUi { 71 | /// Returns the balance postfix numbers. 72 | async fn balance_postfix(&mut self, account: String) -> self::Result; 73 | 74 | /// Uploads the contract behind `contract_path`. 75 | async fn execute_upload(&mut self, upload_input: Upload) -> self::Result; 76 | 77 | /// Executes the RPC call `call`. 78 | async fn execute_rpc(&mut self, call: Call) -> self::Result; 79 | 80 | /// Executes the transaction `call`. 81 | async fn execute_transaction( 82 | &mut self, 83 | call: Call, 84 | ) -> self::TransactionResult; 85 | 86 | /// Updates the metadata 87 | async fn update_metadata( 88 | &mut self, 89 | contract_addr: &String, 90 | new_abi: &PathBuf, 91 | ) -> self::Result; 92 | } 93 | 94 | /// Holds everything necessary to interact with the user interface. 95 | pub struct Ui { 96 | client: Client, 97 | geckodriver: process::Child, 98 | } 99 | 100 | impl Ui { 101 | /// Creates a new `Ui` instance. 102 | /// 103 | /// As part of this set-up a `geckodriver` instance is spawned to a free port. 104 | pub async fn new() -> self::Result { 105 | crate::utils::assert_node_running(); 106 | 107 | let mut port = None; 108 | for retry in 0..10 { 109 | let port_candidate = portpicker::pick_unused_port().expect("no free port"); 110 | log::info!("picked free port candidate {}", port_candidate); 111 | 112 | // add this port to a global variable and check that no other thread has yet chosen it. 113 | let mut picked_ports = 114 | PICKED_PORTS.lock().expect("failed locking `PICKED_PORTS`"); 115 | log::info!("picked ports {:?}", picked_ports); 116 | if !picked_ports.contains(&port_candidate) { 117 | picked_ports.push(port_candidate); 118 | port = Some(port_candidate); 119 | break 120 | } else { 121 | log::info!("port {} was already chosen by another thread, picking another one (try {})", port_candidate, retry); 122 | } 123 | } 124 | let port = port.expect("no free port could be determined!"); 125 | log::info!("picked free port {} for geckodriver instance", port); 126 | 127 | // the output is unfortunately always printed 128 | // https://users.rust-lang.org/t/cargo-test-printing-println-output-from-child-threads/11627 129 | // https://github.com/rust-lang/rust/issues/35136 130 | let geckodriver = process::Command::new("geckodriver") 131 | .args(&["--port", &port.to_string(), "--log", "fatal"]) 132 | .stdout(std::process::Stdio::null()) 133 | .spawn() 134 | .expect("geckodriver can not be spawned"); 135 | 136 | std::thread::sleep(Duration::from_secs(3)); 137 | 138 | // connect to `webdriver` instance that is listening on that port 139 | let client = ClientBuilder::native() 140 | .capabilities(get_capabilities()) 141 | .connect(&format!("http://localhost:{}", port)) 142 | .await?; 143 | Ok(Self { 144 | client, 145 | geckodriver, 146 | }) 147 | } 148 | 149 | /// Closes the `client`. 150 | /// 151 | /// It would be better to have this in `Ui::Drop`, but this is not possible 152 | /// due to the async nature of the `client.close()` method. 153 | pub async fn shutdown(&mut self) -> self::Result<()> { 154 | if !closing_enabled() { 155 | log::info!( 156 | "keeping client open due to env variable `WATERFALL_CLOSE_BROWSER`" 157 | ); 158 | return Ok(()) 159 | } 160 | log::debug!("closing client"); 161 | self.client.to_owned().close().await?; 162 | log::debug!("closed client"); 163 | Ok(()) 164 | } 165 | } 166 | 167 | impl Drop for Ui { 168 | fn drop(&mut self) { 169 | if !closing_enabled() { 170 | log::info!( 171 | "keeping browser open due to env variable `WATERFALL_CLOSE_BROWSER`" 172 | ); 173 | return 174 | } 175 | // We kill the `geckodriver` instance here and not in `Ui::shutdown()`. 176 | // The reason is that if a test fails (e.g. due to an assertion), then the test 177 | // will be interrupted and the shutdown method at the end of a test will not 178 | // be reached, but this drop will. 179 | log::debug!("killing geckodriver"); 180 | self.geckodriver 181 | .kill() 182 | .expect("unable to kill geckodriver, it probably wasn't running"); 183 | log::debug!("killed geckodriver"); 184 | } 185 | } 186 | 187 | #[derive(Clone, Debug)] 188 | pub struct Payment { 189 | /// The payment. 190 | payment: String, 191 | /// The unit of payment. 192 | unit: String, 193 | } 194 | 195 | #[derive(Debug)] 196 | pub struct Event { 197 | /// The header text returned in a status event by the UI. 198 | header: String, 199 | /// The status text returned in a status event by the UI. 200 | status: String, 201 | } 202 | 203 | #[derive(Debug)] 204 | pub struct Events { 205 | /// The events returned by the UI as a result of a RPC call or a transaction. 206 | events: Vec, 207 | } 208 | 209 | impl Events { 210 | /// Creates a new `Events` instance. 211 | pub fn new(events: Vec) -> Self { 212 | Self { events } 213 | } 214 | 215 | /// Returns `true` if the `event` is contained in these events. 216 | pub fn contains(&self, event: &str) -> bool { 217 | self.events.iter().any(|evt| { 218 | assert!( 219 | !evt.header.contains("OutOfGas") && !evt.status.contains("OutOfGas"), 220 | "Found OutOfGas!" 221 | ); 222 | evt.header.contains(event) || evt.status.contains(event) 223 | }) 224 | } 225 | } 226 | 227 | #[derive(Clone)] 228 | pub struct Call { 229 | /// Address of the contract. 230 | contract_address: String, 231 | /// Method to execute. 232 | method: String, 233 | /// Maximum gas allowed. 234 | max_gas_allowed: Option, 235 | /// Values to pass along. 236 | values: Vec<(String, String)>, 237 | /// Items to add as instantiation values. 238 | items: Vec<(String, String)>, 239 | /// The payment to send with the call. 240 | payment: Option, 241 | /// The account from which to execute the call. 242 | caller: Option, 243 | } 244 | 245 | impl Call { 246 | /// Creates a new `Call` instance. 247 | pub fn new(contract_address: &str, method: &str) -> Self { 248 | let method = method.to_string(); 249 | 250 | // the `polkadot-js` ui displays method names in camel-case 251 | #[cfg(feature = "polkadot-js-ui")] 252 | let method = method.to_case(Case::Camel).replace(": I32", ": i32"); 253 | 254 | Self { 255 | contract_address: contract_address.to_string(), 256 | method, 257 | max_gas_allowed: None, 258 | values: Vec::new(), 259 | items: Vec::new(), 260 | payment: None, 261 | caller: None, 262 | } 263 | } 264 | 265 | /// Adds an initial value. 266 | /// 267 | /// TODO: Make `val` an enum of `Boolean` and `String`. 268 | pub fn push_value(mut self, key: &str, val: &str) -> Self { 269 | // the `polkadot-js` ui displays method names in camel-case 270 | #[cfg(feature = "polkadot-js-ui")] 271 | let key = key 272 | .to_case(Case::Camel) 273 | .replace(":", ": ") 274 | .replace(": I32", ": i32"); 275 | 276 | self.values.push((key.to_string(), val.to_string())); 277 | self 278 | } 279 | 280 | /// Adds an item. 281 | pub fn add_item(mut self, key: &str, val: &str) -> Self { 282 | self.items.push((key.to_string(), val.to_string())); 283 | self 284 | } 285 | 286 | /// Sets the maximum gas allowed. 287 | #[allow(dead_code)] 288 | pub fn max_gas(mut self, max_gas: &str) -> Self { 289 | self.max_gas_allowed = Some(max_gas.to_string()); 290 | self 291 | } 292 | 293 | /// Sets the payment submitted with the call. 294 | pub fn payment(mut self, payment: &str, unit: &str) -> Self { 295 | self.payment = Some(Payment { 296 | payment: payment.to_string(), 297 | unit: unit.to_string(), 298 | }); 299 | self 300 | } 301 | 302 | /// Sets the account from which to execute the call. 303 | pub fn caller(mut self, caller: &str) -> Self { 304 | self.caller = Some(caller.to_string()); 305 | self 306 | } 307 | } 308 | 309 | #[derive(Clone)] 310 | pub struct Upload { 311 | /// Path to the contract which should be uploaded. 312 | contract_path: PathBuf, 313 | /// Values to instantiate the contract with. 314 | initial_values: Vec<(String, String)>, 315 | /// Items to add as instantiation values. 316 | items: Vec<(String, String)>, 317 | /// Maximum allowed gas. 318 | #[allow(dead_code)] 319 | max_allowed_gas: String, 320 | /// The constructor to use. If not specified the default selected one is used. 321 | constructor: Option, 322 | /// The caller to use. If not specified the default selected one is used. 323 | caller: Option, 324 | } 325 | 326 | impl Upload { 327 | /// Creates a new `Upload` instance. 328 | pub fn new(contract_path: PathBuf) -> Self { 329 | Self { 330 | contract_path, 331 | initial_values: Vec::new(), 332 | items: Vec::new(), 333 | max_allowed_gas: "5000".to_string(), 334 | constructor: None, 335 | caller: None, 336 | } 337 | } 338 | 339 | /// Adds an initial value. 340 | /// 341 | /// TODO: Make `val` an enum of `Boolean` and `String`. 342 | pub fn push_initial_value(mut self, key: &str, val: &str) -> Self { 343 | // the `polkadot-js` ui displays method names in camel-case 344 | #[cfg(feature = "polkadot-js-ui")] 345 | let key = key 346 | .to_case(Case::Camel) 347 | .replace(":", ": ") 348 | .replace(": I32", ": i32"); 349 | 350 | self.initial_values.push((key.to_string(), val.to_string())); 351 | self 352 | } 353 | 354 | /// Adds an item. 355 | pub fn add_item(mut self, key: &str, val: &str) -> Self { 356 | self.items.push((key.to_string(), val.to_string())); 357 | self 358 | } 359 | 360 | /// Sets the contract path. 361 | #[allow(dead_code)] 362 | pub fn contract_path(mut self, path: PathBuf) -> Self { 363 | self.contract_path = path; 364 | self 365 | } 366 | 367 | /// Sets the max allowed gas. 368 | #[allow(dead_code)] 369 | pub fn max_allowed_gas(mut self, max: &str) -> Self { 370 | self.max_allowed_gas = max.to_string(); 371 | self 372 | } 373 | 374 | /// Sets the constructor to use for instantiation. 375 | pub fn constructor(mut self, constructor: &str) -> Self { 376 | self.constructor = Some(constructor.to_string()); 377 | self 378 | } 379 | 380 | /// Sets the caller to use for instantiation. 381 | pub fn caller(mut self, caller: &str) -> Self { 382 | self.caller = Some(caller.to_string()); 383 | self 384 | } 385 | } 386 | 387 | /// Returns `true` if the shutdown procedure should be executed after a test run. 388 | /// This mostly involves closing the browser. 389 | /// 390 | /// Returns `false` if the environment variable `WATERFALL_CLOSE_BROWSER` is set to `false`. 391 | fn closing_enabled() -> bool { 392 | std::env::var("WATERFALL_CLOSE_BROWSER") 393 | .unwrap_or("true".to_string()) 394 | .parse() 395 | .expect("unable to parse `WATERFALL_CLOSE_BROWSER` into `bool`") 396 | } 397 | 398 | /// Returns the capabilities with which the `fantoccini::Client` is instantiated. 399 | #[cfg(feature = "headless")] 400 | fn get_capabilities() -> Map { 401 | let mut caps = Map::new(); 402 | let opts = serde_json::json!({ "args": ["--headless"] }); 403 | caps.insert("moz:firefoxOptions".to_string(), opts.clone()); 404 | caps 405 | } 406 | 407 | /// Returns the capabilities with which the `fantoccini::Client` is instantiated. 408 | #[cfg(not(feature = "headless"))] 409 | fn get_capabilities() -> Map { 410 | Map::new() 411 | } 412 | -------------------------------------------------------------------------------- /src/utils/cargo_contract.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use regex::Regex; 16 | use std::{ 17 | path::PathBuf, 18 | process::Command, 19 | }; 20 | 21 | /// Builds the contract at `manifest_path` using `cargo contract`. 22 | /// 23 | /// If successful, returns the path to the `.contract` file. 24 | pub(crate) fn build(manifest_path: &PathBuf) -> Result { 25 | let skip_build: String = 26 | std::env::var("WATERFALL_SKIP_CONTRACT_BUILD").unwrap_or(String::from("false")); 27 | if skip_build == "true" { 28 | log::info!("skipping contract build"); 29 | let mut manifest_path = manifest_path.clone(); 30 | manifest_path.pop(); 31 | 32 | // extract example name from manifest path 33 | let example_name = manifest_path 34 | .iter() 35 | .last() 36 | .expect("last must exist") 37 | .to_str() 38 | .expect("to_str must work") 39 | .replace("-", "_"); 40 | 41 | // we need to take special care of examples in sub-directories 42 | let name = match example_name.as_str() { 43 | "accumulator" => { 44 | manifest_path.pop(); 45 | "accumulator/accumulator" 46 | } 47 | "subber" => { 48 | manifest_path.pop(); 49 | "subber/subber" 50 | } 51 | "adder" => { 52 | manifest_path.pop(); 53 | "adder/adder" 54 | } 55 | "set_code_hash" => "incrementer", 56 | _ => example_name.as_str(), 57 | }; 58 | let possibly_target_dir = std::env::var("CARGO_TARGET_DIR"); 59 | let artifact_path = match possibly_target_dir { 60 | Ok(target_dir) => { 61 | let mut path = PathBuf::from(target_dir); 62 | path.push(format!("ink/{}.contract", name)); 63 | path 64 | } 65 | Err(_) => { 66 | manifest_path.push(format!("target/ink/{}.contract", name)); 67 | manifest_path.clone() 68 | } 69 | }; 70 | log::info!("using artifact path {:?}", artifact_path); 71 | return Ok(artifact_path) 72 | } 73 | 74 | assert_wasm_opt_available(); 75 | 76 | let mut dir = manifest_path.clone(); 77 | dir.pop(); // pop `Cargo.toml` from the path 78 | 79 | let output = Command::new("cargo") 80 | .arg("contract") 81 | .arg("build") 82 | .arg("--manifest-path=Cargo.toml") 83 | .current_dir(dir) 84 | // we want to receive the child's output as part of the ink-waterfall stdout 85 | .stdout(std::process::Stdio::piped()) 86 | .spawn() 87 | .map_err(|err| { 88 | format!( 89 | "ERROR while executing `cargo-contract` with {:?}: {:?}", 90 | manifest_path, err 91 | ) 92 | }) 93 | .expect("failed to execute process") 94 | .wait_with_output() 95 | .expect("failed to receive output"); 96 | 97 | if output.status.success() { 98 | let stdout = String::from_utf8(output.stdout).expect("string conversion failed"); 99 | // extract the path to the resulting `.contract` from the output 100 | let re_path = Regex::new( 101 | r"Your contract artifacts are ready. You can find them in:\n([A-Za-z0-9_\-/]+)\n" 102 | ) 103 | .expect("invalid regex"); 104 | let captures = re_path 105 | .captures(&stdout) 106 | .ok_or("regex does not match the command output") 107 | .map_err(|err| format!("{}: '{:?}'", err, stdout))?; 108 | let directory = captures.get(1).expect("no capture group found").as_str(); 109 | 110 | // extract the basename to the resulting `.contract` 111 | let re_basename = 112 | Regex::new(r"\- ([A-Za-z0-9_\-/]+).contract \(code \+ metadata\)\n") 113 | .expect("invalid regex"); 114 | let captures = re_basename 115 | .captures(&stdout) 116 | .ok_or("regex does not match the command output") 117 | .map_err(|err| format!("{}: '{:?}'", err, stdout))?; 118 | let basename = captures.get(1).expect("no capture group found").as_str(); 119 | let path = PathBuf::from(directory).join(format!("{}.contract", basename)); 120 | log::info!("path to the resulting contract bundle: {:?}", path); 121 | Ok(path) 122 | } else { 123 | let stderr = String::from_utf8(output.stderr).expect("string conversion failed"); 124 | Err(format!( 125 | "Failed with exit code: {:?} and '{:?}'", 126 | output.status.code(), 127 | stderr 128 | )) 129 | } 130 | } 131 | 132 | /// Asserts that `wasm-opt` is available. 133 | fn assert_wasm_opt_available() { 134 | assert!( 135 | which::which("wasm-opt").is_ok(), 136 | "ERROR: The `wasm-opt` binary cannot be found!\n\n\ 137 | Please check that it is installed and in your `PATH`.\n\n\ 138 | See the `cargo-contract` readme for instructions on how to install it:\n\ 139 | https://github.com/paritytech/cargo-contract." 140 | ); 141 | } 142 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub mod cargo_contract; 16 | 17 | use serde_json; 18 | use std::{ 19 | fs::File, 20 | io::BufReader, 21 | path::PathBuf, 22 | process::Command, 23 | }; 24 | 25 | /// Returns the name of the test which is currently executed. 26 | pub fn test_name() -> String { 27 | crate::TEST_NAME.with(|test_name| test_name.borrow().clone()) 28 | } 29 | 30 | /// Returns the full path to the ink! example directory for `example`. 31 | /// 32 | /// This method will first try to look the example up in `INK_EXAMPLES_PATH`. 33 | /// If not found there, it will fall back to `./integration-tests/` ++ `example`. 34 | pub fn example_path(example: &str) -> PathBuf { 35 | let examples_path = std::env::var("INK_EXAMPLES_PATH") 36 | .expect("env variable `INK_EXAMPLES_PATH` must be set"); 37 | let mut path = PathBuf::from(examples_path).join(example); 38 | 39 | // Check if path exists, if not assume it's a local example to `ink-waterfall`. 40 | // This is done as a fallback if the waterfall tests are run locally. 41 | // For the CI we copy all `ink-waterfall/examples/` to the `INK_EXAMPLES_PATH`. 42 | if !path.exists() { 43 | path = PathBuf::from("./integration-tests/").join(example); 44 | path = path.canonicalize().unwrap_or_else(|path| { 45 | panic!("canonicalizing {:?} must work", path); 46 | }); 47 | } 48 | 49 | path 50 | } 51 | 52 | /// Extracts the `source.hash` field from the contract bundle. 53 | pub fn extract_hash_from_contract_bundle(path: &PathBuf) -> String { 54 | let file = 55 | File::open(path).expect(&format!("Contract file at {:?} does not exist", path)); 56 | let reader = BufReader::new(file); 57 | let json: serde_json::Value = serde_json::from_reader(reader).unwrap_or_else(|err| { 58 | panic!("JSON at {:?} is not well-formatted: {:?}", path, err) 59 | }); 60 | json.get("source") 61 | .expect("Unable to get 'source' field from contract JSON") 62 | .get("hash") 63 | .expect("Unable to get 'hash' field from contract JSON") 64 | .to_string() 65 | .trim_matches('"') 66 | .to_string() 67 | } 68 | 69 | /// Asserts that some process is listening at the [`node_port`]. 70 | pub fn assert_node_running() { 71 | let url = format!("127.0.0.1:{}", node_port()); 72 | std::net::TcpStream::connect(&url).unwrap_or_else(|_| { 73 | panic!( 74 | "No process listening on {}. Did you start the substrate-contracts-node?", 75 | url 76 | ) 77 | }); 78 | } 79 | 80 | /// Returns the port under which the node is running. 81 | pub fn node_port() -> String { 82 | std::env::var("NODE_PORT").unwrap_or(String::from("9944")) 83 | } 84 | 85 | /// Returns true if the `substrate-contracts-node` log under 86 | /// `/tmp/substrate-contracts-node.log` contains `msg`. 87 | pub fn node_log_contains(msg: &str) -> bool { 88 | let output = Command::new("grep") 89 | .arg("-q") 90 | .arg(msg) 91 | .arg("/tmp/substrate-contracts-node.log") 92 | .spawn() 93 | .map_err(|err| format!("ERROR while executing `grep` with {:?}: {:?}", msg, err)) 94 | .expect("failed to execute process") 95 | .wait_with_output() 96 | .expect("failed to receive output"); 97 | output.status.success() 98 | } 99 | --------------------------------------------------------------------------------